CI & Docker
Because render exits non-zero unless the verdict is publishable, it drops into a pipeline as an end-to-end test whose failure artifact is a video someone will actually watch.
The GitHub Action#
A composite action ships in the repository. It caches and installs Chromium, renders your specs, uploads the artifacts and comments on the pull request.
name: video
on: [pull_request]
jobs:
walkthrough:
runs-on: ubuntu-latest
permissions:
contents: write # only needed for host-pages
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- name: Start the app
run: npm ci && npm run start &
- uses: Cypher-aman/playhead@v0
with:
specs: specs/tour.yaml specs/onboarding.yaml
out: playhead-out
workers: 2
host-pages: true| Input | Meaning |
|---|---|
specs | Required. Space-separated spec paths, or a glob expanded by an earlier shell step. |
out | Output directory for bundles, videos and verdicts. Default playhead-out. |
workers | Concurrent renders when given several specs. Default 2. |
playhead-version | The playhead-cli version to run — an npm dist-tag or an exact version. Default latest; pin it for reproducible builds. |
signing-key | Sets PLAYHEAD_SIGNING_KEY, so verdicts are HMAC-signed and attestable. |
host-pages | Deploy the evidence page to gh-pages so the PR comment is a playable URL instead of an artifact zip. Requires Pages source = gh-pages. |
Outputs: junit (the report path) and evidence-url (the playable page, when host-pages is on).
Failures that read well#
playhead render specs/*.yaml -o out --junit report.xmlJUnit output emits one testcase per verification check, so a failure names the rule it broke — cursor-on-target, captions, output-pixels — rather than reporting that the video job failed. Pair it with any test reporter action and the review UI shows exactly which craft rule regressed.
1 means your app broke the flow, and Playhead still writes a failure clip that ends on the step that broke with the reason on screen. Upload it — it is usually a better bug report than the stack trace beside it. All exit codes →Docker#
The bundled Dockerfile runs the whole pipeline on the Playwright base image, which already carries the browser and its system libraries:
docker build -t playhead .
docker run --rm -v "$PWD:/work" -w /work playhead \
render specs/tour.yaml -o outPoint specs at host.docker.internal (or run the app in the same network) when the app under test lives outside the container.
Container prerequisites#
playhead doctor --ciThis adds the checks that only matter in a container: the narration provider, the browser sandbox, and /dev/shm — a small shm allocation is the classic cause of Chromium dying mid-capture. Run it as the first step of a new pipeline and you will skip an afternoon.
| Symptom | Usually |
|---|---|
| Chromium crashes partway through capture | Too little /dev/shm. Give the container ~1GB. |
| Narration is silent in CI but works locally | The Kokoro model was not installed in the image. Run npx playhead init --no-browser during the build, or accept silent output. |
| Fonts render as boxes | Install the font packages, or let doctor tell you which are missing. |
| Captures differ run to run | Set app.environment (timezone, locale, fixedTime) and block third-party requests with app.network.block. |
Exit 3 immediately | An infrastructure problem — ffmpeg, the browser or fonts. Not your spec. |
Rendering a pool#
playhead render specs/*.yaml -o out -w 4Several specs render concurrently with per-spec subdirectories under out. The process exits non-zero if any spec is not publishable.
Catching regressions#
playhead compare artifacts/baseline current/Verdict drift, newly failing checks, coverage change and per-step timing deltas, exiting 2 when the walkthrough regressed. Keep the last green run as a baseline artifact and this turns “the demo feels slower” into a number.
Reporting into a tracker#
playhead render specs/repro.yaml --jira PROJ-482Attaches the video or failure clip, the verdict, the contact sheet, the JUnit report and the console log to an existing issue, and posts a verdict comment — on success or on a flow failure. Credentials come from the environment; Playhead never creates issues.