CI recipes
Working pipeline snippets for the common shapes. All of them assume
SEKEE_API_TOKEN is set as a secret and that you know your project's
projectId.
GitHub Actions — one job
- name: Upload test reports
if: always() # a failed test run is exactly what you want uploaded
run: |
npx @sekee/sekee-cli upload-reports \
--url ${{ secrets.SEKEE_API_URL }} \
--name "PR #${{ github.event.number }} Tests" \
--projectID ${{ secrets.SEKEE_PROJECT_ID }} \
--source "GitHub Actions" \
--tags "pr,automated" \
--reports "./cypress/reports"
env:
SEKEE_API_TOKEN: ${{ secrets.SEKEE_API_TOKEN }}
:::tip if: always()
Without it the upload is skipped whenever tests fail, which is the run you most
wanted to see. This is the single most common mistake in a Sekee pipeline.
:::
GitHub Actions — parallel shards
Three jobs: open the run, report into it from each shard, close it.
jobs:
init:
runs-on: ubuntu-latest
outputs:
run-id: ${{ steps.init.outputs.run-id }}
steps:
- id: init
run: |
OUTPUT=$(npx @sekee/sekee-cli init \
--url ${{ secrets.SEKEE_API_URL }} \
--name "PR #${{ github.event.number }}" \
--projectID ${{ secrets.SEKEE_PROJECT_ID }} \
--source "GitHub Actions" --tags "pr")
echo "run-id=$(echo $OUTPUT | grep -oP 'Run ID: \K\d+')" >> $GITHUB_OUTPUT
env:
SEKEE_API_TOKEN: ${{ secrets.SEKEE_API_TOKEN }}
test:
needs: init
strategy:
fail-fast: false # one shard failing must not skip the others' uploads
matrix:
shard: [1, 2, 3]
runs-on: ubuntu-latest
steps:
- run: npx playwright test --shard=${{ matrix.shard }}/3
- name: Report this shard
if: always()
run: |
npx @sekee/sekee-cli thread \
--url ${{ secrets.SEKEE_API_URL }} \
--runId ${{ needs.init.outputs.run-id }} \
--name "Shard ${{ matrix.shard }}" \
--report "./results.xml"
env:
SEKEE_API_TOKEN: ${{ secrets.SEKEE_API_TOKEN }}
complete:
needs: [init, test]
if: always() # close the run even if a shard failed
runs-on: ubuntu-latest
steps:
- run: |
npx @sekee/sekee-cli complete \
--url ${{ secrets.SEKEE_API_URL }} \
--runId ${{ needs.init.outputs.run-id }}
env:
SEKEE_API_TOKEN: ${{ secrets.SEKEE_API_TOKEN }}
The two if: always() and the fail-fast: false are all doing the same job:
making sure a red test run still produces a complete record.
GitLab CI
upload-reports:
stage: report
when: always
image: node:20
script:
- npx @sekee/sekee-cli upload-reports
--url "$SEKEE_API_URL"
--name "Pipeline $CI_PIPELINE_IID"
--projectID "$SEKEE_PROJECT_ID"
--source "GitLab CI"
--tags "$CI_COMMIT_REF_NAME"
--reports "./reports"
Jenkins (declarative)
post {
always {
withEnv(["SEKEE_API_TOKEN=${env.SEKEE_API_TOKEN}"]) {
sh '''
npx @sekee/sekee-cli upload-reports \
--url "$SEKEE_API_URL" \
--name "Build ${BUILD_NUMBER}" \
--projectID "$SEKEE_PROJECT_ID" \
--source "Jenkins" \
--reports "./target/surefire-reports"
'''
}
}
}
Producing the XML
Sekee reads JUnit-style XML. Common producers:
| Framework | How |
|---|---|
| Playwright | reporter: [['junit', { outputFile: 'results.xml' }]] |
| Cypress | mocha-junit-reporter, or cypress-multi-reporters alongside another |
| Jest | jest-junit |
| Vitest | --reporter=junit --outputFile=results.xml |
| pytest | pytest --junitxml=results.xml |
| Maven / Gradle | Surefire and Gradle's test task write it by default |
Naming runs so they are findable
The run name is what you will scan a list of. Include the thing that distinguishes this run from the next one — a PR number, a build number, a branch — rather than the thing they all share.
- Good:
PR #412 — checkout,Nightly 2026-09-07,main @ a1b2c3d - Less good:
Test run,CI,Automated tests
Use --source for where it ran and --tags for how you will slice it later.