Continuous Integration
Refract ships with three GitHub Actions workflows. Together they make sure every PR is safe to merge and thatmain always has a clean coverage baseline.
Overview
All three workflows run onubuntu-latest and use Node 24 with pnpm 10.
integration.yml — runs on every PR
Trigger: any PR opened, updated, or reopened against main.
This is the main gate. It runs four parallel jobs, and all four must pass before a PR can merge.
tests job
The most involved job. It:
- Spins up Postgres and Redis as service containers.
- Installs deps and builds all workspace packages (the backend test suite needs the compiled tool packages in
dist/). - Runs the shared/tool package tests (
pnpm test:modules). - Runs the frontend Vitest suite.
- Downloads the coverage baseline from
main— first from themain-coverageworkflow artifact, then from a previous integration run as a fallback. If neither exists, it checks outmain, generates coverage itself, then switches back to the PR branch. - Runs the backend Jest suite and posts a coverage diff comment on the PR via
jest-coverage-report-action.
builds job
Runs pnpm -r run build across every workspace package. Catches TypeScript compile errors that don’t show up at runtime.
lints job
Runs pnpm -r run lint across every workspace package. Fails on any ESLint error.
codegen-verification job
Runs make gql-codegen (backend schema dump + frontend TypeScript hooks generation) and then checks whether any files changed. If the generated code in apps/frontend/src/gql/hooks.ts or the schema file doesn’t match what’s committed, this job fails and tells you to run make gql-codegen and commit the result.
This means you can never accidentally ship a frontend that’s out of sync with the backend’s GraphQL schema.
main-coverage.yml — runs on push to main
Trigger: any push to main (which in practice means every merged PR).
This workflow has one job: run the full backend Jest suite and upload the results as a main-jest-coverage artifact. That artifact is what integration.yml’s tests job downloads for coverage comparison.
The artifact is retained for 90 days. Keeping main’s coverage up to date means every new PR gets an accurate baseline to compare against rather than a stale one.
mintlify-docs-sync.yml — runs on PR merge or manual dispatch
Trigger: PRs merged into main, or manually via workflow_dispatch.
This workflow converts the source Markdown in apps/documentation/ to MDX, rewrites internal links to Mintlify-compatible routes, validates the output, and commits it back to the repo. Mintlify picks up the commit automatically.
See Documentation for a full breakdown of the sync script.
Running it manually
Go to Actions → mintlify-docs-sync → Run workflow. You can optionally set:| Input | Default | What it does |
|---|---|---|
dry_run | false | Runs the sync without committing — useful for debugging |
source_root | apps/documentation | Where to read source docs from |
target_root | apps/documentation | Where to write converted docs to |
Running CI locally
You can’t run the full CI pipeline locally (it needs GitHub’s service containers), but you can run each piece:💡 Tip: if a CI job fails on something that passes locally, the most common cause is a staledist/— the CI always builds from scratch. Trymake clean-dockerandmake fresh-startto get a clean slate.
What’s next?
- Testing — how tests are structured, coverage thresholds, and mocking patterns.
- Linter — ESLint and Prettier configuration.
- Documentation — how the docs sync workflow fits together.