Skip to content

[webhooks] add module#33

Merged
capcom6 merged 5 commits into
masterfrom
webhooks/add-support
Jun 30, 2026
Merged

[webhooks] add module#33
capcom6 merged 5 commits into
masterfrom
webhooks/add-support

Conversation

@capcom6

@capcom6 capcom6 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added Bitbucket “push” webhook endpoint (POST /webhooks/bitbucket/push) with request signature verification and automated processing of referenced tasks from commit messages.
    • Introduced configurable webhook secret and bot user email, including automated creation/updating of the predefined bot user.
    • Added project lookup by repository URL and task lookup by project + number.
  • Bug Fixes
    • Improved commit-reference parsing (keyword-qualified actions and deduplication).
  • Documentation
    • Updated Swagger documentation and added example webhook requests.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@capcom6, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 56 minutes and 21 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b91659a1-28c6-4ad3-8087-21894152b5f1

📥 Commits

Reviewing files that changed from the base of the PR and between 84e5325 and 3d16c47.

📒 Files selected for processing (18)
  • internal/commands/serve/serve.go
  • internal/config/config.go
  • internal/config/module.go
  • internal/db/migrations/20260625000000_create_bot_user.sql
  • internal/projects/repository.go
  • internal/projects/service.go
  • internal/server/docs/docs.go
  • internal/server/module.go
  • internal/server/webhooks/dto.go
  • internal/server/webhooks/handler.go
  • internal/tasks/repository.go
  • internal/tasks/service.go
  • internal/users/service.go
  • internal/webhooks/config.go
  • internal/webhooks/domain.go
  • internal/webhooks/module.go
  • internal/webhooks/service.go
  • requests.http
📝 Walkthrough

Walkthrough

Adds webhook configuration and bot-user seeding, lookup methods for projects, tasks, and users, and a Bitbucket push webhook flow that parses commits, verifies signatures, processes task references, and exposes the endpoint through server wiring, docs, and examples.

Changes

Webhook support plumbing

Layer / File(s) Summary
Config and bot user setup
internal/webhooks/config.go, internal/config/config.go, internal/config/module.go, internal/db/migrations/20260625000000_create_bot_user.sql
Adds the webhooks config type, adds it to config defaults, wires webhooks.Config into Fx, and adds a migration that inserts or deletes the bot user row.
Project, task, and user lookups
internal/projects/repository.go, internal/projects/service.go, internal/tasks/repository.go, internal/tasks/service.go, internal/users/service.go
Adds repository and service methods for project lookup by repo URL, task lookup by project and number, and user lookup by email.
Webhook parsing and processing
internal/webhooks/domain.go, internal/webhooks/module.go, internal/webhooks/service.go
Defines commit-reference parsing, webhook result counters, webhook service initialization, signature verification, commit processing, and status or mention handling.
Bitbucket webhook endpoint
internal/server/webhooks/dto.go, internal/server/webhooks/handler.go, internal/server/module.go, internal/server/docs/docs.go, requests.http
Adds the Bitbucket push DTOs, registers the webhook handler, documents the endpoint and payload schemas, and adds request examples.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • dudina-ma

Possibly related PRs

  • bit-issues/backend#2: Adds the users module and database support that the new webhook service uses to resolve the bot user by email.
  • bit-issues/backend#3: Adds the projects module that the webhook service extends with lookup-by-repository-URL behavior.
  • bit-issues/backend#6: Adds the tasks module that the webhook service extends with lookup-by-project-and-number behavior.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title matches a real part of the change: it adds the new webhooks module and related wiring.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot requested a review from dudina-ma June 25, 2026 00:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
internal/db/migrations/20260625000000_create_bot_user.sql (1)

20-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

ON DUPLICATE KEY UPDATE omits updated_at.

On re-runs the row's name/role/status are refreshed but updated_at stays stale, so it won't reflect the seed change. Minor, but easy to include.

♻️ Optionally refresh updated_at
     ) ON DUPLICATE KEY
 UPDATE name =
 VALUES(name),
     role =
 VALUES(role),
     status =
-VALUES(status);
+VALUES(status),
+    updated_at = NOW();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/db/migrations/20260625000000_create_bot_user.sql` around lines 20 -
26, The `ON DUPLICATE KEY UPDATE` clause in the bot user seed migration only
refreshes `name`, `role`, and `status`, leaving `updated_at` stale on re-runs.
Update the duplicate-key path in the migration so the existing row also gets a
fresh `updated_at` value, alongside the current fields, in the same
insert/upsert block.
internal/projects/service.go (1)

102-104: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Consider validating empty repoURL for consistency.

Service.Exists rejects an empty slug with ErrValidationFailed, but FindByRepoURL passes any value straight through. An empty repoURL would match a project whose repo_url is empty, which is likely unintended. A guard here keeps validation behavior consistent across the service.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/projects/service.go` around lines 102 - 104, Add input validation to
Service.FindByRepoURL so it rejects an empty repoURL with ErrValidationFailed
before calling s.projects.FindByRepoURL, matching the validation behavior used
by Service.Exists. Use the FindByRepoURL method in Service as the place to add
the guard, and keep the existing delegate call unchanged for non-empty values.
internal/projects/repository.go (1)

