Getting a project onto FlowWright
flow init
flow init
Reads the project first, then writes a pipeline.ts that matches it.
✓ created pipeline.ts
detected: npm · lint, typecheck, test, build
next: flow run
What it detects:
| Signal | Effect |
|---|---|
pnpm-lock.yaml / yarn.lock / package-lock.json | Picks the package manager and the right install command |
packageManager in package.json | Overrides the lockfile |
name in package.json | Becomes the pipeline name, scope stripped |
Scripts named lint, typecheck, test, build | Each becomes a stage |
The install command follows the lockfile: npm ci for npm, install --frozen-lockfile
for pnpm and yarn, plain install when there's no lockfile at all. Checks depend on
install, build depends on the checks.
In a directory with no package.json, you get something runnable to edit rather than
an error:
import { pipeline, stage, sh } from "@flowwright/core";
export default pipeline({
name: "bare",
stages: [
stage("Hello", async () => {
await sh`echo "hello from FlowWright"`;
}),
],
});
flow init won't overwrite an existing file — it exits 1 instead. Use -f to write
somewhere else:
flow init -f pipelines/release.ts
--plugins
flow init --plugins
Emits the plugin-node equivalent instead of spelled-out stages:
import { pipeline, group } from "@flowwright/core";
import { nodeStages } from "@flowwright/plugin-node";
export default pipeline({
name: "api",
stages: [...group("ci", nodeStages({ packageManager: "pnpm" }))],
});
✓ created pipeline.ts
detected: pnpm
install: pnpm add -D @flowwright/plugin-node
next: flow run
It needs a package.json to work with. Without one it writes the plain pipeline and
tells you:
! --plugins needs a Node project; wrote a plain pipeline instead
flow migrate
flow migrate jenkinsfile ./Jenkinsfile
flow migrate ./Jenkinsfile # the jenkinsfile keyword is optional
migrate is a regex scan over Groovy, not a Jenkins parser. It reads well-formed
declarative pipelines and gives up gracefully on the rest. Treat the result as a
first draft — read every line before running it.
Migration report · ./Jenkinsfile
4 stages: Build, Lint, Test, Deploy
5 shell steps
difficulty: easy
--- suggested pipeline.ts ---
// Generated by `flow migrate jenkinsfile` — best-effort; review before use.
import { pipeline, stage, sh, onBranch } from "@flowwright/core";
const env = {
"CI": "true",
};
export default pipeline({
name: "migrated-pipeline",
concurrency: 2,
stages: [
stage("Build", {
id: "build",
run: async () => {
await sh.raw("pnpm install", { env });
await sh.raw("pnpm build", { env });
},
}),
],
});
The report comes first — stage count, step count, and a difficulty estimate — so you can judge whether the conversion is worth trusting before reading it.
Note that migrated steps use sh.raw, which runs through a shell. That's the honest
translation of a Jenkins sh step, but converting them to sh([...]) argv arrays as
you review removes a layer of quoting.
Write it to a file instead of stdout with --out:
flow migrate jenkinsfile ./Jenkinsfile --out pipeline.ts
flow export
Prints a CI config that calls FlowWright. Your pipeline logic stays in pipeline.ts;
the exported file is just the wrapper that invokes it.
flow export github-actions
name: CI
on: [push, pull_request]
jobs:
flow:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: 24
- run: npx @flowwright/cli run
flow export gitlab-ci
flow:
image: node:24
script:
- npx @flowwright/cli run
flow export jenkins
pipeline {
agent { docker { image 'node:24' } }
stages {
stage('FlowWright') {
steps {
sh 'npx @flowwright/cli run'
}
}
}
}
Each is printed to stdout with a leading comment naming where it belongs — redirect it yourself:
flow export github-actions > .github/workflows/flowwright.yml
These are starting points, meant to be edited. They're deliberately minimal: one job, one command. Caching, matrices and artifact upload are yours to add — and much of it you may not need, since FlowWright already handles caching and parallelism inside the pipeline.
flow run picks the right output format automatically once it's running in CI. See
Reporters, and Running in CI for the provider-side
half — caching, test reports and what each provider does with the output.