Skip to content

Conversation

@tarikflz
Copy link

General Checklist

  • Affected Issues have been mentioned in the Closing issues section
  • Documentation has been written/updated
  • PR title is ready for inclusion in changelog

Database Migrations

  • This PR does not contain a database migration

Description

This PR improves Lagoon's Bitbucket webhook handler to support Bitbucket Server and Data Center instances, which do not include the repository.links.html.href or repository.full_name fields in webhook payloads.

Instead, Bitbucket Server provides clone URLs under repository.links.clone, including SSH and HTTP(S) links. This update:

  • Extracts the SSH URL from clone if available and uses it directly.
  • Falls back to the current behavior (html.href + full_name) for Bitbucket Cloud compatibility.
  • Ensures giturl generation is resilient and safe across both Bitbucket variants.

Reference

Official documentation for Bitbucket Server webhook payload format:
📄 https://confluence.atlassian.com/bitbucketserver064/event-payload-974388877.html#Eventpayload-repositoryevents

Tested with:

  • Bitbucket Server 7.x payloads
  • Bitbucket Cloud payloads
  • Lagoon webhook triggering via local DDEV setup

Closing issues

This implements: #3961

@tarikflz tarikflz force-pushed the feat/webhook-bitbucket-server branch from c2d7c05 to 1b59a7d Compare August 5, 2025 13:53
@tarikflz tarikflz force-pushed the feat/webhook-bitbucket-server branch from 59aac7a to f3db864 Compare August 12, 2025 11:05
Comment on lines +68 to +113
if (Array.isArray(cloneLinks)) {
const sshLink = cloneLinks.find(link => link.name === 'ssh');
const httpLink = cloneLinks.find(link => link.name === 'http');
if (sshLink?.href) {
giturl = sshLink.href;
} else if (httpLink?.href) {

const match = httpLink.href.match(/https?:\/\/([^\/]+)\/scm\/([^\.]+)\.git/i);
if (match) {
const domain = match[1];
const projectAndRepo = match[2];
giturl = `git@${domain}:${projectAndRepo}.git`;
} else {
throw new Error(`Could not parse project/repo from http clone link: ${httpLink.href}`);
}
}
}
// TODO: Use when single snapshot data is fixed
if (!giturl) {
let repoHref = null;

if (bodyObj.repository.links?.html?.href) {
repoHref = bodyObj.repository.links.html.href;
} else if (bodyObj.repository.links?.self?.[0]?.href) {
repoHref = bodyObj.repository.links.self[0].href;
} else {
throw new Error('Could not determine repository URL from webhook payload.');
}

const regexmatch = repoHref.match(/https?:\/\/([a-z0-9-_.]*)(:[0-9]*)?\//i);
if (!regexmatch) {
throw new Error(`Could not parse domain from repository href: ${repoHref}`);
}

const domain = regexmatch[1];

if (!bodyObj.repository.full_name) {
throw new Error('Missing repository.full_name in webhook payload.');
}

if (!regexmatch[2]) {
giturl = `git@${domain}:${bodyObj.repository.full_name}.git`;
} else {
const port = regexmatch[2];
giturl = `ssh://git@${domain}${port}/${bodyObj.repository.full_name}.git`;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

We have tests for bitbucket cloud based payloads (though these are probably a little bit out of date in some of the data)

Are you able to create tests the same way but call them stash. This would mean copying those bitbucket tests, renaming them to stash and then modifying the references like ci-bitbucket to be ci-stash and then updating the payloads with matching payload data from stash. You will need to modify the payload data to account for the local git that would be created for the tests to use, you can read the existing payload files for bitbucket to see how this is done.

The tests are a little bit difficult to understand, but the main thing would be to ensure that you've provided some example webhook payloads that would be used to verify that the payloads from Stash/BB Server will correctly provision environments.

You'd be able to run the tests locally too, but resetting after a failed test run is a bit cumbersome, its usually easier to just tear it all down and start over like this

make k3d/clean && make k3d/test TESTS=[stash]

Having these tests would ensure that both the bitbucket cloud tests still pass, and that the stash webhooks would work too.

break;

case 'bitbucket:repo:push':
case 'bitbucket:repo:refs_changed':
Copy link
Member

Choose a reason for hiding this comment

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

These may need to be split off from being prefixed with bitbucket to being prefixed with stash. Since the two systems are different, this would make it clear they are different. You'd need to update all the events in logs2notifications to deal with this, and then move this https://github.com/tarikflz/lagoon/blob/feat/webhook-bitbucket-server/services/webhook-handler/src/extractWebhookData.ts#L61 into this if block https://github.com/tarikflz/lagoon/blob/feat/webhook-bitbucket-server/services/webhook-handler/src/extractWebhookData.ts#L85 and add webhooktype = 'stash'; into the clone links block https://github.com/tarikflz/lagoon/blob/feat/webhook-bitbucket-server/services/webhook-handler/src/extractWebhookData.ts#L68

This way its made clear that an event would be stash/bitbucket server, instead of bitbucket cloud when the messages are flying around.

@shreddedbacon
Copy link
Member

May be replaced with #3979

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.

2 participants