93-96: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider a functional index for the case-insensitive lookup.

WHERE LOWER(repo_url) = ? will not use a plain index on repo_url and forces a full scan as the projects table grows. If this lookup is on a webhook hot path, add a functional index (e.g. CREATE INDEX ... ON projects (LOWER(repo_url))) to keep it performant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/projects/repository.go` around lines 93 - 96, The case-insensitive
repo lookup in the repository query uses LOWER(repo_url), which won’t benefit
from a normal index and can become slow as the projects table grows. Update the
schema/migration for the projects table to add a functional index on
LOWER(repo_url), and ensure the lookup in the repository query path that uses
NewSelect().Model(&model).Where("LOWER(repo_url) = ?", strings.ToLower(repoURL))
can use that index. Keep the existing query shape, but make the indexed
expression match the filter exactly.
internal/webhooks/domain.go (1)

44-61: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Hoist keyword registry to package scope.

keywordActions and the verb consts are reconstructed on every ParseCommitMessage call. Since they are constant, declaring them once at package level avoids per-call allocation and keeps the function focused on parsing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/webhooks/domain.go` around lines 44 - 61, Hoist the keyword registry
and verb constants out of ParseCommitMessage-related scope into package-level
declarations in domain.go, so they are initialized once instead of rebuilt per
call. Keep the existing symbol names like keywordActions, verbResolved,
verbClosed, and verbOnHold, and update the parser code to reference these
package-scope values without changing the matching behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/db/migrations/20260625000000_create_bot_user.sql`:
- Around line 20-26: The `ON DUPLICATE KEY UPDATE` clause in the bot user seed
migration only refreshes `name`, `role`, and `status`, leaving `updated_at`
stale on re-runs. Update the duplicate-key path in the migration so the existing
row also gets a fresh `updated_at` value, alongside the current fields, in the
same insert/upsert block.

In `@internal/projects/repository.go`:
- Around line 93-96: The case-insensitive repo lookup in the repository query
uses LOWER(repo_url), which won’t benefit from a normal index and can become
slow as the projects table grows. Update the schema/migration for the projects
table to add a functional index on LOWER(repo_url), and ensure the lookup in the
repository query path that uses NewSelect().Model(&model).Where("LOWER(repo_url)
= ?", strings.ToLower(repoURL)) can use that index. Keep the existing query
shape, but make the indexed expression match the filter exactly.

In `@internal/projects/service.go`:
- Around line 102-104: Add input validation to Service.FindByRepoURL so it
rejects an empty repoURL with ErrValidationFailed before calling
s.projects.FindByRepoURL, matching the validation behavior used by
Service.Exists. Use the FindByRepoURL method in Service as the place to add the
guard, and keep the existing delegate call unchanged for non-empty values.

In `@internal/webhooks/domain.go`:
- Around line 44-61: Hoist the keyword registry and verb constants out of
ParseCommitMessage-related scope into package-level declarations in domain.go,
so they are initialized once instead of rebuilt per call. Keep the existing
symbol names like keywordActions, verbResolved, verbClosed, and verbOnHold, and
update the parser code to reference these package-scope values without changing
the matching behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 85869105-80ee-4f27-9e95-672ead3efb84

📥 Commits

Reviewing files that changed from the base of the PR and between a1d0b04 and 07f1117.

📒 Files selected for processing (9)
  • internal/config/config.go
  • internal/config/module.go
  • internal/db/migrations/20260625000000_create_bot_user.sql
  • internal/projects/repository.go
  • internal/projects/service.go
  • internal/tasks/repository.go
  • internal/tasks/service.go
  • internal/webhooks/config.go
  • internal/webhooks/domain.go

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

🤖 Pull request artifacts

Platform File
🐳 Docker GitHub Container Registry
🍎 Darwin arm64 backend_Darwin_arm64.tar.gz
🍎 Darwin x86_64 backend_Darwin_x86_64.tar.gz
🐧 Linux arm64 backend_Linux_arm64.tar.gz
🐧 Linux i386 backend_Linux_i386.tar.gz
🐧 Linux x86_64 backend_Linux_x86_64.tar.gz
🪟 Windows arm64 backend_Windows_arm64.zip
🪟 Windows i386 backend_Windows_i386.zip
🪟 Windows x86_64 backend_Windows_x86_64.zip

@coderabbitai coderabbitai Bot added the codex label Jun 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/webhooks/domain.go`:
- Around line 160-164: The firstLine helper currently uses strings.Index with
slicing, which triggers the stringscut lint. Update firstLine in domain.go to
use strings.Cut to split msg at the first newline, then return the trimmed
prefix from that split; keep the existing trimming behavior and preserve the
same function signature.

