GitHub Actions
A composite action you copy into your repository that reports a deployment as the job starts and as it ends, the workflow that uses it, the secrets and variables it needs, and the GitHub deployments alternative that needs no step at all.
By the end of this page a GitHub Actions workflow will report every deployment it makes: opened as running when the job starts, finished as succeeded, failed or cancelled when it ends, with the commit, the branch and a link back to the run. The action lives in your own repository, so there is nothing to install and nothing that changes under you.
Before you begin
| You need | Why |
|---|---|
| An ingestion token | A token with ci:deploy, confined to the project; see CI/CD ingestion. |
| The project and environment ids | Both are in the address bar of their pages; one environment id per environment the workflow deploys to. |
jq on the runner | The hosted runners carry it; a self-hosted runner needs it installed. |
Add the action to your repository
Create .github/actions/zuse-bifrost-deployment/action.yml with this content:
name: Report a deployment to Zuse Bifrost
description: Opens a deployment as the job starts and finishes it as the job ends.
inputs:
api:
description: The API base address, without a trailing slash.
required: true
token:
description: An ingestion token that carries ci:deploy.
required: true
project:
description: The id of the project deployed.
required: true
environment:
description: The id of the environment deployed into.
required: true
version:
description: The version deployed.
required: true
phase:
description: start, to open the deployment; finish, to end it.
required: true
deployment:
description: The deployment id the start phase answered; needed by finish.
required: false
status:
description: How the deployment ended; succeeded, failed or cancelled.
required: false
default: succeeded
outputs:
deployment:
description: The id of the deployment opened by the start phase.
value: ${{ steps.report.outputs.deployment }}
runs:
using: composite
steps:
- id: report
shell: bash
env:
BIFROST_API: ${{ inputs.api }}
BIFROST_TOKEN: ${{ inputs.token }}
BIFROST_PROJECT: ${{ inputs.project }}
BIFROST_ENVIRONMENT: ${{ inputs.environment }}
BIFROST_VERSION: ${{ inputs.version }}
BIFROST_PHASE: ${{ inputs.phase }}
BIFROST_DEPLOYMENT: ${{ inputs.deployment }}
BIFROST_STATUS: ${{ inputs.status }}
run: |
set -euo pipefail
run_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
key="$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
if [ "$BIFROST_PHASE" = "start" ]; then
body=$(jq -n \
--arg environment "$BIFROST_ENVIRONMENT" \
--arg version "$BIFROST_VERSION" \
--arg sha "$GITHUB_SHA" \
--arg branch "$GITHUB_REF_NAME" \
--arg url "$run_url" \
'{environment_id: $environment, version: $version, status: "running", commit_sha: $sha, branch: $branch, pipeline_url: $url}')
id=$(curl -sS --fail-with-body -X POST "$BIFROST_API/v1/projects/$BIFROST_PROJECT/deployments" \
-H "Authorization: Bearer $BIFROST_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $key-start" \
-d "$body" | jq -r '.data.id')
echo "deployment=$id" >> "$GITHUB_OUTPUT"
echo "Opened deployment $id of $BIFROST_VERSION."
else
curl -sS --fail-with-body -X PATCH "$BIFROST_API/v1/deployments/$BIFROST_DEPLOYMENT" \
-H "Authorization: Bearer $BIFROST_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $key-finish" \
-d "{\"status\": \"$BIFROST_STATUS\"}" > /dev/null
echo "Finished deployment $BIFROST_DEPLOYMENT as $BIFROST_STATUS."
fiThe run id and the attempt make the idempotency key, so a re-run of the same attempt records nothing twice and a fresh attempt records a fresh deployment.
Use it in a workflow
Call the action once before the deployment and once after, with a finish step that always runs and reads the job's own outcome:
name: Deploy to staging
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- id: report
uses: ./.github/actions/zuse-bifrost-deployment
with:
api: ${{ vars.BIFROST_API }}
token: ${{ secrets.BIFROST_TOKEN }}
project: ${{ vars.BIFROST_PROJECT }}
environment: ${{ vars.BIFROST_STAGING }}
version: ${{ github.sha }}
phase: start
- name: Deploy
run: ./scripts/deploy.sh staging
- if: always()
uses: ./.github/actions/zuse-bifrost-deployment
with:
api: ${{ vars.BIFROST_API }}
token: ${{ secrets.BIFROST_TOKEN }}
project: ${{ vars.BIFROST_PROJECT }}
environment: ${{ vars.BIFROST_STAGING }}
version: ${{ github.sha }}
phase: finish
deployment: ${{ steps.report.outputs.deployment }}
status: ${{ job.status == 'success' && 'succeeded' || (job.status == 'cancelled' && 'cancelled' || 'failed') }}Use a tag or a version file as version on a release workflow (${{ github.ref_name }} on a tag
push); the commit is reported beside it either way. A workflow that deploys to several
environments calls the pair once per environment with that environment's id.
Secrets and variables
Under the repository's settings, add the token as a secret and the rest as variables, so the workflow file carries no ids of its own:
| Name | Kind | Value |
|---|---|---|
BIFROST_TOKEN | secret | The ingestion token, shown once when it was minted. |
BIFROST_API | variable | The API base address your workspace answers on. |
BIFROST_PROJECT | variable | The project's id. |
BIFROST_STAGING | variable | The staging environment's id; one variable per environment. |
An organisation that deploys many repositories into the same workspace keeps the token and the address as organisation-level secrets and variables, and the ids per repository.
Report in one step instead
A workflow that only wants the outcome on record reports once, after the deployment, with the outcome as the status:
- if: always()
env:
BIFROST_API: ${{ vars.BIFROST_API }}
BIFROST_TOKEN: ${{ secrets.BIFROST_TOKEN }}
BIFROST_PROJECT: ${{ vars.BIFROST_PROJECT }}
BIFROST_STAGING: ${{ vars.BIFROST_STAGING }}
OUTCOME: ${{ job.status == 'success' && 'succeeded' || 'failed' }}
run: |
curl -sS --fail-with-body -X POST "$BIFROST_API/v1/projects/$BIFROST_PROJECT/deployments" \
-H "Authorization: Bearer $BIFROST_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT" \
-d "{\"environment_id\": \"$BIFROST_STAGING\", \"version\": \"$GITHUB_SHA\", \"status\": \"$OUTCOME\", \"commit_sha\": \"$GITHUB_SHA\", \"branch\": \"$GITHUB_REF_NAME\", \"pipeline_url\": \"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\"}"Or use GitHub deployments
A workflow that creates a GitHub deployment, through the Deployments API or an action built on it,
needs none of the above when the repository was picked from the team's App installation: the App
hears the deployment and each status the workflow gives it, and records them on the environment
whose name matches the one the workflow used. Name the environments in Zuse Bifrost the way the
workflow names them, and set a version field in the deployment's payload when you want the
record to carry a version rather than the branch or the tag; see
CI and deployments.
One or the other
Use the action, or GitHub deployments, not both for the same job: each writes its own record, and a job that does both shows twice.
Frequently asked
The start step fails with 403. The token lacks ci:deploy, is confined to another project, or
belongs to someone whose role does not carry deployment:record. Mint it again as described on
CI/CD ingestion.
The finish step fails with 422. The deployment ended already, which happens when a re-run of the same attempt reaches the finish step twice; the record is right and the job may ignore it.
The start step fails with 404. The environment id is not one of the project's. Copy it from the environment's page address, and keep one variable per environment.
Can I report the test results too? The test management module brings ci:test-results and a
step that uploads a JUnit report; the deployment steps stay as they are.
Related
- CI/CD ingestion: the token and the calls the action makes.
- GitLab CI: the same from GitLab, and from any other pipeline.
- CI and deployments: what the record looks like inside the application.
- GitHub: the App, whose deployment events are the alternative.
How is this guide?
Last updated on
GitHub
Install the GitHub App once for the team, pick its repositories onto each project and open branches from items, or register a webhook by hand per repository; what arrives either way, how deliveries are verified, and what to check when nothing links.
GitLab CI and other pipelines
A GitLab CI job that reports a deployment as it starts and as it ends, with the pipeline id as the idempotency key, and the two calls that do the same from Bitbucket Pipelines, Jenkins or any other runner.