Skip to main content

Running in CI

This page is about keeping the CI you already have. If you'd rather run your own control plane instead, that's Self-hosting — and if you want FlowWright itself to react to a push, that's Integrations, which is a different mechanism that shares every proper noun with this page.

FlowWright doesn't replace GitHub Actions or GitLab CI. It replaces the body of the job: instead of twenty YAML steps, one step that runs flow run, with the actual work described in pipeline.ts.

The payoff is that the same pipeline runs on your laptop. flow run locally and flow run in CI execute the identical plan — no "works on my machine" gap because there's no second definition of the build.

What the job has to provide

Four things, in every provider:

  1. A checkout of your repository.
  2. Node.js 24 or newer.
  3. Your dependencies, if pipeline.ts imports anything beyond @flowwright/core.
  4. flow run.

That's the whole integration. Everything else — ordering, parallelism, caching, containers — is in your pipeline, not in the CI config.

flow export prints a starting point for each provider — see Project setup.

Output adapts automatically

flow run detects the provider from the environment and picks a matching reporter:

EnvironmentReporterWhat you get
GITHUB_ACTIONS=truegithubCollapsible log groups, failure annotations, a job summary
GITLAB_CI=truegitlabCollapsed sections per stage
CI=true or CI=1ciPlain text, no color
otherwiseprettyColored, live

You don't normally set anything. Override with --reporter when the detection guesses wrong. Full detail on Reporters.

Caching between jobs

FlowWright caches inside the pipeline — cache.restore/cache.save in a stage body, covered in Artifacts, cache and stash. That cache lives in .flowwright/cache, relative to the directory you run from. The path is fixed; there's no flag or environment variable to move it.

To make it survive between CI jobs, persist that directory with your provider's own cache action. The per-provider recipes are on each page.

Two things worth knowing before you write the key:

  • Entries are content-addressed and never overwritten. A key that already exists is a no-op on save, so a restore-keys prefix chain can only ever hit an entry that was written for exactly that key. Prefix restores are safe.
  • The directory only grows. There's no eviction. If your runner's cache quota bites, put something that rotates into the key — a date, or a lockfile hash.

Test reports

--junit <path> writes a JUnit XML file alongside whatever the console reporter is doing. Every provider on this list can ingest it, and it's the one integration that works everywhere:

flow run --junit report.xml

One stage becomes one test case. Failures carry the failing command and its exit code.

Upload it unconditionallyif: always() on GitHub, when: always on GitLab. The run that failed is the one whose report you want, and a step that only runs on success will skip exactly then.

Cancellation

When a runner cancels a job it sends SIGTERM. FlowWright stops the pipeline, finishes writing its reports — the JUnit file and the GitHub job summary — and exits 130. So a cancelled job still produces a report showing which stages had completed.

What each provider gives you

GitHub ActionsGitLab CIJenkins
Log grouping::group:: per stagecollapsed sections
Failure annotationsyes, on the diff
Job summaryyes
Test reportvia --junitvia --junitvia --junit
Dedicated reporteryesyesno — uses ci

Jenkins has no dedicated reporter: it gets the plain ci output plus JUnit, which is the whole integration.

Anything else

For a provider with no page here, --reporter ci plus --junit is the whole integration, and it works everywhere. If you want to drive your own dashboard, --reporter json streams the run as newline-delimited events.