Skip to content
Crow CI

Approval gates

Crow supports two distinct approval mechanisms:

  1. Pipeline-level approval — gates an entire pipeline before any workflow runs. Configured per repo via Approval requirements.
  2. Workflow-level approval gates — gates a single workflow in the middle of a pipeline. Configured per workflow in .crow.yml.

This page is about the second.

See approval in the workflow syntax reference for the schema.

  1. The pipeline starts and runs all upstream workflows normally.
  2. When a gated workflow’s upstream deps all finish successfully, the workflow goes to the blocked state. No agent slot is held while it waits.
  3. An authorized user approves or declines via the UI or CLI. If neither happens, the timeout sweeper auto-declines after the gate’s configured timeout.
  4. On approve, the gated workflow runs.
  5. On decline (or timeout), the gated workflow is marked declined, all of its downstream workflows are marked skipped, and the pipeline finishes with a top-level declined status.

While a workflow waits at the gate, the overall pipeline status is blocked, not success — the pipeline is not done. After a decline or timeout it rolls up to declined. The status precedence is killed > declined > failure > blocked > running > success, so a single blocked workflow surfaces as a blocked pipeline even when every other workflow has finished.

Crow gates at the workflow level, not the step level. You cannot put approval: on an individual step — the schema only accepts it at the top level of a workflow. A workflow is all-or-nothing once it starts; the gate sits in front of a workflow’s steps, never between them.

To gate a single step, split it into its own workflow. Put the free-running steps in one workflow, and put the step(s) you want behind the gate in a second workflow that depends_on the first and carries the approval block.

.crow/01-ci.yaml runs unguarded:

steps:
  - name: build
    image: golang:1.24
    commands:
      - go build ./...
  - name: test
    image: golang:1.24
    commands:
      - go test ./...
  - name: lint
    image: golang:1.24
    commands:
      - golangci-lint run

.crow/02-deploy.yaml waits for 01-ci and gates the deploy behind approval:

depends_on:
  - 01-ci
approval:
  required: true
  approvers:
    users:
      - alice
  timeout: 24h
steps:
  - name: deploy
    image: deploy-tool:latest
    commands:
      - ./deploy.sh production

For a checkpoint in the middle of a sequence (approve after step A, before step B), split again into more workflows chained with depends_on. Each gate is a per-workflow boundary, so every extra checkpoint is one more workflow.

Open the pipeline view. Gated workflows show an “Approval required” panel with Approve and Decline buttons. Add an optional reason for the audit trail.

The navbar pending-approvals indicator also surfaces workflow gates you can approve, across all your repos.

crow pipeline workflow approve <repo> <pipeline-number> <workflow-name> [--reason "..."]
crow pipeline workflow decline <repo> <pipeline-number> <workflow-name> [--reason "..."]

See pipeline workflow in the CLI reference.

Three environment variables control gate behaviour:

  • CROW_APPROVAL_GATE_TIMEOUT_DEFAULT (default 24h) — applied when YAML omits timeout.
  • CROW_APPROVAL_GATE_TIMEOUT_MAX (default 168h, 7d) — hard cap. YAML with a longer timeout fails lint.
  • CROW_APPROVAL_GATE_SWEEP_INTERVAL (default 30s, minimum 5s) — how often the timeout sweeper runs.

See Approval gates in the server configuration reference for details.

Pipeline-levelWorkflow-level
Configured inRepo settings.crow.yml per workflow
Triggered byForge events (e.g. PR from fork)approval.required: true
ApproversAnyone with push permConfigurable list + push perm fallback
GranularityEntire pipeline before anything runsSingle workflow mid-pipeline