--- title: error / Gitea act job jq bootstrap 누락 source_type: error-note status: raw related_branches: [feature-build-release-supply-chain-contract] related_projects: [ca-skeleton] tags: [error, ca-skeleton, ci-cd, build-tooling, supply-chain] created: 2026-06-23 status_label: resolved --- # error: Gitea act job jq bootstrap 누락 > Layer: `raw/errors/` — Gitea/act minimal job image에서 jq가 없어서 공급망 계약 테스트가 차단된 원인과 보완 기록. ## Parent / 부모 - [[raw/branch-notes/feature-build-release-supply-chain-contract]] ## 증상 / Symptom - 에러 메시지 (CI 로그 원문): ```text /workspace/donghyun.kang/ca-tmpl/.github/scripts/create-release-manifest.sh: line 52: jq: command not found exitcode '127': command not found, please refer to https://github.com/nektos/act/issues/107 for more information ``` - 발생 컨텍스트: `ci-quality-gates/gate-matrix-lint`에서 gate matrix와 D1-D13 정적 계약이 성공한 다음 `test-supply-chain-scripts.sh`가 release manifest를 생성할 때 발생. - 발생 시점: 2026-06-21 05:38 UTC. - 발생 환경: Gitea Actions `k8s-runner-1 v0.2.11`, job image `node:20-bullseye`. - 재현 가능 여부: `always` — jq가 없는 동일 job image에서 해당 스크립트를 실행하면 종료 코드 127. ## 재현 절차 / Reproduction 1. Gitea/act runner에서 `ubuntu-latest`를 jq가 포함되지 않은 `node:20-bullseye`로 매핑한다. 2. `.github/workflows/ci-quality-gates.yml`의 `gate-matrix-lint`를 실행한다. 3. 기대 결과는 공급망 behavior test 성공이지만, 실제 결과는 `create-release-manifest.sh`의 첫 jq 호출에서 종료 코드 127이다. 4. upstream `gate-matrix-lint` 실패를 받은 `release-gate`는 release-blocking gate 실패로 정상 차단된다. ## 조사 단계 / Investigation log - 2026-06-23 — 두 CI 로그를 대조했다. gate matrix와 `verify-supply-chain-contract.sh`는 성공했고, behavior test만 jq 부재로 실패했다. `release-gate`는 이 upstream failure를 정상적으로 전파했다. - 2026-06-23 — workflow와 간접 호출을 전수 대조해 jq가 필요한 job environment 6개를 확인했다: `gate-matrix-lint`, `contract`, `verify`, `promote`, `audit-retention`, `trivy-fs`. - 2026-06-23 — 구현 전 정적 계약을 강화해 installer 부재, 6개 job 배선 누락, inline download 잔존을 합쳐 15개 위반으로 실패하는 RED를 확인했다. - 2026-06-23 — jq 1.8.1 AMD64 asset을 job-local 경로에 다운로드하고 고정 SHA-256을 검증한 뒤 실행했다. 설치된 jq로 공급망 manifest/retention 양·음수 테스트가 성공했다. - 2026-06-23 — 실패 로그와 동일한 third-party container에 private workspace를 mount하는 검증은 안전 정책으로 거부되어 중단했다. 우회하지 않고 실제 Gitea CI 재실행을 잔여 확인으로 남겼다. ## 근본 원인 / Root cause - 직접 원인: `create-release-manifest.sh`가 jq를 호출했지만 job의 `PATH`에 jq executable이 없었다. - 근본 원인: workflow가 jq를 명시적 job dependency로 bootstrap하지 않고 hosted runner의 ambient tool에 의존했다. Gitea/act의 minimal image는 이 암묵적 전제를 만족하지 않았다. - 트리거 조건: jq가 없는 job image에서 직접 `jq`를 호출하거나 `create-release-manifest.sh`/`audit-rollback-retention.sh`를 간접 호출한다. ## Sources / 근거 - [jq 1.8.1 release](https://github.com/jqlang/jq/releases/tag/jq-1.8.1) — Linux AMD64/ARM64 release assets와 checksum 고정 기준. - [jq 1.8.1 release API](https://api.github.com/repos/jqlang/jq/releases/tags/jq-1.8.1) — asset digest metadata 확인. - [[raw/errors/gitea-act-action-tag-and-dependency-graph-2026-06-20]] — 같은 runner에서 composite action의 CLI 설치 누락을 직접 CLI 설치로 전환한 선행 사례. ## 해결 / Resolution - 적용한 조치: - `.github/scripts/install-jq.sh`에 jq 1.8.1, Linux AMD64/ARM64 asset, 공식 SHA-256을 고정했다. - `RUNNER_ARCH`에 따라 asset을 선택하고 `RUNNER_TEMP` 아래 설치한 뒤 `GITHUB_PATH`로 다음 step에 전달한다. - checksum mismatch, 다운로드 실패, 미지원 architecture는 fail-closed한다. - jq 소비 job 6개가 같은 installer를 호출하도록 연결하고 dependency workflow의 inline jq 다운로드를 제거했다. - `verify-supply-chain-contract.sh`가 installer 불변식, job-level 호출, inline download 금지를 검사한다. - 검증 방법: - RED: 정적 계약 15개 예상 위반. - GREEN: 공급망 정적 계약과 gate matrix lint 성공. - 공식 AMD64 asset checksum 검증 및 `jq-1.8.1` 실행 성공. - 설치된 jq로 `test-supply-chain-scripts.sh` 성공. - 네 workflow YAML parse 성공. - Gradle architecture, ArchUnit, full test, `check verifyPublicPathSnapshot` 성공. - 잔여 위험 / 후속 작업: 변경 commit으로 실제 Gitea/act `gate-matrix-lint`를 재실행해 설치와 behavior test 로그를 확인해야 한다. github.com egress가 없는 runner의 internal mirror 정책은 별도 운영 결정이다. ## 회고 / Lessons - 빨리 감지하는 신호: 정적 계약은 성공했는데 behavior test가 종료 코드 127 또는 `command not found`로 실패하면 runner tool bootstrap 누락부터 확인한다. - 예방 체크리스트 항목 후보: shell script가 사용하는 외부 CLI를 호출 graph 기준으로 추적하고, 각 독립 job에 설치 step이 있는지 정적 계약으로 검사한다. - wiki로 끌어올릴 가치가 있는 일반화된 교훈: runner ambient tool 대신 version/checksum이 고정된 job-local bootstrap을 사용하고, 설치 구현은 한 파일로 중앙화한다. ## Related / 관련 - Parent: [[raw/branch-notes/feature-build-release-supply-chain-contract]]. - 선행 유사 오류: [[raw/errors/gitea-act-action-tag-and-dependency-graph-2026-06-20]]. - 별도 interview prep: 이번 보완에서는 신규 추출 없음. 기존 [[raw/interviews/digest-first-supply-chain-release-gates]]로 충분하다. - 별도 blog topic: 이번 보완에서는 신규 추출 없음. 기존 [[raw/blog-topics/digest-first-java-release-pipeline-2026-06-21]]에 포함 가능한 하위 사례다.