Skip to content

Run it in Bitbucket Pipelines

Goal: every push to your default branch keeps a release pull request up to date, and merging it tags the commit that landed.

Bitbucket Cloud has no release object, so on Bitbucket the tag is the release. publish pushes it and says so, and there is no tag pipeline to write, because there is nothing for release to create and nothing for announce to read. Forge support says what else Bitbucket can and cannot do.

The pipeline below is the one TestReleasingInBitbucketPipelines in test/integration committed and ran on mattcockayne/colophon-e2e on 2026-11-02, installing colophon v0.19.2 in the golang:1.27 image. The test switched Pipelines on, let the pipeline propose and then tag, and only merged the pull request itself, as a person would. It ran the pipeline on a test branch rather than main; nothing else differs.

1. Switch Pipelines on

In the repository, open Repository settings, then Pipelines, then Settings, and enable Pipelines.

2. Provide a token

colophon signs in to Bitbucket with an Atlassian API token and the account's email address. Make the token with Create API token with scopes, choose the Bitbucket app, and give it:

  • read:repository and write:repository, to push the release branch and the tag;
  • read:pullrequest and write:pullrequest, to open, update and comment on the release pull request.

A token made with the plain Create API token signs in and is then refused on every call, which reads as a bad token.

Those are the scopes colophon's calls use. The run above gave the pipeline the integration suite's own token, which carries more, so this is not a measured minimum the way the Gitea and Forgejo scopes are. The suite's token also carried write:pipeline and admin:pipeline to switch Pipelines on and set the variables through the API; doing that by hand in the settings needs neither.

Add two repository variables under Repository settings, then Pipelines, then Repository variables, both marked Secured:

  • COLOPHON_TOKEN, the API token;
  • COLOPHON_USER, the email address of the account that made it.

colophon reads BITBUCKET_USERNAME, BITBUCKET_TOKEN and BITBUCKET_APP_PASSWORD, but Bitbucket reserves the BITBUCKET_ prefix for its own variables, so the pipeline sets them from yours.

3. Add the pipeline

# bitbucket-pipelines.yml
image: golang:1.27

clone:
  depth: full

pipelines:
  branches:
    main:
      - step:
          name: publish
          script:
            - export BITBUCKET_USERNAME="$COLOPHON_USER" BITBUCKET_TOKEN="$COLOPHON_TOKEN" BITBUCKET_APP_PASSWORD="$COLOPHON_TOKEN"
            - go install gitlab.com/phpboyscout/colophon/cmd/[email protected]
            - colophon --ci publish --target "$BITBUCKET_BRANCH" --author-name colophon --author-email [email protected]
      - step:
          name: propose
          script:
            - export BITBUCKET_USERNAME="$COLOPHON_USER" BITBUCKET_TOKEN="$COLOPHON_TOKEN" BITBUCKET_APP_PASSWORD="$COLOPHON_TOKEN"
            - go install gitlab.com/phpboyscout/colophon/cmd/[email protected]
            - colophon --ci propose --target "$BITBUCKET_BRANCH" --author-name colophon --author-email [email protected]

Why each part is there:

  • clone: depth: full. Pipelines clones 50 commits deep by default, and colophon refuses a shallow checkout, because the version decision needs the history the clone left out.
  • Two steps, propose after publish. When a release pull request has just merged, publish tags it. propose must then see that tag, or it tries to propose the version that has just shipped and stops. Each step clones afresh, so the second carries the tag the first pushed. In one step, in one checkout, it does not.
  • A pinned colophon version. colophon decides what ships, so @latest would let that decision change without a commit saying so.
  • --ci. Marks the run as unattended. Bitbucket has no labels, so there is no label prompt to suppress here anyway.

4. Merge the release pull request

Merge it however the repository merges. colophon resolves the commit that landed against the target branch, rather than trusting the pull request's head.

If it fails

symptom cause
this checkout is shallow (depth-limited clone) clone: depth: full is missing
that version has already been released … propose must run AFTER publish publish and propose run in the same step; give propose a step of its own
bitbucket API returned HTTP 401: the credential is not valid COLOPHON_USER is not the account's email address, or the token has no Bitbucket scopes
Your credentials lack one or more required privilege scopes on the push the token lacks write:repository