Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/content/docs/providers/bitbucket-datacenter.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ Create a webhook on the repository following this guide:
- Pull Request -> Opened
- Pull Request -> Source branch updated
- Pull Request -> Comments added
- Pull Request -> Merged
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

- Pull Request -> Modified
- Pull Request -> Approved

### Create the Secret

Expand Down
2 changes: 1 addition & 1 deletion pkg/provider/bitbucketdatacenter/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (v *Provider) Detect(req *http.Request, payload string, logger *zap.Sugared

switch e := eventPayload.(type) {
case *types.PullRequestEvent:
if provider.Valid(event, []string{"pr:from_ref_updated", "pr:opened"}) {
if provider.Valid(event, []string{"pr:from_ref_updated", "pr:opened", "pr:merged", "pr:modified", "pr:reviewer:approved"}) {
return setLoggerAndProceed(true, "", nil)
}
Comment on lines +41 to 43
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

⚠️ 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, a push event (repo:refs_changed) is already triggered and handled to run push pipelines. Running pull_request pipelines 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.

Suggested change
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)
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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(event, []string{"pr:comment:added"}) {
Expand Down
21 changes: 21 additions & 0 deletions pkg/provider/bitbucketdatacenter/detect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ func TestProviderDetect(t *testing.T) {
isBS: true,
processReq: true,
},
{
name: "pull_request merged event",
event: types.PullRequestEvent{},
eventType: "pr:merged",
isBS: true,
processReq: true,
},
{
name: "pull_request modified event",
event: types.PullRequestEvent{},
eventType: "pr:modified",
isBS: true,
processReq: true,
},
{
name: "retest comment",
event: types.PullRequestEvent{
Expand Down Expand Up @@ -110,6 +124,13 @@ func TestProviderDetect(t *testing.T) {
isBS: true,
processReq: true,
},
{
name: "pull_request user approval event",
event: types.PullRequestEvent{},
eventType: "pr:reviewer:approved",
isBS: true,
processReq: true,
},
}

for _, tt := range tests {
Expand Down
4 changes: 2 additions & 2 deletions pkg/provider/bitbucketdatacenter/parse_payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (v *Provider) ParsePayload(_ context.Context, _ *params.Run, request *http.

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"}) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

processedEvent.TriggerTarget = triggertype.PullRequest
processedEvent.EventType = triggertype.PullRequest.String()
} else if provider.Valid(eventType, []string{"pr:comment:added", "pr:comment:edited"}) {
Comment on lines +110 to 113
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

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()
		}

Expand Down Expand Up @@ -232,7 +232,7 @@ func parsePayloadType(event string) (any, error) {
var localEvent string
if strings.HasPrefix(event, "pr:") {
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)
}
Comment on lines 234 to 238
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Since these events should not trigger pipeline runs, they should not be registered as supported pull request payload types here.

Suggested change
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)
}

Expand Down
Loading