Skip to content

CI and Pinning Workflows

These workflows enforce supply-chain and catalog integrity as required CI gates. pin-check is a required status check in every repo; reusable-actionlint validates workflow syntax before merge; catalog-check keeps the workflow catalog honest.

Calling convention: reference every workflow by the .github repo’s full 40-char commit SHA:

uses: attested-delivery/.github/.github/workflows/<name>.yml@<sha> # vX.Y.Z

Asserts that every GitHub Actions uses: reference in the caller’s .github directory is pinned to a full 40-character commit SHA. Mutable tags (@v4, @main) and floating refs (@master) fail the run. Exempt from checking: local reusable-workflow calls (uses: ./...) and digest-pinned container actions (uses: docker://...@sha256).

This is a required status check in every repo. No inputs, no outputs, no secrets.

NameTypeRequiredDefaultDescription
scan-dirstringno.githubDirectory to scan for workflow and action files

Top-level permissions: {}. The internal job pin-check needs contents: read.

None beyond GitHub-created actions/checkout.

Job id: pin-check / name: pin-check → required-check context: pin-check / pin-check.

jobs:
pin-check:
uses: attested-delivery/.github/.github/workflows/pin-check.yml@<sha>

Workflow-syntax lint via actionlint. Downloads the actionlint binary at a pinned version, verifies the download against a SHA-256 digest (fail-closed), and runs it against the caller’s workflow files. There is no allow-listed pinned action for actionlint; the verified fetch is centralized here so callers do not reinvent it.

When overriding version, the sha256 input must also be updated to match. Resolve the correct digest from the release’s actionlint_<version>_checksums.txt file; do not rely on a remembered value.

NameTypeRequiredDefaultDescription
versionstringno1.7.7actionlint release version (no leading v)
sha256stringno023070a287cd8cccd71515fedc843f1985bf96c436b7effaecce67290e7e0757SHA-256 of actionlint_<version>_linux_amd64.tar.gz — must match version
filesstringno''Space-separated globs to lint; empty lints all files under .github/workflows
permissions:
contents: read

None beyond GitHub-created actions/checkout.

jobs:
actionlint:
permissions:
contents: read
uses: attested-delivery/.github/.github/workflows/reusable-actionlint.yml@<sha>

Keeps the workflow catalog (workflow-catalog.md) honest against the actual set of reusable workflows in .github/workflows/. Enforces two directions:

  • Forward: every workflow_call reusable workflow under .github/workflows/ must have a matching entry in the catalog.
  • Reverse: every workflow path the catalog names must resolve to a real file on disk.

This is a repo-level CI gate (not a reusable workflow — it has no workflow_call trigger). It runs on pull requests and pushes to main that touch .github/workflows/** or the catalog file.

Triggers:

on:
pull_request:
paths:
- '.github/workflows/**'
- '.github/skills/attested-delivery/references/workflow-catalog.md'
push:
branches: [main]
paths:
- '.github/workflows/**'
- '.github/skills/attested-delivery/references/workflow-catalog.md'
permissions:
contents: read

Exits non-zero and emits ::error:: annotations when:

  • A workflow_call reusable workflow has no catalog entry.
  • The catalog names a workflow file that does not exist on disk.

Exits zero with a summary line when all reusables are documented and all catalog entries resolve.

None beyond GitHub-created actions/checkout.


Discussion

Comments are powered by GitHub Discussions. Sign in with GitHub to ask a question or share how this applies in your org.