139 lines
9.3 KiB
Markdown
139 lines
9.3 KiB
Markdown
---
|
|
kind: CASE
|
|
slug: a-release-gate-with-no-evidence-producer
|
|
title: 릴리스 게이트가 읽는 증거를 아무도 생산하지 않는다
|
|
topic: what-a-gate-does-not-prove
|
|
project: clean-architecture-backend-template
|
|
status: 게시 전
|
|
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
rootTreeNode: case:a-release-gate-with-no-evidence-producer
|
|
evidenceCapturedOn: 2026-09-02
|
|
assets:
|
|
- key: a-release-gate-with-no-evidence-producer
|
|
file: ../../../final/evidence/rendered/a-release-gate-with-no-evidence-producer.svg
|
|
evidence:
|
|
- ../../../final/evidence/raw/a-release-gate-with-no-evidence-producer.txt
|
|
- ../../../final/evidence/raw/tl-grpc-release-gate-no-producer.txt
|
|
source:
|
|
- final/document.md#7-6
|
|
- final/document.md#a20
|
|
- 분석 문서는 gRPC 플랫폼 편 §3.2 다. 그 절이 지원 매트릭스의 현재 시제 문장과 게이트의 설계 근거를 인용하고, 생성 지점 넷이 전부 테스트라는 것과 messaging 이 그것을 세 층으로 닫았다는 대비를 적는다.
|
|
- 같은 절이 근거로 든 검색은 리프의 build.gradle 안에서 태스크 등록 문자열을 세는 것이라 0 이 나온다. 이 저장소는 등록을 컨벤션 플러그인으로 옮겨 두었으므로 그 수는 등록된 태스크 수가 아니다. 위 터미널 출력의 레인 목록이 그것을 보여 준다.
|
|
---
|
|
|
|
# 릴리스 게이트가 읽는 증거를 아무도 생산하지 않는다
|
|
|
|
gRPC 안정 릴리스 게이트는 증거 객체를 읽어 판정한다. 그 객체를 만드는 곳은 게이트 자신의 단위 테스트뿐이다. 그 테스트는 매 PR 의 check 를 타고 돌지만, 거기서 게이트가 읽는 것은 테스트가 손으로 채운 값이다.
|
|
|
|
## 관계
|
|
|
|
- **아무도 돌리지 않는 레인의 게이트는 마지막으로 돌린 사람이 본 것을 보고한다**
|
|
이 사건에서 레인이 실행되지 않으면 게이트가 실제 결과 대신 이전 상태나 손으로 만든 입력만 보게 된다는 규칙을 정리했다.
|
|
- **빠뜨림이 통과가 되는 게이트는 게이트가 아니다**
|
|
증거 생산자가 연결되지 않은 상태를 성공으로 처리하지 않도록 하는 규칙이다.
|
|
- **시작 검증기 13개 규칙이 유일한 조립 지점에서 호출되지 않는다**
|
|
같은 gRPC 블록에서 검증 로직은 존재하지만 확인한 조립·실행 경로가 이를 호출하지 않았다.
|
|
|
|
## 문제
|
|
|
|
게이트는 증거 객체를 입력으로 받아 안정 등급 승격을 판정한다. 판정 로직은 존재하고 테스트되어 있다.
|
|
|
|
증거 객체의 다섯 성분은 전부 호출자가 넘기는 값이다. 등급과 능력이 둘이고, 문서 셋의 존재 여부가 나머지다. 어느 것도 파일 시스템이나 테스트 리포트를 보지 않는다.
|
|
|
|
## 결론
|
|
|
|
searched direct construction 기준으로 증거 객체를 만드는 코드는 게이트 자신의 테스트 네 군데에서만 확인했다.
|
|
|
|
등급 넷에 대응하는 증거 레인 넷은 존재한다. 리프의 build.gradle 이 아니라 컨벤션 플러그인이 등록하고, 지원 매트릭스가 등급마다 그 이름을 적는다.
|
|
|
|
없는 것은 둘이다. 레인이 낸 결과를 증거 객체로 바꾸는 코드가 없고, 그 레인 넷을 check 나 워크플로에 붙이는 배선이 없다.
|
|
|
|
messaging 쪽은 그 둘을 세 층으로 닫았다. 레인이 증거 파일을 쓰고, Gradle 태스크가 실행 산출물과 커밋본을 대조하고, 워크플로가 실제 브로커를 띄워 그 태스크를 돌린다.
|
|
|
|
gRPC 블록이 어떤 런타임 컴포지션에도 속하지 않는다는 것은 지원 매트릭스가 스스로 적고 있으며 결함이 아니다. 남는 것은 게이트의 입력이다.
|
|
|
|
## 검증 환경
|
|
|
|
OpenJDK : 21.0.12
|
|
Gradle : 9.0.0
|
|
확인 방식 : 정적 도달성 확인과 태스크 그래프 확인
|
|
소스 수정 : x
|
|
|
|
## 재현 조건
|
|
|
|
1. 증거 레코드의 성분을 읽고, 그중 파일 시스템이나 리포트를 보는 것이 있는지 확인한다.
|
|
2. 그 객체를 생성하는 코드를 저장소 전체에서 찾고 main 과 test 를 구분한다.
|
|
3. 게이트의 테스트에 태그가 붙어 있는지 확인하고, check 그래프에 그 테스트가 들어 있는지 확인한다.
|
|
4. grpc-testkit 에 등록된 증거 레인을 나열하고, 그중 몇 개가 check 그래프에 있는지 센다.
|
|
5. CI 워크플로 중 grpc 를 담은 것의 수를 세고, root check 를 도는 워크플로를 확인한다.
|
|
6. 대비를 위해 messaging 쪽의 증거 파일과 대조 태스크와 워크플로를 확인한다.
|
|
|
|
## 본문
|
|
|
|
<!-- body:start -->
|
|
|
|
`docs/compatibility/grpc-support-matrix.md` 는 안정 릴리스 게이트를 현재 시제로 적는다. 인증된 레인에 결과가 없거나 실패하면 릴리스를 막는다는 것이다.
|
|
|
|
게이트는 그 판정을 증거 객체 하나에서 읽는다.
|
|
|
|
## 다섯 성분은 전부 호출자가 넘긴다
|
|
|
|
:::evidence key="a-release-gate-with-no-evidence-producer" alt="코드베이스에서 게이트가 읽는 증거 레코드의 다섯 성분과 그 객체를 만드는 네 지점, 그 테스트에 태그가 없어 check 그래프 안에 있다는 것과 그 check 를 도는 워크플로 줄, 등급 넷에 대응해 컨벤션이 등록한 증거 레인 넷과 그중 check 그래프에 들어 있는 수와 grpc 를 담은 워크플로 수, 그리고 messaging 쪽에서 레인이 쓰는 증거 파일과 그것을 대조하는 태스크와 그 태스크를 돌리는 워크플로 단계를 뽑은 출력 39줄. 게이트의 테스트는 check 안에 있고 증거 레인 넷은 밖에 있다는 것이 그 출력에 보인다." caption="증거의 다섯 성분 · 생성 지점 넷 전부 테스트 · 그 테스트는 check 안 · 레인 넷은 check 밖 · messaging 의 세 층 — 39줄" zoom="true"
|
|
:::
|
|
|
|
증거 레코드는 실행한 등급 집합과 인증된 능력 집합, 그리고 런북과 결정 기록과 지원 매트릭스의 존재 여부 셋을 담는다.
|
|
|
|
문서 존재 여부 셋은 파일 시스템을 직접 확인하지 않고 호출자가 넘긴 boolean을 그대로 저장한다. 실행 등급 집합과 인증 능력 집합도 호출자가 구성한다.
|
|
|
|
## 확인한 직접 생성 지점은 게이트 테스트 네 곳이다
|
|
|
|
생성 지점이 넷이고 전부 같은 파일, 게이트 자신의 단위 테스트다.
|
|
|
|
그 테스트에는 태그가 붙어 있지 않다. 그래서 기본 test 태스크에 들어가고, check 그래프 안에 있고, 매 PR 마다 도는 워크플로가 그 check 를 돌린다.
|
|
|
|
판정 로직 자체는 기본 테스트에서 실행된다. 다만 그 실행에서 게이트가 읽는 다섯 성분은 실제 lane 산출물이 아니라 테스트가 직접 구성한 값이다.
|
|
|
|
## 레인은 등록돼 있는데 아무 게이트에도 붙어 있지 않다
|
|
|
|
증거 등급이 넷이고, 그 넷에 대응하는 레인이 넷 등록되어 있다. 인프로세스 계약, 실제 Netty, 결함 주입, 성능이다.
|
|
|
|
등록은 리프의 build.gradle 이 아니라 컨벤션 플러그인이 한다. 지원 매트릭스가 등급마다 이 이름들을 적어 둔다.
|
|
|
|
그 레인 넷 중 check 그래프에 들어 있는 것은 0 이다. 28개 워크플로 중 grpc 를 이름이나 내용에 담은 것도 0 이다.
|
|
|
|
레인 태스크는 직접 실행할 수 있지만 check 그래프와 확인한 CI workflow에서는 호출되지 않는다.
|
|
|
|
## 없는 것은 레인과 증거 사이의 코드다
|
|
|
|
레인이 낸 결과를 증거 객체의 다섯 성분으로 바꾸는 코드가 없다.
|
|
|
|
그래서 게이트가 읽는 값은 레인이 실제로 무엇을 했는지와 연결되어 있지 않다. 블록이 출하되기 시작해도 이 연결은 저절로 생기지 않는다.
|
|
|
|
## 같은 저장소에 그 사슬을 이어 놓은 예가 있다
|
|
|
|
messaging 쪽은 세 층이다.
|
|
|
|
레인이 실행 결과를 증거 파일로 쓴다. Gradle 태스크가 그 산출물과 커밋된 매니페스트를 대조하고, 커밋본이 레인이 내지 않은 시나리오를 주장하면 실패한다. 워크플로가 실제 브로커를 띄워 그 태스크를 돌리고 결과를 산출물로 올린다.
|
|
|
|
해당 태스크는 결과 캐시를 끄고, 인증 판단에는 이번 실행에서 생성한 증거가 필요하다는 이유를 주석으로 남긴다.
|
|
|
|
워크플로는 레인이 현재 커밋 해시를 읽어 증거에 기록하도록 한다. 인증 결과를 특정 소스 트리와 연결하기 위해서다.
|
|
|
|
## 게이트의 설계가 차단 사유를 만드는 방식
|
|
|
|
게이트는 빠진 결과와 빠진 등급과 스키마 판정과 문서 존재를 합쳐 차단 사유 목록을 만든다.
|
|
|
|
문서 누락도 차단 사유로 둔다. 런북 없이 동작만 출하하면 운영자가 장애 시점에 절차를 즉석에서 추론해야 하기 때문이다.
|
|
|
|
## 출하되지 않는다는 사실은 결함이 아니다
|
|
|
|
지원 매트릭스는 모든 gRPC leaf의 runtime membership을 비워 두고 build-only로 표시한다. 이 상태에서는 코드는 컴파일되고 증거 lane도 실행할 수 있지만 현재 배포 산출물에는 포함되지 않는다.
|
|
|
|
같은 문서는 안정 릴리스 전에 추가로 필요한 입력으로 성능 측정 결과와 스키마 코드 생성 검증을 적는다.
|
|
|
|
## 확인하지 못한 것
|
|
|
|
gRPC 블록이 어떤 배포에도 들어가지 않아서 런타임에서 볼 방법이 없다. 레인 넷을 실제로 돌려 결과를 보지는 않았다.
|
|
|
|
<!-- body:end -->
|