diff --git a/go.mod b/go.mod index e245cb6..b755f56 100644 --- a/go.mod +++ b/go.mod @@ -4,21 +4,14 @@ go 1.19 require ( github.com/MakeNowJust/heredoc v1.0.0 - github.com/cli/go-gh v0.1.1-0.20220913125123-04019861008e github.com/spf13/cobra v1.5.0 ) require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.7.5 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) -require ( - github.com/gorilla/websocket v1.5.0 - github.com/kr/text v0.2.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require github.com/gorilla/websocket v1.5.0 replace golang.org/x/crypto => github.com/cli/crypto v0.0.0-20210929142629-6be313f59b03 diff --git a/go.sum b/go.sum index c9b6207..03749af 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,14 @@ github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/cli/go-gh v0.1.1-0.20220913125123-04019861008e h1:zK2hqxSk5D/Jt4o+0NVH/qdEFh7fUhgGkhbukwPMzQU= -github.com/cli/go-gh v0.1.1-0.20220913125123-04019861008e/go.mod h1:UKRuMl3ZaitTvO4LPWj5bVw7QwZHnLu0S0lI9WWbdpc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q= -github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/webhook/forward.go b/webhook/forward.go index 88d660d..f259e9e 100644 --- a/webhook/forward.go +++ b/webhook/forward.go @@ -8,41 +8,40 @@ import ( "log" "net/http" "os" + "os/exec" "strings" "time" "github.com/MakeNowJust/heredoc" - "github.com/cli/go-gh/pkg/auth" "github.com/gorilla/websocket" "github.com/spf13/cobra" ) const ( - gitHubAPIProdURL = "api.github.com" webhookForwarderProdURL = "wss://webhook-forwarder.github.com" ) type hookOptions struct { Out io.Writer - GitHubHost string + ErrOut io.Writer WebhookForwarder string EventTypes []string Repo string Org string - URL string Secret string } // NewCmdForward returns a forward command. func NewCmdForward(runF func(*hookOptions) error) *cobra.Command { + var githubHostname string + var localURL string opts := &hookOptions{ - Out: os.Stdout, + Out: os.Stdout, + ErrOut: os.Stderr, } cmd := &cobra.Command{ - Use: "forward --events= --repo|org= [--url=\"\"] [--github-host=]", - Short: "Receive test events on a server running locally", - Long: heredoc.Doc(`To output event payloads to stdout instead of sending to a server, - omit the --url flag.`), + Use: "forward --events= [--url=]", + Short: "Receive test events locally", Example: heredoc.Doc(` # create a dev webhook for the 'issue_open' event in the monalisa/smile repo in GitHub running locally, and # forward payloads for the triggered event to http://localhost:9999/webhooks @@ -51,30 +50,25 @@ func NewCmdForward(runF func(*hookOptions) error) *cobra.Command { $ gh webhook forward --events=issues --org=github --url="http://localhost:9999/webhooks" `), RunE: func(*cobra.Command, []string) error { - if opts.EventTypes == nil { - return errors.New("`--events` flag required") - } if opts.Repo == "" && opts.Org == "" { return errors.New("`--repo` or `--org` flag required") } - if opts.GitHubHost == "" { - opts.GitHubHost = gitHubAPIProdURL - } - if opts.URL == "" { - fmt.Fprintf(opts.Out, "No --url specified, printing webhook payloads to stdout.\n") + if localURL == "" { + fmt.Fprintln(opts.ErrOut, "note: no `--url` specified; printing webhook payloads to stdout") } if runF != nil { return runF(opts) } - token, _ := auth.TokenForHost(opts.GitHubHost) - if token == "" { - return fmt.Errorf("you must be authenticated to run this command") + token, err := authTokenForHost(githubHostname) + if err != nil { + return fmt.Errorf("fatal: error fetching gh token: %w", err) + } else if token == "" { + return errors.New("fatal: you must be authenticated with gh to run this command") } - var err error if opts.WebhookForwarder == "" { opts.WebhookForwarder = webhookForwarderProdURL } @@ -86,7 +80,7 @@ func NewCmdForward(runF func(*hookOptions) error) *cobra.Command { Secret: opts.Secret, } for i := 0; i < 3; i++ { - if err = runFwd(opts.Out, opts.URL, token, wsURL, chp); err != nil { + if err = runFwd(opts.Out, localURL, token, wsURL, chp); err != nil { if websocket.IsCloseError(err, websocket.CloseNormalClosure) { return nil } @@ -95,12 +89,13 @@ func NewCmdForward(runF func(*hookOptions) error) *cobra.Command { return err }, } - cmd.Flags().StringSliceVarP(&opts.EventTypes, "events", "E", []string{}, "(required) Names of the event types to forward. Event types can be separated by commas (e.g. `issues,pull_request`) or passed as multiple arguments (e.g. `--events issues --events pull_request`). Use `*` to forward all events.") + cmd.Flags().StringSliceVarP(&opts.EventTypes, "events", "E", nil, "Names of the event `types` to forward. Use `*` to forward all events.") + cmd.MarkFlagRequired("events") cmd.Flags().StringVarP(&opts.Repo, "repo", "R", "", "Name of the repo where the webhook is installed") - cmd.Flags().StringVarP(&opts.GitHubHost, "github-host", "H", "", "(optional) Host for the GitHub API, default: api.github.com") - cmd.Flags().StringVarP(&opts.URL, "url", "U", "", "(optional) Local address where the server which will receive webhooks is running") + cmd.Flags().StringVarP(&githubHostname, "github-host", "H", "github.com", "GitHub host name") + cmd.Flags().StringVarP(&localURL, "url", "U", "", "Address of the local server to receive events. If omitted, events will be printed to stdout.") cmd.Flags().StringVarP(&opts.Org, "org", "O", "", "Name of the org where the webhook is installed") - cmd.Flags().StringVarP(&opts.Secret, "secret", "S", "", "(optional) webhook secret for incoming events") + cmd.Flags().StringVarP(&opts.Secret, "secret", "S", "", "Webhook secret for incoming events") return cmd } @@ -237,3 +232,20 @@ func forwardEvent(url string, ev wsEventReceived) (*httpEventForward, error) { Body: body, }, nil } + +func authTokenForHost(host string) (string, error) { + ghExe := os.Getenv("GH_PATH") + if ghExe == "" { + var err error + ghExe, err = exec.LookPath("gh") + if err != nil { + return "", err + } + } + cmd := exec.Command(ghExe, "auth", "token", "--secure-storage", "--hostname", host) + result, err := cmd.Output() + if err != nil { + return "", err + } + return strings.TrimSpace(string(result)), nil +}