Skip to content

Commit

Permalink
add support for org replaying
Browse files Browse the repository at this point in the history
Signed-off-by: Chmouel Boudjnah <[email protected]>
  • Loading branch information
chmouel committed Dec 20, 2023
1 parent 9b0a944 commit 9a772f0
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 20 deletions.
31 changes: 22 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ If you intend to use <https://smee.io>, you might want to generate your own smee
Once you have it, the basic usage is as follows:

```shell
gosmee client https://smee.io/aBcDeF https://localhost:8080
gosmee client https://smee.io/aBcDeF https://localhost:8080
```

This command will relay all payloads received at the smee URL to a service running on <http://localhost:8080>.
Expand Down Expand Up @@ -189,10 +189,10 @@ Here is a `proxy_pass location` to a locally running gosmee server on port local
```nginx
location / {
proxy_pass http://127.0.0.1:3333;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_read_timeout 372h;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_read_timeout 372h;
}
```

Expand All @@ -204,9 +204,13 @@ Alternatively if you don't want to use a relay server and use GitHub you can
replay the hooks deliveries via the GitHub API. Compared to the relay server
method this is more reliable and you don't have to worry about the relay server
being down. The downside is that it only works with GitHub and you need to have
a GitHub token with the `repo` scope.
a GitHub token. The scopes needed are:

It currently only supports Repository and not yet organisation hooks.
- For repository webhooks, the token must have the `read:repo_hook` or `repo` scope
- For organizations webhooks, you must have the `admin:org_hook` scope.

It currently only supports replaying webhook installed on Repositories and
Organizations but not supporting webhooks events from GitHub apps.

You will need to know the Hook ID of the webhook you want to replay, you can
get it with the `--hook-id` command:
Expand All @@ -215,8 +219,17 @@ get it with the `--hook-id` command:
goplay replay --github-token=$GITHUB_TOKEN --list-hooks org/repo
```

This will list all the hooks for the repository and their ID. When you grab the
appropriate you can start to listen to the events and replay them on a local
This will list all the hooks for the repository and their ID.

If you want to list the hooks on an organization you can just specify the org
name with no slash (same goes for the rest of this documentation, it behaves the
same between org and repo):

```shell
goplay replay --github-token=$GITHUB_TOKEN --list-hooks org
```

When you grab the appropriate you can start to listen to the events and replay them on a local
server:

```shell
Expand Down
11 changes: 7 additions & 4 deletions gosmee/hook_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import (
)

