Local CI with act
apps/docs/src/content/docs/guides/local-ci Click to copy apps/docs/src/content/docs/guides/local-ci Local CI with act
Section titled “Local CI with act”HELiX uses nektos/act to run GitHub Actions workflows locally inside Docker containers. The local Docker pass via act is one piece of the full preflight gate — the canonical “ran the full local quality gate” command is pnpm run preflight, which composes lint, format, type-check, build, smart tests, CEM drift, changeset check, and an act-ci.sh invocation (Docker-CI parity).
This is a mandatory quality gate. Code does not push to GitHub until pnpm run preflight passes (which includes the act Docker-CI step on machines that have Docker available).
Prerequisites
Section titled “Prerequisites”Before running act, you need:
-
Docker Desktop — running and healthy
Terminal window docker info # must succeed with no errors -
act — install via Homebrew
Terminal window brew install act -
.actrc— already checked into the repo root (use the project’s repo-root file; act also reads~/.actrcif present, but the repo-root file is the source of truth here). Contains performance flags (--bind,--reuse,--pull=false).
Quick Start
Section titled “Quick Start”Run all quality gates locally:
./scripts/act-ci.shThis runs the act-ci.yml workflow — a lightweight mirror of the production ci.yml that avoids GitHub-specific actions incompatible with local Docker.
Total time: ~4 minutes (jobs run in parallel where possible).
Available Commands
Section titled “Available Commands”Run all gates
Section titled “Run all gates”./scripts/act-ci.shRun a specific job
Section titled “Run a specific job”./scripts/act-ci.sh --job lint./scripts/act-ci.sh --job format./scripts/act-ci.sh --job type-check./scripts/act-ci.sh --job build./scripts/act-ci.sh --job testList available jobs
Section titled “List available jobs”./scripts/act-ci.sh --listClean stale containers before running
Section titled “Clean stale containers before running”--clean removes all act-* containers (whether running or stopped) before the run. Normal runs already prune exited containers in passing — use --clean when a leaked container is blocking re-runs.
./scripts/act-ci.sh --cleanAvailable Flags
Section titled “Available Flags”| Flag | Description |
|---|---|
--job <name> | Run a specific job instead of all gates |
--list | Print available job names and exit |
--help / -h | Show usage text and exit |
--native | Use linux/arm64 containers (no Rosetta emulation — faster on Apple Silicon) |
--full | Run the complete test suite instead of smart tests (changed components only) |
--matrix | Run the full Node.js × Playwright matrix test job (test-full) — slow |
--batch | Run tests in batches of 10 to avoid Docker OOM |
--clean | Remove all act-* containers before running |
--fast | Run the consolidated single-job fast workflow |
CI Jobs
Section titled “CI Jobs”The act-ci.yml workflow runs these jobs. The table reflects the default subset run by ./scripts/act-ci.sh; the workflow also defines a test-full job that only fires with --matrix (ACT_MATRIX_TESTS / ACT_FULL_TESTS):
| Job | What it checks | Approximate time |
|---|---|---|
lint | ESLint strict | ~12s (warm) |
format | Prettier check | ~10s (warm) |
type-check | TypeScript strict — no any | ~15s (warm) |
build | Full library build + CEM validation | ~90s |
test | Vitest browser tests (Playwright/Chromium) | ~120s |
test-full | Full Node × Playwright matrix test (opt-in) | ~10–15min |
quality-gates | Aggregate — required jobs must pass | ~1s |
Jobs that need Playwright browsers will use a cached volume mount on repeat runs — first run downloads browsers, subsequent runs reuse the cache.
Apple Silicon (M1/M2/M3)
Section titled “Apple Silicon (M1/M2/M3)”By default, act runs linux/amd64 containers via Rosetta 2 emulation. This uses 2–3× more memory than native.
Recommended setup for Apple Silicon:
# Native ARM64 containers — no Rosetta overhead./scripts/act-ci.sh --native
# Best combo for full test runs (avoids OOM)./scripts/act-ci.sh --native --batchWhen to use each flag:
--native— faster, less memory for all runs--batch— runs tests in groups of 10 to avoid Docker OOM on large test suites--full— runs every test (slow; use only when you need complete coverage locally)
Troubleshooting
Section titled “Troubleshooting”act not found
Section titled “act not found”brew install actDocker not running
Section titled “Docker not running”Start Docker Desktop, then verify:
docker infoContainer startup fails / image not found
Section titled “Container startup fails / image not found”act pulls images on first run. Ensure you have network access and the repo-root .actrc keeps --pull=false only after the initial pull. Remove --pull=false from the repo-root .actrc temporarily to force a fresh pull:
act pull_request -W .github/workflows/act-ci.yml --pull=alwaysTests OOM / Docker kills containers
Section titled “Tests OOM / Docker kills containers”Reduce memory pressure with --native (skips Rosetta overhead) and --batch (limits concurrent tests):
./scripts/act-ci.sh --native --batchAlso increase Docker Desktop memory in Settings → Resources → Memory (recommend 8 GB+).
Stale containers blocking re-runs
Section titled “Stale containers blocking re-runs”./scripts/act-ci.sh --cleanOr manually:
docker ps -aq --filter "name=act-" | xargs docker rm -fPlaywright browsers re-downloading every run
Section titled “Playwright browsers re-downloading every run”act mounts a host-side Playwright cache at ~/.cache/ms-playwright-{arch}. Verify the directory exists:
ls ~/.cache/ms-playwright-amd64 # defaultls ~/.cache/ms-playwright-arm64 # with --nativeIf the directory is missing, act creates it automatically on the next run and Playwright downloads browsers once.
quality-gates fails with a passing job name
Section titled “quality-gates fails with a passing job name”The aggregate gate requires all jobs to succeed. Check the output for the specific failing job name and re-run it in isolation:
./scripts/act-ci.sh --job <failing-job>How It Fits in the Push Workflow
Section titled “How It Fits in the Push Workflow”pnpm run preflight is the canonical pre-push gate; it composes the fast local gates and the ./scripts/act-ci.sh Docker run into a single command:
pnpm run format # fix formattingpnpm run preflight # full local CI gate (lint/format/type-check/build/smart tests/CEM/changesets + Docker CI via act-ci.sh)git push # only after preflight passesIf preflight fails, fix the errors before pushing. Do not skip this gate. See Build Pipeline for the full gate list and the production CI counterpart.