릴리스
TRUSCA 릴리스는 vX.Y.Z 형식의 git 태그를 push하면 시작됩니다. 나머지는
.github/workflows/release.yml
워크플로가 처리합니다. 목표는 하나입니다. 사용자가 실제로 pull하게 될 바로 그
이미지로 설치가 되는지 확인하기 전에는 어떤 릴리스도 공개하지 않습니다.
게이트 개요
워크플로는 네 단계를 순서대로 실행하며, 각 단계는 앞 단계에 의존합니다.
build— 각 이미지(trusca-backend,trusca-backend-worker,trusca-frontend)를 amd64와 arm64 네이티브 러너에서 빌드하고 GitHub Container Registry에 다이제스트 단위로 push합니다.merge— 이미지마다 멀티아치 매니페스트 리스트를 만들고 버전 태그를 붙입니다(X.Y.Z는 불변,X.Y는 이동 가능 —:latest는 절대 쓰지 않습니다).release— GitHub Release를 draft로 생성합니다. 릴리스 노트는docs-site/docs/release-notes/X.Y.Z.md가 있으면 그것을, 없으면 GitHub가 자동 생성한 노트를 씁니다. 이 잡은 릴리스 자신의 소스 트리에 대한 CycloneDX SBOM도 생성해(syft) Release 자산으로 첨부합니다(trusca-X.Y.Z.cdx.json) — SCA 제품은 자기 SBOM을 함께 내놓습니다.release-gate— 방금 발행한X.Y.Z이미지를 pull해서 프로덕션docker-compose.yml을 기동합니다. 이때 작은 오버레이docker-compose.smoke.yml이 backend와 frontend 포트를 노출해 Traefik/DNS/TLS 없이도 스모크를 돌릴 수 있게 합니다. 그다음 문서화된 Quickstart first-scan 스모크를 실행합니다. 헬스 폴링 →create_super_admin→ 로그인 → projects API 순서입니다. 성공하면gh release edit <tag> --draft=false --latest로 Release를 공개하고, 이어서deploy-hetzner.yml에 그 태그로 공개 데모를 올려 달라고 요청합니다. 이 배포는demo환경의 승인자를 기다리므로, 릴리스가 아무도 모르는 사이에 데모 호스트까지 나가지는 않습니다.
build ──▶ merge ──▶ release (draft) ──▶ release-gate ──▶ 공개 (draft=false)
다이제스트 버전 GitHub Release 발행 이미지 pull 스모크 통과 시에만
push 태그 아직 숨김 + first-scan 스모크 공개 + latest
latest 태그0.21.0까지의 릴리스는 latest 이미지 태그도 함께 올렸습니다.
docker/metadata-action이 별도로 끄지 않으면 태그를 하나 더
붙이는데(flavor: latest=auto) 워크플로가 그것을 끄지 않았습니다. 지금은
꺼져 있어 새로 생기지는 않습니다. 이미 GHCR에 올라간 태그는 그냥 지울 수
없습니다. GHCR은 태그가 아니라 패키지 버전 단위로 삭제하는데, latest가
0.21.0, 0.21과 같은 버전에 붙어 있기 때문입니다. 다음 릴리스 전에
latest로 버릴 매니페스트를 하나 올린 뒤 그 버전만 삭제해서 태그를 떼십시오.
그냥 두면 latest는 최신인 척하면서 계속 0.21.0을 가리킵니다.
이미지를 먼저 발행하고 Release는 나중에 공개하는 이유
컨테이너 이미지는 Release가 존재하기 전에 build와 merge에서 발행됩니다.
이는 의도된 설계입니다. 게이트는 운영자가 하는 방식 그대로 실제 발행 이미지를
pull해서 실행해봐야만 설치 가능 여부를 증명할 수 있기 때문입니다. Release는
사람에게 알리는 공지이므로, 그 증명이 끝날 때까지 draft로 붙잡아 둡니다.