Configuration: Examples by stack

Configuration

Config examples

Ready-to-use .hookci.yml files for Node, Python, Go, Rust, docker-compose, and monorepos.

Without a .hookci.yml, HookCI detects your stack. Detectors exist for node (npm, yarn, pnpm, bun, turbo), python (pip, uv), go, rust, and docker-compose. Check what it found:

~/my-app
~/my-app recording
$ hookci detect
✓ node (pnpm) detected
  jobs: lint, test, build
$ hookci init
✓ wrote .hookci.yml

Illustration. hookci detect prints the jobs it would run. hookci init writes them to .hookci.yml.

Detection is a starting point. Write an explicit file when you need needs, limits, caching, services, or extra egress hosts. The examples below show what to write. Validate any of them with hookci validate.

Node

Package manager is detected from the lockfile: package-lock.json (npm), yarn.lock, pnpm-lock.yaml, bun.lockb or bun.lock. A turbo.json selects turbo.

version: 1
checks:
  setup:
    - run: npm ci
      cache: { key: [package-lock.json], paths: [node_modules] }
  jobs:
    lint: { run: npm run lint, annotate: eslint }
    typecheck: { run: npx tsc --noEmit, annotate: tsc }
    test: { run: npm test, needs: [lint] }
    build: { run: npm run build, needs: [test] }
  egress: [registry.npmjs.org]

For other package managers change the setup step, the cache key, and the registry host as needed:

# pnpm
setup:
  - run: pnpm install --frozen-lockfile
    cache: { key: [pnpm-lock.yaml], paths: [node_modules] }
# yarn
setup:
  - run: yarn install --frozen-lockfile
    cache: { key: [yarn.lock], paths: [node_modules] }
# bun
setup:
  - run: bun install --frozen-lockfile
    cache: { key: [bun.lockb], paths: [node_modules] }

With turbo, let turbo run the tasks:

version: 1
checks:
  setup:
    - run: pnpm install --frozen-lockfile
      cache: { key: [pnpm-lock.yaml], paths: [node_modules] }
  jobs:
    lint: { run: pnpm turbo run lint }
    test: { run: pnpm turbo run test, needs: [lint] }
  egress: [registry.npmjs.org]

Python

version: 1
checks:
  setup:
    - run: pip install -r requirements.txt
      cache: { key: [requirements.txt], paths: [.venv] }
  jobs:
    lint: { run: ruff check . }
    test: { run: pytest, needs: [lint] }
  egress: [pypi.org, files.pythonhosted.org]

With uv:

version: 1
checks:
  setup:
    - run: uv sync --frozen
      cache: { key: [uv.lock], paths: [.venv] }
  jobs:
    lint: { run: uv run ruff check . }
    test: { run: uv run pytest, needs: [lint] }
  egress: [pypi.org, files.pythonhosted.org]

Go

version: 1
checks:
  setup:
    - run: go mod download
      cache: { key: [go.sum], paths: [/var/lib/hookci/go-mod] }
  jobs:
    vet: { run: go vet ./... }
    test: { run: go test ./..., needs: [vet] }
  egress: [proxy.golang.org, sum.golang.org]

Adjust the cache path to wherever your GOMODCACHE points.

Rust

version: 1
checks:
  setup:
    - run: cargo fetch --locked
      cache: { key: [Cargo.lock], paths: [target] }
  jobs:
    clippy: { run: cargo clippy --all-targets -- -D warnings }
    test: { run: cargo test --locked, needs: [clippy] }
  limits: { memory: 8G }
  egress: [index.crates.io, static.crates.io]

docker-compose with postgres

Use services.postgres for a database the tests can connect to, instead of managing a compose stack in the job.

version: 1
checks:
  services:
    postgres: true
  setup:
    - run: npm ci
      cache: { key: [package-lock.json], paths: [node_modules] }
  jobs:
    test:
      run: npm test
      timeout: 20m
  egress: [registry.npmjs.org]

If a docker-compose.yml is present and no config exists, the docker-compose detector builds jobs from it. Check with hookci detect.

Monorepo with needs

Fan out independent packages and join them with needs:

version: 1
checks:
  on:
    pull_request: true
    push: { branches: [main] }
  limits: { cpus: 2, memory: 4G, timeout: 15m, tasks: 512 }
  setup:
    - run: pnpm install --frozen-lockfile
      cache: { key: [pnpm-lock.yaml], paths: [node_modules] }
  jobs:
    lint: { run: pnpm -r lint, annotate: eslint }
    api-test: { run: pnpm --filter api test, needs: [lint], limits: { memory: 6G } }
    web-test: { run: pnpm --filter web test, needs: [lint] }
    e2e: { run: pnpm --filter e2e test, needs: [api-test, web-test], timeout: 25m }
  egress: [registry.npmjs.org]

api-test and web-test run in parallel up to --slots. e2e starts only after both pass.

Auto-detected versus explicit

Auto-detected Explicit .hookci.yml
Jobs Derived from your tooling Whatever you list
needs ordering Chosen by the detector You define it
Limits Agent defaults checks.limits and per job
Cache Detector defaults Your setup[].cache
Trust and forks Safe defaults You set them, see Trust and forks
Egress hosts Hosts for the detected package registry Your egress list

Field details: reference.