CI & GitHub Actions
The CLI exits non-zero on any failure, so it drops into any pipeline as a required check.
The pattern
Run the CLI against your case directory. A non-zero exit fails the job, which fails the check, which blocks the merge — the same gate you already trust for unit tests, now covering release-critical integration behavior and flag proof.
name: Release-proof gate
on:
pull_request:
jobs:
release-proof:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: "8.0.x"
- run: dotnet build src/ReleaseTwin.Cli/ReleaseTwin.Cli.csproj -c Release --nologo
- name: Run release-proof cases
run: >
dotnet run --project src/ReleaseTwin.Cli -c Release --no-build --
cases/With the versioned CLI image, the build step goes away and the gate is a single line:
- run: docker run --rm -v "$PWD/cases:/workspace:ro" ghcr.io/ernestoalejowitt22/releasetwin/cli:0.4.0 run /workspaceAlready have a suite?
If your pipeline already runs Playwright, pytest, or Jest, you do not need a case file to get its results here. Upload the JUnit XML that run already writes — see Bring your existing suite.
Credentials
- HTTP-only cases need nothing. Case files reference
${ENV_VAR}; set those from your CI's secret store. - A flag-proof leg needs its flag source's credentials as job env — e.g.
LAUNCHDARKLY_API_TOKEN: ${{ secrets.LAUNCHDARKLY_API_TOKEN }}. - Connecting the hosted dashboard from GitHub Actions needs no secret. Give the job
permissions: id-token: writeand name your project; the CLI exchanges the job's own GitHub OIDC token for a short-lived, ingest-only credential. The project must be bound to the repository on its Settings page, so neither a stray repository nor a stray project can route an upload where it should not go. - Other CI systems, or a stored token by choice: add
RELEASETWIN_API_TOKENandRELEASETWIN_API_URL. A stored token always wins when both are configured.
permissions:
contents: read
id-token: write
jobs:
release-proof:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker run --rm -v "$PWD:/workspace:ro" \
-e ACTIONS_ID_TOKEN_REQUEST_URL -e ACTIONS_ID_TOKEN_REQUEST_TOKEN \
-e RELEASETWIN_PROJECT_ID=<project id from the dashboard URL> \
ghcr.io/ernestoalejowitt22/releasetwin/cli:0.4.0 run /workspace/casesNaming a project is treated as intent: if the permission is missing or the exchange is refused, the run stops with one line naming the fix, and the summary records why so the PR comment can show it. Fork pull requests get no OIDC token from GitHub and upload nothing, exactly as before. Either way, run history and evidence then land on the dashboard for every CI run.
PR annotations
The integrations/github-action Action runs your cases and renders the result onto the pull request — a comment (updated in place on re-runs) with the pass/fail totals and flag-proof verdict, plus a ReleaseTwin check run you can make a required status check. It talks only to GitHub's own APIs with the workflow's GITHUB_TOKEN — no ReleaseTwin account. It is Apache-2.0, so you can fork and adapt it. The GitHub Marketplace listing is the install card for this Action, not how a stranger discovers the product — see the case file it produces, no account required.
permissions:
contents: read
pull-requests: write
checks: write
jobs:
release-proof:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: releasetwin/releasetwin-action@v0
with:
cases-path: cases
image: ghcr.io/ernestoalejowitt22/releasetwin/cli:0.4.0The CLI still writes a machine-readable summary on its own with --summary-json <path> (or RELEASETWIN_SUMMARY_JSON) if you want to render it somewhere else.
Bitbucket, GitLab, and other CI
The GitHub Action is a convenience wrapper. The portable primitive is the non-zero exit code plus the --summary-json file — nothing about either is GitHub-specific. On Bitbucket Pipelines the gate is one step:
pipelines:
pull-requests:
'**':
- step:
name: Release-proof gate
services: [docker]
script:
- >
docker run --rm -v "$BITBUCKET_CLONE_DIR/cases:/workspace:ro" -v "$BITBUCKET_CLONE_DIR:/out"
ghcr.io/ernestoalejowitt22/releasetwin/cli:0.4.0 run /workspace --summary-json /out/releasetwin-summary.json
artifacts:
- releasetwin-summary.jsonA non-zero exit fails the step and blocks the merge with no extra wiring. To render the summary as a Bitbucket PR comment, parse releasetwin-summary.json and POST it to the /2.0/repositories/{workspace}/{repo}/pullrequests/{id}/comments API — the same shape as the GitHub Action's render.mjs, which you can adapt.
Merge gate from CI
The exit code proves the cases you just ran. The flag-proof merge gate asks a different question: has every flag this PR ships been proven for the release it targets, from the flag-proof evidence already uploaded? Call it from CI with the project's API token, the release label, and the flag keys; block the merge on the answer.
curl -sS -X POST "$RELEASETWIN_API_URL/api/cli/flag-proof-gate/check" \
-H "Authorization: Bearer $RELEASETWIN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "release": "4.2", "flagKeys": ["checkout-v2"] }'{
"release": "4.2",
"verdict": "pass",
"flags": [
{ "flagKey": "checkout-v2", "state": "proven", "caseIds": ["CHECKOUT-1"], "reason": null }
]
}pass— every flag isproven: its backing cases are green in the release's recency window.fail— at least one flag isnot_proven: a backing case is failing.incomplete— no flag is failing, but at least one isincomplete: no case declares it, its evidence is stale, or its case is flaky. Thereasonnames which.
The verdict is computed from the same release rollup the dashboard shows, and every check that reaches a verdict is recorded on the project's Merge gate card. Both release and a non-empty flagKeys are required (400 otherwise). The entitlement is checked before any verdict is computed: a project whose tier lacks the gate gets a 403 and nothing is recorded.
{ "error": "entitlement-required", "entitlement": "flagProofMergeGate" }Gate the job on the verdict — for example, fail the step unless verdict is pass — and make it a required check, the same way as the ghcr.io/ernestoalejowitt22/releasetwin/cli:0.4.0 run above. Every tier grants the gate; the recorded history is on the project's dashboard, and readable over the programmatic API on Team.
Proven live, not just typed
The CLI is as stack-agnostic on the application side as it is on the CI side above: case files describe generic pipeline operations, not framework-specific adapters, so nothing here is Express-, React-, or Angular-specific either. These three different stacks are examples proving that, not the limit of what's supported.
releasetwin-ci-examples runs those three real demo apps (Express, React, Angular) through real CI platforms on every push, mirrored to releasetwin-ci-example-projects on Bitbucket — not illustrative snippets. Bitbucket Pipelines is verified green: build #1. Azure Pipelines is verified green too: build #239 — all three jobs (Express, React, Angular) passed, built directly from the GitHub repo via a service connection, no mirror needed.
Live example in this repo
.github/workflows/releasetwin-demo.yml runs the zero-credential HTTP case on every PR and on demand — a real, green release-proof gate you can copy. A passing run's step output:
$ dotnet run --project src/ReleaseTwin.Cli -c Release --no-build -- demo/quickstart/cases
PASS HTTP-DEMO-1
1 passed, 0 failedNext
Case files for what each case can assert, or Hosted platform to send CI run history to the dashboard.