Gate
Release Gate
How the Bumpkit release gate decides pass, needs approval or fail, and how to run it on GitHub, GitLab, Bitbucket and Azure DevOps.
Risky changes, often written by agents, reach production because no rule stopped them; the gate applies your policy to every change, for engineering leads and platform teams.
The release gate decides whether a change may ship under your repository's bumpkit.yml policy. It runs a release check, works out whether a coding agent wrote the change, counts human approvals, and returns one of three outcomes:
| Outcome | Meaning | GitHub check conclusion |
|---|---|---|
pass |
Nothing in the policy stops it | success |
needs_approval |
A rule needs a person's approval first | action_required |
fail |
A blocking finding or failed requirement | failure |
Run the gate
bumpkit gate
bumpkit gate --base v1.4.2 --comment
bumpkit gate --json| Flag | Meaning |
|---|---|
--base |
Version or ref the release starts from (default: the pull request's base, else the latest tag). With the GitHub App installed, outside a pull request it must be a branch or tag |
--policy |
Policy file to use instead of bumpkit.yml at the base. With the GitHub App installed, the decision uses the base branch's copy instead |
--comment |
Post or update the decision as a pull request comment |
--hotfix |
Gate as a hotfix (see Hotfixes) |
--json |
Print the decision as JSON |
The command exits 0 on pass, 1 on needs_approval or fail, and 2 when Bumpkit can't decide (or 0 when on_error: pass). In GitHub Actions it also writes the decision to the job summary. The API equivalent is POST /v1/gate; see the API Reference.
The policy comes from the base
The gate reads bumpkit.yml (or .bumpkit.yml) as it is at the base of the change, so a pull request can't loosen its own rules. A repository without one uses the default policy:
gate:
block_on: [secret, data_loss]
require_approval:
- breaking
- area: [authentication, authorization, billing, cryptography]
when: agent
- untested: true
when: agent
vulnerabilities: high
on_error: failA policy with a mistake is an error, never a silent default: the gate fails closed, even with on_error: pass. The full schema is in the bumpkit.yml Reference.
Where Bumpkit reads the file depends on whether the Bumpkit GitHub App is installed on the repository for your workspace:
| Setup | Policy the decision uses |
|---|---|
| The App is installed | Bumpkit reads bumpkit.yml from GitHub itself and ignores the policy the CLI or Action sends, including --policy and the agents: section. It reads the default branch. A pull request into the default branch uses its base commit. A pull request into any other branch, including a stacked pull request or a release branch, uses the default branch's copy. --base never picks the policy |
| No App | The CLI reads bumpkit.yml at the base from your checkout and sends it with the request |
If GitHub can't return the file, the gate fails closed with 503 POLICY_UNAVAILABLE. See Troubleshooting.
With the App installed, only the decisions Bumpkit makes on a diff it fetched itself count. A decision from bumpkit gate, the API or MCP uses a diff the client sent, so it's a report: it never changes the required check, a deployment, a vote or a release train. Approvals the client sends are dropped, and Bumpkit reads them from the pull request's GitHub reviews instead. The agent timeline marks those decisions Sent by a client, from its own diff.
The decision records where the policy came from. The POST /v1/gate response has "policy_source": "client" or "base_branch:<ref>" (for example base_branch:main), and the reasons say more:
| Reason | When |
|---|---|
policy_source: client (pass) |
No App: "The policy came from the client and wasn't verified from the base branch, because the Bumpkit GitHub App isn't installed on this repository for this workspace." |
policy_source: base_branch:main (policy read from your repository) (pass) |
The App read the policy from main and applied it. The request carried a different copy, which wasn't used |
These reasons show in the pull request comment's table and in the compliance evidence. Without the App, a pull request that edits its own workflow could send a looser policy, so install the App on every repository you gate.
How the gate decides
The gate considers only deterministic, unsuppressed findings. It applies these steps in order, and the worst outcome wins:
- Blocking findings. A high-severity finding whose kind is in
block_on(secret,data_loss,breaking,contract_breaking,version_mismatchorcompatibility_break) fails. - Vulnerabilities. With
vulnerabilitiesset tohigh,mediumorlow, avulnerable_dependencyat or above it fails. If the vulnerability database couldn't be reached, the gate needs approval. - Approval rules. Each
require_approvalrule that applies and is triggered needs approvals (see Approvals). - Hotfix scope or back-ports. A hotfix is held to
hotfixrules; any other release is checked for fixes missing from recent release tags. See Hotfixes. - Blast radius. Reaching an area in
blast_radius.approve_areasneeds approval. - Fix tasks. With Close the loop (Beta, flag
close_the_loop), each fix task still open on the branch fails the gate underfix_tasks: block(the default) or needs approval underfix_tasks: approve. - Regression evidence. With
require_regression_evidence(Beta, flagregression_evidence), the gate fails or needs approval unless every test the change's blast radius expects passed in CI-attested results for this exact commit. - Agent budgets and track records. An agent over a limit in the
agentssection (Beta, flagagent_budgets) fails or needs approval, and anagent_track_record_belowrule (Beta, flagagent_track_records) needs approval. - Incomplete check. If the diff was too large to check in full, the gate needs an approval from someone with write access or a code owner. With the GitHub App or a connection, Bumpkit also reads
bumpkit.ymland the CODEOWNERS files directly at the base and the head, so a large diff can't hide an edit to them. - Policy change. A change to
bumpkit.yml,.bumpkit.ymlor a CODEOWNERS file, including renaming or deleting it, needs one approval when the change targets the default branch, with the rulepolicy_change. The approver can't be the author and needs write access to the repository or to be a code owner of the file, as read from GitHub or GitLab. On Bitbucket and Azure DevOps, Bumpkit counts the approvals those hosts report. This is built in: a repository's policy can't turn it off, and the gate reads the policy from the default branch, so the same pull request can't remove it either. A change against another branch isn't flagged, because Bumpkit doesn't read that branch's copy; the edit is flagged when it reaches the default branch. Only those exact paths are covered: a file that one of them links to is treated like any other file. - CI evidence.
require_checksfail when a named check didn't run or didn't pass on the commit,require_check_appsalso pins each check to the GitHub App that must report it, andsecurity_alertsfail when open code scanning alerts are at or above the threshold. These need the GitHub App. Without it, a person must approve to vouch forrequire_checksandsecurity_alerts, andrequire_check_appsfails. - Denials. A deny vote from Slack, the dashboard or the CLI fails the gate.
- Windows and freezes. A release window or change freeze (Beta, flag
change_freezes) fails the gate or needs approval. - Override. A break-glass override lets a failing or waiting commit through.
Approval rules
A rule is breaking, contract_breaking, untested, intent, or a mapping:
gate:
require_approval:
- breaking
- area: [billing]
when: agent_or_unknown
approvers: ["@acme/payments", "sam"]
count: 2
- untested: true
when: agent
codeowners: true| Key | Meaning |
|---|---|
breaking |
A high-severity breaking finding, or a major version bump |
untested |
An untested finding |
area |
A risk_area finding for one of these areas |
when |
always (default), agent or agent_or_unknown |
approvers |
Only these GitHub logins or @org/team teams count |
count |
Approvals needed, 1 to 10 (default 1) |
codeowners |
The CODEOWNERS of the triggering files count as approvers |
Who wrote the change
Bumpkit tells agent-written code from human-written code with no setup. Every release check names a committer (the pull request author, then everyone who pushed commits) and a writer (a person, or the coding agent that wrote the code).
Authorship is verified when agent hooks or Agent Telemetry reported the session that made the commit, or a coding agent's bot opened the pull request. The agent hooks report each session's work as it happens, including work by subagents and in other git worktrees of the same repository, so a change counts as agent-written even when it is committed under your name with no Co-authored-by: trailer. The release check names the evidence, for example hook attestation from Claude Code session 1a2b3c4d. Keep the Bumpkit CLI current (bumpkit update) so the hooks can report. It is declared when the agent signed the change, for example with a Co-authored-by: trailer. Anything weaker is inferred. Confidence is shown only where Bumpkit has measured it on your own verified releases; otherwise you see the evidence and no percentage. Run bumpkit report authorship-coverage to see how many of your releases are verified.
Policy uses the difference:
when: agentapproval rules apply to every change that isn't confirmed as written by a person: any agent signal, inferred included, and any change with no signal at all. An agent using a developer's git identity on a neutral branch leaves no signal, so no signal isn't proof of a person.- A change counts as written by a person only when all of this holds: no agent signal on the commits, pull request, branch or vendor usage; agent hooks or Agent Telemetry are on for the repository; and at least 90% of such changes turned out to be human across at least 20 verified releases (your own, or pooled if you opted in).
when: agenthard blocks (a quality rule withaction: block) fail the gate only for declared or verified agent changes. Any other change the rule covers needs an approval instead, so an inferred signal never blocks a release on its own.when: agent_or_unknownis the same aswhen: agentfor approval rules, and stays valid.
Every change that triggers a when: agent approval rule needs an approval unless Bumpkit has proof about that commit. No agent signal isn't proof of a person, and hooks on the repository say nothing about one commit, so a change with no signal still asks for the approval. A hard block applies only to a verified or declared agent.
For verified authorship with no setup on developer machines, an admin can turn on Agent Telemetry once for the whole org. For how gate decisions and authorship serve as audit evidence, see Security and Compliance.
When Bumpkit can't decide
on_error decides what an error means. fail (the default) fails the gate; pass makes the gate advisory. Repository and teammate counts are unlimited, and an error never uses a credit on its own.
Scanner results
Bumpkit doesn't run scanners. It reads the results from the ones you already run and shows each finding next to the areas the change reaches, highest reach first. It keeps only findings on lines the change touches or in files it reaches, and stores those findings, never the file you send.
| Tool | What to send | Flag |
|---|---|---|
| CodeQL | SARIF 2.1.0, e.g. codeql database analyze --format=sarif-latest |
--sarif |
| Semgrep | SARIF 2.1.0, e.g. semgrep scan --sarif |
--sarif |
| Snyk | Snyk Code SARIF 2.1.0, e.g. snyk code test --sarif |
--sarif |
| Socket | The response of Socket's Stream full scan API, saved as is: one JSON artifact per line | --socket |
bumpkit gate --sarif codeql.sarif --sarif semgrep.sarif --socket socket.jsonbumpkit check takes the same flags.
The API takes the same files as scans on POST /v1/checks and POST /v1/gate: a list of {"format": "sarif" | "socket", "content": "<file contents>"}.
| Limit | Value |
|---|---|
| Reports per check | 5 |
| Size per report | 4 MiB |
| Findings kept per tool | 50 |
| Uploads per API key | 60 a minute |
Severity comes from the result's security-severity score when present (9 and up critical, 7 high, 4 medium, else low), otherwise from its SARIF level (error high, warning medium, note low). From Socket, Bumpkit reads each artifact's namespace, name, version and manifestFiles, and each alert's type, severity, file, start, end, action and props.cveId or props.ghsaId. It reads start and end as line numbers, uses the artifact's first manifest file when an alert has no file, and skips alerts whose action is ignore. Socket's middle reads as medium. When Bumpkit has no blast radius for the change, the finding says "Reachability not measured." Each upload is recorded in the audit log. To require a person's approval, add scanner_finding to require_approval.
GitHub
On GitHub, the Bumpkit GitHub App runs the gate, with no workflow or token to set up. To run it in your own pipeline instead, see Run it in CI.
The GitHub App
Install the Bumpkit GitHub App on a repository and the gate runs on every pull request as a check named Bumpkit release gate, with no workflow or token to set up. It runs when a pull request is opened, pushed to, reopened or marked ready for review, when a review is submitted or dismissed, for merge queue groups, when a required check or status finishes, and when someone re-runs the check.
The App reads bumpkit.yml and CODEOWNERS from the base commit of a pull request into the default branch, and from the default branch for every other pull request. When two open pull requests have the same head commit, the check takes the stricter result and names the pull request holding it. When the policy has require_checks, the check stays in progress, showing what it is waiting for, until those checks finish on the commit. The App verifies approvals from GitHub reviews, resolves @org/team approvers and CODEOWNERS, and posts findings as line annotations. The decision, with each rule's reason, is the check's summary, and its details link opens the release on the dashboard. On each automatic run, the App also posts the decision as one pull request comment from bumpkit[bot] and edits that comment on later runs instead of adding new ones. Checks you start by hand (bumpkit github check, the MCP check tool or the dashboard's run) update the check but leave the comment alone.
Make Bumpkit release gate a required status check in your branch protection to block merges on it.
The pull request comment
bumpkit gate --comment posts the decision as a pull request comment: as bumpkit[bot] when the Bumpkit GitHub App is installed on the repository, otherwise with the job's token. This comment is separate from the one the App posts on its own runs. It says it was sent by a client from its own diff, and the Bumpkit check on the commit decides whether the change can merge. It comments only in a CI job that runs for a pull request, such as a GitHub Actions pull_request workflow. Run from a laptop, it can't find the pull request and posts nothing. The comment starts with a headline for the outcome, then says what to do, the suggested version and who wrote the change, and lists each rule's reason and the findings by severity. The headline is one of:
| Outcome | Headline |
|---|---|
pass |
✅ Ready to ship |
needs_approval |
⏳ Needs a human approval from sam, priya (whose approval it needs, for example "⏳ Needs a human approval from anyone except the author (not an AI agent)") |
fail |
⛔ Blocked: followed by the first failing reason |
Bumpkit release gate is the name of the check and commit status, not part of the comment. Bumpkit edits the same comment on every run instead of adding new ones.
GitLab, Bitbucket and Azure DevOps
Connect these hosts from the dashboard's Repository page: pick GitLab, Bitbucket or Azure DevOps under Code host, and the page walks you through it. The same form is on Integrations.
You can run the gate on these hosts in two ways, and you can combine them:
- A connection. Bumpkit receives a signed webhook, runs the gate on its own and reports a status and a comment. No CI job is needed, and approvals are verified on the host.
- A CI job.
bumpkit gateruns in your pipeline, detects the host from its environment, counts approvals and comments with a token you provide. With a connection for the same repository, Bumpkit verifies those approvals itself. See Run it in CI.
Add a connection
Workspace owners and admins add a connection on the dashboard's Repository or Integrations page, or with the CLI:
bumpkit gate connections add --provider gitlab --path acme/api
bumpkit gate connections add --provider bitbucket --path acme/api
bumpkit gate connections add --provider azure_devops --host https://dev.azure.com/acme --path "Payments/api"
bumpkit gate connections add --provider ghes --host https://github.acme.example --path acme/api
bumpkit gate connections list
bumpkit gate connections remove CONNECTION_IDadd prompts for the access token (or pass --token - to read it from stdin) and stores it encrypted. It prints the webhook URL, https://bumpkit.dev/hooks/gate/{provider}/{token}, and a webhook secret that is shown only once. The same endpoints are GET, POST /v1/gate/connections and DELETE /v1/gate/connections/{id}.
| Provider | --path |
--host |
Access token |
|---|---|---|---|
gitlab |
group/project, or a group to cover all its projects |
Empty for gitlab.com, or your self-managed GitLab's https address | A project or group access token with the api scope |
bitbucket |
workspace/repository |
Empty for Bitbucket Cloud | A repository or workspace access token that can read pull requests and write build statuses |
azure_devops |
project/repository |
Your organization, like https://dev.azure.com/your-org |
A personal access token with Code (read and status) and Pull Request Threads scopes |
ghes |
owner/repository, or an owner to cover all its repositories |
Required: your server's https address, like https://github.acme.example |
A personal access token with the repo scope, from a user who can write to the repository. Add read:org if your rules name teams |
Then add the webhook on the host and paste in the URL and secret:
| Provider | Where | URL goes in | Secret goes in | Events |
|---|---|---|---|---|
| GitLab | The project or group's Settings → Webhooks → Add new webhook | URL | Secret token | Merge request events and Pipeline events |
| Bitbucket | The repository's Repository settings → Webhooks → Add webhook | URL | Secret | Pull request Created, Updated, Approved and Approval removed, plus Repository Build status created and Build status updated |
| Azure DevOps | Project settings → Service hooks → Web Hooks | URL | Basic authentication password, with any username | Pull request created and Pull request updated, one subscription each |
| GitHub Enterprise Server | The repository's Settings → Hooks → Add webhook, with Content type set to application/json |
Payload URL | Secret | Pull requests, Pull request reviews, Check runs and Statuses |
Then make the gate block merges:
| Provider | Require the gate |
|---|---|
| GitLab | Add a status check named Bumpkit release gate under Settings → Merge requests → Status checks, or turn on Pipelines must succeed and run the CI job below |
| Bitbucket | Under Repository settings → Branch restrictions, edit the target branch and add a merge check that requires at least one successful build and no failed builds. The gate posts the Bumpkit release gate build status |
| Azure DevOps | Add Bumpkit release gate as a required status check in the target branch's policy |
| GitHub Enterprise Server | Open a pull request so the status appears once, then require the Bumpkit release gate status check in the branch protection rule under Settings → Branches |
Open a pull request or merge request to try it. The connection shows Connected on the Repository and Integrations pages, and the first decision appears on the change within a minute.
Webhook deliveries are rate limited per address, and unsigned or wrongly signed deliveries are rejected.
What each host gets
| Provider | Runs on | Status | Comment | Approvals counted |
|---|---|---|---|---|
| GitLab | Merge request opened, reopened, approved, unapproved, pushed or retargeted; pipelines finishing for required checks | An external status check named Bumpkit release gate if the project has one, else a commit status with that name | A merge request note | Merge request approvals after the latest push |
| Bitbucket | Pull request created, updated, approved or unapproved, changes requested or removed; build statuses for required checks | A build status with key bumpkit-release-gate |
A pull request comment | Participant approvals after the latest push |
| Azure DevOps | Pull request created or updated while active | A pull request status bumpkit/release-gate |
A pull request thread | Reviewer votes of approved or approved with suggestions, after the latest push |
| GitHub Enterprise Server | Pull request opened, reopened, pushed, retargeted or marked ready for review; reviews submitted or dismissed; check runs and statuses finishing for required checks | A commit status named Bumpkit release gate | A pull request comment | Reviews that approve the latest commit |
On GitLab, Bitbucket, Azure DevOps and GitHub Enterprise Server, needs_approval is reported as a failed status so the merge stays blocked.
A vote in Bumpkit or Slack counts for the pull request it was cast on, not for another pull request that shares the commit. On GitHub and GitHub Enterprise Server, changing a pull request's base starts approval over: reviews and Bumpkit approvals from before the change don't count toward the new base, so someone has to approve again. A denial stays until that person changes it.
A decision made from a diff your CI sent is shown on its check page as reported by CI. It stays in the record, but it can't be voted on or overridden there, and it doesn't appear in the approvals inbox or count toward agent track records. The approvals inbox lists decisions Bumpkit made itself from a connected repository.
Your org policy and its locks apply on these hosts too. The policy and CODEOWNERS come from the default branch, at the target commit when the change targets the default branch, so a change against another branch can't choose looser rules. If the default branch or the file can't be read, the gate fails closed. When open changes share a commit, the strictest decision on that commit applies to each of them; Bumpkit lists them through the provider's API, and if the list can't be read in full, the gate fails closed. CODEOWNERS is read from CODEOWNERS, docs/CODEOWNERS or .gitlab/CODEOWNERS on GitLab, CODEOWNERS or .bitbucket/CODEOWNERS on Bitbucket, and CODEOWNERS, .azuredevops/CODEOWNERS or docs/CODEOWNERS on Azure DevOps. Approvals given before the latest push don't count, nor do the author's, bots' or coding agents'.
CODEOWNERS on GitHub Enterprise Server is read from .github/CODEOWNERS, CODEOWNERS or docs/CODEOWNERS.
GitHub Enterprise Server
Beta, early access. Flags:
github_enterprise_serverandrelease_gate.
A connection gives pull requests on your own server the same gate as github.com, with a commit status in place of the GitHub App's check run.
- Your server must be reachable. Bumpkit calls your server over https on port 443, at
/api/v3. The address must be a public host name that resolves to a public address. IP addresses, other ports and private addresses are refused, both when you connect and on every call. - Repositories are matched by server.
https://github.acme.example/acme/apiandgithub.com/acme/apiare two different repositories. Overrides, approvals, freezes and history for one never apply to the other. - The policy comes from the server. Every gate reads
bumpkit.ymland CODEOWNERS from the repository's default branch, at the target commit when the pull request targets it, so a pull request can't loosen its own rules. If a client sends a different policy, Bumpkit uses the one on your server, and the decision says so. If Bumpkit can't read the policy, the gate fails closed. - Approvals come from the server. A
bumpkit gaterun in CI counts the reviews on your server, not the approvals it was sent. If the run names no pull request, the commit isn't the head of the pull request, or the reviews can't be read, no approvals count. An address on your server that Bumpkit can't read asowner/repositoryfails closed. - Retargeting starts approval over. Changing a pull request's base runs the gate again. Reviews and Bumpkit approvals from before the change don't count toward the new base, so someone has to approve again. A denial stays until that person changes it.
- Required checks and code scanning count.
require_checks,require_check_appsandsecurity_alertsread check runs, commit statuses and open code scanning alerts on your server.
To connect, add the connection, then add the webhook and require the status as shown above. The first pull request shows Bumpkit release gate as a commit status and one Bumpkit comment, and the decision appears on the Checks page.
Run it in CI
Add bumpkit gate to your pipeline, and a blocked release fails the job. The setup is the same in every CI:
- Create an API key on the dashboard's API keys page, and store it as a secret named
BUMPKIT_API_KEY. - Add your CI's configuration from this section. It downloads the CLI, verifies its checksum and runs the gate.
- Add your code host's token if the table below calls for one. The gate uses it to count approvals and post its comment.
- Make the job a required check on your target branch, so a failed gate blocks the merge.
| CI | Code hosts | Token to add |
|---|---|---|
| GitHub Actions | GitHub | None. The workflow passes the job's token |
| GitLab CI | GitLab | GITLAB_TOKEN |
| Bitbucket Pipelines | Bitbucket Cloud | BITBUCKET_ACCESS_TOKEN |
| Azure Pipelines | Azure Repos | None. The step passes System.AccessToken |
| Buildkite | GitHub, GitLab, Bitbucket Cloud | By code host |
| CircleCI | GitHub, Bitbucket Cloud | By code host |
| Jenkins | GitHub, GitLab, Bitbucket Cloud | By code host |
Open a pull request to try it. The job passes on pass, and fails on needs_approval, fail, or when Bumpkit can't decide, unless your policy sets on_error: pass. The job counts approvals when it runs, so re-run it after someone approves.
Run it in GitHub Actions
The GitHub App needs no workflow, so use this only if you want the gate in your own workflow. Save this file as .github/workflows/bumpkit-gate.yml:
name: Bumpkit release gate
on: pull_request
permissions:
contents: read
pull-requests: write
jobs:
bumpkit-release-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Bumpkit release gate
env:
BUMPKIT_API_KEY: ${{ secrets.BUMPKIT_API_KEY }}
GITHUB_TOKEN: ${{ github.token }}
run: |
set -eu
arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
name="bumpkit_linux_${arch}.tar.gz"
url="https://bumpkit.dev/download/latest"
dir=$(mktemp -d) && repo=$PWD && cd "$dir"
curl -fsSLO "$url/$name" && curl -fsSLO "$url/checksums.txt"
grep " $name\$" checksums.txt | sha256sum -c -
tar -xzf "$name"
cd "$repo"
"$dir/bumpkit" gate --comment Add BUMPKIT_API_KEY under the repository's Settings → Secrets and variables → Actions, then require the bumpkit-release-gate job in branch protection.
GitHub doesn't give repository secrets to pull requests from forks, so the job can't get a decision for them.
Run it in GitLab CI
bumpkit-release-gate:
stage: test
image: alpine:3.20
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
variables:
GIT_DEPTH: "0"
BUMPKIT_VERSION: latest
script:
- apk add --no-cache git curl tar >/dev/null
- |
arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
name="bumpkit_linux_${arch}.tar.gz"
url="https://bumpkit.dev/download/$BUMPKIT_VERSION"
mkdir -p /tmp/bumpkit && cd /tmp/bumpkit
curl -fsSLO "$url/$name" && curl -fsSLO "$url/checksums.txt"
grep " $name\$" checksums.txt | sha256sum -c -
tar -xzf "$name"
cd "$CI_PROJECT_DIR"
git config --global --add safe.directory "$CI_PROJECT_DIR"
/tmp/bumpkit/bumpkit gate --comment Set these CI/CD variables (masked):
| Variable | Meaning |
|---|---|
BUMPKIT_API_KEY |
Your Bumpkit API key |
GITLAB_TOKEN or BUMPKIT_GITLAB_TOKEN |
A token with the api scope, to count approvals and post the note |
BUMPKIT_API_URL |
Optional; defaults to https://bumpkit.dev/v1 |
Without a token, the job falls back to CI_JOB_TOKEN, which can't read merge request approvals, so none are counted.
Run it in Bitbucket Pipelines
image: alpine:3.20
definitions:
steps:
- step: &bumpkit-release-gate
name: Bumpkit release gate
clone:
depth: full
script:
- apk add --no-cache git curl tar >/dev/null
- export BUMPKIT_VERSION="${BUMPKIT_VERSION:-latest}" BUMPKIT_DOWNLOAD_URL="${BUMPKIT_DOWNLOAD_URL:-https://bumpkit.dev/download}"
- arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && name="bumpkit_linux_${arch}.tar.gz"
- url="$BUMPKIT_DOWNLOAD_URL/$BUMPKIT_VERSION"
- mkdir -p /tmp/bumpkit && cd /tmp/bumpkit && curl -fsSLO "$url/$name" && curl -fsSLO "$url/checksums.txt"
- grep " $name\$" checksums.txt | sha256sum -c - && tar -xzf "$name" && cd "$BITBUCKET_CLONE_DIR"
- /tmp/bumpkit/bumpkit gate --comment
pipelines:
pull-requests:
'**':
- step: *bumpkit-release-gateSet BUMPKIT_API_KEY and BITBUCKET_ACCESS_TOKEN (or BUMPKIT_BITBUCKET_TOKEN) as secured repository variables. The token is sent as a bearer token, or as basic authentication when it has the form user:password.
Run it in Azure Pipelines
steps:
- checkout: self
fetchDepth: 0
persistCredentials: true
- bash: |
set -euo pipefail
os=$(uname -s | tr A-Z a-z)
arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
name="bumpkit_${os}_${arch}.tar.gz"
url="https://bumpkit.dev/download/latest"
dir="$AGENT_TEMPDIRECTORY/bumpkit" && mkdir -p "$dir" && cd "$dir"
curl -fsSLO "$url/$name" && curl -fsSLO "$url/checksums.txt"
grep " $name\$" checksums.txt | sha256sum -c -
tar -xzf "$name"
cd "$BUILD_SOURCESDIRECTORY"
"$dir/bumpkit" gate --comment
displayName: Bumpkit release gate
condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))
env:
BUMPKIT_API_KEY: $(BUMPKIT_API_KEY)
SYSTEM_ACCESSTOKEN: $(System.AccessToken)Run the pipeline for pull requests (for example as a build validation policy on the target branch), since the step only runs when Build.Reason is PullRequest. Store BUMPKIT_API_KEY as a secret pipeline variable. SYSTEM_ACCESSTOKEN lets the job read reviewer votes and post the thread.
Run it in Buildkite
Buildkite, CircleCI and Jenkins build code that lives somewhere else, so gate picks the API from the repository URL. See Tokens for your code host.
steps:
- label: "Bumpkit release gate"
if: build.pull_request.id != null
env:
BUILDKITE_GIT_FETCH_FLAGS: "-v --prune --tags"
command: |
set -eu
os=$$(uname -s | tr A-Z a-z)
arch=$$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
name="bumpkit_$${os}_$${arch}.tar.gz"
url="https://bumpkit.dev/download/latest"
dir=$$(mktemp -d) && repo=$$PWD && cd "$$dir"
curl -fsSLO "$$url/$$name" && curl -fsSLO "$$url/checksums.txt"
grep " $$name$$" checksums.txt | sha256sum -c -
tar -xzf "$$name"
cd "$$repo"
"$$dir/bumpkit" gate --comment Turn on pull request builds in the pipeline's settings. Give the step BUMPKIT_API_KEY and your code host's token with Buildkite secrets or an agent environment hook, not in this file. Don't set checkout.depth on this step.
Run it in CircleCI
version: 2.1
jobs:
bumpkit-release-gate:
docker:
- image: cimg/base:current
steps:
- checkout:
method: full
- run:
name: Bumpkit release gate
command: |
set -eu
arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
name="bumpkit_linux_${arch}.tar.gz"
url="https://bumpkit.dev/download/latest"
dir=$(mktemp -d) && repo=$PWD && cd "$dir"
curl -fsSLO "$url/$name" && curl -fsSLO "$url/checksums.txt"
grep " $name\$" checksums.txt | sha256sum -c -
tar -xzf "$name"
cd "$repo"
"$dir/bumpkit" gate --comment
workflows:
release-gate:
jobs:
- bumpkit-release-gateSet BUMPKIT_API_KEY and your code host's token as project environment variables or in a context. Turn on Only build pull requests in the project's advanced settings, so the job has a pull request to read.
CircleCI has no variable for the target branch, so gate reads it from the code host with your token. Without a token, pass --base. CircleCI sets CIRCLE_PULL_REQUEST for GitHub and Bitbucket Cloud, and picks one at random when a commit belongs to several pull requests. It doesn't set it for GitLab, so on a GitLab pipeline pass --base and --pr. That pipeline counts no approvals and posts no comment.
Run it in Jenkins
Use a Multibranch Pipeline with a branch source plugin that discovers pull requests. Only those set CHANGE_ID, CHANGE_TARGET and CHANGE_URL.
pipeline {
agent any
stages {
stage('Bumpkit release gate') {
when { changeRequest() }
environment {
BUMPKIT_API_KEY = credentials('bumpkit-api-key')
GITHUB_TOKEN = credentials('github-token')
}
steps {
withCredentials([gitUsernamePassword(credentialsId: 'git-checkout', gitToolName: 'Default')]) {
sh '''
set -eu
os=$(uname -s | tr A-Z a-z)
arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
name="bumpkit_${os}_${arch}.tar.gz"
url="https://bumpkit.dev/download/latest"
dir=$(mktemp -d) && cd "$dir"
curl -fsSLO "$url/$name" && curl -fsSLO "$url/checksums.txt"
grep " $name\\$" checksums.txt | sha256sum -c -
tar -xzf "$name"
cd "$WORKSPACE"
"$dir/bumpkit" gate --comment
'''
}
}
}
}
}Add these credentials in Jenkins:
| Credential | Kind | Meaning |
|---|---|---|
bumpkit-api-key |
Secret text | Your Bumpkit API key |
github-token |
Secret text | Your code host's token. On GitLab, name the variable GITLAB_TOKEN. On Bitbucket Cloud, name it BITBUCKET_ACCESS_TOKEN |
git-checkout |
Username with password | Lets gate fetch the target branch and tags over HTTPS |
gitUsernamePassword needs the Git plugin and an HTTPS remote. For an SSH remote, load the key in the agent instead. Leave Shallow clone off, and keep Fetch tags on if you add Advanced clone behaviours.
Tokens for your code host
In Buildkite, CircleCI and Jenkins, gate picks the API from the repository's URL:
| Repository | API | Token |
|---|---|---|
github.com |
GitHub | BUMPKIT_GITHUB_TOKEN or GITHUB_TOKEN, with read access to pull requests. To post --comment, it also needs write access to pull requests |
gitlab.com |
GitLab | BUMPKIT_GITLAB_TOKEN or GITLAB_TOKEN, with the api scope |
bitbucket.org |
Bitbucket Cloud | BUMPKIT_BITBUCKET_TOKEN or BITBUCKET_ACCESS_TOKEN |
| Any other host | GitLab if a GitLab token is set, otherwise GitHub Enterprise if a GitHub token is set | The same variables. GitHub Enterprise's API is read from /api/v3 on that host; set GITHUB_API_URL if yours differs |
Without a token, the gate still runs, but it counts no approvals and posts no comment, so a policy that requires approvals reports needs_approval. Bitbucket Data Center and Azure Repos aren't read from these three CIs.
Hosts detected in CI
| Host | Detected by | Pull request | Token |
|---|---|---|---|
| GitHub Actions | default | the event payload | GITHUB_TOKEN |
| GitLab CI | GITLAB_CI=true |
CI_MERGE_REQUEST_IID |
BUMPKIT_GITLAB_TOKEN, GITLAB_TOKEN, else CI_JOB_TOKEN |
| Bitbucket Pipelines | BITBUCKET_BUILD_NUMBER |
BITBUCKET_PR_ID |
BUMPKIT_BITBUCKET_TOKEN or BITBUCKET_ACCESS_TOKEN |
| Azure Pipelines | TF_BUILD=true |
SYSTEM_PULLREQUEST_PULLREQUESTID |
SYSTEM_ACCESSTOKEN |
| Buildkite | BUILDKITE=true |
BUILDKITE_PULL_REQUEST |
By code host |
| CircleCI | CIRCLECI=true |
CIRCLE_PULL_REQUEST |
By code host |
| Jenkins | BUILD_TAG starting with jenkins- |
CHANGE_ID |
By code host |
Overrides
When a commit has to ship anyway, a workspace owner, admin or policy admin can break glass. The override applies to that exact commit only; a new push needs a fresh decision.
An override needs a person signed in to the dashboard. Open the commit's check, enter a reason of 10 to 500 characters under Override this commit and choose Override gate. The pull request's GitHub check re-runs right away. An API key can't override: bumpkit override, the override MCP tool and POST /v1/gate/override answer 403 HUMAN_APPROVAL_REQUIRED with the link, whatever role the key's owner has, so an agent holding an owner's key can't turn the check green.
- Only owners, admins and policy admins can override (see Roles), and each commit can be overridden once. A second override of the same commit gets
409. - An override turns
failorneeds_approvalintopassand adds abreak_glassreason naming who overrode it and why. - With
break_glass: false, the override is recorded but the gate's outcome doesn't change. - Every override is in the gate decision, the audit log and the compliance evidence, and can require a post-hoc review. See Hotfixes & Break Glass.