Argo
Record Argo CD deploys and gate releases in Argo Workflows with a ready-made WorkflowTemplate.
Record Argo CD deploys and gate releases in Argo Workflows. Open the Argo card on the Integrations page; each part has its own status and setup.
Argo CD deploy events
Beta, early access. A workspace without access gets
403 FEATURE_EARLY_ACCESSand a link to request it. Flag:argocd_deploys.
Argo CD notifications record each synced revision as a deployment, so release health, incidents and change tickets follow what actually shipped.
- Add a source with the repository on the Argo card, or with
bumpkit sources add argocd --repository owner/name. Bumpkit shows a URL and a secret once. - Add a webhook service to
argocd-notifications-cmwith the URL and the headerX-Bumpkit-Secret: <secret>. - Add an
on-sync-succeededtemplate with this body:
{
"repository": "{{.app.spec.source.repoURL}}",
"revision": "{{.app.status.operationState.syncResult.revision}}",
"environment": "{{.app.metadata.name}}",
"phase": "{{.app.status.operationState.phase}}",
"finished_at": "{{.app.status.operationState.finishedAt}}"
}The application name becomes the environment. Redeploying an earlier revision marks the newer change rolled back. The sources_add and sources_list MCP tools manage sources from an agent.
Argo Workflows gate step
A ready-made WorkflowTemplate puts the release gate, deploy records and rollback inside Argo Workflows.
Beta, early access. A workspace without access gets
403 FEATURE_EARLY_ACCESSand a link to request it. Flags:release_healthorforensicsfor deploy records,intelligent_rollbackfor rollback. The gate is on for every workspace.
What you get
| Template | What it does |
|---|---|
gate |
Checks out the revision, runs bumpkit gate and fails the step unless the release can ship. Exposes the outcome, decision, check and dashboard link as Argo output parameters |
record-deploy |
Runs bumpkit deploy after your deploy, with success or failure, so release health and what broke production follow what actually shipped |
record-deploy-hook |
Records a successful deploy with an Argo http template: no pod, no API key, just the signed deploy webhook |
rollback |
Plans the smallest revert after a failed deploy and opens it as a pull request for a person to review and merge |
Everything runs the same bumpkit CLI you run on a laptop, with no terminal, no config file and no prompts.
Prerequisites
- An API key in a Kubernetes Secret. A key carries the role of the person who created it. The gate and deploy records need a key from a member or above. Opening a rollback needs a key from an owner or admin, so keep that one in a separate Secret. See roles.
- The Bumpkit GitHub App on the repository. With the App, the gate reads
bumpkit.ymlfrom the base on GitHub itself, so a pull request can't loosen its own rules, and it counts the pull request's approvals. Without it, the gate uses the policy in the checkout and marks the decisionpolicy_source: client. See the policy comes from the base. - Argo Workflows with permission to read the Secrets in the workflow's namespace.
- An image with
sh,git,curl,tarandsha256sum. The templates default tobuildpack-deps:bookworm-scm; mirror it to your registry and pin it by digest. Bumpkit doesn't publish a container image.
Quick start
1. Store the keys
kubectl -n argo create secret generic bumpkit --from-file=api-key=./bumpkit-api-key.txt
kubectl -n argo create secret generic bumpkit-rollback --from-file=api-key=./bumpkit-owner-key.txt
kubectl -n argo create secret generic git-credentials --from-file=username=./git-username.txt --from-file=password=./git-token.txtCreate the keys on the dashboard's Keys page and write each to its file without echoing it to your shell history; delete the files afterward. bumpkit-rollback is only for the rollback template, with a key from an owner or admin; skip it if you don't roll back from Argo. git-credentials holds a read-only token for the repository; for a public repository, drop usernameSecret and passwordSecret from the templates.
2. Install the WorkflowTemplate
Save this as bumpkit-workflowtemplate.yaml and run kubectl -n argo apply -f bumpkit-workflowtemplate.yaml. Set bumpkit-version to the version at https://bumpkit.dev/download/latest/version.
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: bumpkit
spec:
templates:
- name: gate
inputs:
parameters:
- name: repo
- name: revision
- name: base
value: ""
- name: pr
value: ""
- name: on-needs-approval
value: fail
- name: image
value: buildpack-deps:bookworm-scm
- name: bumpkit-version
value: v0.94.0
- name: bumpkit-sha256
value: ""
- name: api-url
value: https://bumpkit.dev/v1
artifacts:
- name: source
path: /work/src
git:
repo: "{{inputs.parameters.repo}}"
revision: "{{inputs.parameters.revision}}"
usernameSecret:
name: git-credentials
key: username
passwordSecret:
name: git-credentials
key: password
outputs:
parameters:
- name: outcome
valueFrom:
path: /tmp/bumpkit/outcome
default: error
- name: ship
valueFrom:
path: /tmp/bumpkit/ship
default: "false"
- name: decision-id
valueFrom:
path: /tmp/bumpkit/decision-id
default: ""
- name: check-id
valueFrom:
path: /tmp/bumpkit/check-id
default: ""
- name: url
valueFrom:
path: /tmp/bumpkit/url
default: ""
- name: version
valueFrom:
path: /tmp/bumpkit/version
default: ""
volumes:
- name: bumpkit-bin
emptyDir: {}
initContainers:
- name: install-bumpkit
image: "{{inputs.parameters.image}}"
env:
- name: BUMPKIT_VERSION
value: "{{inputs.parameters.bumpkit-version}}"
- name: BUMPKIT_SHA256
value: "{{inputs.parameters.bumpkit-sha256}}"
volumeMounts:
- name: bumpkit-bin
mountPath: /opt/bumpkit
command: [sh, -c]
args:
- |
set -eu
case "$(uname -m)" in
x86_64|amd64) arch=amd64 ;;
aarch64|arm64) arch=arm64 ;;
*) echo "Unsupported architecture $(uname -m)" >&2; exit 1 ;;
esac
name="bumpkit_linux_${arch}.tar.gz"
cd "$(mktemp -d)"
curl -fsSLO "https://bumpkit.dev/download/${BUMPKIT_VERSION}/${name}"
curl -fsSLO "https://bumpkit.dev/download/${BUMPKIT_VERSION}/checksums.txt"
grep " ${name}\$" checksums.txt | sha256sum -c -
if [ -n "${BUMPKIT_SHA256}" ]; then
echo "${BUMPKIT_SHA256} ${name}" | sha256sum -c -
fi
tar -xzf "${name}" -C /opt/bumpkit ./bumpkit
/opt/bumpkit/bumpkit --version
script:
image: "{{inputs.parameters.image}}"
workingDir: /work/src
command: [sh]
env:
- name: BUMPKIT_API_KEY
valueFrom:
secretKeyRef:
name: bumpkit
key: api-key
- name: BUMPKIT_API_URL
value: "{{inputs.parameters.api-url}}"
- name: BASE
value: "{{inputs.parameters.base}}"
- name: PR
value: "{{inputs.parameters.pr}}"
- name: ON_NEEDS_APPROVAL
value: "{{inputs.parameters.on-needs-approval}}"
- name: PATH
value: /opt/bumpkit:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: GIT_CONFIG_COUNT
value: "1"
- name: GIT_CONFIG_KEY_0
value: safe.directory
- name: GIT_CONFIG_VALUE_0
value: /work/src
volumeMounts:
- name: bumpkit-bin
mountPath: /opt/bumpkit
source: |
set -u
if [ -n "$PR" ] && [ -n "$BASE" ]; then
BASE=$(git merge-base HEAD "$BASE" 2>/dev/null || echo "$BASE")
fi
bumpkit gate ${BASE:+--base "$BASE"} ${PR:+--pr "$PR"} --output-dir /tmp/bumpkit
code=$?
if [ "$code" -eq 1 ] && [ "$(cat /tmp/bumpkit/outcome)" = needs_approval ] && [ "$ON_NEEDS_APPROVAL" = pass ]; then
echo "Needs approval; passing because on-needs-approval is pass."
code=0
fi
if [ "$code" -eq 0 ]; then echo true > /tmp/bumpkit/ship; else echo false > /tmp/bumpkit/ship; fi
exit "$code"
- name: record-deploy
inputs:
parameters:
- name: repo
- name: revision
- name: environment
value: production
- name: status
value: success
- name: version
value: ""
- name: base
value: ""
- name: image
value: buildpack-deps:bookworm-scm
- name: bumpkit-version
value: v0.94.0
- name: bumpkit-sha256
value: ""
- name: api-url
value: https://bumpkit.dev/v1
artifacts:
- name: source
path: /work/src
git:
repo: "{{inputs.parameters.repo}}"
revision: "{{inputs.parameters.revision}}"
usernameSecret:
name: git-credentials
key: username
passwordSecret:
name: git-credentials
key: password
volumes:
- name: bumpkit-bin
emptyDir: {}
initContainers:
- name: install-bumpkit
image: "{{inputs.parameters.image}}"
env:
- name: BUMPKIT_VERSION
value: "{{inputs.parameters.bumpkit-version}}"
- name: BUMPKIT_SHA256
value: "{{inputs.parameters.bumpkit-sha256}}"
volumeMounts:
- name: bumpkit-bin
mountPath: /opt/bumpkit
command: [sh, -c]
args:
- |
set -eu
case "$(uname -m)" in
x86_64|amd64) arch=amd64 ;;
aarch64|arm64) arch=arm64 ;;
*) echo "Unsupported architecture $(uname -m)" >&2; exit 1 ;;
esac
name="bumpkit_linux_${arch}.tar.gz"
cd "$(mktemp -d)"
curl -fsSLO "https://bumpkit.dev/download/${BUMPKIT_VERSION}/${name}"
curl -fsSLO "https://bumpkit.dev/download/${BUMPKIT_VERSION}/checksums.txt"
grep " ${name}\$" checksums.txt | sha256sum -c -
if [ -n "${BUMPKIT_SHA256}" ]; then
echo "${BUMPKIT_SHA256} ${name}" | sha256sum -c -
fi
tar -xzf "${name}" -C /opt/bumpkit ./bumpkit
/opt/bumpkit/bumpkit --version
script:
image: "{{inputs.parameters.image}}"
workingDir: /work/src
command: [sh]
env:
- name: BUMPKIT_API_KEY
valueFrom:
secretKeyRef:
name: bumpkit
key: api-key
- name: BUMPKIT_API_URL
value: "{{inputs.parameters.api-url}}"
- name: ENVIRONMENT
value: "{{inputs.parameters.environment}}"
- name: STATUS
value: "{{inputs.parameters.status}}"
- name: VERSION
value: "{{inputs.parameters.version}}"
- name: BASE
value: "{{inputs.parameters.base}}"
- name: PATH
value: /opt/bumpkit:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: GIT_CONFIG_COUNT
value: "1"
- name: GIT_CONFIG_KEY_0
value: safe.directory
- name: GIT_CONFIG_VALUE_0
value: /work/src
volumeMounts:
- name: bumpkit-bin
mountPath: /opt/bumpkit
source: |
set -eu
bumpkit deploy --environment "$ENVIRONMENT" --status "$STATUS" ${VERSION:+--version "$VERSION"} ${BASE:+--base "$BASE"}
- name: record-deploy-hook
inputs:
parameters:
- name: hook-url
- name: repository
- name: revision
- name: environment
value: production
http:
url: "{{inputs.parameters.hook-url}}"
method: POST
timeoutSeconds: 30
headers:
- name: Content-Type
value: application/json
- name: X-Bumpkit-Secret
valueFrom:
secretKeyRef:
name: bumpkit-deploy-hook
key: secret
body: '{"repository": "{{inputs.parameters.repository}}", "revision": "{{inputs.parameters.revision}}", "environment": "{{inputs.parameters.environment}}", "phase": "Succeeded"}'
successCondition: response.statusCode == 204
- name: rollback
inputs:
parameters:
- name: repo
- name: revision
- name: base
- name: failure
value: ""
- name: image
value: buildpack-deps:bookworm-scm
- name: bumpkit-version
value: v0.94.0
- name: bumpkit-sha256
value: ""
- name: api-url
value: https://bumpkit.dev/v1
artifacts:
- name: source
path: /work/src
git:
repo: "{{inputs.parameters.repo}}"
revision: "{{inputs.parameters.revision}}"
usernameSecret:
name: git-credentials
key: username
passwordSecret:
name: git-credentials
key: password
outputs:
parameters:
- name: plan-id
valueFrom:
path: /tmp/bumpkit/plan-id
default: ""
volumes:
- name: bumpkit-bin
emptyDir: {}
initContainers:
- name: install-bumpkit
image: "{{inputs.parameters.image}}"
env:
- name: BUMPKIT_VERSION
value: "{{inputs.parameters.bumpkit-version}}"
- name: BUMPKIT_SHA256
value: "{{inputs.parameters.bumpkit-sha256}}"
volumeMounts:
- name: bumpkit-bin
mountPath: /opt/bumpkit
command: [sh, -c]
args:
- |
set -eu
case "$(uname -m)" in
x86_64|amd64) arch=amd64 ;;
aarch64|arm64) arch=arm64 ;;
*) echo "Unsupported architecture $(uname -m)" >&2; exit 1 ;;
esac
name="bumpkit_linux_${arch}.tar.gz"
cd "$(mktemp -d)"
curl -fsSLO "https://bumpkit.dev/download/${BUMPKIT_VERSION}/${name}"
curl -fsSLO "https://bumpkit.dev/download/${BUMPKIT_VERSION}/checksums.txt"
grep " ${name}\$" checksums.txt | sha256sum -c -
if [ -n "${BUMPKIT_SHA256}" ]; then
echo "${BUMPKIT_SHA256} ${name}" | sha256sum -c -
fi
tar -xzf "${name}" -C /opt/bumpkit ./bumpkit
/opt/bumpkit/bumpkit --version
script:
image: "{{inputs.parameters.image}}"
workingDir: /work/src
command: [sh]
env:
- name: BUMPKIT_API_KEY
valueFrom:
secretKeyRef:
name: bumpkit-rollback
key: api-key
- name: BUMPKIT_API_URL
value: "{{inputs.parameters.api-url}}"
- name: BASE
value: "{{inputs.parameters.base}}"
- name: FAILURE
value: "{{inputs.parameters.failure}}"
- name: PATH
value: /opt/bumpkit:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: GIT_CONFIG_COUNT
value: "1"
- name: GIT_CONFIG_KEY_0
value: safe.directory
- name: GIT_CONFIG_VALUE_0
value: /work/src
volumeMounts:
- name: bumpkit-bin
mountPath: /opt/bumpkit
source: |
set -eu
mkdir -p /tmp/bumpkit
bumpkit rollback plan --base "$BASE" --head HEAD --error "$FAILURE" --json > /tmp/bumpkit/plan.json
plan=$(sed -n 's/^ "id": "\([^"]*\)",$/\1/p' /tmp/bumpkit/plan.json)
printf '%s\n' "$plan" > /tmp/bumpkit/plan-id
if [ -z "$plan" ]; then
cat /tmp/bumpkit/plan.json
echo "Bumpkit found no commit that explains this failure, so it opened nothing." >&2
exit 1
fi
bumpkit rollback open "$plan" --yes 3. Gate before you deploy
Call gate with templateRef as the step before your deploy. When the gate blocks, the step fails, the workflow stops, and nothing deploys:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: shop-release-
spec:
entrypoint: release
arguments:
parameters:
- name: revision
- name: base
value: origin/main
- name: pr
value: ""
templates:
- name: release
steps:
- - name: gate
templateRef:
name: bumpkit
template: gate
arguments:
parameters:
- name: repo
value: https://github.com/acme/shop.git
- name: revision
value: "{{workflow.parameters.revision}}"
- name: base
value: "{{workflow.parameters.base}}"
- name: pr
value: "{{workflow.parameters.pr}}"
- - name: deploy
template: deploy
- name: deploy
container:
image: registry.example.com/acme/deployer:1.4.0
args: [deploy, --revision, "{{workflow.parameters.revision}}"]Submit it with argo submit -n argo shop-release.yaml -p revision=<sha>, and add -p pr=<number> for a pull request.
WorkflowTemplate reference
gate
| Parameter | Default | Description |
|---|---|---|
repo |
Git URL of the repository, e.g. https://github.com/acme/shop.git |
|
revision |
The commit, branch or tag being released | |
base |
The latest tag before the revision | What the release starts from. With pr, the template diffs from the merge base of base and the revision. Without pr and with the GitHub App, use a branch or tag, such as origin/main or v1.4.0 |
pr |
The pull request number. Bumpkit reads its approvals and its base's policy through the GitHub App | |
on-needs-approval |
fail |
pass lets a needs_approval decision through, for pipelines where a person approves later |
image |
buildpack-deps:bookworm-scm |
The image for the install step and the gate |
bumpkit-version |
The release pinned in the template | The CLI release to download. Set it to the version at https://bumpkit.dev/download/latest/version |
bumpkit-sha256 |
The SHA-256 of bumpkit_linux_<arch>.tar.gz for your nodes. When set, the download must match it as well as checksums.txt |
|
api-url |
https://bumpkit.dev/v1 |
The Bumpkit API |
| Output parameter | Value |
|---|---|
outcome |
pass, needs_approval, fail, or error when Bumpkit couldn't decide |
ship |
true when the step passed, else false |
decision-id |
The gate decision's ID |
check-id |
The release check's ID, for bumpkit rollback open and the API |
url |
The release page on the dashboard |
version |
The suggested version, like 1.5.0 |
| Step exit | When |
|---|---|
0 |
Ready (pass), or needs_approval with on-needs-approval: pass, or Bumpkit couldn't decide and the policy sets on_error: pass |
1 |
Blocked (fail), or needs_approval with on-needs-approval: fail |
2 |
Bumpkit couldn't decide: bad flag, invalid bumpkit.yml, missing key, git or API error |
An invalid bumpkit.yml always exits 2, even with on_error: pass. See CLI exit codes.
record-deploy
| Parameter | Default | Description |
|---|---|---|
repo, revision |
The repository and the commit that was deployed | |
environment |
production |
Where it went |
status |
success |
success, failure or in_progress |
version |
A tag on the commit | The version that went live, like v1.5.0 |
base |
The tag before the commit | The previous release, so Bumpkit gets the commits it shipped |
image, bumpkit-version, bumpkit-sha256, api-url |
As for gate |
It exits 0 once recorded, 1 on an API error, and 2 on a bad flag, a missing key or a git error. Pass version and base when your clone has no tags.
record-deploy-hook
| Parameter | Description |
|---|---|
hook-url |
The source's webhook URL, like https://bumpkit.dev/hooks/deploys/<token> |
repository |
owner/name, like acme/shop |
revision |
The deployed commit SHA |
environment |
Where it went (default production) |
rollback
| Parameter | Description |
|---|---|
repo, revision |
The repository and the commit that failed |
base |
The last good release, like v1.4.0 |
failure |
The error and stack trace, so Bumpkit can rank the commits behind it |
image, bumpkit-version, bumpkit-sha256, api-url |
As for gate |
Its plan-id output is the saved rollback plan. The step exits 1 when no commit explains the failure.
Install the CLI in a step
Each template installs the CLI in an init container, into a shared emptyDir:
- It downloads
bumpkit_linux_<arch>.tar.gzandchecksums.txtfromhttps://bumpkit.dev/download/<version>/. - It checks the archive's SHA-256 against
checksums.txt, and againstbumpkit-sha256when you set it. - It unpacks only
bumpkitand runsbumpkit --version.
That's the same verification the install script does, without bumpkit install editing shell startup files in a throwaway container. Pinning bumpkit-sha256 means a changed archive fails the step instead of running.
Only the current release is served. When Bumpkit ships a new version, /download/<old version>/ answers 404 and the install step fails until you update bumpkit-version and bumpkit-sha256. To pin for longer, and to drop the download from every run, bake the CLI into your own CI image with the install script, in its build file:
FROM buildpack-deps:bookworm-scm
RUN curl -fsSL https://bumpkit.dev/install.sh | BUMPKIT_VERSION=v0.94.0 BUMPKIT_INSTALL_DIR=/usr/local/bin shThen remove the initContainers, volumes and volumeMounts from the templates, and set image to your image.
Gate outputs
bumpkit gate --output-dir DIR writes one file per value, which is what Argo's valueFrom.path reads: outcome, decision-id, check-id, url and version. It writes them on every exit once the flags parse, with outcome set to error when Bumpkit couldn't decide. The templates' default: values cover a bad flag, so a failed gate never turns into a "missing output" error.
bumpkit gate --json prints the whole decision instead. If your image has jq, you can pick fields from it yourself:
- name: gate-json
script:
image: registry.example.com/ci/bumpkit:0.91.0
workingDir: /work/src
command: [sh]
source: |
bumpkit gate --json > /tmp/decision.json
code=$?
jq -r '.check.id' /tmp/decision.json > /tmp/check-id
exit "$code"
outputs:
parameters:
- name: check-id
valueFrom:
path: /tmp/check-id
default: ""Skip deploy unless the gate passed
To keep the workflow running when the gate blocks, for example to post a message, let the gate step fail without stopping the workflow and put a when on the deploy:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: shop-release-
spec:
entrypoint: release
arguments:
parameters:
- name: revision
templates:
- name: release
steps:
- - name: gate
templateRef:
name: bumpkit
template: gate
continueOn:
failed: true
arguments:
parameters:
- name: repo
value: https://github.com/acme/shop.git
- name: revision
value: "{{workflow.parameters.revision}}"
- name: base
value: v1.4.0
- - name: deploy
template: deploy
when: "{{steps.gate.outputs.parameters.ship}} == true"
- name: notify
template: notify
when: "{{steps.gate.outputs.parameters.ship}} != true"
arguments:
parameters:
- name: outcome
value: "{{steps.gate.outputs.parameters.outcome}}"
- name: url
value: "{{steps.gate.outputs.parameters.url}}"
- name: deploy
container:
image: registry.example.com/acme/deployer:1.4.0
args: [deploy, --revision, "{{workflow.parameters.revision}}"]
- name: notify
inputs:
parameters:
- name: outcome
- name: url
container:
image: registry.example.com/acme/notifier:2.0.0
args: ["Release gate: {{inputs.parameters.outcome}}", "{{inputs.parameters.url}}"]Without continueOn, a failed gate stops the workflow before when is evaluated.
Deploy events
Record each deploy so release health compares errors before and after it and what broke production ranks the releases that actually shipped. Run record-deploy as the final steps, keyed on the deploy step's status:
- - name: deploy
template: deploy
continueOn:
failed: true
- - name: record-success
templateRef:
name: bumpkit
template: record-deploy
when: "{{steps.deploy.status}} == Succeeded"
arguments:
parameters:
- name: repo
value: https://github.com/acme/shop.git
- name: revision
value: "{{workflow.parameters.revision}}"
- name: record-failure
templateRef:
name: bumpkit
template: record-deploy
when: "{{steps.deploy.status}} != Succeeded"
arguments:
parameters:
- name: repo
value: https://github.com/acme/shop.git
- name: revision
value: "{{workflow.parameters.revision}}"
- name: status
value: failureKey the record on the deploy step, not on {{workflow.status}}. An onExit handler runs when the gate blocked too, and recording a failed deploy that never happened skews release health.
Without the CLI
record-deploy-hook posts to an existing Argo CD deploy source with an Argo http template, so no pod starts and no API key is in the workflow.
- Add the source:
bumpkit sources add argocd --repository acme/shop. It shows the webhook URL and a signing secret, once. - Store the secret:
kubectl -n argo create secret generic bumpkit-deploy-hook --from-file=secret=./bumpkit-hook-secret.txt. - Call the template after a successful deploy:
- - name: record
templateRef:
name: bumpkit
template: record-deploy-hook
when: "{{steps.deploy.status}} == Succeeded"
arguments:
parameters:
- name: hook-url
value: https://bumpkit.dev/hooks/deploys/<token>
- name: repository
value: acme/shop
- name: revision
value: "{{workflow.parameters.revision}}"The hook records successes only, sent without commits, and Bumpkit shows them as Argo CD deploys. It needs the argocd_deploys flag. Argo Workflows runs http templates in its agent, which needs RBAC for workflowtasksets and to read the Secret. An Argo Events sensor can post the same body with an HTTP trigger.
Rollback
Beta, early access. Flag:
intelligent_rollback. Opening the pull request needs the Bumpkit GitHub App on the repository.
After a failed deploy, rollback runs bumpkit rollback plan with the failure text. It finds the commits since base most likely behind it, picks the smallest revert that applies cleanly and saves the plan. bumpkit rollback open PLAN_ID --yes then opens that revert as a pull request through the GitHub App.
--yesis required, because nobody's at a terminal to confirm. It opens the pull request; it doesn't approve or merge anything.- The human approval is the revert pull request itself. The gate runs on it like any pull request, and a person merges it. Bumpkit never merges.
- Opening a rollback needs an owner or admin key, kept in the
bumpkit-rollbackSecret, and it's written to the audit log asrollback.open(loop.rollback.openwhen you pass a check ID with Close the loop). - With Close the loop on (Beta, flag
close_the_loop), you can skip the plan and runbumpkit rollback open CHECK_ID --yeswith the gate'scheck-idoutput. Bumpkit then plans on its side and reverts the whole release when no evidence points at specific commits.
Stacked pull requests and merge queues
- Stacked pull requests. Pass the parent's branch as
base, likeorigin/feature/checkout-v2, and the pull request's number aspr. The template diffs from the merge base, so each pull request is gated on its own changes, not its parent's. With the GitHub App and thestacked_prsflag, Bumpkit also tracks the stack andstacks.require_parent_mergedcan hold a child until its parent merges. - Merge queues. With the GitHub App, Bumpkit already gates each merge queue group as a check. To gate the queue's commit in Argo too, run
gateon that commit with noprand the target branch asbase, likeorigin/main, so the decision covers what will actually land. - Re-runs. A decision isn't cached: retrying the step gates the revision again, so an approval given since the last run counts.
Secrets
- The API key lives only in a Kubernetes Secret and reaches the gate as
BUMPKIT_API_KEYthroughsecretKeyRef. It never appears in parameters, arguments, logs or outputs. bumpkitneeds no config file in the container. It never prompts, opens a browser or writes the key anywhere.- Keep the rollback key, which can open pull requests, in its own Secret and give it only to the
rollbacktemplate. - Use a read-only git token in
git-credentials. Bumpkit doesn't need write access to the repository; the GitHub App opens rollback pull requests. - Output parameters hold IDs and a dashboard link, never a secret.
Network egress
The steps only need outbound HTTPS to:
| Host | For |
|---|---|
bumpkit.dev |
The API and the CLI download |
| Your git host | The checkout |
| Your image registry | The step image |
Bumpkit itself talks to GitHub through the GitHub App, so your pods don't need access to api.github.com. Set HTTPS_PROXY in the template's env if your cluster requires a proxy. Baking the CLI into your own image drops the download.
Troubleshooting
| You see | Why | Fix |
|---|---|---|
403 FEATURE_EARLY_ACCESS |
The feature isn't on for your workspace yet | Request access from the link. See feature not enabled |
400 INVALID_POLICY or on_error must be fail or pass |
bumpkit.yml doesn't parse. The gate always fails closed |
See the policy is invalid |
503 POLICY_UNAVAILABLE |
Bumpkit couldn't read bumpkit.yml from the base with the GitHub App |
Retry the step |
not signed in: run bumpkit login, or set BUMPKIT_API_KEY |
The Secret or its api-key key is missing |
See missing API key |
v1.4.0 is not a commit, tag or branch in . |
The clone doesn't have the base | Pass a branch the clone has, like origin/main, or a tag your git host serves. See shallow checkout |
detected dubious ownership in repository |
The checkout belongs to another user than the step | Keep the GIT_CONFIG_* variables in the template's env |
curl: (22) The requested URL returned error: 404 in install-bumpkit |
bumpkit-version is no longer the current release |
Update bumpkit-version and bumpkit-sha256, or bake the CLI into your image |
sha256sum: WARNING: 1 computed checksum did NOT match |
The archive doesn't match checksums.txt or bumpkit-sha256 |
Don't run it. Check bumpkit-sha256 for your nodes' architecture |
The gate says NEEDS APPROVAL after an approval |
Approvals only count with pr and the GitHub App, on the latest commit |
See an approval didn't count |
pass --yes to confirm when not running interactively |
A command asked for confirmation with no terminal | Add --yes |
409 GITHUB_APP_REQUIRED |
Rollback needs the GitHub App on the repository | Install the App. See open the revert pull request |
Every gate reason links to its explanation, and the decision's url output opens it on the dashboard.
End-to-end example
Check out, gate, deploy, record the result, and open a rollback for review when the deploy fails:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: shop-release-
spec:
entrypoint: release
arguments:
parameters:
- name: revision
- name: last-good
value: v1.4.0
templates:
- name: release
steps:
- - name: gate
templateRef:
name: bumpkit
template: gate
arguments:
parameters:
- name: repo
value: https://github.com/acme/shop.git
- name: revision
value: "{{workflow.parameters.revision}}"
- name: base
value: "{{workflow.parameters.last-good}}"
- - name: deploy
template: deploy
continueOn:
failed: true
- - name: record-success
templateRef:
name: bumpkit
template: record-deploy
when: "{{steps.deploy.status}} == Succeeded"
arguments:
parameters:
- name: repo
value: https://github.com/acme/shop.git
- name: revision
value: "{{workflow.parameters.revision}}"
- name: base
value: "{{workflow.parameters.last-good}}"
- name: record-failure
templateRef:
name: bumpkit
template: record-deploy
when: "{{steps.deploy.status}} != Succeeded"
arguments:
parameters:
- name: repo
value: https://github.com/acme/shop.git
- name: revision
value: "{{workflow.parameters.revision}}"
- name: base
value: "{{workflow.parameters.last-good}}"
- name: status
value: failure
- - name: rollback
templateRef:
name: bumpkit
template: rollback
when: "{{steps.deploy.status}} != Succeeded"
arguments:
parameters:
- name: repo
value: https://github.com/acme/shop.git
- name: revision
value: "{{workflow.parameters.revision}}"
- name: base
value: "{{workflow.parameters.last-good}}"
- name: failure
value: "{{steps.deploy.outputs.parameters.failure}}"
- - name: deploy-failed
template: deploy-failed
when: "{{steps.deploy.status}} != Succeeded"
- name: deploy
outputs:
parameters:
- name: failure
valueFrom:
path: /tmp/failure.txt
default: ""
script:
image: registry.example.com/acme/deployer:1.4.0
command: [sh]
source: |
deployer deploy --revision "{{workflow.parameters.revision}}" > /tmp/deploy.log 2>&1
code=$?
cat /tmp/deploy.log
tail -c 4000 /tmp/deploy.log > /tmp/failure.txt
exit "$code"
- name: deploy-failed
script:
image: buildpack-deps:bookworm-scm
command: [sh]
source: |
echo "The deploy failed. Bumpkit recorded it and opened a revert for review." >&2
exit 1 The gate blocks first, so a blocked release never deploys and never records a deploy. A failed deploy is recorded as failure, Bumpkit opens the revert pull request, and the last step fails the workflow so your alerts still fire.