릴리스·배포 운영 정본
2026-09-15 개정(오너 지시, #1661·#1659) — 승격 검사 3단계 — ① 작업 PR→release는 큐 Merge Check만(PR 전 precheck 폐기) ② release→staging은 GitHub Full CI precheck 레인(
static-checks+migration-smoke) ③ staging→main은 QA2 레인(qa2-product-contracts)만. QA1은 완전히 퇴역(어느 워크플로도 실행하지 않음). 앱 반영 PR #1663·#1664(release/v0.19.3), 첫 실측 승격 ① run 34966023249. 아래 2026-09-10 배너와 본문의 "사전검증·full CI·동일 tree 재사용" 서술은 그 개정 전 기록이며, §1~§3과 실행 기준 표는 개정 내용으로 고쳤다.
2026-09-10 승인 정책 (#1491, v0.17.8 대상): 작업 세션은
ci:precheck-local, 작업→release 큐는 Merge Check만 수행한다. 최종 release→staging 후보에서 full CI를 실행하고, staging→main은 동일 tree의 유효한 full 성공 기록과 현재 staging 배포·smoke 증거를 확인해 재사용한다. 명시적merge:request --dry-run은 진단용 full 실행을 유지한다. 앱 main6afe58ec에 설치됐으며 정상 큐의 실제 실행 시간과 운영 배포 성공은 아직 확인 전이다. 상세 상태는 적용 기록에서 구분한다. 아래 과거 전환 기록보다 이 절차가 우선한다.
#1463 저장소 분리 이후 적용: 아래 앱 release·DB·데이터 보호 규칙은 앱 저장소에 적용한다. 과거 docs/admin 결합 빌드·같은 SHA 배포·앱 main 문서 직행·문서 문자열 검사 설명은 이관 전 기록이다. 모든 문서와 작업 기록은 dekerd/Barbelic-docs에서 관리하며, 현행 저장소 경계와 문서·관리자 독립 운영이 그 부분을 대체한다. 세션 제목은 현재 Phase/전체 Phase 규칙을 따른다. 코드 준비·원격 활성화·실제 배포 성공은 독립 운영 문서의 기록으로 구분한다.
동일 tree의 full CI 결과 재사용
2026-09-15 개정(오너 지시, #1659) — v0.19.3 출시부터 QA1은 완전히 퇴역한다(워크플로 어디서도 자동 실행되지 않음). 승격 CI는 두 레인뿐이다: release→staging = precheck 레인(
static-checks+migration-smoke: 마이그레이션 전체 재적용·schema.sql 대조), staging→main = QA2 레인(qa2-product-contracts)뿐. 동일 tree 성공 기록 재사용, QA1 단위·브라우저·viewport·e2e-evidence 잡, 배포 뒤 QA1 CRUD smoke 잡(smoke (staging/production)),prod-smoke.yml·social-login-daily-check.yml, 앱의qa1.lock.json·scripts/qa1.mjs·npm run test·smoke:*·QA1 npm 자동 훅은 모두 제거됐다. 아래 본문의 full CI·smoke·재사용 서술은 그 이전 기록이다.
Git tree는 커밋의 파일 내용·경로·모드를 나타내므로 커밋 SHA가 달라도 tree가 같을 수 있다. #1476의 성공 기록 형식은 유지하고, #1491에서 재사용 시점을 staging→main으로 좁힌다. 일반 작업→release 큐는 full CI를 실행하지 않으며, full 기록을 조회해 재사용하거나 새 기록을 만들지도 않는다. release→staging은 최종 병합 후보의 full CI를 실행한다.
- 전체 Git 이력의 공통 최초 커밋에
full-ci-tree:<tree SHA>check-run으로 full 성공 기록을 저장한다. 저장소·tree·검증 commit/base·출처·workflow 경로/ref/SHA·실행 차수·Actions 링크를 검증한다. 연결된 원 실행도 같은 저장소에서 해당 차수로 완료·성공해야 한다. 실제 full을 실행한 성공만 기록하며 사전검증·Merge Check·재사용 실행은 full 기록을 만들지 않는다. - staging→main은 먼저 현재 PR/head/base·허용 승격 경로를 확인한다. main 병합 tree와 staging head tree의 일치, 해당 staging SHA의 실제 DB·Edge·프론트 배포·CRUD smoke 성공을 확인한 뒤 full 기록을 조회한다. 현재 base의 기존 이력 불변성·migration 순서/위험 검사를 유지한다. 앱에서 제거된 문서 검사는 복원하지 않는다.
- 동일 tree의 기록이 유효하면 full 작업을 건너뛰고
verify가 scope 성공·재사용 tree·원 실행 링크·full 작업의 의도된 skipped 상태를 검사한다. 기록 없음·무효·조회 오류는 full 실행으로 돌아간다. full 경로는 모든 필수 검사가 성공해야 하며 예상하지 않은 skip·실패·취소를 성공으로 바꾸지 않는다. 기록 저장 오류는 실제 full 성공을 실패로 바꾸지 않는다. - 명시적 수동 full과 큐
--dry-run은 진단을 위해 full을 실행한다.--dry-run은 병합하지 않는다. 일반 release 병합의 full 생략을 진단 실행의 생략으로 확대하지 않는다.
앱 main의 workflow·제어 코드가 바뀐 뒤 시작한 요청부터 적용된다. 코드 준비·release 통합·main 활성화·실제 실행 결과는 #1491 적용 기록을 따른다. 과거 #1476·v0.17.7 실행 결과는 해당 업데이트 문서에 보존한다. 배포·QA·최신 base·병합 보호는 유지한다.
2026-09-09 개정 (이슈 #1456·#1457·#1458, 오너 결정) — 명령·워크플로 이름이 바뀌었다. 작업 PR 통합 요청
npm run merge:request(구 landing:request), PR 전 사전 검증npm run ci:precheck-local(최신 release 합치기 → verify → 서버 변경 시 DB 단계, 브라우저 없음), 전체 검증npm run ci:full-local(릴리스 큐 runner 안에서만; 세션은--only <단계>재현만). 워크플로 표시 이름:3-Release Merge Request(release-merge-request.yml) ·GitHub Full CI(policy-contract.yml) ·1-Production Deploy/2-Staging Deploy(production-deploy.yml·staging-deploy.yml, 단계 정의는 deploy-steps.yml) ·Production smoke (manual)·Social login daily check·Daily data backup.landing:lock·db:preflight스크립트는 폐기됐다(GitHub concurrency 가 잠금, DB 검사는 사전 검증과 큐가 한다). 아래 본문의 옛 이름은 그 개정 전 기록이다.
결정과 실제 적용 상태
#1463 재개 시 main 확인: v0.17.6 승격 #1481의 main은 df394f77ab0efe16ad018aecf2799a2306b10731이며 #1460 명령·워크플로 변경을 포함한다. 아래 표는 초기 브랜치/배포 전환 당시 기록이다. 문서·관리자 분리 후보에 대한 큐 호환은 아직 미활성이며 HQ가 선행 경로를 결정 중이다. 현행 #1463 통합·배포 상태는 문서·관리자 독립 운영을 따른다. main 도달과 Production 배포 성공을 동일시하지 않는다.
2026-09-08 오너 결정: 작업 브랜치 → release/vX.Y.Z → staging → main(Production)을 사용한다. 작업은 릴리스별로 통합한다. 2026-09-10 #1491: release별 슬롯은 최신 후보의 Merge Check와 병합까지만 유지하며, full은 최종 release→staging 후보에서 실행한다. staging→main의 재사용 기준은 위 절을 따른다. 릴리스 담당자가 한 후보씩 처리하는 승격에는 별도 promotion 큐·전역 락·배포까지 이어지는 락을 추가하지 않는다. 이 문서는 운영 정책의 정본이며, 코드 구현과 원격 활성화·실제 배포 성공은 아래 표에서 구분한다.
| 항목 | 2026-09-09(KST) 확인 상태 | 전환 후 기준 |
|---|---|---|
| 브랜치 | staging, release/v0.17.3 생성됨 | 릴리스별 통합, 공용 staging, 운영 main |
| 배포 워크플로·대상 설정 | main → Production, staging → staging 설치·활성화 완료(#1436). 실제 배포 성공은 아래 실행 기록으로 구분 | repository variable DEPLOYMENT_BRANCH_CUTOVER=true에서 새 Deploy 활성화 |
| Vercel 브랜치·환경 | 앱·docs 모두 Production=main, custom staging=staging을 원격 확인함. docs staging의 공개 Supabase 값·BARBELIC_TARGET=staging, VERCEL_DOCS_PROJECT_ID 저장소 변수 설정 완료. 실제 Linux Deploy 검증은 미완료 | 앱과 docs/admin 모두 main/staging에서 해당 환경의 DB·Edge 이후 Actions로 배포 |
| Supabase 연결 | 두 프로젝트 모두 Supabase branch가 없음. 기존 Production·staging 프로젝트와 secret을 그대로 사용 | Git 브랜치에 따른 Actions 대상 선택; 새 DB 생성·초기화 없음 |
| 전환 코드의 로컬 검증 | npm run check: 3,399 통과·DB 조건부 27 건너뜀·실패 0. docs Production 및 실제 Vercel staging 환경 값의 관리자 빌드·산출물 검사 통과 | 이 검증은 원격 main 설치·활성화·실제 배포 성공을 대신하지 않음 |
| GitHub CI | 두 승격의 hosted full CI·경로 검사 적용. 이번 인프라 bootstrap은 오너의 CI 생략 지시로 관리자 병합 | release/* → staging, staging → main에서 각각 full CI; 일반 작업은 release 큐 |
| 승격 보호 규칙 | main/staging 모두 verify 필수·최신 base 필수·merge commit만 허용. 기존 landing-queue 필수 검사는 제거 | staging/main 승격은 최신 base에서 full CI 통과 필수 |
| 랜딩 자동화 | Windows self-hosted runner·main workflow·release 보호 규칙 활성화 완료. 실제 큐 대기·실패 차단·검증 커밋 병합까지 실측 완료 | GitHub가 release별로 CI 시작부터 검증 커밋 병합까지 직렬화 |
| 로컬 CI | 개별 실행의 기본 비교 대상 origin/main, docs 빌드 별도 | 큐는 고정 release base SHA로 full 실행하고 docs/admin도 빌드 |
release 통합 큐는 그대로 유지한다. 이번 배포 전환은 앱 배포 브랜치와 두 승격의 hosted CI를 정렬하는 작업이다. 기존 Supabase 두 프로젝트·secret을 유지하며 DB를 새로 만들거나 초기화하지 않는다. DEPLOYMENT_BRANCH_CUTOVER가 true가 되기 전에는 새 Deploy가 실행되지 않는다. 활성화 뒤에는 기존 main 랜딩(작업 PR→main→staging)을 사용할 수 없으며, release 통합 명령을 승격·배포 명령으로 확장하지 않는다.
전환 완료는 다음 항목의 실제 확인으로 판정한다.
- [x] 현재 운영과 main의 제품·DB 코드가 동일함을 확인하고 이력을 보존했다.
- [x] Vercel 앱·docs/admin의 운영 브랜치와 staging 환경을 새 브랜치에 맞췄다.
- [x] 배포 대상·워크플로·최신 배포 SHA 검사·수동 실행 제한을 main/staging 기준으로 변경했다.
- [x] 일반 PR의 Actions full CI를 끄고 두 승격 PR의 full CI·경로 검사·최신 base 요구를 적용했다.
- [x] release 큐의 원격 workflow·업데이트 권한·보호 규칙을 적용하고 실제 CI·병합을 검증했다. 이 release별 큐의 범위는 변경하지 않는다.
- [x] main/staging 초기 정렬 후
DEPLOYMENT_BRANCH_CUTOVER=true를 설정하고 기존 main 랜딩을 차단했다. - 실제 배포 성공·실패와 최신 SHA는 staging Deploy 실행 기록을 확인한다. 초기 실행 #34248222983의 DB·Edge·앱은 성공했고 docs CLI 경로 보정이 필요했다. 운영은 이번 설정 변경으로 재배포하지 않고 다음 승인된 staging→main 승격에서 배포한다.
브랜치와 출시 범위
작업 브랜치 ── precheck → GitHub 큐 / Merge Check·병합 ──> release/vX.Y.Z
│
승격 PR / 최종 후보 GitHub full CI
↓
staging ── 자동 배포·smoke·QA
│
승격 PR / 동일 tree full 기록·staging 증거 확인 / 기존 운영 승인
↓
main ── 자동 운영 배포·smoke·버전 태그| 브랜치 | 역할 | PR 대상·제약 |
|---|---|---|
feat/*, fix/*, docs/* 등 | 이슈 단위 작업 | 해당 release/vX.Y.Z에서 분기하고 같은 release로 PR; 순수 문서는 아래 예외 적용 가능 |
release/vX.Y.Z | 그 버전에 포함할 변경만 통합 | staging으로 승격 PR; 미래 버전 코드 혼입 금지 |
staging | 현재 출시 후보 하나의 실환경 검증 | main으로 승격 PR; 여러 릴리스 동시 통합 금지 |
main | 운영 배포 기준 | 제품 변경은 검증한 staging에서만 승격; 순수 문서 PR 예외는 아래 기준 적용 |
main의 최신 SHA가 곧 현재 서비스 중인 버전은 아니다. 배포가 실패하거나 진행 중일 수 있으므로 마지막으로 성공한 운영 배포 SHA·버전 태그·배포 실행을 별도로 기록한다. GitHub Project는 이슈·담당·목표 릴리스·진척을 보여 주는 관리판이다. Project나 milestone 지정만으로 Git 커밋의 출시 범위가 분리되지는 않는다. 실제 경계는 작업 PR의 base, 릴리스 브랜치의 포함 커밋, 두 승격 PR이 만든다.
순수 문서 변경 예외
2026-09-08 오너 요청에 따라 실행 동작을 바꾸지 않는 문서는 최신 main에서 분기한 docs/* → main PR로 직접 병합할 수 있다. release·staging을 거치지 않으며, 오너가 CI 생략을 요청한 문서 PR은 로컬 CI와 GitHub CI를 생략하고 그 사실을 PR에 기록한다. 2026-09-08의 운영 문서 단독 변경에 적용한 예외이며 이번 배포 설정·workflow 변경에는 적용하지 않는다.
- 대상은 설명용 Markdown과
AGENTS.md같은 작업 지침이다. 앱·admin 코드, DB SQL, workflow, Vercel/Supabase 설정, 의존성, 실행되는 docs 빌드 설정·컴포넌트는 제외한다.docs/경로에 있다는 이유만으로 예외가 되지 않는다. - release에서 작성했더라도 문서 커밋만 최신 main 위로 옮긴다. PR base만 바꿔 미출시 코드가 함께 포함되지 않게 하고, main 기준 전체 diff가 문서뿐인지 확인한다.
- GitHub CI를 생략할 때 문서 커밋과 최종 squash/merge 커밋에
[skip ci]를 넣는다. 기존 필수 검사가 Pending으로 남으면, 오너가 승인한 해당 문서 PR에 한해 기존 관리자 bypass로 병합한다. 보호 규칙을 끄거나 미실행 검사를 성공으로 제출하지 않는다. - 열린 릴리스(승격) PR이 있는 동안은
[skip ci]를 쓰지 않는다.[skip ci]커밋이 main에 들어가면 main을 head로 둔 릴리스 PR의 head가 그 커밋으로 바뀌는데, GitHub는 head 커밋 메시지에[skip ci]가 있으면 pull_request 검사까지 건너뛴다. 그러면 필수 검사verify가 "Expected — Waiting for status to be reported"로 비어 릴리스 PR 머지가 막히고,full-ci라벨이나 재실행으로도 살아나지 않는다(2026-09-08 #1428 병합 → #1426 실측). 이미 벌어졌으면[skip ci]없는 커밋(이 문서 수정 같은 문서 PR이면 충분)을 main에 하나 더 넣어 검사를 다시 돌린다. 이때 그 커밋의 제목·본문과 PR 본문 어디에도 그 문구를 따옴표 안에라도 적지 않는다 — squash 머지는 PR 제목·본문을 커밋 메시지로 쓰므로 문구가 들어가면 그 커밋도 똑같이 건너뛰어진다(2026-09-08 #1429 실측, 이 문장을 적기 위해 #1430이 한 번 더 필요했다). - 이 예외는 제품 코드의 배포 승인이 아니다. 실행 코드와 함께 변경하거나
release → staging,staging → main으로 승격하는 PR에는 원래의 검증 기준을 적용한다. 문서 직접 병합 후 진행 중인 release에도 해당 문서를 반영한다. - CI·배포를 생략해 main에 직접 합친 순수 문서는 다음 해당 환경 배포에 문서 사이트로 반영된다. 앱과 docs 프로젝트의 main/staging 직접 Git 배포를 모두 끄므로 Markdown 병합만으로 공개 docs/admin이 즉시 바뀌지는 않는다.
1. 작업 브랜치에서 릴리스에 통합
작업 시작 시 목표 릴리스를 정하고, 해당 브랜치에서 별도 worktree 또는 작업 브랜치를 만든다. 병합된 작업 브랜치를 재사용하지 않는다.
git fetch origin
git switch -c feat/<issue-slug> origin/release/v0.17.3
# 작업·커밋 후 PR base는 release/v0.17.3으로 지정한다.같은 release의 PR은 GitHub 대기열에서 한 건씩 최신화→Merge Check→병합한다(PR 전 precheck는 2026-09-15 폐기). 작업 세션은 구현·관련 테스트를 병렬로 진행하고, 최종 통합은 아래 명령으로 요청한다. 실제 원격 활성화 여부와 설치·복구 절차는 릴리스 랜딩 큐의 상태 표가 정본이다.
# 변경을 커밋·push하고 같은 release를 base로 PR을 연 뒤 요청한다.
npm run merge:request -- --pr <N>
# 병합 없이 Merge Check만 해 보려면:
npm run merge:request -- --pr <N> --dry-run- 요청은 clean worktree와 로컬 HEAD = PR head를 요구한다. GitHub가 목적 release별 슬롯을 배정하고 self-hosted runner가 슬롯 안에서 읽은 최신 release base와 요청한 PR head로 두 부모 merge commit을 만든다. 요청 시점에 뒤처진 PR도 충돌이 없으면 최신 base와 합친 후보를 검증한다.
- 일반 요청은 고정 base의 이력 불변성·migration 순서/위험을 검사한다(Merge Check). 코드 검사는 하지 않으며 full 성공 기록도 만들지 않는다. 작업자는 Phase마다
npm run check(정적 검사)를 통과한 head를 push할 뿐 PR 전 precheck를 하지 않는다(2026-09-15 개정). 명시적--dry-run은 같은 Merge Check를 하고 병합만 하지 않는다. - 큐가 최신 base 조회부터 원격 병합 확인까지 슬롯을 보유한다. 같은 release의 다른 요청은 기다리며 작업 브랜치의 개발·커밋은 계속할 수 있다. 로컬 파일 락·heartbeat·세션의 대기 유지가 필요하지 않다.
- PR head/base·라벨·clean 상태·검증한 tree를 마지막에 확인하고 예상 base일 때만 검증한 merge commit 그대로 release에 fast-forward push한다. release 큐에서 squash하거나 최종 확인 뒤 새 commit을 만들지 않는다. 두 승격도 merge commit으로 이력을 보존한다.
- 같은 PR의 중복 요청·충돌·Merge Check 실패·실행 중 head/base 변경은 병합하지 않는다. 수정 후 새 조합으로 요청한다. 다음 PR은 앞 PR이 반영된 최신 release로 후보를 만들고 Merge Check를 수행한다.
merge:request는 작업→release 통합에 사용한다. 배포 전환 후 기존 main 대상 랜딩은 차단되며 승격은 별도 PR로 진행한다. release 경로는 배포하지 않는다..git/landing-preflight.json은 DB preflight 기록이며 전체 CI 통과 인증서나 원격 쓰기 권한이 아니다.- GitHub job summary에 PR·head·base·candidate commit·tree·결과를 기록한다. 업데이트 제한·전용 deploy key·force push/삭제 보호의 실제 적용 여부는 큐 문서에서 확인한다.
문서·관리자 검사는 분리된 문서 저장소에서 실행한다. 앱 release 큐에 docs/admin 빌드나 문서 검사를 추가하지 않는다. 앱 의존성은 lockfile과 저장소의 Node·Supabase 도구 버전을 따른다.
2. release에서 staging으로 승격
- 릴리스 담당자가 포함 PR·커밋·migration 목록을 확정한다. 현재 staging이 다른 릴리스의 QA 중이면 그 릴리스 완료 또는 후보 철회·환경 복구를 먼저 처리한다.
- 최신 main의 운영 수정 사항을 release에 반영한다. 저장소 변수
RELEASE_VERSION을 이 릴리스의 버전(점 없는vX.Y.Z)으로 설정한다 —gh variable set RELEASE_VERSION --body vX.Y.Z --repo dekerd/Barbelic. 승격 CI·배포 실행 4종의 이름이 이 값을 읽고, full CI의 scope 잡이 승격 PR의 버전과 대조한다(실행 이름, 2026-09-15 오너 지시 #1659). 그 뒤release/vX.Y.Z → stagingPR을 열고 후보 head와 staging base를 기록한다. - 최신 staging base와 release head를 합친 최종 후보에서 GitHub Full CI의 precheck 레인(
static-checks+migration-smoke)을 통과시킨다(2026-09-15 개정 — 그 전에는 full CI였다). 이 단계는 과거 같은 tree의 기록으로 생략하지 않는다. 작업 head만 검사한 결과나 개별 precheck로 승격하지 않는다. - 검증한 조합 그대로 merge commit으로 병합한다. staging push는 CI를 반복하지 않고 DB→Edge→프론트 배포를 실행한다(QA1 smoke 잡은 2026-09-15 퇴역).
- 실제 staging 배포 SHA와 배포 3잡(database·functions·frontend) 성공을 확인하고 릴리스에 필요한 사용자 흐름을 QA한다. 수정은 해당 release의 작업 PR부터 같은 경로로 다시 올린다.
공용 staging에 v0.18.0을 통합한 뒤 v0.17.x만 골라 main으로 합칠 수는 없다. 같은 staging 이력에 들어간 미래 코드도 승격에 따라오므로, 후보를 바꾸려면 코드와 DB migration 적용 상태를 함께 정리해야 한다. 앱 코드를 되돌려도 이미 적용한 migration이 자동으로 되돌아가지는 않는다.
3. staging에서 main으로 출시
- staging 자동 배포·QA가 성공한 후보로
staging → mainPR을 연다. 제목은[Release vX.Y.Z] 변경 요약으로 작성한다. - main과 합친 결과가 배포한 staging과 같은 tree인지 확인하고 GitHub Full CI의 QA2 레인(
qa2-product-contracts)을 통과시킨다(2026-09-15 개정 — 동일 tree 성공 기록 재사용은 폐기). 현재 staging 배포(database·functions·frontend) 성공은 scope 잡이 확인한다. - main에 합쳐질 코드 tree가 실제 배포·QA한 staging의 코드 tree와 같음을 확인한다. merge commit의 SHA가 다를 수 있으므로 SHA 문자열만 비교하지 않는다.
- 포함 목록과 QA2 레인 결과·staging QA 증거를 오너가 확인해 운영 반영을 승인하면 merge commit으로 병합한다. 기존 승인 절차를 유지하며 일반 작업마다 추가 승인을 요구하지 않는다.
- main push가 DB→Edge→프론트→운영 smoke를 실행한다. 성공한 배포에 버전 태그·GitHub Release를 연결하고 실제 배포 SHA를 기록한다. 실패한 배포를 출시 완료로 표시하지 않는다.
검증 또는 QA 도중 main에 다른 패치가 먼저 들어오면 최신 main→release 반영→staging 승격 CI→재배포·QA→main 승격 CI를 다시 거친다. main PR 안에서만 충돌을 해결해 staging에서 보지 않은 코드를 운영에 내보내지 않는다. 릴리스 담당자는 staging과 main 승격을 한 후보씩 진행한다. 승격 PR의 최신 base·해당 레인 통과와 병합 직전 재확인을 사용하며 별도 승격 큐나 전역 락을 만들지 않는다. release별 락을 배포 완료까지 연장하지도 않는다. 원격 보호 규칙의 실제 적용 여부는 전환 목록에서 확인한다.
긴급 패치도 최신 운영 기준으로 release/vX.Y.Z를 만들고 같은 두 승격을 거친다. 다른 후보가 staging을 사용 중이면 먼저 후보 전환과 DB 호환을 정리한다. 패치 출시 후 최신 main을 진행 중인 다른 release들에 반영한다. 별도 비상 복구는 배포 파이프라인의 기존 절차를 따른다.
CI와 배포 실행 기준
| 시점 | 검증 | 자동 배포 |
|---|---|---|
| 작업 중 | 변경 관련 로컬 확인과 Phase마다 npm run check(정적 검사). PR 전 precheck 없음(2026-09-15 개정). 마이그레이션을 만졌으면 로컬 supabase db reset --local --no-seed + npm run schema:snapshot -- --check | 필요 시 기존 프리뷰 범위 |
| 작업→release PR | GitHub 큐에서 최신 base 병합·충돌·migration 순서/위험·head/base/tree 확인 후 조건부 병합. full 실행·성공 기록 생성 없음 | 없음 |
| 순수 문서 docs→main PR | 문서 diff 확인; 오너 요청 시 로컬·GitHub CI 생략 | 생략 시 없음; 문서 사이트는 다음 해당 환경 배포에 반영 |
| release→staging PR | 최종 병합 후보의 GitHub Full CI precheck 레인(static-checks + migration-smoke; 2026-09-15 개정) | 성공 후 병합하면 staging |
| staging→main PR | GitHub Full CI QA2 레인(qa2-product-contracts)·현재 staging 배포(database·functions·frontend) 성공·QA 증거·기존 운영 승인 (2026-09-15 개정; 동일 tree 재사용 폐기) | 성공 후 병합하면 Production |
| staging/main 병합 push | 환경별 배포 검사(DB→Edge→프론트), CI 중복 없음. QA1 smoke 잡은 퇴역 | 해당 환경 |
| head/base 변경·실패 수정 | 바뀐 최종 조합 재검증 | 검증·병합 조건 충족 후 |
승격·배포 실행 이름
2026-09-15 오너 지시(#1659): Actions 목록에서 어느 릴리스의 어느 단계인지 바로 읽히도록 승격 CI·배포 실행 4종의 이름을 [vX.Y.Z] 단계로 고정한다. 버전은 저장소 변수 RELEASE_VERSION(지금 나가는 릴리스 하나)에서 읽으므로 승격 ①을 열기 전에 설정한다.
| 실행 | 워크플로 | 이름 |
|---|---|---|
| release→staging PR의 GitHub Full CI | policy-contract.yml | [vX.Y.Z] Release -> Stage Merge |
| staging push 배포 | staging-deploy.yml(2-Staging Deploy) | [vX.Y.Z] Staging Deploy |
| staging→main PR의 GitHub Full CI | policy-contract.yml | [vX.Y.Z] Stage -> Main Merge |
| main push 배포 | production-deploy.yml(1-Production Deploy) | [vX.Y.Z] Production Deploy |
- 변수가 없으면
[v?]로 표시된다. Full CI의 scope 잡(scripts/check-promotion.mjs)은 변수 값을 승격 PR의 버전(release→staging은 head 브랜치release/vX.Y.Z, staging→main은 제목[Release vX.Y.Z])과 대조해 다르면 실패하고 설정 명령을 안내한다. 실행 이름은 실행이 만들어질 때 정해지므로 변수를 고친 뒤 push하거나 PR을 닫았다 열어 새 실행을 만든다 — 실패한 잡의 재실행은 이름을 바꾸지 않는다. - 수동 실행(workflow_dispatch)만
(manual)꼬리를 붙여 진짜 게이트와 구분한다. 워크플로 표시 이름(1-Production Deploy·2-Staging Deploy·GitHub Full CI·3-Release Merge Request)과 큐의 실행 이름([#이슈 → release/vX.Y.Z] …)은 바뀌지 않는다. - 변수는 저장소에 하나뿐이다(staging에 후보 하나만 두는 정책과 같다). 핫픽스가 끼어들면 그 릴리스로 바꾸며 가드가 누락을 잡는다. 릴리스 사이에 main에 직접 들어간 변경(문서·QA 핀)의 Production 배포도 직전 릴리스 버전으로 표시된다.
실제 실행하는 full CI는 정적 검사·단위 테스트·앱 빌드·격리 Supabase migration/pgTAP·브라우저/viewport 검증·검증 증거를 포함한다. 2026-09-15 #1604부터 QA2 제품 계약 검증(qa2-product-contracts, QA2 승격 게이트)도 verify의 필수 선행 job이다. 전체 테스트는 격리 환경에서 실행하고 실환경 DB를 초기화하지 않는다. 실제 서비스의 DB·Edge·프론트 결합은 배포 후 smoke로 확인한다.
승격 workflow는 같은 저장소의 release/* → staging, staging → main PR을 대상으로 하며 경로·최신 base를 확인한다. release→staging은 변경 경로와 관계없이 최종 후보의 full CI를 실행한다. staging→main만 동일 tree의 유효한 full 기록과 현재 staging 배포·smoke 증거로 재사용한다. draft는 준비 완료 후 실행하고 관계없는 label 변경으로 full을 중복 실행하지 않는다. 일반 작업 PR·push와 일반 release 병합 요청에서는 full을 실행하지 않는다. 진행 중인 release 병합이나 DB 변경을 포함한 배포를 새 요청으로 중간 취소하지 않는다. 명시적 수동 full·큐 dry-run은 진단용으로 유지한다.
일일 사용자 데이터 백업은 CI 절감과 별개로 유지한다. 정기 외부 인증 smoke도 full CI와 다른 운영 검사이며, 주기 변경은 별도 운영 결정으로 관리한다.
배포 연결 전환 절차
오너는 Vercel 앱의 Production 브랜치를 main, staging 브랜치를 staging으로 변경했다고 전달했다. 콘솔 설정 변경만으로 Actions·보호 규칙·배포 검증이 완료되지는 않는다. 새 Deploy의 활성화 변수로 초기 전환 중 배포를 막고 나머지 연결을 맞춘다.
- 현재 main/production/staging/release의 SHA, 실제 운영 성공 배포, 열린 PR base, 기본 브랜치, 보호 규칙을 기록하고 보존용 ref를 확보한다. 전환할 짧은 시간 동안 병합·기존 자동 랜딩·배포를 멈춘다. 사용자 데이터 백업은 유지한다.
- 운영 기준에서 전환용 인프라 변경을 준비·검증하고 필요한 release에도 반영한다. 배포 연결 변경에 미출시 제품 코드가 우연히 포함되지 않게 한다.
- 현재 main의 제품·DB 코드가 승인된 운영 기준과 일치하는지 확인하고 기존 production 이력을 보존한다. staging에는 전환할 승인 후보와 인프라 코드를 정렬한다. 이미 존재하는 브랜치를 과거 계획대로 일괄 rename하거나 main을 과거 SHA로 강제 초기화하지 않는다.
- 새 기본 브랜치 main, staging의 승인 후보, 작업 PR의 release base를 확인한다. 브랜치 rename이 워크플로 문자열까지 변경하지 않으므로 아래 소스·보호 규칙을 함께 적용한다.
- Vercel Production Branch Tracking은 main, staging custom environment의 Branch Tracking은 staging으로 설정한다. 운영 도메인·환경변수는 유지하고 staging의 DB URL/key·인증 redirect·도메인을 따로 확인한다. 앱과 docs/admin 두 프로젝트의 공개 배포 범위를 새 출시 경계에 맞춘다.
- Supabase는 기존 운영·staging 프로젝트를 유지하고 Actions의 환경별 자격증명으로 연결한다. 새 DB 생성·초기화가 필요한 작업이 아니다. Supabase GitHub Integration이 같은 DB에 migration을 중복 적용하지 않는지 확인한다.
- 새 워크플로·보호 규칙·환경 제한이 main/staging에 설치된 것을 확인한 뒤 repository variable
DEPLOYMENT_BRANCH_CUTOVER=true를 설정한다. 설정 전에는 새 Deploy를 실행하지 않는다. 활성화 후 기존 main 랜딩 차단, staging의 DB→Edge→프론트→smoke, 운영 브랜치의 최신 SHA 검사를 실제로 확인하고 실행 링크·SHA를 기록한다. 인프라 전환 자체를 별도 제품 릴리스의 운영 반영 승인으로 취급하지 않는다.
| 대상 | 브랜치 | Vercel 환경 | 유지할 Supabase project ref |
|---|---|---|---|
| Production | main | production | kobxeylancdimqhfkbnl |
| Staging | staging | staging | ajqddlglezvxzsawquku |
앱과 docs 프로젝트의 main/staging Git 직접 자동 배포를 모두 끄고 Actions 한 경로가 DB→Edge→앱·docs/admin→smoke를 집행한다. 사용자는 staging에 병합하는 것으로 자동 배포를 시작한다. docs/admin도 같은 배포 SHA로 빌드하고 BARBELIC_TARGET을 명시해 staging 관리자 셸은 staging Supabase, 운영 관리자 셸은 Production Supabase를 보게 한다. docs Vercel 프로젝트 ID는 앱과 별도 repository variable VERCEL_DOCS_PROJECT_ID로 지정한다. 문서만 CI·배포 생략 예외로 합치면 공개 사이트에는 다음 배포에서 반영된다.
| 함께 전환할 위치 | 변경 내용 |
|---|---|
deployment-targets.json, deploy-steps.yml(1-Production Deploy·2-Staging Deploy 가 호출), assert-deployment-head.mjs | main/staging 매핑, 최신 ref, 수동 실행 제한, 운영 SHA·태그 |
vercel.json, docs/vercel.json, Vercel 설정 | 직접 앱 배포 중복 방지, docs/admin 출시 경계 |
policy-contract.yml, GitHub rulesets | 두 승격 full CI, 허용 경로, 최신 base 필수, 기존 landing-queue 요구 정리 |
release-merge-request.yml, landing-request.mjs, 랜딩 서비스 코드 | 기존 release별 CI·병합 큐 유지, 전환 활성화 후 legacy main 랜딩 차단 |
ci-local.mjs 및 관련 검사·문서 | release base, docs 검증, head/base 실행 기록 |
로컬 성공을 GitHub 필수 검사로 강제하려면 실제 결과를 제출하는 실행기와 신뢰할 출처를 구현해야 한다. 아직 존재하지 않는 상태 검사를 필수로 등록하거나, 실행하지 않은 CI에 성공 상태를 올리지 않는다. DB migration은 migration 전략, 랜딩·데이터 보존 규칙, 환경 분리 runbook의 데이터 보존·프로젝트 링크 원칙을 유지한다. 오래된 브랜치/큐 전제는 이 문서의 전환 상태와 구분한다.
릴리스 기록과 PR 본문
릴리스마다 목표 버전과 실제 배포 버전을 구분해 기록한다. v0.17.3은 오너가 당시 main의 미출시 변경 전체 포함을 선택했지만, 그 뒤 main 또는 release에 추가된 변경까지 자동으로 승인된 범위로 취급하지 않는다. 최종 승격 PR의 포함 목록으로 범위를 확인한다.
아래는 승격 PR 본문 예시다. release→staging에서는 아직 없는 운영 결과를 미실행으로 두고, staging→main에서는 staging 배포·QA 증거를 채운다.
목표 버전: vX.Y.Z
승격 경로: release/vX.Y.Z → staging 또는 staging → main
후보 head SHA / 검증 base SHA / 통합 코드 tree:
포함 변경: PR 번호·커밋·사용자에게 달라지는 동작
포함 migration: 파일·데이터 영향·기존 사용자 데이터 보존 확인
제외/다음 버전: 이번 후보에 섞이지 않았는지 확인
로컬 CI 기록: 작업 PR별 head/base·명령·결과
release→staging full: 실행 링크·검증 SHA/tree·결과
staging→main: 실제 full 또는 재사용 tree·원 full 링크·현재 staging 배포/smoke 확인 결과
Staging: 실제 배포 SHA·배포 링크·smoke·QA 결과
Production: 오너 승인 / 배포 결과 / 실제 버전·태그·SHA
실패·미실행 검증 및 알려진 제한:완료 범위 — 2026-09-10 오너 정정
구현이 끝나면 최신 목적 release 반영·필수 precheck·자기 PR의 Merge Check·실제 release 병합까지 이어서 처리한다. Draft PR이나 선택 검증에서 멈추고 오너에게 다음 실행을 재요청하지 않는다. 이미 승인된 정책의 미반영은 자기 작업에 필요한 최소 변경으로 해결하며 다른 담당자의 브랜치·큐는 조작하지 않는다. 상세 승인 경계와 완료 기준은 공통 지침을 따른다.