CI/CD examples¶
These examples run a Helena test from a pipeline and fail the job unless the run passes. Each one needs:
- A service account with the Write scope and access to the test's environment.
- The environment UUID and test UUID, stored as pipeline variables (
HELENA_ENV_UUID,HELENA_TEST_UUID). curlandjqon the runner.
Prefer OIDC workload identity where your CI platform supports it. Then there is no Helena secret to store, leak or rotate.
GitHub Actions (OIDC workload identity)¶
On the service account, add an OIDC trust:
| Field | Value |
|---|---|
| Issuer URL | https://token.actions.githubusercontent.com |
| Subject (sub claim) | repo:acme/app:ref:refs/heads/main |
| Audience (aud claim) | https://app.helena.dev/api/v1/service-account/auth (the default) |
| Additional claim conditions | repository_owner = acme (optional) |
Store the trust's client ID as the repository variable HELENA_CLIENT_ID.
name: Helena checks
on:
push:
branches: [main]
schedule:
- cron: "0 6 * * *"
permissions:
id-token: write # required to request the OIDC token
contents: read
jobs:
helena:
runs-on: ubuntu-latest
steps:
- name: Get Helena token
run: |
ID_TOKEN=$(curl -sf -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://app.helena.dev/api/v1/service-account/auth" | jq -r .value)
TOKEN=$(curl -sf -X POST https://app.helena.dev/api/v1/service-account/auth \
-d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
-d client_id=${{ vars.HELENA_CLIENT_ID }} -d assertion="$ID_TOKEN" | jq -r .access_token)
echo "::add-mask::$TOKEN"
echo "HELENA_TOKEN=$TOKEN" >> "$GITHUB_ENV"
- name: Run Helena test
run: |
curl -sf -X POST -H "Authorization: Bearer $HELENA_TOKEN" -H 'Content-Type: application/json' \
-d '{"wait": true}' \
"https://app.helena.dev/api/v1/environments/${{ vars.HELENA_ENV_UUID }}/tests/${{ vars.HELENA_TEST_UUID }}/runs" \
| tee result.json | jq -e '.data.status == "passed"'
- name: Show failed assertions
if: failure()
run: jq '.data.assertions[] | select(.passed == false)' result.json
For pull requests the subject is repo:acme/app:pull_request. For a GitHub environment, it is
repo:acme/app:environment:production. Add one trust per subject you want to allow.
GitLab CI (OIDC workload identity)¶
On the service account, add an OIDC trust:
| Field | Value |
|---|---|
| Issuer URL | https://gitlab.com (or your self-managed GitLab URL) |
| Subject (sub claim) | project_path:acme/app:ref_type:branch:ref:main |
| Audience (aud claim) | https://app.helena.dev/api/v1/service-account/auth |
helena:
image: alpine:3.20
id_tokens:
HELENA_ID_TOKEN:
aud: https://app.helena.dev/api/v1/service-account/auth
before_script:
- apk add --no-cache curl jq
script:
- >
TOKEN=$(curl -sf -X POST https://app.helena.dev/api/v1/service-account/auth
-d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
-d client_id=$HELENA_CLIENT_ID -d assertion=$HELENA_ID_TOKEN | jq -r .access_token)
- >
curl -sf -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json'
-d '{"wait": true}'
"https://app.helena.dev/api/v1/environments/$HELENA_ENV_UUID/tests/$HELENA_TEST_UUID/runs"
| tee result.json | jq -e '.data.status == "passed"'
rules:
- if: $CI_COMMIT_BRANCH == "main"
A self-managed GitLab instance must be reachable from the internet over HTTPS, so Helena can fetch its OIDC discovery document and keys.
Any CI (client secret)¶
Store the client ID and secret as masked secrets (HELENA_CLIENT_ID, HELENA_CLIENT_SECRET).
#!/usr/bin/env bash
set -euo pipefail
TOKEN=$(curl -sf -X POST https://app.helena.dev/api/v1/service-account/auth \
-d grant_type=client_credentials \
-d client_id="$HELENA_CLIENT_ID" \
-d client_secret="$HELENA_CLIENT_SECRET" | jq -r .access_token)
curl -sf -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"wait": true}' \
"https://app.helena.dev/api/v1/environments/$HELENA_ENV_UUID/tests/$HELENA_TEST_UUID/runs" \
| tee result.json | jq -e '.data.status == "passed"'
# Revoke the token when done
curl -sf -X POST -H "Authorization: Bearer $TOKEN" https://app.helena.dev/api/v1/service-account/auth/revoke >/dev/null
Running every test in an environment¶
for TEST in $(curl -sf -H "Authorization: Bearer $TOKEN" \
"https://app.helena.dev/api/v1/environments/$HELENA_ENV_UUID/tests" \
| jq -r '.data[] | select(.enabled) | .uuid'); do
STATUS=$(curl -sf -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"wait": true}' \
"https://app.helena.dev/api/v1/environments/$HELENA_ENV_UUID/tests/$TEST/runs" | jq -r .data.status)
echo "$TEST: $STATUS"
[ "$STATUS" = "passed" ] || FAILED=1
done
exit ${FAILED:-0}
Triggering is limited to 30 runs per minute per service account. Add a short sleep for large environments.
Queue and poll instead of waiting¶
If your runner has a short HTTP timeout, queue the run and poll its Location:
LOCATION=$(curl -s -D - -o /dev/null -X POST -H "Authorization: Bearer $TOKEN" \
"https://app.helena.dev/api/v1/environments/$HELENA_ENV_UUID/tests/$HELENA_TEST_UUID/runs" \
| awk 'tolower($1)=="location:" {print $2}' | tr -d '\r')
until curl -sf -H "Authorization: Bearer $TOKEN" "$LOCATION" \
| jq -e '.data.status | IN("passed","failed","error")' >/dev/null; do
sleep 2
done
curl -sf -H "Authorization: Bearer $TOKEN" "$LOCATION" | jq -e '.data.status == "passed"'