func (r *replayOpts) listHooks(ctx context.Context) error {
hooks, _, err := r.client.Repositories.ListHooks(ctx, r.org, r.repo, nil)
if err != nil {
var hooks []*github.Hook
var err error
if hooks, _, err = r.ghop.ListHooks(ctx, r.org, r.repo, nil); err != nil {
return fmt.Errorf("cannot list hooks: %w", err)
}

Expand All @@ -30,8 +31,10 @@ func (r *replayOpts) listHooks(ctx context.Context) error {
}

func (r *replayOpts) listDeliveries(ctx context.Context, hookID int64) error {
deliveries, _, err := r.client.Repositories.ListHookDeliveries(ctx, r.org, r.repo, hookID, &github.ListCursorOptions{PerPage: 50})
if err != nil {
var deliveries []*github.HookDelivery
var err error
opt := &github.ListCursorOptions{PerPage: 50}
if deliveries, _, err = r.ghop.ListHookDeliveries(ctx, r.org, r.repo, hookID, opt); err != nil {
return fmt.Errorf("cannot list deliveries: %w", err)
}
fmt.Fprintf(os.Stdout, ansi.Color(fmt.Sprintf("%-12s %-12s %s\n", "ID", "Event", "Delivered At"), "cyan+b"))
Expand Down
72 changes: 72 additions & 0 deletions gosmee/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package gosmee

import (
"context"

"github.com/google/go-github/v57/github"
"golang.org/x/exp/slog"
)

type GHOp interface {
ListHooks(ctx context.Context, org, repo string, opt *github.ListOptions) ([]*github.Hook, *github.Response, error)
ListHookDeliveries(ctx context.Context, org, repo string, hookID int64, opt *github.ListCursorOptions) ([]*github.HookDelivery, *github.Response, error)
GetHookDelivery(ctx context.Context, org, repo string, hookID, deliveryID int64) (*github.HookDelivery, *github.Response, error)
Starting()
}

var _ GHOp = (*RepoOP)(nil)
var _ GHOp = (*OrgOP)(nil)

type RepoOP struct {
client *github.Client
logger *slog.Logger
org, repo string
}

func NewRepoLister(client *github.Client, logger *slog.Logger, org, repo string) *RepoOP {
return &RepoOP{client: client, logger: logger, org: repo, repo: repo}
}

func (r *RepoOP) Starting() {
r.logger.Info("watching deliveries on", "org", r.org, "repo", r.repo)
}

func (r *RepoOP) ListHooks(ctx context.Context, org, repo string, opt *github.ListOptions) ([]*github.Hook, *github.Response, error) {
return r.client.Repositories.ListHooks(ctx, org, repo, opt)
}

func (r *RepoOP) ListHookDeliveries(ctx context.Context, org, repo string, hookID int64, opt *github.ListCursorOptions) ([]*github.HookDelivery, *github.Response, error) {
return r.client.Repositories.ListHookDeliveries(ctx, org, repo, hookID, opt)
}

func (r *RepoOP) GetHookDelivery(ctx context.Context, org, repo string, hookID, deliveryID int64) (*github.HookDelivery, *github.Response, error) {
return r.client.Repositories.GetHookDelivery(ctx, org, repo, hookID, deliveryID)
}

var _ GHOp = (*RepoOP)(nil)

type OrgOP struct {
client *github.Client
logger *slog.Logger
org, repo string
}

func NewOrgLister(client *github.Client, logger *slog.Logger, org, repo string) *OrgOP {
return &OrgOP{client: client, logger: logger, org: org, repo: repo}
}

func (r *OrgOP) ListHooks(ctx context.Context, org, _ string, opt *github.ListOptions) ([]*github.Hook, *github.Response, error) {
return r.client.Organizations.ListHooks(ctx, org, opt)
}

func (r *OrgOP) ListHookDeliveries(ctx context.Context, org, _ string, hookID int64, opt *github.ListCursorOptions) ([]*github.HookDelivery, *github.Response, error) {
return r.client.Organizations.ListHookDeliveries(ctx, org, hookID, opt)
}

func (r *OrgOP) GetHookDelivery(ctx context.Context, org, _ string, hookID, deliveryID int64) (*github.HookDelivery, *github.Response, error) {
return r.client.Organizations.GetHookDelivery(ctx, org, hookID, deliveryID)
}

func (r *OrgOP) Starting() {
r.logger.Info("watching deliveries on", "org", r.org)
}
24 changes: 17 additions & 7 deletions gosmee/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type replayOpts struct {
org string
logger *slog.Logger
sinceTime time.Time
ghop GHOp
}

// chooseDeliveries reverses the deliveries slice and only show the deliveries since the last date we parsed.
Expand All @@ -47,10 +48,10 @@ func (r *replayOpts) chooseDeliveries(dlvs []*github.HookDelivery) []*github.Hoo
}

func (r *replayOpts) replayHooks(ctx context.Context, hookid int64) error {
r.logger.Info(fmt.Sprintf("starting watching deliveries for %s/%s", r.org, r.repo))
r.ghop.Starting()
for {
opt := &github.ListCursorOptions{PerPage: 100}
deliveries, _, err := r.client.Repositories.ListHookDeliveries(ctx, r.org, r.repo, hookid, opt)
deliveries, _, err := r.ghop.ListHookDeliveries(ctx, r.org, r.repo, hookid, opt)
if err != nil {
return fmt.Errorf("cannot list deliveries: %w", err)
}
Expand All @@ -63,7 +64,7 @@ func (r *replayOpts) replayHooks(ctx context.Context, hookid int64) error {
for range []int{1, 2, 3} {
var resp *github.Response
var err error
delivery, resp, err = r.client.Repositories.GetHookDelivery(ctx, r.org, r.repo, hookid, hd.GetID())
delivery, resp, err = r.ghop.GetHookDelivery(ctx, r.org, r.repo, hookid, hd.GetID())
if resp.StatusCode == http.StatusNotFound {
time.Sleep(1 * time.Second)
continue
Expand Down Expand Up @@ -141,11 +142,15 @@ func replay(c *cli.Context) error {

orgRepo := c.Args().Get(0)
if strings.Contains(orgRepo, "/") {
ropt.org = strings.Split(orgRepo, "/")[0]
ropt.repo = strings.Split(orgRepo, "/")[1]
spt := strings.Split(orgRepo, "/")
ropt.org = spt[0]
ropt.repo = spt[1]
} else {
ropt.org = orgRepo
}
if ropt.org == "" || ropt.repo == "" {
return fmt.Errorf("org and repo are required, example: org/repo")

if ropt.org == "" {
return fmt.Errorf("at least an org is required or an org/repo")
}

if c.IsSet("list-hooks") {
Expand Down Expand Up @@ -205,6 +210,11 @@ func replay(c *cli.Context) error {
}
ropt.sinceTime = since
}
if ropt.repo == "" {
ropt.ghop = NewOrgLister(client, logger, ropt.org, ropt.repo)
} else {
ropt.ghop = NewRepoLister(client, logger, ropt.org, ropt.repo)
}

ropt.replayDataOpts = &replayDataOpts{
targetURL: targetURL,
Expand Down

0 comments on commit 9a772f0

Please sign in to comment.