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
+65 -9
View File
@@ -1,6 +1,12 @@
# CI quality-gate orchestration
`config/ci/gates.json` is the executable registry for all 26 gates. The Gitea
`config/ci/gates.json` is the executable registry for all 26 gates and the sole
workflow model. `.gitea/workflows/quality-gates.yml` is generated from that
strict v2 contract; it is not an independent configuration authority. Update
the registry, run `corepack pnpm generate:ci-workflow`, and commit the generated
bytes together. `corepack pnpm check:ci-workflow` rejects missing, stale, CRLF,
or otherwise byte-different output. `corepack pnpm check:ci` runs artifact-schema
checking, the typed contract checker, and this workflow drift check. The Gitea
adapter runs each gate as an independent matrix check with full fan-out and no
soft-fail wiring.
@@ -36,16 +42,54 @@ Release tier의 `immutable_build`는 `dist`의 hidden `.vite` entries, raw
`pnpm-lock.yaml`, build/module inventory와 local release/security evidence를 한
번 생성해 run ID/attempt로 구분된 archive로 게시한다. Raw lock bytes의 SHA-256은
dependency inventory와 candidate manifest 양쪽의 `lockfileSha256`과 같아야 한다.
Vulnerability/provenance provider job은 같은 archive를 각각 내려받고
`CANDIDATE_LOCKFILE_PATH`, `CANDIDATE_DIST_SHA256`를 외부 command에 전달한다.
Promotion은 같은 archive와 두 report를 별도 경로로 내려받은 뒤 local evidence를
read-only로 다시 계산하고 Ed25519 signature/digest를 확인한다. Promotion job에는
build/rebuild command가 없으며 검증한 archive 자체를 그대로 승격한다.
Producer는 게시 전에 manifest가 선언한 exact member set과 각 byte digest를
검증하고 archive SHA-256을 job output으로 고정한다. Vulnerability/provenance
provider job은 같은 archive를 각각 내려받아 output SHA-256과 exact member set을
확인한 뒤 checkout과 분리된 root에만 추출하고, `CANDIDATE_LOCKFILE_PATH`
`CANDIDATE_DIST_SHA256`를 외부 command에 전달한다. Provider command 실행 후에는
archive와 추출된 candidate를 다시 검증하고, strict provider JSON과 candidate
digest가 일치할 때만 report를 업로드한다. Promotion은 같은 archive와 두 report를
별도 경로로 내려받고 SHA/member 검증을 마친 뒤 격리된 root에 추출하여 local
evidence를 read-only로 다시 계산하고 Ed25519 signature/digest를 확인한다.
Promotion job에는 build/rebuild command가 없으며 검증한 archive 자체를 변경 없이
그대로 승격한다.
Provider baseline은 Gitea 1.26.4 이상과 Gitea Runner 1.0.0 이상이다. 이
workflow를 required check로 전환하기 전에 staging instance에서
`permissions`, `gitea.run_attempt`, `actions/upload-artifact@v4`를 포함한 한
번의 전체 provider smoke를 통과시켜야 한다. 모든 setup step은
workflow의 provider job은 Linux runner에서 실행 권한이 있는
`/usr/bin/bwrap`를 필수로 요구하며, 사용할 Node 실행 파일도 sandbox 안의
`/usr/local/bin/node`로 고정한다. Provider command는 bubblewrap 안에서
`/bin/sh -eu -c`로 비대화식 실행되고 30분 안에 종료되어야 한다. Sandbox는
workspace를 read-only로 bind하고 `.git`을 가리며, 별도의 `untrusted`
raw-evidence 하위 디렉터리만 writable로 노출한다. 따라서 command는 전달된
candidate/environment 값을 읽고 지정된 raw report 하나만 기록해야 하며,
workspace 수정, host home/toolcache 접근, sealed evidence 직접 기록에 의존하면
안 된다. Supervisor는 provider 종류에 해당하는 credential prefix와 제한된
환경만 전달하고, sandbox 또는 출력 경계를 만들 수 없으면 fail closed한다.
Workflow가 실행하는 action은 `scripts/contracts/ci-gates.ts`의 단일 typed,
runtime-frozen registry에서만 resolve된다. `uses:`에는 repository 별칭, tag,
branch 또는 short SHA를 허용하지 않고 다음 absolute upstream URL과 full 40-hex
commit만 사용한다.
- `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)
Gitea 1.22 공식 Actions 문서는 artifact v4 호환 경로로 이
`ChristopherHX` patched upload/download fork를 권고한다. 이 권고는 실제
설치 환경의 호환성을 증명하지 않으므로, 지원 baseline은 계속 Gitea 1.26.4+
및 Gitea Runner 1.0.0+이며 action 변경은 registry, semantic tests, snapshot과
generated workflow를 함께 갱신해야 한다.
이 workflow를 required check로 전환하기 전에 staging instance에서
`permissions`, `gitea.run_attempt`, 위 digest-pinned patched artifact action을
포함한 한 번의 실제 end-to-end provider smoke를 반드시 통과시켜야 한다.
mock/local generation 결과는 required-check enablement evidence가 아니다. 모든 setup step은
`node-version-file: .nvmrc`를 사용하므로 CI Node.js 버전은 `.nvmrc`의 exact
pin과 같다. CI contract는 `.nvmrc`가 full semantic version인지, 모든 job이 이
파일을 사용하는지 함께 검사한다. `ubuntu-latest` runner label은
@@ -152,6 +196,18 @@ If any external provider command, report, trust path, or key ID is absent,
promotion remains unavailable with `FAIL_UNVERIFIED`; there is no local
generator/restore fallback.
Promotion verification/staging step과 promoted-release upload action 사이에는
어떤 step도 둘 수 없고 upload에는 `if: always()`를 사용할 수 없다. 이 인접성은
실패한 검증의 publication을 막지만 staging path를 upload action에
cryptographically hand off하지는 않는다. 따라서 promotion runner는 trusted,
exclusive, single-tenant여야 하고 staging step 종료부터 바로 다음 upload action이
파일을 여는 시점까지 같은 UID로 실행되는 provider command나 다른 process가
살아남지 않는다는 운영 가정을 만족해야 한다. 업로드된 artifact의 downstream
consumer도 artifact service나 transfer action을 신뢰 경계 밖으로 보고, candidate
manifest와 signed provider evidence에 바인딩된 digest를 다운로드 후 다시
검증해야 한다. 현재 producer-side adjacency 자체는 consumer-side digest
revalidation을 대신하지 않는다.
Branch protection must mark each `FE-GATE-* / <name>` check required for its
declared tier. This repository cannot configure server-side protection by
committing a file.