9.7 KiB
9.7 KiB
Main Branch Delivery Flows
This document explains what runs when code is proposed to main, merged into main, and released via tags.
Use this with:
Event Summary
| Event | Main workflows |
|---|---|
PR activity (pull_request_target) |
pr-intake-checks.yml, pr-labeler.yml, pr-auto-response.yml |
PR activity (pull_request) |
ci-run.yml, sec-audit.yml, plus path-scoped pub-docker-img.yml, workflow-sanity.yml, pr-label-policy-check.yml |
Push to main |
ci-run.yml, sec-audit.yml, plus path-scoped workflows |
Tag push (v*) |
pub-release.yml, pub-docker-img.yml publish job |
| Scheduled/manual | sec-codeql.yml, feature-matrix.yml, test-fuzz.yml, pr-check-stale.yml, pr-check-status.yml, sync-contributors.yml, test-benchmarks.yml, test-e2e.yml |
Runtime and Docker Matrix
Observed averages below are from recent completed runs (sampled from GitHub Actions on February 17, 2026). Values are directional, not SLA.
| Workflow | Typical trigger in main flow | Avg runtime | Docker build? | Docker run? | Docker push? |
|---|---|---|---|---|---|
pr-intake-checks.yml |
PR open/update (pull_request_target) |
14.5s | No | No | No |
pr-labeler.yml |
PR open/update (pull_request_target) |
53.7s | No | No | No |
pr-auto-response.yml |
PR/issue automation | 24.3s | No | No | No |
ci-run.yml |
PR + push to main |
74.7s | No | No | No |
sec-audit.yml |
PR + push to main |
127.2s | No | No | No |
workflow-sanity.yml |
Workflow-file changes | 34.2s | No | No | No |
pr-label-policy-check.yml |
Label policy/automation changes | 14.7s | No | No | No |
pub-docker-img.yml (pull_request) |
Docker build-input PR changes | 240.4s | Yes | Yes | No |
pub-docker-img.yml (push/workflow_dispatch) |
main push (build-input paths), tag push v*, or manual dispatch |
139.9s | Yes | No | Yes |
pub-release.yml |
Tag push v* |
N/A in recent sample | No | No | No |
Notes:
pub-docker-img.ymlis the only workflow in the main PR/push path that builds Docker images.- Container runtime verification (
docker run) occurs in PR smoke only. - Container registry push occurs on
mainbuild-input pushes, tag pushes, and manual dispatch. ci-run.yml"Build (Smoke)" builds Rust binaries, not Docker images.
Step-By-Step
1) PR from branch in this repository -> main
- Contributor opens or updates PR against
main. pull_request_targetautomation runs (typical runtime):pr-intake-checks.ymlposts intake warnings/errors.pr-labeler.ymlsets size/risk/scope labels.pr-auto-response.ymlruns first-interaction and label routes.
pull_requestCI workflows start:ci-run.ymlsec-audit.yml- path-scoped workflows if matching files changed:
pub-docker-img.yml(Docker build-input paths only)workflow-sanity.yml(workflow files only)pr-label-policy-check.yml(label-policy files only)
- In
ci-run.yml,changescomputes:docs_onlydocs_changedrust_changedworkflow_changed
buildruns for Rust-impacting changes.- On PRs, full lint/test/docs checks run when PR has label
ci:full:lintlint-strict-deltatestdocs-quality
- If
.github/workflows/**changed,workflow-owner-approvalmust pass. lint-feedbackposts actionable comment if lint/docs gates fail.CI Required Gateaggregates results to final pass/fail.- Maintainer merges PR once checks and review policy are satisfied.
- Merge emits a
pushevent onmain(see scenario 3).
2) PR from fork -> main
- External contributor opens PR from
fork/<branch>intozeroclaw:main. - Immediately on
opened:pull_request_targetworkflows start with base-repo context and base-repo token:pr-intake-checks.ymlpr-labeler.ymlpr-auto-response.yml
pull_requestworkflows are queued for the fork head commit:ci-run.ymlsec-audit.yml- path-scoped workflows (
pub-docker-img.yml,workflow-sanity.yml,pr-label-policy-check.yml) if changed files match.
- Fork-specific permission behavior in
pull_requestworkflows:- token is restricted (read-focused), so jobs that try to write PR comments/status extras can be limited.
- secrets from the base repo are not exposed to fork PR
pull_requestjobs.
- Approval gate possibility:
- if Actions settings require maintainer approval for fork workflows, the
pull_requestrun stays inaction_required/waiting state until approved.
- if Actions settings require maintainer approval for fork workflows, the
- Event fan-out after labeling:
pr-labeler.ymland manual label changes emitlabeled/unlabeledevents.- those events retrigger
pull_request_targetautomation (pr-labeler.ymlandpr-auto-response.yml), creating extra run volume/noise.
- When contributor pushes new commits to fork branch (
synchronize):- reruns:
pr-intake-checks.yml,pr-labeler.yml,ci-run.yml,sec-audit.yml, and matching path-scoped PR workflows. - does not rerun
pr-auto-response.ymlunless label/open events occur.
- reruns:
ci-run.ymlexecution details for fork PR:changescomputesdocs_only,docs_changed,rust_changed,workflow_changed.buildruns for Rust-impacting changes.lint/lint-strict-delta/test/docs-qualityrun on PR whenci:fulllabel exists.workflow-owner-approvalruns when.github/workflows/**changed.CI Required Gateemits final pass/fail for the PR head.
- Fork PR merge blockers to check first when diagnosing stalls:
- run approval pending for fork workflows.
workflow-owner-approvalfailing on workflow-file changes.CI Required Gatefailure caused by upstream jobs.- repeated
pull_request_targetreruns from label churn causing noisy signals.
- After merge, normal
pushworkflows onmainexecute (scenario 3).
3) Push to main (including after merge)
- Commit reaches
main(usually from a merged PR). ci-run.ymlruns onpush.sec-audit.ymlruns onpush.- Path-filtered workflows run only if touched files match their filters.
- In
ci-run.yml, push behavior differs from PR behavior:- Rust path:
lint,lint-strict-delta,test,buildare expected. - Docs/non-rust paths: fast-path behavior applies.
- Rust path:
CI Required Gatecomputes overall push result.
Docker Publish Logic
Workflow: .github/workflows/pub-docker-img.yml
PR behavior
- Triggered on
pull_requesttomainwhen Docker build-input paths change. - Runs
PR Docker Smokejob:- Builds local smoke image with Blacksmith builder.
- Verifies container with
docker run ... --version.
- Typical runtime in recent sample: ~240.4s.
- No registry push happens on PR events.
Push behavior
publishjob runs onpushandworkflow_dispatch.- Workflow trigger includes
mainpushes with Docker build-input changes and tag pushesv*. - Login to
ghcr.iouses${{ github.actor }}and${{ secrets.GITHUB_TOKEN }}. - Tag computation includes:
latest+ SHA tag (sha-<12 chars>) formain- semantic tag from pushed git tag (
vX.Y.Z) + SHA tag for tag pushes - branch name + SHA tag for non-
mainmanual dispatch refs
- Multi-platform publish is used for tag pushes (
linux/amd64,linux/arm64), whilemainpublish stayslinux/amd64. - Typical runtime in recent sample: ~139.9s.
- Result: pushed image tags under
ghcr.io/<owner>/<repo>.
Important: Docker publish now runs on qualifying main pushes; no release tag is required to refresh latest.
Release Logic
Workflow: .github/workflows/pub-release.yml
- Triggered only on tag push
v*. - Builds release artifacts across matrix targets.
- Generates SBOM (
CycloneDX+SPDX). - Generates
SHA256SUMS. - Signs artifacts with keyless cosign.
- Publishes GitHub Release with artifacts.
Merge/Policy Notes
- Workflow-file changes (
.github/workflows/**) activate owner-approval gate inci-run.yml. - PR lint/test strictness is intentionally controlled by
ci:fulllabel. sec-audit.ymlruns on both PR and push, plus scheduled weekly.- Some workflows are operational and non-merge-path (
pr-check-stale,pr-check-status,sync-contributors, etc.). - Workflow-specific JavaScript helpers are organized under
.github/workflows/scripts/.
Mermaid Diagrams
PR to Main (Internal/Fork)
flowchart TD
A["PR opened or updated -> main"] --> B["pull_request_target lane"]
B --> B1["pr-intake-checks.yml"]
B --> B2["pr-labeler.yml"]
B --> B3["pr-auto-response.yml"]
A --> C["pull_request CI lane"]
C --> C1["ci-run.yml"]
C --> C2["sec-audit.yml"]
C --> C3["pub-docker-img.yml (if Docker paths changed)"]
C --> C4["workflow-sanity.yml (if workflow files changed)"]
C --> C5["pr-label-policy-check.yml (if policy files changed)"]
C1 --> D["CI Required Gate"]
D --> E{"Checks + review policy pass?"}
E -->|No| F["PR stays open"]
E -->|Yes| G["Merge PR"]
G --> H["push event on main"]
Push/Tag Delivery
flowchart TD
A["Commit reaches main"] --> B["ci-run.yml"]
A --> C["sec-audit.yml"]
A --> D["path-scoped workflows (if matched)"]
T["Tag push v*"] --> R["pub-release.yml"]
T --> P["pub-docker-img.yml publish job"]
R --> R1["Artifacts + SBOM + checksums + signatures + GitHub Release"]
P --> P1["Push ghcr image tags (version + sha)"]
Quick Troubleshooting
- Unexpected skipped jobs: inspect
scripts/ci/detect_change_scope.shoutputs. - Workflow-change PR blocked: verify
WORKFLOW_OWNER_LOGINSand approvals. - Fork PR appears stalled: check whether Actions run approval is pending.
- Docker not published: confirm changed files match Docker build-input paths, or run workflow dispatch manually.