From 166ac440c9d8cf950e72a3e63e8151702242fcb4 Mon Sep 17 00:00:00 2001 From: phil Date: Thu, 30 Apr 2026 12:53:30 +0900 Subject: [PATCH] =?UTF-8?q?chore(ci):=20GitHub=20Actions=20=ED=8F=90?= =?UTF-8?q?=EA=B8=B0=20+=20=EB=A1=9C=EC=BB=AC=204=20=EA=B3=84=EC=B8=B5=20?= =?UTF-8?q?=EA=B2=8C=EC=9D=B4=ED=8A=B8=20(RFC=200002,=20ADR=200009)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 글로벌 CLAUDE.md §2 (GitHub Actions 영구 금지, 2026-04-29 사고 트리거 — organization billing 1건 실패 → 전 저장소 전 PR 4초 만에 fail) 적용. 본 프로젝트의 .github/workflows/{ci,upstream-watch}.yml을 폐기하고 로컬 4 계층(pre-commit · pre-push · Makefile · PR review)으로 일원화. 폐기: - ci.yml — 5 jobs (lint, test, matrix-build, e2e, scan) 모두 로컬로 마이그 - upstream-watch.yml — Citus 신 릴리스 cron, RemoteTrigger/사용자 schedule로 대체 (후속) 신규: - .pre-commit-config.yaml L1 pre-commit: golangci-lint L2 pre-push: go mod tidy drift, make test, make audit, gitleaks - Makefile audit 타겟 — trivy fs --severity HIGH,CRITICAL --ignore-unfixed - docs/adr/0009-no-github-actions-rfc-0002.md — 결정 근거 + 4 계층 매핑 - README.md "Development (로컬 게이트)" 섹션 — 1회 셋업 + PR body 증거 블록 검증: - make lint: 0 issues - make audit: 0 vulnerabilities (PR #1 deps fix 효과 객관 확인) - pre-commit validate-config: PASS 후속 작업: 1. branch protection "Required status checks" 제거 (admin이 GitHub UI에서) 2. upstream-watch 대체 (RemoteTrigger / 사용자 schedule) 3. e2e cert-manager 통합 완성 PR (PR #1 fail의 진짜 fix) Refs: 글로벌 CLAUDE.md §2, RFC 0002 (2026-04-29), ADR 0009 Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 119 -------------------- .github/workflows/upstream-watch.yml | 52 --------- .pre-commit-config.yaml | 63 +++++++++++ Makefile | 5 + README.md | 41 +++++++ docs/adr/0009-no-github-actions-rfc-0002.md | 109 ++++++++++++++++++ 6 files changed, 218 insertions(+), 171 deletions(-) delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/upstream-watch.yml create mode 100644 .pre-commit-config.yaml create mode 100644 docs/adr/0009-no-github-actions-rfc-0002.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index e02ec08b..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,119 +0,0 @@ -name: CI - -on: - push: - branches: [main] - pull_request: - branches: [main] - -permissions: - contents: read - -concurrency: - group: ci-${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - lint: - name: Lint (golangci + .custom-gcl) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - cache: true - - name: Verify lint config - run: make lint-config - - name: Run linter - run: make lint - - test: - name: Unit + envtest - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - cache: true - - name: go mod tidy (drift check) - run: | - go mod tidy - git diff --exit-code go.mod go.sum - - name: Run tests - run: make test - - name: Upload coverage - if: hashFiles('cover.out') != '' - uses: codecov/codecov-action@v4 - with: - files: ./cover.out - fail_ci_if_error: false - - matrix-build: - name: PG ${{ matrix.pg }} x Citus ${{ matrix.citus }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - { pg: '16', citus: '12.1' } - - { pg: '16', citus: '13.0' } - - { pg: '17', citus: '13.0' } - steps: - - uses: actions/checkout@v4 - - uses: docker/setup-buildx-action@v3 - - name: Build PG image - env: - PG: ${{ matrix.pg }} - CITUS: ${{ matrix.citus }} - run: | - docker buildx build \ - --build-arg PG_MAJOR="$PG" \ - --build-arg CITUS_VERSION="$CITUS" \ - -f "build/images/pg/${PG}/Dockerfile" \ - -t "test-pg:${PG}-citus${CITUS}" \ - --load . - - e2e: - # Pillar 라벨 — roadmap.md의 14 Pillar 단위로 e2e 통과율을 추적한다. - # 현재는 P1(Core Lifecycle)만 활성. 다른 Pillar는 해당 마일스톤(P2-M1 등) - # 도달 시 matrix.pillar에 추가한다. - name: e2e (kind, PG ${{ matrix.pg }}, ${{ matrix.pillar }}) - runs-on: ubuntu-latest - needs: [test] - strategy: - fail-fast: false - matrix: - pg: ['16', '17'] - pillar: ['p1'] - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - cache: true - - uses: helm/kind-action@v1 - with: - version: v0.24.0 - - name: go mod tidy - run: go mod tidy - - name: Run e2e - env: - # matrix.pg와 matrix.pillar는 본 workflow 정의에서만 채워지며 외부 - # 입력(issue/PR title 등)에서 오지 않는다. 따라서 명령행 주입 위험 없음. - PG_MAJOR: ${{ matrix.pg }} - PILLAR: ${{ matrix.pillar }} - run: make test-e2e PILLAR="$PILLAR" - - scan: - name: Security scan (trivy fs) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: aquasecurity/trivy-action@v0.36.0 - with: - scan-type: fs - severity: HIGH,CRITICAL - exit-code: '1' - ignore-unfixed: true diff --git a/.github/workflows/upstream-watch.yml b/.github/workflows/upstream-watch.yml deleted file mode 100644 index 4e35e76a..00000000 --- a/.github/workflows/upstream-watch.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Upstream Watch - -# Citus와 PostgreSQL의 새 릴리스를 매일 감지해 internal/version/matrix.go에 자동 PR 생성. -# PG18 + Citus 호환 마이너 발표 시점에 preview-pg18 채널 자동 활성화. - -on: - schedule: - - cron: '0 6 * * *' - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - -jobs: - watch: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Fetch latest Citus releases - env: - GH_TOKEN: ${{ github.token }} - run: | - gh api repos/citusdata/citus/releases --paginate \ - --jq '.[] | select(.draft==false) | {tag: .tag_name, name: .name, body: .body}' \ - > /tmp/citus-releases.json || echo '[]' > /tmp/citus-releases.json - - name: Detect PG18 support in Citus - id: pg18 - run: | - if jq -e 'select(.body | test("(?i)PostgreSQL[[:space:]]*18"))' /tmp/citus-releases.json > /tmp/pg18-citus.json; then - echo "supported=true" >> "$GITHUB_OUTPUT" - else - echo "supported=false" >> "$GITHUB_OUTPUT" - fi - - name: Open PR if matrix needs update - if: steps.pg18.outputs.supported == 'true' - uses: peter-evans/create-pull-request@v6 - with: - branch: chore/upstream-watch-pg18 - title: 'chore(version): activate PG18 in matrix (Citus support detected)' - body: | - Citus 새 릴리스가 PostgreSQL 18을 지원하는 것으로 감지되었습니다. - - - internal/version/matrix.go의 PG18 항목 활성화 필요 (preview-pg18 채널) - - build/images/pg/18/Dockerfile의 CITUS_VERSION 기본값 갱신 필요 - - e2e 매트릭스에 PG17->18 업그레이드 시나리오 추가 필요 - - 이 PR은 자동 생성되었습니다. - commit-message: 'chore(version): activate PG18 in matrix' - labels: | - automation - preview-pg18 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..edc8d00f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,63 @@ +# Local 4-tier gate (RFC 0002 — GH Actions 영구 금지). ADR 0009 참조. +# +# 도입: +# pre-commit install --hook-type pre-commit --hook-type pre-push +# +# 우회: +# `--no-verify`는 사고 보고 의무 (incident-kb.md). 남용 금지. +# +# 매핑: +# L1 pre-commit — lint (빠른 피드백) +# L2 pre-push — test (unit + envtest), audit (trivy fs), secrets (gitleaks), +# go.mod drift check +# L3 Makefile — e2e (kind), matrix-build (docker buildx) — 수동 실행 +# L4 PR review — PR body의 "로컬 게이트 PASS" 증거 블록 확인 + +repos: + - repo: local + hooks: + # ================================================================ + # L1 pre-commit — fast feedback (< 30s 목표) + # ================================================================ + - id: golangci-lint + name: golangci-lint (with .custom-gcl) + language: system + entry: bash -c 'make lint-config && make lint' + pass_filenames: false + always_run: true + stages: [pre-commit] + + # ================================================================ + # L2 pre-push — comprehensive verification (1-3 min 목표) + # ================================================================ + - id: go-mod-tidy-drift + name: go.mod / go.sum drift check + language: system + entry: bash -c 'go mod tidy && git diff --exit-code go.mod go.sum' + pass_filenames: false + always_run: true + stages: [pre-push] + + - id: go-test + name: Unit + envtest (make test) + language: system + entry: make test + pass_filenames: false + always_run: true + stages: [pre-push] + + - id: trivy-fs-audit + name: Trivy fs (HIGH+CRITICAL — make audit) + language: system + entry: make audit + pass_filenames: false + always_run: true + stages: [pre-push] + + - id: gitleaks + name: gitleaks (staged secrets scan) + language: system + entry: bash -c 'command -v gitleaks >/dev/null 2>&1 && gitleaks protect --staged --redact || { echo "[warn] gitleaks 미설치 — brew install gitleaks 권장"; exit 0; }' + pass_filenames: false + always_run: true + stages: [pre-push] diff --git a/Makefile b/Makefile index 2bfa0395..7dd33ecc 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,11 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes lint-config: golangci-lint ## Verify golangci-lint linter configuration "$(GOLANGCI_LINT)" config verify +.PHONY: audit +audit: ## Run vulnerability scan (trivy fs, HIGH+CRITICAL severity, ignore-unfixed). RFC 0002 / ADR 0009. + @command -v trivy >/dev/null 2>&1 || { echo "[error] trivy not installed: brew install trivy (or apt install trivy)"; exit 1; } + trivy fs --severity HIGH,CRITICAL --exit-code 1 --ignore-unfixed --skip-dirs vendor,bin,tmp . + ##@ Build .PHONY: build diff --git a/README.md b/README.md index cf85fba7..306dd4fd 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,47 @@ psql "host=localhost port=5432 dbname=app user=app sslmode=require" --- +## Development (로컬 게이트) + +본 프로젝트는 [ADR 0009](docs/adr/0009-no-github-actions-rfc-0002.md)에 따라 **GitHub Actions를 사용하지 않습니다** (글로벌 RFC 0002, 2026-04-29 사고 트리거). 모든 게이트(lint·test·audit·secrets)는 *로컬 4 계층*으로 일원화됐습니다. + +### 1회 셋업 + +```bash +# 보안 도구 설치 (macOS) +brew install gitleaks trivy +# Linux는 apt/공식 binary 참조: https://aquasecurity.github.io/trivy/ + +# pre-commit hook 활성화 (1회 실행) +pip install pre-commit # 또는 brew install pre-commit +pre-commit install --hook-type pre-commit --hook-type pre-push +``` + +### 4 계층 게이트 + +| 계층 | 시점 | 명령 | 차단 기준 | +|---|---|---|---| +| **L1 pre-commit** | `git commit` | `make lint` | lint error 1건 이상 | +| **L2 pre-push** | `git push` | `make test`, `make audit`, gitleaks, go.mod drift | error 1건 이상 | +| **L3 Makefile** | 개발자 수시 | `make test-e2e` (kind 7-9분), `make build` | 로컬 명시 검증 | +| **L4 PR review** | merge 전 | PR body의 "로컬 게이트 PASS" 증거 블록 | 증거 부재 시 머지 차단 | + +### PR 머지 증거 블록 (필수) + +PR 본문 또는 첫 commit 메시지에 다음 형식 포함 (`standards/ci.md §2`): + +``` +로컬 게이트 PASS: +- pre-commit run --all-files: PASS +- pre-push hooks: PASS +- make test: PASS +- make audit: PASS (HIGH+CRITICAL = 0) +``` + +부재 시 리뷰어가 머지를 차단합니다. 우회(`--no-verify`)는 사고 보고 의무 (`incident-kb.md`). + +--- + ## 기여하기 - 행동강령: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) (Contributor Covenant 2.1) diff --git a/docs/adr/0009-no-github-actions-rfc-0002.md b/docs/adr/0009-no-github-actions-rfc-0002.md new file mode 100644 index 00000000..afb52dcb --- /dev/null +++ b/docs/adr/0009-no-github-actions-rfc-0002.md @@ -0,0 +1,109 @@ +# ADR 0009 — GitHub Actions 폐기 + 로컬 4 계층 게이트 적용 (RFC 0002 적용) + +- **상태**: Accepted +- **날짜**: 2026-04-30 +- **결정자**: @keiailab/maintainers +- **관련**: 글로벌 CLAUDE.md §2 (GitHub Actions 영구 금지), 글로벌 RFC 0002 (2026-04-29), `standards/ci.md` v1.0 +- **트리거**: 글로벌 §2 사고 (2026-04-28) — organization billing 1건 실패 → 전 저장소 전 PR의 모든 workflow runner 4초 만에 fail → 머지 불가 24시간+. 단일 외부 SaaS 의존이 SPOF. + +## 컨텍스트 + +본 프로젝트는 `.github/workflows/{ci,upstream-watch}.yml` 두 워크플로를 *현재 활성*. 글로벌 §2가 *RFC 0002에 따라 GitHub Actions 영구 금지*를 명문화한 시점(2026-04-29) 이후 본 프로젝트는 *마이그레이션 미적용 잔재*. 본 ADR이 그 적용 결정을 기록. + +또한 PR #1 머지 직전 발견된 *e2e fail*은 *기존 main의 cert-manager 통합 미완*이 원인이며, GH Actions 자체가 *PR 머지 차단의 단일 SPOF*로 작용하는 위험을 노출. + +## 결정 + +### 폐기 대상 + +- `.github/workflows/ci.yml` — 5 jobs (lint, test, matrix-build, e2e, scan) 모두 *로컬 4 계층*으로 마이그레이션 후 삭제. +- `.github/workflows/upstream-watch.yml` — Citus 신 릴리스 감지 cron. *RemoteTrigger 또는 사용자 schedule*로 대체 (본 ADR 후속 작업). + +### 4 계층 매핑 (글로벌 `ci.md` §1 표준) + +| 기존 ci.yml job | 새 위치 | 명령 | +|---|---|---| +| lint (golangci + .custom-gcl) | **L1 pre-commit** | `make lint-config && make lint` | +| test (Unit + envtest + go mod tidy drift) | **L2 pre-push** | `make test`, `go mod tidy && git diff --exit-code go.mod go.sum` | +| scan (trivy fs HIGH+CRITICAL) | **L2 pre-push** | `make audit` (신규 타겟, trivy fs 호출) | +| matrix-build (PG×Citus 3 조합) | **L3 Makefile (수동)** | release tag 시점 또는 `make build-pg-images` 수동 | +| e2e (kind, PG 16/17, p1) | **L3 Makefile (수동)** | `make test-e2e` (kind 7-9분, pre-push 부적합) | +| upstream-watch (cron) | **RemoteTrigger 또는 사용자 schedule** | 본 PR에서는 폐기만, 대체는 후속 | + +### 예외 3종 + +본 프로젝트는 글로벌 §2의 예외 3종 *어디에도 해당하지 않음*: +- ① GitHub Pages 정적 배포 — 사용 안 함 +- ② Dependabot/Renovate 도구 자체 — `.github/dependabot.yml` 부재 (필요 시 별도 추가) +- ③ release tag → GitHub Release 본문 자동 생성 — 현재 release tag 워크플로 부재 + +### 도구 설치 + 강제 + +- 개발자 환경: `pre-commit install --hook-type pre-commit --hook-type pre-push` 1회 실행 강제 (README 안내). +- `.pre-commit-config.yaml`이 본 PR과 함께 도입. +- 우회(`--no-verify`)는 *사고 보고 의무* (`incident-kb.md`). + +### PR 머지 증거 (글로벌 `ci.md` §2) + +PR 본문 또는 첫 commit 메시지에 다음 블록 포함 강제 (PR 리뷰어가 확인): + +``` +로컬 게이트 PASS: +- pre-commit run --all-files: PASS +- pre-push hooks: PASS (또는 N/A if no hook) +- make test: PASS (or specific subset) +- make audit: PASS (high+ vulnerabilities = 0) +``` + +부재 시 리뷰어가 머지 차단. + +## 근거 + +### 왜 *지금* 마이그레이션인가 + +1. **글로벌 §2 명시 위반** — 2026-04-29 이후 *비정합 상태*. 본 프로젝트가 다른 Kei* repo(force-infra-modules, force-tenant-house 등)와 *일관된 표준*을 유지하려면 즉시 적용. +2. **PR #1 e2e fail의 PR-blocking 효과 제거** — GH Actions 폐기 후에는 e2e fail이 *로컬 검증 차원*에 머무르며 *PR 머지 차단 SPOF 해소*. 단 e2e 자체 fix는 별도 PR. +3. **사고 트리거 회피** — organization billing 단일 SPOF가 다시 발생하면 본 프로젝트도 동일 영향. 마이그레이션 지연은 위험 수동 수용. + +### 왜 e2e/matrix-build를 L2가 아닌 L3로 두는가 + +- e2e: kind cluster 부팅 + 7-9분 소요. 매 push 마다 실행은 *개발자 경험 저해*. PR 진입 직전 또는 release 시점에 명시 실행이 합리적. +- matrix-build: docker buildx로 PG image 3 조합 build. release tag 시점에만 필요. + +이는 글로벌 `ci.md` §3 도구 카탈로그가 e2e/build를 별도 트랙으로 두는 패턴과 부합. + +### 왜 lefthook 아닌 pre-commit인가 + +글로벌 `enforcement.md` §1.1은 lefthook 권장이지만, 본 프로젝트는 *Go 단일 언어 + Python(pre-commit) 사용자 친숙*이라 pre-commit이 더 자연스러움. 글로벌 §3 우선순위 "Tier-3 프로젝트 > Tier-2 standards"에 따라 본 ADR로 정당화. 향후 lefthook 채택은 별도 ADR. + +## 트레이드오프 + +- **upstream-watch 자동화 손실**: cron 기반 Citus 신 릴리스 감지가 일시 중단. 완화: 후속 작업에서 RemoteTrigger 또는 사용자 schedule 도구로 대체. +- **개발자 환경 의존**: pre-commit + 보안 도구(gitleaks, trivy) 로컬 설치 강제. 완화: README에 `brew install` 또는 동등 명령 명시. macOS/Linux 환경 가정. +- **PR 리뷰어 부담 증가**: 로컬 게이트 PASS 증거 블록을 *수동* 확인. 완화: 글로벌 표준이 동일하므로 *전 Kei* repo 공통 부담* — 학습 곡선이 한 번. +- **e2e가 PR 자동 검증에서 빠짐**: 단 *e2e는 현재 이미 fail 상태* (cert-manager 미완)이므로 *기존 PR 차단을 막는 효과* 발생. 진짜 e2e fix(P7 cert-manager 통합 PR)에서 *L3 명시 실행*으로 검증. + +## 결과 + +- `.github/workflows/{ci,upstream-watch}.yml` 두 파일 *삭제*. +- `.pre-commit-config.yaml` 신규 — L1 + L2 hook 정의. +- `Makefile`에 `audit` 타겟 신규 (`trivy fs` 호출). +- `README.md`에 "로컬 게이트 PASS 증거 블록" + 개발자 환경 설정 안내 추가. +- 본 PR 머지 후 *외부 단계*: GitHub branch protection의 "Required status checks" 제거 또는 로컬 hook 결과 마커로 교체 (admin이 별도 수행). + +## 강제 메커니즘 + +| 메커니즘 | 위치 | 도입 시점 | +|---|---|---| +| `.pre-commit-config.yaml` | repo root | 본 ADR 동시 | +| `Makefile` audit 타겟 | `Makefile` | 본 ADR 동시 | +| 개발자 환경 안내 | `README.md` | 본 ADR 동시 | +| PR 본문 증거 블록 강제 | `commits.md §3` PR 체크리스트 | 글로벌 표준 | +| upstream-watch 대체 | RemoteTrigger / 사용자 schedule | 후속 작업 | + +## 후속 작업 + +1. **upstream-watch 대체** — RemoteTrigger 또는 사용자 schedule 도구로 Citus 신 릴리스 감지 자동화 재구성. +2. **branch protection rule 갱신** — admin이 GitHub UI에서 Required status checks 제거. +3. **lefthook 마이그레이션 검토** (향후) — 글로벌 `enforcement.md` §1.1 권장과 정합 시 별도 ADR. +4. **e2e cert-manager 통합 완성 PR** — config/certmanager/ + Certificate CR + e2e BeforeAll wait. 본 PR과 별도 진행.