GitLab CI
The minimum
flow export gitlab-ci > .gitlab-ci.yml
flow:
image: node:24
script:
- npx @flowwright/cli run
The reporter is selected automatically — GITLAB_CI is always true in a job.
A realistic job
flow:
image: node:24
cache:
key:
files:
- pnpm-lock.yaml
paths:
- .flowwright/cache
- node_modules
script:
- corepack enable
- pnpm install --frozen-lockfile
- pnpm exec flow run -j 4 --junit report.xml
artifacts:
when: always
reports:
junit: report.xml
when: always on the artifacts block is the important line — without it you lose the
report on the runs that failed, which are the ones worth looking at.
GitLab's cache.key.files recomputes the key when the lockfile changes, which pairs
well with how FlowWright keys its own entries. See
the overview for why prefix restores are safe here.
What the log looks like
Each stage is a collapsed section you can expand:
FlowWright · acme-api
section_start:1785130147:install[collapsed=true]Install
$ npm ci
[07:29:07] up to date, audited 1 package in 140ms
section_end:1785130147:install
✓ install 201ms
section_start:1785130147:lint[collapsed=true]Lint
$ npm run lint
[07:29:07] lint: 0 problems
section_end:1785130147:lint
✓ lint 98ms
Sections are collapsed by default, so a long job reads as a list of stages with timings. A failed stage shows its exit code:
✗ test 22ms (exit 2)
⊘ deploy (skipped: upstream-failed)
Under -j each section is emitted whole when its stage finishes — overlapping section
markers are undefined behaviour in GitLab, so concurrent stages are buffered.
Test reports
artifacts.reports.junit makes GitLab render the results in the merge request widget
and the pipeline's Tests tab. One stage becomes one test case, and a failure carries
the command and exit code:
<testcase name="test" classname="acme-api" time="0.022">
<failure message="stage failed">command: node -e process.exit(2)
exit code: 2</failure>
</testcase>
Cancellation
Cancelling a job sends SIGTERM. FlowWright stops, writes the JUnit file, and exits
130 — so the report survives and shows what had finished.
Stages: GitLab's and FlowWright's
Both use the word, and they're different things. A GitLab stage is a group of jobs that
run together; a FlowWright stage is a node in your pipeline's graph. Almost always you
want one GitLab job running the whole FlowWright pipeline, and the DAG inside
pipeline.ts. Splitting FlowWright stages across GitLab jobs means each job re-clones,
re-installs, and can't share the cache in-process.
FlowWright's own repository has no .gitlab-ci.yml. The reporter output above is
captured from a real GITLAB_CI=true run, but the YAML is written from GitLab's
documented format rather than copied from a pipeline we operate. If something here
doesn't match your GitLab version, trust GitLab's docs.