콘텐츠로 이동

릴리스 흐름

이 문서는 기본 전략인 pr-flow 기준입니다. trunk-based는 브랜치 전략을 참고하세요.

  1. 기능 PR이 develop에 머지됨develop PR과 push마다 타입별 CI가 실행됩니다.
  2. 릴리스 PR 열기: develop → mainPROJECT-COMMON-AUTO-CHANGELOG-CONTROL이 시작됩니다.
  3. 버전 확정커밋 타입으로 다음 버전 결정(semver-auto). 버전 파일을 모두 동기화해 PR 브랜치에 커밋합니다.
  4. 릴리스 노트 작성요약 엔진 체인: 사용자 AI API → Copilot(선택) → 규칙 기반. PR 댓글로 남깁니다.
  5. CHANGELOG.md / CHANGELOG.json 갱신[skip ci]로 PR 브랜치에 커밋합니다.
  6. automergemain에 merge commit으로 병합합니다.
  7. 태그 vX.Y.Z + GitHub Releasemain push에서 PROJECT-COMMON-RELEASE-PUBLISH가 발행합니다.
  8. README 버전 줄 + 배포README-VERSION-UPDATE가 돌고, 릴리스 브랜치의 서버·스토어 배포 워크플로우가 실행됩니다.
pr-flow: develop에서 main으로 가는 릴리스 PR 하나가 릴리스 하나가 됩니다.

PROJECT-COMMON-AUTO-CHANGELOG-CONTROL은 개발 브랜치에서 릴리스 브랜치로 가는 PR 중 아직 열려 있는 것에서 실행됩니다. PR을 이미 머지한 실행 뒤에 대기하던 실행은 사전 점검에서 바로 종료합니다.

semver_auto: true(기본)이면 지난 릴리스 이후 커밋을 분류합니다:

커밋 승격
feat: minor
표준 타입 뒤 ! (feat!:, fix(api)!:), 또는 본문 줄 맨 앞의 BREAKING CHANGE: 푸터 major
그 외 patch

타입은 대소문자를 가리지 않습니다. 커밋 본문은 BREAKING CHANGE: 푸터만 읽고 나머지는 판정에 쓰지 않습니다.

결과가 patch인데 컨벤션을 따르지 않는 커밋이 있고, 사용자 지정 AI나 Copilot이 설정되어 있으면 AI가 patch→minor 승격 여부만 보조 판단합니다. major는 만들지 않습니다. AI가 꺼져 있으면(기본) 규칙 결과를 그대로 씁니다.

semver_auto: false면 항상 patch+1이고, major/minor는 version.yml에서 직접 고칩니다.

새 버전은 version.yml과 타입별 버전 파일에 기록되어 PR 브랜치에 커밋됩니다.

changelog_manager.py가 커밋 제목과 상한이 걸린 git diff --stat(파일 단위 요약, diff 본문 없음)으로 요약을 만듭니다. 같은 내용을 PR 댓글로 남기고(재실행 시 그 댓글을 수정), CHANGELOG.json과 CHANGELOG.md에 기록합니다. 항목은 커밋 제목에서 만들어지므로 language 설정은 항목 본문을 번역하지 않습니다. 요약 엔진을 참고하세요.

merge commit 방식으로 automerge를 켜고, 안 되면 직접 머지로 폴백합니다. 방금 push한 head를 GitHub이 반영할 때까지 백오프로 재시도하며, 이미 머지된 PR은 성공으로 봅니다.

브랜치 보호 규칙이 머지를 막을 때

섹션 제목: “브랜치 보호 규칙이 머지를 막을 때”

