refactor: generate CI workflow from gate contracts

This commit is contained in:
DongHyeonka
2026-08-02 13:53:25 +09:00
parent 777ce5c9ed
commit 1bb2cc4a20
45 changed files with 8599 additions and 1177 deletions
+63 -6
View File
@@ -45,10 +45,16 @@ including Ed448, are rejected even if a document labels its algorithm
`.vite` files), the build manifest, module inventory, release verification,
secret-scan result, and local supply-chain evidence once. The candidate
manifest hashes the raw lockfile bytes and requires that digest to equal the
dependency inventory's `lockfileSha256`. The two provider jobs download this
same archive separately and receive `CANDIDATE_LOCKFILE_PATH` and
`CANDIDATE_DIST_SHA256`; configured `VULNERABILITY_PROVIDER_COMMAND` and
`PROVENANCE_PROVIDER_COMMAND` must emit the signed reports.
dependency inventory's `lockfileSha256`. Before upload, the producer validates
the manifest-bound exact archive member set and every member digest, then
publishes the archive SHA-256 as an immutable job output. The two provider jobs
download this same archive separately, compare that output digest, validate the
exact member set before extracting only into isolated roots, and receive
`CANDIDATE_LOCKFILE_PATH` and `CANDIDATE_DIST_SHA256`; configured
`VULNERABILITY_PROVIDER_COMMAND` and `PROVENANCE_PROVIDER_COMMAND` must emit the
signed reports. After each external command returns, provider upload validation
rechecks the unchanged archive and extracted candidate, parses the provider JSON
with its strict schema, and binds its dist and lockfile digests before upload.
If either provider input is absent, local verification remains meaningful but
`artifacts/security/supply-chain-verification.json` records
@@ -57,8 +63,59 @@ If either provider input is absent, local verification remains meaningful but
set and digests, then read-only revalidates the archived executable schemas,
raw lockfile, module inventory, build outputs, release coherence, SBOM,
provenance, security scan and supply-chain coherence. It never rebuilds or
rewrites candidate evidence. Scanner or signing outages are not converted to
an empty PASS.
rewrites candidate evidence. Promotion uploads the already verified archive
itself with the two provider reports and verification records; it does not
create a replacement archive from extracted files. Scanner or signing outages
are not converted to an empty PASS.
The generated workflow is also a supply-chain control. `config/ci/gates.json`
is its sole typed authority. Run `corepack pnpm generate:ci-workflow` after a
contract change and `corepack pnpm check:ci-workflow` (or the encompassing
`corepack pnpm check:ci`) to reject byte drift in the checked-in Gitea adapter.
Action resolution is separately closed over one typed, runtime-frozen registry
in `scripts/contracts/ci-gates.ts`. Every generated `uses:` value is an absolute
upstream URL pinned to a full commit SHA:
- `https://github.com/actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5`
(`v4.3.1`)
- `https://github.com/actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020`
(`v4.4.0`)
- `https://github.com/ChristopherHX/gitea-upload-artifact@81f940d004763f986ba3582c007fd842dd5cb0d7`
(patched `v4` branch)
- `https://github.com/ChristopherHX/gitea-download-artifact@75635f32b4c1c41c4b3d64e8f85210112ed4c9c7`
(patched `v4` branch)
Unknown actions, relative repositories, tags/branches and short SHAs are
rejected. Gitea 1.22's official Actions documentation recommends the
`ChristopherHX` patched artifact forks for v4 compatibility; the supported
deployment baseline is nevertheless Gitea 1.26.4+ with Gitea Runner 1.0.0+.
A real end-to-end provider smoke on the staging Gitea instance remains
mandatory before any generated job becomes a required check.
External provider supervision is fail-closed and requires a Linux runner with
an executable `/usr/bin/bwrap`. Bubblewrap mounts the repository workspace
read-only, hides `.git`, pins the trusted Node executable at
`/usr/local/bin/node`, and exposes only the sibling `untrusted` raw-evidence
directory as writable. Provider commands run non-interactively through
`/bin/sh -eu -c`, receive a minimized environment plus only their own
provider-prefixed credentials, and have a 30-minute limit. They must consume
the supplied candidate paths and digests, write exactly the configured raw
report, and must not depend on workspace mutation, host home/toolcache access,
or direct access to the sealed evidence path. Missing sandbox support, stale or
misplaced outputs, command failure/timeout, and post-command candidate drift
all stop publication.
The final promotion verification/staging step must be immediately adjacent to
the promoted-release upload, and that upload must not use `always()`. This
reduces the post-verification mutation window but does not seal a pathname
across two action steps. The runner is therefore required to be trusted,
exclusive and single-tenant, with no provider command or other same-UID process
surviving from staging into the immediately following upload. The artifact
service and transfer actions also remain outside the candidate's cryptographic
identity: every downstream consumer must revalidate the downloaded archive,
manifest member digests and signed provider evidence. Producer-side adjacency
does not provide consumer-side digest revalidation.
Approved vulnerability exceptions require vulnerability/package identity,
owner, a different reviewer, reason and expiry. Expired or self-approved