Skip to content

Commit 703b061

Browse files
authored
feat: Enable event forwarding in CI (#48)
* feat: add CI and APIKey flags to listen command * feat: Support CI client in listen command * feat: Add `hookdeck ci` command * feat: Read HOOKDECK_API_KEY env variable * feat: Validate api_key in ci command * feat: Remove CI flag from listen command * feat: Add CI name flag * refactor: Improve error handling check * fix: Remove unused CI & APIKey flag from listen command * chore: Change "account" to "workspace" to describe CI login command * docs: Add CI command instruction
1 parent 9f8e596 commit 703b061

File tree

6 files changed

+157
-0
lines changed

6 files changed

+157
-0
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,29 @@ Configure auto-completion for Hookdeck CLI. It is run on install when using Home
196196
hookdeck completion
197197
```
198198

199+
### Running in CI
200+
201+
If you want to use Hookdeck in CI for tests or any other purposes, you can use your HOOKDECK_API_KEY to authenticate and start forwarding events.
202+
203+
```sh-session
204+
$ hookdeck ci --api-key $HOOKDECK_API_KEY
205+
Done! The Hookdeck CLI is configured in workspace MyWorkspace
206+
207+
$ hookdeck listen 3000 shopify orders
208+
209+
👉 Inspect and replay webhooks: https://dashboard.hookdeck.com/cli/events
210+
211+
Shopify Source
212+
🔌 Webhook URL: https://events.hookdeck.com/e/src_DAjaFWyyZXsFdZrTOKpuHnOH
213+
214+
Connections
215+
Inventory Service forwarding to /webhooks/shopify/inventory
216+
217+
218+
⣾ Getting ready...
219+
220+
```
221+
199222
## Developing
200223

201224
Build from source by running:

pkg/cmd/ci.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package cmd
2+
3+
import (
4+
"log"
5+
"os"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/hookdeck/hookdeck-cli/pkg/login"
10+
"github.com/hookdeck/hookdeck-cli/pkg/validators"
11+
)
12+
13+
type ciCmd struct {
14+
cmd *cobra.Command
15+
apiKey string
16+
name string
17+
}
18+
19+
func newCICmd() *ciCmd {
20+
lc := &ciCmd{}
21+
22+
lc.cmd = &cobra.Command{
23+
Use: "ci",
24+
Args: validators.NoArgs,
25+
Short: "Login to your Hookdeck workspace in CI",
26+
Long: `Login to your Hookdeck workspace to forward events in CI`,
27+
RunE: lc.runCICmd,
28+
}
29+
lc.cmd.Flags().StringVar(&lc.apiKey, "api-key", os.Getenv("HOOKDECK_API_KEY"), "Your API key to use for the command")
30+
lc.cmd.Flags().StringVar(&lc.name, "name", "", "Your CI name (ex: $GITHUB_REF)")
31+
32+
return lc
33+
}
34+
35+
func (lc *ciCmd) runCICmd(cmd *cobra.Command, args []string) error {
36+
err := validators.APIKey(lc.apiKey)
37+
if err != nil {
38+
log.Fatal(err)
39+
}
40+
return login.CILogin(&Config, lc.apiKey, lc.name)
41+
}

pkg/cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ func init() {
104104

105105
rootCmd.Flags().BoolP("version", "v", false, "Get the version of the Hookdeck CLI")
106106

107+
rootCmd.AddCommand(newCICmd().cmd)
107108
rootCmd.AddCommand(newLoginCmd().cmd)
108109
rootCmd.AddCommand(newLogoutCmd().cmd)
109110
rootCmd.AddCommand(newListenCmd().cmd)

pkg/hookdeck/ci.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package hookdeck
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
)
9+
10+
type CIClient struct {
11+
Claimed bool `json:"claimed"`
12+
UserID string `json:"user_id"`
13+
UserName string `json:"user_name"`
14+
TeamID string `json:"team_id"`
15+
TeamName string `json:"team_name"`
16+
TeamMode string `json:"team_mode"`
17+
APIKey string `json:"key"`
18+
ClientID string `json:"client_id"`
19+
}
20+
21+
type CreateCIClientInput struct {
22+
DeviceName string `json:"device_name"`
23+
}
24+
25+
func (c *Client) CreateCIClient(input CreateCIClientInput) (CIClient, error) {
26+
input_bytes, err := json.Marshal(input)
27+
if err != nil {
28+
return CIClient{}, err
29+
}
30+
res, err := c.Post(context.Background(), "/cli-auth/ci", input_bytes, nil)
31+
if err != nil {
32+
return CIClient{}, err
33+
}
34+
if res.StatusCode != http.StatusOK {
35+
return CIClient{}, fmt.Errorf("unexpected http status code: %d %s", res.StatusCode, err)
36+
}
37+
ciClient := CIClient{}
38+
postprocessJsonResponse(res, &ciClient)
39+
return ciClient, nil
40+
}

pkg/login/client_login.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"io"
88
"io/ioutil"
9+
"log"
910
"net/http"
1011
"net/url"
1112
"os"
@@ -155,6 +156,49 @@ func GuestLogin(config *config.Config) (string, error) {
155156
return guest_user.Url, nil
156157
}
157158

159+
func CILogin(config *config.Config, apiKey string, name string) error {
160+
parsedBaseURL, err := url.Parse(config.APIBaseURL)
161+
if err != nil {
162+
return err
163+
}
164+
165+
client := &hookdeck.Client{
166+
BaseURL: parsedBaseURL,
167+
APIKey: apiKey,
168+
}
169+
170+
deviceName := name
171+
if deviceName == "" {
172+
deviceName = config.Profile.DeviceName
173+
}
174+
response, err := client.CreateCIClient(hookdeck.CreateCIClientInput{
175+
DeviceName: deviceName,
176+
})
177+
if err != nil {
178+
return err
179+
}
180+
181+
if err := validators.APIKey(response.APIKey); err != nil {
182+
return err
183+
}
184+
185+
config.Profile.APIKey = response.APIKey
186+
config.Profile.ClientID = response.ClientID
187+
config.Profile.DisplayName = response.UserName
188+
config.Profile.TeamName = response.TeamName
189+
config.Profile.TeamMode = response.TeamMode
190+
config.Profile.TeamID = response.TeamID
191+
192+
if err := config.Profile.CreateProfile(); err != nil {
193+
return err
194+
}
195+
196+
message := SuccessMessage(response.UserName, response.TeamName, response.TeamMode == "console")
197+
log.Println(message)
198+
199+
return nil
200+
}
201+
158202
func getLinks(baseURL string, deviceName string) (*Links, error) {
159203
parsedBaseURL, err := url.Parse(baseURL)
160204
if err != nil {

pkg/login/login_message.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ func SuccessMessage(displayName string, teamName string, isConsole bool) string
1616
"Done! The Hookdeck CLI is configured with your console Sandbox",
1717
)
1818
}
19+
20+
if displayName == "" {
21+
return fmt.Sprintf(
22+
"Done! The Hookdeck CLI is configured in workspace %s\n",
23+
color.Bold(teamName),
24+
)
25+
}
26+
1927
return fmt.Sprintf(
2028
"Done! The Hookdeck CLI is configured for %s in workspace %s\n",
2129
color.Bold(displayName),

0 commit comments

Comments
 (0)