릴리스 브랜치의 고정 규칙이 머지를 거부하면(예: 승인(approving review)이 필요하거나 필수 체크가 통과하지 않은 경우) 실행이 실패하고, 어떤 종류의 규칙인지와 선택지를 알려 줍니다. 워크플로우는 릴리스 PR 제목도 chore(release): vX.Y.Z (PR #N)으로 바꾸고 안내 댓글을 한 번 남기므로, 직접 Create a merge commit으로 머지해도 릴리스가 발행됩니다.

  • 다음부터 워크플로우가 머지하게 하려면, 머지하는 계정(WORKFLOW_PAT 소유자, PAT가 없으면 GitHub Actions 봇)이 머지할 수 있도록 규칙을 바꾸세요.
  • 실패하는 실행 없이 항상 직접 머지하려면 release_automerge: false로 설정하세요(아래 참고).
  • 무료 플랜의 private 레포에서는 브랜치 보호와 ruleset을 만들 수 없으므로, 규칙을 만들 수 있는 곳에서만 해당합니다.

automerge는 기본으로 켜져 있습니다. 사람이 직접 머지하고 싶다면 version.yml의 release_automerge: false로 바꾸거나 --no-release-automerge로 다시 설치하세요. 그러면 워크플로우가 automerge와 머지 대기 잡을 건너뛰고, 릴리스 PR 제목을 chore(release): vX.Y.Z (PR #N)으로 바꾼 뒤 안내 댓글을 한 번 남깁니다.

  • 그 워크플로우 실행이 끝난 뒤에 머지하세요. 버전 확정과 PR 제목 변경이 그 실행에서 이뤄집니다.
  • 어떤 머지 방식이든 릴리스가 발행됩니다. 다음 릴리스 노트가 기대는 이력을 보존하는 Create a merge commit을 권장합니다.
  • 자동 제목 변경이 실패하면 댓글이 릴리스가 발행되려면 어떤 머지 메시지를 지정해야 하는지 알려 줍니다.

머지 방식별로 릴리스가 어떻게 되는지는 아래와 같습니다.

머지 방식 릴리스 발행 이유
Create a merge commit 됩니다 릴리스 게이트가 머지 커밋 메시지에 들어간 바뀐 PR 제목의 chore(release):를 읽습니다.
Squash and merge 됩니다 기본 메시지(커밋 목록 포함)면 squash 커밋 본문에 버전 확정 커밋의 [skip ci]가 들어가 GitHub가 push 워크플로우를 건너뜁니다. 그러면 PROJECT-RELEASE-PR-MERGED가 머지된 릴리스 PR을 보고 PROJECT-RELEASE-PUBLISH를 실행합니다. 메시지를 PR 제목만으로 squash하면 평소처럼 push가 발행합니다. 원래 커밋이 릴리스 브랜치 이력에 없어서 다음 릴리스 노트에 이전 변경이 다시 기재될 수 있습니다.
Rebase and merge 됩니다 릴리스 브랜치의 마지막 커밋이 버전 확정 커밋([skip ci])이라 push 워크플로우가 건너뛰어지고, PROJECT-RELEASE-PR-MERGED가 PROJECT-RELEASE-PUBLISH를 실행합니다. 다음 릴리스 노트에는 rebase된 커밋이 반복되지 않지만, develop이 릴리스 브랜치를 역머지하기 전까지만 그렇고 역머지 뒤에는 다시 기재됩니다.
실행이 끝나기 전에 머지 패치 릴리스가 하나 더 생김 PR 제목이 바뀌지 않고 버전도 확정되기 전이라, 안전망 워크플로우가 자체 패치 버전을 발행합니다.

버전 확정 커밋은 [skip ci]를 유지합니다. 봇이 열린 릴리스 PR에 이 커밋을 push하므로, 표시가 없으면 GitHub가 그 PR의 워크플로우를 승인 대기로 만들기 때문입니다. PROJECT-RELEASE-PR-MERGED는 [skip ci]의 영향을 받지 않는 pull_request_target을 받습니다. PR 코드를 checkout하지 않고, fork PR은 무시하며, 토큰은 내용 읽기와 워크플로우 실행만 할 수 있습니다. safety-net 역병합 커밋에는 [skip ci]를 붙이지 않아, 그 직후 연 릴리스 PR도 워크플로우가 실행됩니다. 그래도 릴리스가 발행되지 않았다면 릴리스 브랜치에서 PROJECT-RELEASE-PUBLISH를 수동 실행(Actions > Run workflow)하세요. 여러 번 실행해도 안전합니다.

알려진 제약입니다.

  • 릴리스 노트가 기대는 릴리스 브랜치 이력은 머지 커밋만 보존합니다. squash 머지는 다음 노트를 모을 때 새 작업과 구분되지 않습니다.
  • 이전 버전(0.15.1 이하)의 마법사는 release_automerge 키를 모르기 때문에 그 버전으로 다시 설치하면 키가 지워져 automerge가 다시 켜집니다. 0.16.0 이상으로 설치하거나 키를 다시 추가하세요.

릴리스 브랜치 push에서 PROJECT-COMMON-RELEASE-PUBLISH는 head 커밋 메시지가 릴리스 확정 패턴 chore(release):과 일치할 때만 진행합니다. vX.Y.Z 태그를 push하고 GitHub Release를 만듭니다. 릴리스 노트는 CHANGELOG 항목에 GitHub이 생성한 노트(기여자, 비교 링크)를 더한 것입니다. 태그와 Release를 따로 확인하므로, 절반만 발행된 릴리스도 재실행하면 마저 발행됩니다.

그다음 README-VERSION-UPDATE를 실행해 README.md의 <!-- AUTO-VERSION-SECTION --> 마커 아래 버전 줄을 갱신합니다.

서버·스토어 배포 워크플로우는 릴리스 브랜치 push에서 실행됩니다.

모든 변경이 릴리스 PR로 들어오지는 않습니다. PROJECT-COMMON-VERSION-CONTROL은 릴리스 브랜치에 직접 push된 경우를 위한 안전망입니다. patch 버전을 올리고, 버전 파일을 동기화하고, 마지막 태그 이후 커밋으로 CHANGELOG 항목을 만든 뒤 RELEASE-PUBLISH를 실행합니다. 다음 릴리스 PR이 버전 파일에서 충돌하지 않도록 릴리스 브랜치를 개발 브랜치에 다시 병합합니다.

릴리스 PR에서 온 push(merge commit에 chore(release):가 있고 version.yml이 바뀐 경우)는 건너뛰며, trunk-based 모드에서는 아예 동작하지 않습니다.

릴리스 흐름은 GitHub Release에서 끝납니다. npm, PyPI, 앱스토어에 스스로 배포하지 않습니다(스토어 배포는 Flutter용 별도 워크플로우입니다). 패키지를 배포하려면 release: published 이벤트에 워크플로우를 추가하세요. 이 레포가 npm 배포를 그렇게 합니다 — 태그는 불변 스냅샷이라 무엇이 배포되는지가 워크플로우 실행 타이밍에 좌우되지 않습니다.

GITHUB_TOKEN으로 만든 Release는 다른 워크플로우를 트리거하지 않으므로, release: published 워크플로우는 RELEASE-PUBLISH가 WORKFLOW_PAT으로 실행될 때만 동작합니다.