KeiSeiKit-1.0/.github/workflows/ci.yml
Parfii-bot c778b7d9a3 feat(v0.20.1): workflow-file validation infrastructure
Three layers of defense against the dtolnay-SHA-class bug reaching main
(today's incident: agent SHA-pinned dtolnay/rust-toolchain with a pin
that was real but semantically wrong — lost 'install current stable'
meaning, locked to rust 1.94.1 branch tip, broke CI).

Layer 1 — actionlint static lint
  scripts/install-actionlint.sh (65 LOC) — installs rhysd/actionlint
    v1.7.12 [VERIFIED] to ~/.local/bin or suggests brew install.
  scripts/lint-workflows.sh (40 LOC) — runs actionlint on
    .github/workflows/*.yml, exit 0 on clean, advisory when binary
    missing.

Layer 2 — SHA existence check (today's bug class)
  scripts/validate-workflow-shas.sh (98 LOC) — extracts every
    'uses: <repo>@<40-hex>' from workflow files + dependabot.yml,
    checks each via GitHub REST commits API (exit 200/404/422).
    Supports 'validate-workflow-shas: skip=<reason>' trailing
    comment for intentional exceptions. Falls back to anonymous
    API (60/hr quota) if GITHUB_TOKEN probe fails.
  DESIGN PIVOT from spec: spec said 'git ls-remote <repo> <sha>'
    but that only resolves REFS (branch/tag tips), not arbitrary
    commit SHAs — would have given false-positive 100% MISSING
    report. Switched to REST API /commits/{sha} for unambiguous
    200/404/422.

Layer 3 — CI gate
  .github/workflows/ci.yml — new 'workflow-lint' job after
    shell-lint. Installs actionlint + runs both scripts on every
    push to main and PR. Blocks CI on any fabricated SHA.

Layer 4 — optional pre-commit hook
  scripts/pre-commit-workflow-lint.sh (54 LOC) — detects staged
    .github/workflows/*.{yml,yaml} + .github/dependabot.yml
    changes, runs layers 1+2, blocks commit on failure.
  Install via: ln -sf ../../scripts/pre-commit-workflow-lint.sh
    .git/hooks/pre-commit

REAL EXECUTION VERIFIED (not claim-only):
  - actionlint ran: zero findings on current workflows
  - validate-workflow-shas.sh ran: 21 SHA pins checked, 21 OK,
    0 MISSING (confirms all current v0.19.1+ pins resolve)
  - bash -n on every new script: clean
  - bash-3.2 parser bug workaround: case-in-subshell → grep -E

RULE 0.2 exception #6 (shell is external convention for git hooks
+ GH Actions runs — Rust rewrite would add zero value).
RULE 0.13 respected — no git invocations except read-only API calls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:50:23 +08:00

105 lines
5.1 KiB
YAML

name: CI
on:
push:
branches: [main]
pull_request:
# v0.19.1 supply-chain hardening (H5): every third-party action is pinned
# by full commit SHA. A floating tag like @v4 can be re-pointed by a
# compromised maintainer (CVE-2025-30066 class). The `# vN.m.k` comment
# next to each SHA is a human-readable hint only — the SHA is the load-
# bearing identifier. When Dependabot proposes a bump, review the new SHA
# against the release tag before merging.
jobs:
rust-assembler:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: dtolnay/rust-toolchain@stable # exception to SHA-pin rule: this action uses named-branch convention (stable/nightly/beta/1.NN.0) — pinning a SHA locks to a specific Rust version (validator V-2026-04-22 confirmed 3c5f7ea was rust 1.94.1 branch tip, not generic "install stable"). dtolnay is a trusted maintainer (author of serde/anyhow/cxx). Supply-chain risk of @stable re-point is LOW and accepted here.
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: _assembler
- run: cd _assembler && cargo test --release
rust-primitives:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: dtolnay/rust-toolchain@stable # exception to SHA-pin rule: this action uses named-branch convention (stable/nightly/beta/1.NN.0) — pinning a SHA locks to a specific Rust version (validator V-2026-04-22 confirmed 3c5f7ea was rust 1.94.1 branch tip, not generic "install stable"). dtolnay is a trusted maintainer (author of serde/anyhow/cxx). Supply-chain risk of @stable re-point is LOW and accepted here.
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: _primitives/_rust
- run: cd _primitives/_rust && cargo test --workspace --release
ts-packages:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
node: ['20', '22']
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ matrix.node }}
- run: cd _ts_packages && npm ci
- run: cd _ts_packages && npm run build --workspaces
- run: cd _ts_packages && npm test --workspaces --if-present
install-dry-run:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: dtolnay/rust-toolchain@stable # exception to SHA-pin rule: this action uses named-branch convention (stable/nightly/beta/1.NN.0) — pinning a SHA locks to a specific Rust version (validator V-2026-04-22 confirmed 3c5f7ea was rust 1.94.1 branch tip, not generic "install stable"). dtolnay is a trusted maintainer (author of serde/anyhow/cxx). Supply-chain risk of @stable re-point is LOW and accepted here.
- name: Install hard deps (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get install -y jq pandoc
- name: Install hard deps (macOS)
if: matrix.os == 'macos-latest'
run: brew install jq pandoc
- run: bash -n install.sh
- run: ./install.sh --no-execute --profile=minimal
- run: ./install.sh --no-execute --profile=dev
- run: ./install.sh --no-execute --profile=full
shell-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- run: sudo apt-get update && sudo apt-get install -y shellcheck
- name: shellcheck (advisory)
# v0.15.1: kept advisory because local shellcheck sweep not yet clean
# (quoted-var nits in hooks). Flip to fatal once the sweep is committed;
# planned for v0.16.
# v0.20.1: explicit `|| true` in addition to continue-on-error — the
# latter doesn't always suppress the step-level exit-1 in the GH
# Actions annotation stream.
run: |
find hooks _primitives -name '*.sh' -exec shellcheck -S warning {} + || \
echo "shellcheck emitted warnings (advisory-only, not blocking)"
continue-on-error: true
workflow-lint:
# v0.20.1: guards against the dtolnay-SHA-class incident (2026-04-22).
# actionlint catches workflow syntax; validate-workflow-shas.sh catches
# fabricated / force-pushed SHA pins. Runs fast (<30s).
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Install actionlint
run: bash scripts/install-actionlint.sh
- name: Lint workflows (actionlint)
run: PATH="${HOME}/.local/bin:${PATH}" bash scripts/lint-workflows.sh
- name: Validate pinned SHAs
run: bash scripts/validate-workflow-shas.sh