Skip to content

Add webhook management service - #182

Open
abhinavgautam01 wants to merge 4 commits into
git-pkgs:mainfrom
abhinavgautam01:add-webhook-service
Open

abhinavgautam01 wants to merge 4 commits into
git-pkgs:mainfrom
abhinavgautam01:add-webhook-service

Conversation

@abhinavgautam01

@abhinavgautam01 abhinavgautam01 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Closes #175

Adds a WebhookService to the Forge interface so consumers can create, list, get and delete repository webhooks through the same normalized API used for the other services. Parsing incoming webhook payloads stays separate in #50.

API

type WebhookService interface {
	List(ctx context.Context, owner, repo string, opts ListWebhookOpts) ([]Webhook, error)
	Get(ctx context.Context, owner, repo string, id int64) (*Webhook, error)
	Create(ctx context.Context, owner, repo string, opts CreateWebhookOpts) (*Webhook, error)
	Delete(ctx context.Context, owner, repo string, id int64) error
}

The shape mirrors DeployKeyService, including the Limit/Page/PerPage list options and ErrNotFound on 404.

Backend support

Forge Support
GitHub Full
GitLab Full (TinyGo fallback returns ErrNotSupported)
Gitea / Forgejo Full
Bitbucket, Gerrit, Tangled ErrNotSupported

Design notes

  • Event names follow GitHub (push, issues, issue_comment, pull_request, release and so on). GitHub receives them as is. Gitea/Forgejo and GitLab translate them to and from their own events and reject names they cannot map, before any request is sent.
  • Gitea/Forgejo translate them in both directions:
Normalized Sent on create Read back from
push, create, delete, fork, release, repository, package same name same name
gollum wiki wiki
issues issues_only, issue_assign, issue_label, issue_milestone issues, issue_assign, issue_label, issue_milestone
issue_comment issue_comment, pull_request_comment issue_comment, pull_request_comment
pull_request pull_request_only, pull_request_assign, pull_request_label, pull_request_milestone, pull_request_sync, pull_request_review_request pull_request plus the same sub-events
pull_request_review pull_request_review pull_request_review, pull_request_review_approved, pull_request_review_rejected, pull_request_review_comment
  • Gitea treats issues and pull_request on create as groups that also enable their comment and review events, so the *_only forms plus explicit sub-events keep GitHub's narrower meaning. Reads fold the granular events back into normalized names in a stable order. Any reported event with no normalized name is kept as reported.
  • status, workflow_run and workflow_job are rejected on Gitea/Forgejo for now. Forgejo has no status event and only granular workflow_run_*/workflow_job_* events. Older Gitea lacks them too. Both silently ignore unknown names. They can come back once the backend can tell Gitea and Forgejo versions apart.
  • GitLab maps them onto its per-event flags:
Normalized GitLab flag
push push_events
tag_push (GitLab only) tag_push_events
issues issues_events
issue_comment note_events
pull_request merge_requests_events
release releases_events
deployment deployment_events
gollum wiki_page_events
workflow_run pipeline_events
workflow_job job_events
  • Every GitLab flag is sent explicitly because GitLab enables push events by default.
  • No events means push only, which matches each forge's own default.
  • Webhooks are created active. GitLab project hooks have no active flag, so a disable option would not map cleanly. On reads, GitLab's alert_status drives Active.
  • ContentType defaults to json. GitLab always sends JSON, so it is ignored there.
  • InsecureSSL maps to insecure_ssl on GitHub and enable_ssl_verification on GitLab. Gitea/Forgejo have no per-hook setting, so it is ignored there.

CLI

forge webhook list
forge webhook add --url https://example.com/hook -e push -e pull_request --secret s3cret
forge webhook delete 123

webhook delete parses the whole ID and rejects malformed or non-positive values such as 7oops, 7.5 and 0 without sending a request.

Tests

  • httptest coverage for GitHub, GitLab and Gitea: list, get, create, delete and not found. The create tests check the request body, including the GitLab event flags.
  • Gitea event mapping tests covering every supported event on create and read, unsupported events and the push default.
  • A command-execution test asserting that malformed webhook delete IDs send no delete request.
  • A Bitbucket not-supported test, a TinyGo fallback entry and a CLI subcommand test.
  • go test -race ./..., go test -tags=tinygo ./gitlab, the js/wasip1 WebAssembly builds and golangci-lint v2.14.0 all pass locally.

🤖 Generated with Claude Code

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address these two points:

  • In internal/cli/webhook.go:140, fmt.Sscanf accepts a numeric prefix without consuming the entire argument. Running forge webhook delete 7oops --yes against a local mock server sent DELETE /api/v1/repos/owner/repo/hooks/7, printed Deleted webhook 7, and exited successfully. Parse the entire ID, reject malformed values, and add a command-execution test asserting that invalid input sends no delete request.
  • In gitea/webhooks.go:76-92, event names pass through unchanged despite the public API promising GitHub event names. For example, gollum cannot subscribe to Gitea's wiki event, and reads return wiki without normalization. Translate names on create and read, reject unsupported events, and add tests for the mapping. Gitea documents the wiki event here: https://docs.gitea.com/usage/repository/webhooks/

@abhinavgautam01

Copy link
Copy Markdown
Contributor Author

Thanks, both fixed.

  • webhook delete now parses the full ID with strconv.ParseInt and rejects malformed and non-positive values. A new test runs the command with inputs like 7oops, 7.5 and 0 and asserts no delete request is sent.
  • Gitea/Forgejo now translates event names in both directions and rejects names it cannot map (gollum ↔ wiki, for example). Since Gitea's issues and pull_request on create also enable their comment and review sub-events, create sends issues_only/pull_request_only plus the specific sub-events to keep GitHub's meaning. Reads fold the granular events back into GitHub names in a stable order. Tests cover every mapped event both ways.

# Conflicts:
#	internal/cli/pr_checkout_test.go

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both earlier points are fixed, thanks.

One more. status, workflow_run and workflow_job in gitea/webhooks.go don't reliably reach the server. Forgejo's addHook has no status event, and only has granular workflow_run_* and workflow_job_* events. Gitea added status in late 2024 and the workflow events in 1.25.0. Both servers silently ignore any event name they don't know. So forge webhook add -e workflow_run against Forgejo or an older Gitea returns 201 and creates a hook that subscribes to nothing, which contradicts the "reject names they cannot map" contract on Webhook.

Please remove those three entries from giteaWebhookEvents so toGiteaEvents rejects them before any request is sent, and add a test that covers the rejection. They can be added back once the backend can tell Gitea and Forgejo versions apart.

@abhinavgautam01

Copy link
Copy Markdown
Contributor Author

Thanks, fixed. I removed status, workflow_run and workflow_job from giteaWebhookEvents, so toGiteaEvents now rejects them before any request is sent. The table comment explains why. A new test covers the rejection both directly and through Create against a mock server that fails if a request arrives. I also checked Forgejo's addHook and every remaining name in the table is accepted by both servers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Webhook management

2 participants