In `@internal/webhooks/service.go`:
- Around line 95-101: The webhook handler in the FindByRepoURL path is treating
every lookup error as a missing project, which can incorrectly return success
for transient failures. Update the error handling around
s.projectsSvc.FindByRepoURL in the service method so only a genuine not-found
case is logged as “no project found” and allows the current 202 behavior; for
any other error, log it as a failure and return an error/failed response so the
webhook is retried.
- Around line 240-244: The shortHash helper uses a hardcoded truncation length
that triggers the mnd lint and hides intent. Replace the literal length in
shortHash with a named constant defined near the helper in service.go, and use
that constant for both the length check and slice so the 7-character truncation
is explicit and easy to maintain.
- Around line 60-63: The VerifyPushEvent method in Service currently fails open
by returning nil when s.secret is empty, which allows unauthenticated webhook
writes if configuration is missing. Update VerifyPushEvent to reject requests
when the secret is unset by returning an error instead of success, and ensure
the webhook handling path that calls VerifyPushEvent treats this as a hard
failure rather than proceeding with task status updates or comment creation.
- Line 184: The partial TaskUpdate literal in tasks.TaskUpdate initialization
triggers exhaustruct, so update the TaskUpdate construction in the webhook
service to initialize every field explicitly. Use the TaskUpdate type in the
webhook handling path and fill in all remaining fields with their appropriate
zero values or data from the webhook payload, while keeping Status set from
ref.Action.Status.
- Around line 65-80: Update the signature verification path in the webhooks
service so all bad-signature branches return errors wrapped with
ErrInvalidSignature instead of plain fmt.Errorf values. In the signature check
logic that handles the sha256 prefix, hex decoding, and hmac.Equal comparison,
wrap each invalid signature failure with ErrInvalidSignature so handler.go’s
errors.Is(err, webhooks.ErrInvalidSignature) matches and maps these cases to
401.
- Around line 93-101: Normalize the repository URL used in the webhook project
lookup so it matches the stored Bitbucket repo URL format. In the `Handle` flow
where `repoURL` is built before calling `projectsSvc.FindByRepoURL`, remove the
trailing slash and ensure the URL ends with `.git` before querying. Use the
existing `repoFullName` construction point to apply the normalization so valid
Bitbucket webhooks resolve correctly.

In `@requests.http`:
- Around line 421-423: The Bitbucket webhook examples are missing the required
signature header, so they don’t reflect how `webhooks/bitbucket/push` is
actually verified. Update the request examples in `requests.http` to include a
placeholder `X-Hub-Signature-256` header alongside the existing content type,
and apply the same change to the other Bitbucket webhook example block
referenced in the comment so both examples show signed requests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 86381d33-b688-4791-9c92-206e3995175b

📥 Commits

Reviewing files that changed from the base of the PR and between 1c90b8f and 84e5325.

📒 Files selected for processing (10)
  • internal/commands/serve/serve.go
  • internal/server/docs/docs.go
  • internal/server/module.go
  • internal/server/webhooks/dto.go
  • internal/server/webhooks/handler.go
  • internal/users/service.go
  • internal/webhooks/domain.go
  • internal/webhooks/module.go
  • internal/webhooks/service.go
  • requests.http
✅ Files skipped from review due to trivial changes (2)
  • internal/server/webhooks/dto.go
  • internal/server/docs/docs.go

Comment thread internal/webhooks/domain.go Outdated
Comment thread internal/webhooks/service.go
Comment thread internal/webhooks/service.go
Comment thread internal/webhooks/service.go Outdated
Comment thread internal/webhooks/service.go Outdated
Comment thread internal/webhooks/service.go Outdated
Comment thread internal/webhooks/service.go
Comment thread requests.http
@capcom6 capcom6 force-pushed the webhooks/add-support branch from 70bc9d0 to 3d16c47 Compare June 26, 2026 10:10
@capcom6 capcom6 changed the title [webhooks] steps 1-5 [webhooks] add module Jun 26, 2026
@capcom6 capcom6 marked this pull request as ready for review June 26, 2026 23:58
@capcom6 capcom6 added ready PR is ready to merge deployed PR is deployed to staging and removed codex labels Jun 27, 2026
@capcom6 capcom6 merged commit 66e8a95 into master Jun 30, 2026
7 checks passed
@capcom6 capcom6 deleted the webhooks/add-support branch June 30, 2026 01:14
@coderabbitai coderabbitai Bot mentioned this pull request Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deployed PR is deployed to staging ready PR is ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant