feat(bitbucketdatacenter): Added support for pull request events merged, modified, and approved#2749
feat(bitbucketdatacenter): Added support for pull request events merged, modified, and approved#2749mluckam wants to merge 3 commits into
Conversation
…ent merged "pr:merged"
…ent modified "pr:modified"
…ent approved "pr:reviewer:approved"
There was a problem hiding this comment.
Code Review
This pull request adds support for Bitbucket Data Center webhook events pr:merged, pr:modified, and pr:reviewer:approved to trigger pull request pipelines. However, the reviewer identified a critical issue: mapping these events directly to the standard pull_request event type will cause Pipelines-as-Code to trigger redundant and incorrect pipeline runs (e.g., when a PR is merged, modified for metadata, or approved). The reviewer recommends removing these events from the active triggering and payload parsing logic to prevent wasting CI/CD resources.
| if provider.Valid(event, []string{"pr:from_ref_updated", "pr:opened", "pr:merged", "pr:modified", "pr:reviewer:approved"}) { | ||
| return setLoggerAndProceed(true, "", nil) | ||
| } |
There was a problem hiding this comment.
⚠️ Critical Triggering Issue
Mapping pr:merged, pr:modified, and pr:reviewer:approved directly to the standard pull_request event type will cause Pipelines-as-Code to trigger and run the full suite of pull_request PipelineRuns every time a PR is merged, modified (e.g., title/description edits), or approved. This is highly inefficient and leads to redundant pipeline runs, wasting significant CI/CD resources.
pr:merged: When a PR is merged, apushevent (repo:refs_changed) is already triggered and handled to runpushpipelines. Runningpull_requestpipelines again on a merged PR is redundant and incorrect.pr:modified: This event is triggered for metadata changes (like title/description updates). Running the entire CI suite for a title change is usually undesirable.pr:reviewer:approved: Standard Pipelines-as-Code does not trigger new pipeline runs on approval; doing so here would restart the PR pipelines every time a reviewer approves.
If these events are not intended to trigger new PipelineRuns, they should not be included in the active triggering list.
| if provider.Valid(event, []string{"pr:from_ref_updated", "pr:opened", "pr:merged", "pr:modified", "pr:reviewer:approved"}) { | |
| return setLoggerAndProceed(true, "", nil) | |
| } | |
| if provider.Valid(event, []string{"pr:from_ref_updated", "pr:opened"}) { | |
| return setLoggerAndProceed(true, "", nil) | |
| } |
There was a problem hiding this comment.
None of these events trigger a pipeline run unless they are configured to do so in the bitbucket datacenter repository as specified in the PaC documentation. The goal with this PR is to be able to trigger a build on merge without needing to create a second pipeline run (push event (repo:refs_changed)) as suggested by this comment. If the repository maintainer determines that the events merged, modified, or approved are desirable event to trigger a build, why should the maintainer be limited by the provider implementation?
There was a problem hiding this comment.
I would agree with Gemini's comment here wrt pr:merged, this would introduce confusing behaviour for users who have separate PipelineRuns configured for pull_request and push (on main). When a PR is merged to main, Bitbucket DC fires both pr:merged and repo:refs_changed. With the current mapping, both would trigger pipelines: pr:merged would run all on-event: [pull_request] PipelineRuns, and repo:refs_changed would run all on-event: [push] PipelineRuns. Users end up with multiple, possibly redundant PipelineRuns for a single merge.
This also introduces problems when the source branch is deleted after merge. When the system fetches pipeline definitions from the PR's source branch (which is the normal pull_request code path), those definitions become unreachable after a merge and delete. The system silently does nothing, users won't know why their close-event pipeline never triggered.
pr:merged should be handled in its own block and mapped to triggertype.PullRequestClosed, which is what every other provider does:
- GitHub: action == "closed" --> PullRequestClosed
- Bitbucket Cloud: pullrequest:fulfilled --> PullRequestClosed
- GitLab: action == "close" --> PullRequestClosed
- Gitea: action == "closed" --> PullRequestClosed
The PullRequestClosed path (pipelineascode.go:L66-L81) skips normal matching entirely, it doesn't create new PipelineRuns, it only cancels in-progress ones for that PR. This is the semantics for a merge (close) event followed by all other providers.
As for pr:modified, I don't fully understand the requirement of supporting it since it looks like a metadata-only change event (title, description, target branch edits). If a PR title is updated while pipelines are running and cancel-in-progress is enabled, the running PipelineRuns would be cancelled and restarted from scratch due to the new pull_request event. Could you elaborate how this event helps in your use case?
pr:reviewer:approved would also have similar cancel-in-progress behaviour, an approval would cancel running CI and restart it, which seems counterproductive.
| if provider.Valid(eventType, []string{"pr:from_ref_updated", "pr:opened", "pr:merged", "pr:modified", "pr:reviewer:approved"}) { | ||
| processedEvent.TriggerTarget = triggertype.PullRequest | ||
| processedEvent.EventType = triggertype.PullRequest.String() | ||
| } else if provider.Valid(eventType, []string{"pr:comment:added", "pr:comment:edited"}) { |
There was a problem hiding this comment.
Similarly to the detection logic, mapping these events to triggertype.PullRequest with EventType = triggertype.PullRequest.String() will cause the controller to execute the pull_request pipelines. They should be excluded from this block to prevent unintended pipeline execution.
if provider.Valid(eventType, []string{"pr:from_ref_updated", "pr:opened"}) {
processedEvent.TriggerTarget = triggertype.PullRequest
processedEvent.EventType = triggertype.PullRequest.String()
}| if !provider.Valid(event, []string{ | ||
| "pr:from_ref_updated", "pr:opened", "pr:comment:added", "pr:comment:edited", | ||
| "pr:from_ref_updated", "pr:opened", "pr:comment:added", "pr:comment:edited", "pr:merged", "pr:modified", "pr:reviewer:approved", | ||
| }) { | ||
| return nil, fmt.Errorf("event \"%s\" is not supported", event) | ||
| } |
There was a problem hiding this comment.
Since these events should not trigger pipeline runs, they should not be registered as supported pull request payload types here.
| if !provider.Valid(event, []string{ | |
| "pr:from_ref_updated", "pr:opened", "pr:comment:added", "pr:comment:edited", | |
| "pr:from_ref_updated", "pr:opened", "pr:comment:added", "pr:comment:edited", "pr:merged", "pr:modified", "pr:reviewer:approved", | |
| }) { | |
| return nil, fmt.Errorf("event \"%s\" is not supported", event) | |
| } | |
| if !provider.Valid(event, []string{ | |
| "pr:from_ref_updated", "pr:opened", "pr:comment:added", "pr:comment:edited", | |
| }) { | |
| return nil, fmt.Errorf("event \"%s\" is not supported", event) | |
| } |
theakshaypant
left a comment
There was a problem hiding this comment.
Thanks for the contribution @mluckam! Adding pr:merged support is a good idea and fills a real gap for Bitbucket Data Center (it's currently the only provider without a PullRequestClosed mapping). However, the PR has a few questions that need to be addressed before it can be merged.
The core problem is that all three events (pr:merged, pr:modified, pr:reviewer:approved) are mapped to triggertype.PullRequest with EventType = "pull_request". This doesn't match how any other provider handles merge/close events, and it introduces confusing and potentially destructive behaviour, especially around cancel-in-progress.
I would also request @tektoncd/pipelines-as-code-maintainers to weigh in.
| if provider.Valid(event, []string{"pr:from_ref_updated", "pr:opened", "pr:merged", "pr:modified", "pr:reviewer:approved"}) { | ||
| return setLoggerAndProceed(true, "", nil) | ||
| } |
There was a problem hiding this comment.
I would agree with Gemini's comment here wrt pr:merged, this would introduce confusing behaviour for users who have separate PipelineRuns configured for pull_request and push (on main). When a PR is merged to main, Bitbucket DC fires both pr:merged and repo:refs_changed. With the current mapping, both would trigger pipelines: pr:merged would run all on-event: [pull_request] PipelineRuns, and repo:refs_changed would run all on-event: [push] PipelineRuns. Users end up with multiple, possibly redundant PipelineRuns for a single merge.
This also introduces problems when the source branch is deleted after merge. When the system fetches pipeline definitions from the PR's source branch (which is the normal pull_request code path), those definitions become unreachable after a merge and delete. The system silently does nothing, users won't know why their close-event pipeline never triggered.
pr:merged should be handled in its own block and mapped to triggertype.PullRequestClosed, which is what every other provider does:
- GitHub: action == "closed" --> PullRequestClosed
- Bitbucket Cloud: pullrequest:fulfilled --> PullRequestClosed
- GitLab: action == "close" --> PullRequestClosed
- Gitea: action == "closed" --> PullRequestClosed
The PullRequestClosed path (pipelineascode.go:L66-L81) skips normal matching entirely, it doesn't create new PipelineRuns, it only cancels in-progress ones for that PR. This is the semantics for a merge (close) event followed by all other providers.
As for pr:modified, I don't fully understand the requirement of supporting it since it looks like a metadata-only change event (title, description, target branch edits). If a PR title is updated while pipelines are running and cancel-in-progress is enabled, the running PipelineRuns would be cancelled and restarted from scratch due to the new pull_request event. Could you elaborate how this event helps in your use case?
pr:reviewer:approved would also have similar cancel-in-progress behaviour, an approval would cancel running CI and restart it, which seems counterproductive.
| switch e := eventPayload.(type) { | ||
| case *types.PullRequestEvent: | ||
| if provider.Valid(eventType, []string{"pr:from_ref_updated", "pr:opened"}) { | ||
| if provider.Valid(eventType, []string{"pr:from_ref_updated", "pr:opened", "pr:merged", "pr:modified", "pr:reviewer:approved"}) { |
There was a problem hiding this comment.
Semantically speaking, pr:merged should be mapping to PullRequestClosed, following the Bitbucket Cloud pattern (bitbucketcloud/parse_payload.go:L134-L136).
While BitBucket DC is the only provider which does not have a PullRequestClosed mapping at the moment, this would make the provider's behaviour inconsistent with others.
| - Pull Request -> Opened | ||
| - Pull Request -> Source branch updated | ||
| - Pull Request -> Comments added | ||
| - Pull Request -> Merged |
There was a problem hiding this comment.
If pr:merged is being introduced, docs should note the dual-trigger situation: when a PR is merged, Bitbucket DC fires both pr:merged and repo:refs_changed. Users who enable both webhooks should configure their PipelineRun annotations to match only the event type they need to avoid duplicate execution.
|
we don't support PullRequestClosed in PipelineRun matching so supporting |
📝 Description of the Change
Adds support for the following pull request events for provider bitbucket datacenter:
🔗 Linked GitHub Issue
Fixes #2748
🧪 Testing Strategy
🤖 AI Assistance
AI assistance can be used for various tasks, such as code generation,
documentation, or testing.
Please indicate whether you have used AI assistance
for this PR and provide details if applicable.
✅ Submitter Checklist
fix:,feat:) matches the "Type of Change" I selected above.make testandmake lintlocally to check for and fix anyissues. For an efficient workflow, I have considered installing
pre-commit and running
pre-commit installtoautomate these checks.