Bowire in CI
bowire is a self-contained .NET tool, so any CI runner that can install a .NET global tool can run your API tests headless. Every runner exits non-zero on failure (unless you soften it with --fail-on never) and can emit JUnit XML + SARIF for the platform's native reporters.
The command
dotnet tool install -g Kuestenlogik.Bowire.Tool
# one flow / collection
bowire test ./flows/smoke.json --junit results.xml --sarif results.sarif
# every flow in a git-native workspace directory (aggregates pass/fail)
bowire test --workspace ./bowire --junit results.xml
| Flag | Meaning |
|---|---|
--junit <file> |
JUnit XML — Jenkins, GitLab CI, Azure DevOps, GitHub test reporters. |
--sarif <file> |
SARIF 2.1.0 — GitHub Code Scanning tab. |
--annotations |
GitHub ::error inline PR annotations (no reporter action needed). |
--fail-on any \| never |
any (default) exits non-zero on any failed check; never runs + reports but always exits 0 (a step error still exits 2, so a broken backend is never masked). |
--workspace <dir> |
Run every Flow JSON in a workspace directory's flows/ folder; per-flow reports are written as <report>.<flow>.<ext> so a glob picks them all up. |
--env-file <f> / --env KEY=VAL |
Feed the {{var}} resolver; secrets stay in env vars, never in checked-in files. |
--secret <NAME> / --secret-file <f> |
Mark variables whose resolved values must be redacted from every output (see Secrets). |
Exit codes: 0 all passed · 1 an assertion / expectation failed · 2 a step errored before evaluation (backend down, malformed file).
Redacting secrets
A failing assertion prints the expected + actual values, and those land verbatim in the TTY log, the JUnit failure text, the SARIF message, and the GitHub ::error annotation. If a token or password flows through a request or a response, mark the variable that carries it as a secret and Bowire masks its value everywhere it would otherwise appear:
# resolve DEPLOY_TOKEN normally, but redact its value from every output
bowire test ./flows/smoke.json \
--env-file .env.ci \
--secret DEPLOY_TOKEN --secret API_KEY \
--junit results.xml --sarif results.sarif --annotations
--secret <NAME>is repeatable;--secret-file <file>reads one variable name per line (blank lines and#comments ignored) so a long list stays off the command line. The file holds names, never values — the value is resolved from the run's variable scope (--env,--env-file,--keyring), so secret values never live in a checked-in file.- The variable still resolves normally into the request/assertion — only its rendered value is masked in the output. A value longer than eight characters renders as
***plus its last four (e.g.***c5f9) so two different secrets stay distinguishable in a diff; a shorter value collapses to a bare***. The mask is stable across a run. - Empty / whitespace values are never redacted (masking
""would blank every line).
Feed the actual value in from your CI's secret store through --env / --env-file (or --keyring), exactly as you already do for any other variable — the checked-in flow file only references {{DEPLOY_TOKEN}}:
- run: >-
bowire test ./flows/smoke.json --junit results.xml --annotations
--env DEPLOY_TOKEN=${{ secrets.DEPLOY_TOKEN }}
--secret DEPLOY_TOKEN
GitHub Actions
Use the bundled composite action — it installs the tool and runs any bowire command:
name: API tests
on: [push, pull_request]
jobs:
bowire:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: Kuestenlogik/Bowire/packaging/github-action@v2
with:
dotnet-version: "10.0.x"
args: test --workspace ./bowire --junit results.xml --sarif results.sarif --annotations
- name: Publish test report
if: always()
uses: dorny/test-reporter@v1
with:
name: Bowire
path: results*.xml
reporter: java-junit
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
Or without the action, straight from a shell step:
- uses: actions/setup-dotnet@v4
with: { dotnet-version: "10.0.x" }
- run: dotnet tool install -g Kuestenlogik.Bowire.Tool
- run: bowire test ./flows/smoke.json --junit results.xml --annotations
Contract testing in CI
The consumer publishes, the provider verifies — see Contract testing:
# consumer pipeline
- uses: Kuestenlogik/Bowire/packaging/github-action@v2
with:
args: >-
contract publish traces.bwr --provider order-service
--broker-url ${{ secrets.PACT_BROKER_URL }}
--consumer-version ${{ github.sha }} --tag ${{ github.ref_name }}
# provider pipeline
- uses: Kuestenlogik/Bowire/packaging/github-action@v2
with:
args: >-
contract verify --broker-url ${{ secrets.PACT_BROKER_URL }}
--provider order-service --tag main
--provider-url http://localhost:8080 --junit contract-results.xml
GitLab CI
bowire:
image: mcr.microsoft.com/dotnet/sdk:10.0
script:
- dotnet tool install -g Kuestenlogik.Bowire.Tool
- export PATH="$PATH:$HOME/.dotnet/tools"
- bowire test --workspace ./bowire --junit results.xml
artifacts:
when: always
reports:
junit: results*.xml