Configuration
.hookci.yml reference
Every key in .hookci.yml, with types, defaults, and a snippet for each section.
.hookci.yml lives at the root of your repository. If the file is missing, HookCI auto-detects your stack and builds a default job list (see examples). Write hookci init to generate a starter file from detection, then edit it.
hookci init
hookci validate
hookci validate [path] [--self-hosted] [--json] lints the file against the schema. --self-hosted also applies the stricter self-hosted rules. Exit code 0 means valid.
Editor autocompletion
HookCI publishes a JSON schema, hookci.schema.json. Add a yaml-language-server comment as the first line and editors that use the YAML language server (VS Code YAML extension, Neovim, Zed) offer completion and inline errors.
# yaml-language-server: $schema=https://hookci.com/hookci.schema.json
version: 1
Full shape
version: 1
checks:
on:
pull_request: true
push: { branches: [main] }
trust: collaborators
forks: gated
limits: { cpus: 2, memory: 4G, timeout: 15m, tasks: 512 }
setup:
- run: npm ci
cache: { key: [package-lock.json], paths: [node_modules] }
jobs:
lint: { run: npm run lint, annotate: eslint }
test: { run: npm test, needs: [lint], timeout: 15m, limits: { memory: 6G } }
services:
postgres: true
egress: [registry.npmjs.org]
notify:
slack: { channel: "#ci", on: [failure], notify: normal, alert: channel }
redact: [MY_SECRET]
logs: hub
version
| Field | Type | Default | Description |
|---|---|---|---|
version |
integer | none | Schema version. Use 1. |
version: 1
checks.on
Which events start a run.
| Field | Type | Default | Description |
|---|---|---|---|
pull_request |
boolean | true |
Run on pull requests. Who may trigger runs is controlled by checks.trust and checks.forks. |
push.branches |
list of strings | none | Run on pushes to these branches. |
checks:
on:
pull_request: true
push: { branches: [main, release] }
checks.trust
| Field | Type | Default | Description |
|---|---|---|---|
trust |
collaborators | approve | all |
none | collaborators runs pull requests from repo collaborators only. approve waits for an Approve click in Slack. all runs everything and is not recommended for public repos. |
checks:
trust: approve
See Trust and forks for the full outcome table.
checks.forks
| Field | Type | Default | Description |
|---|---|---|---|
forks |
gated | never |
none | gated: pull requests from forks need approval before they run. never: fork pull requests are ignored. |
checks:
forks: never
checks.limits
Resource limits applied to each job through its systemd transient unit under hookci.slice. See Isolation and egress.
| Field | Type | Default | Description |
|---|---|---|---|
cpus |
number | none | CPU quota in cores. |
memory |
size string, e.g. 4G |
none | Memory limit. |
timeout |
duration, e.g. 15m |
none | Wall-clock limit for a job. |
tasks |
integer | none | Maximum number of processes and threads. |
checks:
limits: { cpus: 2, memory: 4G, timeout: 15m, tasks: 512 }
Each job can override limits and timeout in its own block (see checks.jobs).
checks.setup and cache
setup is a list of steps run before the jobs, for example installing dependencies. Setup output is available with hookci logs <run> setup.
| Field | Type | Default | Description |
|---|---|---|---|
setup[].run |
string | none | Shell command for this step. |
setup[].cache.key |
list of file paths | none | Files whose contents are hashed to form the cache key. When they change, the cache is rebuilt. |
setup[].cache.paths |
list of directories | none | Directories saved and restored under that key. |
checks:
setup:
- run: pnpm install --frozen-lockfile
cache:
key: [pnpm-lock.yaml]
paths: [node_modules]
checks.jobs
A map of job name to job definition. Jobs run in dependency order and in parallel up to --slots (default: half the CPUs).
| Field | Type | Default | Description |
|---|---|---|---|
run |
string | none | Shell command. A non-zero exit fails the job. |
needs |
list of job names | none | Jobs that must succeed first. A job whose dependency fails does not run. |
timeout |
duration | inherits checks.limits.timeout |
Per-job time limit. |
limits |
map | inherits checks.limits |
Per-job override of cpus, memory, tasks. |
annotate |
eslint | tsc |
none | Parse the job output into file and line annotations on the GitHub check run. At most 500 annotations per job are kept. |
checks:
jobs:
lint:
run: npm run lint
annotate: eslint
typecheck:
run: npx tsc --noEmit
annotate: tsc
test:
run: npm test
needs: [lint, typecheck]
timeout: 15m
limits: { memory: 6G }
Run one job locally with hookci run test.
checks.services
| Field | Type | Default | Description |
|---|---|---|---|
postgres |
boolean | none | Start a PostgreSQL service for the jobs. See the docker-compose example. |
checks:
services:
postgres: true
checks.egress
| Field | Type | Default | Description |
|---|---|---|---|
egress |
list of hostnames | none | Hostnames jobs may reach on port 443 through the egress proxy. Everything else is blocked. |
checks:
egress:
- registry.npmjs.org
- github.com
How the proxy works, and how to find the host you are missing: Isolation and egress.
deployments
deployments:
production:
on: { push: { branch: main } }
strategy: restart # restart | compose-bluegreen
target: /srv/app
build: npm run build
migrate: npm run migrate
env_file: /etc/app.env
health: { url: http://127.0.0.1:3000/health, timeout: 60s }
rollback: auto # planned, value shape not final
approve: true
| Field | Type | Description |
|---|---|---|
on.push.branch |
string | Branch that triggers the deploy. |
strategy |
restart | compose-bluegreen |
How the new version replaces the old one. |
target |
path | Directory on the agent host. |
build |
string | Build command. |
migrate |
string | Migration command, run before switching. |
env_file |
path | Environment file on the host. Contents are not sent to the hub. |
health.url, health.timeout |
string, duration | Health check polled after the switch. |
rollback |
string | Rollback behavior. Not final. |
approve |
boolean | Require an approval click before deploying. |
notify.slack
| Field | Type | Default | Description |
|---|---|---|---|
channel |
string | none | Slack channel for run messages, for example "#ci". |
on |
list of failure | success | deploy | all |
none | Which outcomes post. |
notify |
quiet | normal | verbose |
none | How much each message shows. |
alert |
channel | dm |
none | Where failure alerts go: the channel or a direct message. |
notify:
slack:
channel: "#ci"
on: [failure]
notify: normal
alert: channel
See Slack.
redact
A list of names or values scrubbed from job logs before they leave the agent.
| Field | Type | Default | Description |
|---|---|---|---|
redact |
list of strings | none | Secrets or values to replace in logs. |
redact: [MY_SECRET, STAGING_TOKEN]
logs
| Field | Type | Default | Description |
|---|---|---|---|
logs |
hub | local |
none | hub uploads job logs to the hub (capped at 10 MiB per job) and serves them through signed links valid 15 minutes. local keeps logs on the agent only. |
logs: local
Read local logs with hookci logs <run> [job|setup] [-f].