외부 리뷰("현재 상태를 유지하기 위한 검증이 너무 많고, 그 검증 자체를
다시 검증하는 구조까지 생겼다")를 설계 문서로 정리하고 코드로 반영한다.
설계·판단 근거는 docs/superpowers/specs/2026-09-16-verification-surface-reduction-design.md.
삭제
- .github/ci-gate-matrix.yml(1,025줄) + verify-gate-matrix.sh(568줄):
Gradle task graph와 workflow graph에 이미 있는 정보의 3중 복제
- verify-gradle-wrapper.sh(799줄): workflow 바이트 해시 잠금.
wrapper 검증은 gradle/actions/wrapper-validation(full SHA 핀)에 위임
- DeveloperExperienceContractTest 등의 CI YAML mutation 테스트:
애플리케이션 test suite가 GitHub Actions YAML 파서를 검증하던 계층 역전
- 문서 drift 파서: verifyReadmeCommands, verifyRunbookReferences,
verifyDocumentedLeafCount, verifyTestSourceSetRegistry
- 빈 레지스트리를 지키던 커스텀 YAML 파서: verifyTrivyignore,
verifyQuarantineSunset, flaky-quarantine.yaml
- verifyConfigurationPropertiesProcessor, verifyOneTypePerFile:
각각 ca.spring-config convention과 Checkstyle OneTopLevelClass가 대체
- 정상 입력으로도 성공할 수 없던 messaging always-fail task
- ModuleRegistry의 JSON 필드 집합 정확 일치, sample-portfolio negative guard
이동
- java/quality/spring 공통 설정을 configure(subprojects) 블록에서
ca.java-conventions / ca.quality-conventions / ca.java-library /
ca.spring-library convention plugin으로
- 아키텍처 검증을 ca.architecture로, JPA·messaging qualification을
gradle/qualification/ 아래로, verifyEnvKeys를 :app-bootstrap 소유로
완화
- Git revision은 releaseCheck·아카이브 생성에서만 요구. 일반 빌드는 SNAPSHOT
- SpotBugs/FindSecBugs는 로컬 check에서 빼고 qualityCheck 레인으로
task 계층
- leaf check는 그 leaf만. architectureCheck / qualityCheck /
configContractCheck / integrationCheck / ci / releaseCheck로 이름 분리
CI
- _reusable-gradle.yml 신규. checkout + wrapper validation + JDK/캐시 공통화
- fileserver-release.yml -> fileserver-certification.yml (CD가 아니라 certification)
- GitHub Actions = CI + artifact, Argo CD = CD 경계를 docs/ci-cd/boundary.md로 고정
순증감 +3,274 / -7,483.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
42 lines
2.4 KiB
YAML
42 lines
2.4 KiB
YAML
name: Set up Java and Gradle
|
|
description: >-
|
|
Installs the repository's pinned Temurin JDK, then configures Gradle through the official
|
|
setup-gradle action — which validates every checked-in wrapper jar and manages the Gradle cache.
|
|
Every Gradle job used to carry the JDK block verbatim, so the JDK patch level lived in fifty-nine
|
|
places; every job also carried a separate three-line wrapper-validation step, so the pinned action
|
|
SHA lived in forty.
|
|
|
|
# Wrapper validation is INSIDE this action now.
|
|
#
|
|
# It could not be before, and the reason was not a GitHub limitation: .github/scripts/
|
|
# verify-gradle-wrapper.sh read every workflow job and required it to contain, literally and in this
|
|
# order, an `actions/checkout@` step, the exact three-field pinned wrapper-validation step, and then
|
|
# the Gradle invocation. That literalness was the whole guard — "this job validated the wrapper" had
|
|
# to be answerable from the workflow file alone — and it is what made the step uninlineable.
|
|
#
|
|
# That script is gone (it also byte-hashed all twelve workflow files, so a comment change needed a
|
|
# hash update, while an attacker with write access would simply have updated both). The guarantee it
|
|
# was protecting is now the official action's own: `gradle/actions/setup-gradle` validates all
|
|
# wrapper jars by default (`validate-wrappers`, default true), and the action is pinned to a full
|
|
# commit SHA here — which GitHub's own hardening guide calls the only immutable action reference.
|
|
#
|
|
# `actions/checkout` still cannot move here: a `./.github/actions/...` reference is resolved from the
|
|
# checked-out working copy, so this file does not exist until checkout has already run. A composite
|
|
# action cannot contain the step that makes itself readable.
|
|
#
|
|
# So a Gradle job is two lines — checkout, then this action.
|
|
|
|
runs:
|
|
using: composite
|
|
steps:
|
|
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
|
|
with:
|
|
distribution: temurin
|
|
java-version: "21.0.11+10"
|
|
# Gradle's own caching, not setup-java's `cache: gradle`. The two cache the same directory with
|
|
# different keys, and running both is how a job restores one cache and saves the other.
|
|
- uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # gradle/actions@v6
|
|
with:
|
|
build-scan-publish: false
|
|
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|