Docs

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_ACCESS and 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.

  1. 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.
  2. Add a webhook service to argocd-notifications-cm with the URL and the header X-Bumpkit-Secret: <secret>.
  3. Add an on-sync-succeeded template with this body:
JSON
{
  "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_ACCESS and a link to request it. Flags: release_health or forensics for deploy records, intelligent_rollback for 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.yml from 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 decision policy_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, tar and sha256sum. The templates default to buildpack-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 ​

Terminal
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.txt

Create 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.

YAML
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:

YAML
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:

  1. It downloads bumpkit_linux_<arch>.tar.gz and checksums.txt from https://bumpkit.dev/download/<version>/.
  2. It checks the archive's SHA-256 against checksums.txt, and against bumpkit-sha256 when you set it.
  3. It unpacks only bumpkit and runs bumpkit --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:

Text
FROM buildpack-deps:bookworm-scm
RUN curl -fsSL https://bumpkit.dev/install.sh | BUMPKIT_VERSION=v0.94.0 BUMPKIT_INSTALL_DIR=/usr/local/bin sh

Then 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:

YAML
    - 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:

YAML
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:

YAML
        - - 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: failure

Key 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.

  1. Add the source: bumpkit sources add argocd --repository acme/shop. It shows the webhook URL and a signing secret, once.
  2. Store the secret: kubectl -n argo create secret generic bumpkit-deploy-hook --from-file=secret=./bumpkit-hook-secret.txt.
  3. Call the template after a successful deploy:
YAML
        - - 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.

  • --yes is 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-rollback Secret, and it's written to the audit log as rollback.open (loop.rollback.open when 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 run bumpkit rollback open CHECK_ID --yes with the gate's check-id output. 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, like origin/feature/checkout-v2, and the pull request's number as pr. 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 the stacked_prs flag, Bumpkit also tracks the stack and stacks.require_parent_merged can 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 gate on that commit with no pr and the target branch as base, like origin/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_KEY through secretKeyRef. It never appears in parameters, arguments, logs or outputs.
  • bumpkit needs 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 rollback template.
  • 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:

YAML
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.

↑↓ to move, Enter to open