Release Drafter의 commitish 문제 — release 브랜치 기준으로 릴리스 노트 생성하기

2026-03-19 hit count image

Release Drafter의 commitish가 main에 고정되어 있을 때 발생하는 릴리스 노트 불일치 문제를 분석하고, 워크플로우를 autolabel과 release note 생성으로 분리하여 release 브랜치 기준으로 정확한 릴리스 노트를 생성하는 방법을 소개합니다.

github_actions

문제 상황

Release Drafter는 PR의 라벨을 기반으로 릴리스 노트 초안을 자동 생성해 주는 GitHub Action입니다. 모노레포에서 서비스별로 Release Drafter 설정을 분리하여 사용하고 있었는데, 릴리스 노트에 포함되어야 할 PR이 빠지거나, 포함되지 않아야 할 PR이 들어가는 문제가 발생했습니다.

Release Drafter를 사용하여 Release notes를 자동화하는 방법과 라벨을 검사하는 방법에 대해서는 다음 블로그 포스트를 참고하시기 바랍니다.

Release Drafter의 동작 원리

Release Drafter는 다음과 같은 순서로 릴리스 노트를 생성합니다.

  1. tag-prefix로 가장 최신 태그(이전 릴리스)를 찾는다
  2. 이전 릴리스 태그부터 commitish(대상 브랜치)의 HEAD까지의 커밋 이력을 조회한다
  3. 해당 커밋들에 연결된 PR을 찾아내고, PR의 라벨에 따라 카테고리를 분류한다
  4. 템플릿에 맞춰 릴리스 노트 초안을 생성한다

여기서 핵심은 **commitish**입니다. 이 값이 어떤 브랜치를 가리키느냐에 따라 릴리스 노트에 포함되는 PR의 범위가 결정됩니다.

commitish: main의 문제점

기존 설정에서는 release-drafter.ymlcommitishmain으로 고정되어 있었습니다.

# .github/release-drafter.yml (수정 전)
commitish: main

이 경우 다음과 같은 문제가 발생합니다.

1. release 브랜치에만 있는 변경사항이 누락된다

핫픽스나 cherry-pick으로 release 브랜치에 직접 반영한 변경사항은 main 브랜치에 없으므로, 릴리스 노트에 포함되지 않습니다.

         hotfix ──────┐

release: ──A──B──C─┼──H──    ← H(hotfix)는 main에 없으므로 릴리스 노트에서 누락

main: ──A──B──C──D──E──F──

2. release 브랜치 생성 이후 main에 머지된 내용이 포함된다

release 브랜치를 만든 시점 이후에 main에 머지된 PR도 릴리스 노트에 포함됩니다. 이들은 실제로 이번 릴리스에 포함되지 않는 변경사항입니다.

main: ──A──B──C──D──E──F──    ← E, F는 다음 릴리스 대상이지만 릴리스 노트에 포함됨

release: ──A──B──C─┘

해결 방법

핵심 아이디어: 워크플로우 분리

Release Drafter는 크게 두 가지 기능을 제공합니다.

기능설명실행 시점
AutolabelerPR에 라벨을 자동으로 부여PR이 생성·수정될 때
Release note 생성라벨 기반으로 릴리스 노트 초안 작성릴리스 대상 브랜치에 변경이 있을 때

기존에는 하나의 워크플로우에서 두 기능을 동시에 수행했지만, 각 기능의 실행 시점과 대상 브랜치가 다르기 때문에 분리하는 것이 합리적입니다.

  • Autolabel: PR 이벤트에서 실행 → main 브랜치 대상
  • Release note 생성: release 브랜치에 push될 때 실행 → release 브랜치 대상

워크플로우 1: PR Label Checker (autolabel 전용)

PR이 생성되거나 수정될 때 라벨만 자동으로 부여하는 워크플로우입니다. disable-releaser: true로 릴리스 노트 생성 기능을 비활성화합니다.

# .github/workflows/pr-label-checker.yml
name: PR Label Checker

on:
  pull_request:
    types: [opened, reopened, synchronize]

permissions:
  contents: read

jobs:
  check_pr_labels:
    permissions:
      contents: write
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4
      - name: Extract branch and service name
        id: extract
        uses: ./.github/actions/extract_branch_and_service_name
      - name: Set environment variables
        run: |
          echo "SERVICE_NAME=${{ steps.extract.outputs.SERVICE_NAME }}" >> $GITHUB_ENV

      - name: Autolabel
        uses: release-drafter/release-drafter@v6
        with:
          config-name: release-drafter.yml
          disable-releaser: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

disable-releaser: true를 설정하면 Release Drafter가 릴리스 노트 초안을 생성하지 않고, autolabel 기능만 수행합니다.

워크플로우 2: Release Drafter (릴리스 노트 전용)

release 브랜치에 push가 발생할 때 릴리스 노트를 생성하는 워크플로우입니다. disable-autolabeler: true로 라벨 부여 기능을 비활성화하고, commitish를 해당 release 브랜치로 동적 설정합니다.

# .github/workflows/release-drafter-on-release-branch.yml
name: Release Drafter (on release branch)

on:
  push:
    branches:
      - 'release/**'

permissions:
  contents: read

jobs:
  update_release_draft:
    permissions:
      contents: write
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4
      - name: Extract branch and service name
        id: extract
        uses: ./.github/actions/extract_branch_and_service_name
      - name: Set environment variables
        run: |
          echo "SERVICE_NAME=${{ steps.extract.outputs.SERVICE_NAME }}" >> $GITHUB_ENV
          echo "FULL_BRANCH_NAME=${{ steps.extract.outputs.FULL_BRANCH_NAME }}" >> $GITHUB_ENV

      - name: Run Release Drafter
        uses: release-drafter/release-drafter@v6
        with:
          config-name: release-drafter.yml
          commitish: ${{ env.FULL_BRANCH_NAME }}
          disable-autolabeler: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

핵심은 commitish: ${{ env.FULL_BRANCH_NAME }}입니다. release 브랜치 이름(예: release/serviceName/1.2.0)을 동적으로 전달하여, 해당 release 브랜치에 포함된 변경사항만 릴리스 노트에 반영합니다.

release-drafter.yml 설정 변경

공통 설정 파일에서 commitish를 제거합니다. 워크플로우에서 동적으로 전달하므로 설정 파일에 고정할 필요가 없습니다.

# .github/release-drafter.yml (수정 후)
version-resolver:
  minor:
    labels:
      - 'minor'
  patch:
    labels:
      - 'patch'
  default: minor
# commitish: main  ← 삭제
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
change-title-escapes: '\<*_&'
template: |
  ## Changes
  $CHANGES

Composite Action 수정: push 이벤트 대응

기존의 브랜치명 추출 Composite Action은 pull_request 이벤트만 지원했습니다. release 브랜치에 대한 push 이벤트에서도 동작하도록 수정이 필요합니다.

Composite Action을 사용하는 방법에 대해서는 다음 링크를 참고하시기 바랍니다.

# .github/actions/extract_branch_and_service_name/action.yml
runs:
  using: composite
  steps:
    - id: extract_branch_and_service_name
      shell: bash
      run: |
        # 수정 전
        # FULL_BRANCH_NAME=${{ github.event.pull_request.head.ref }}

        # 수정 후: PR 이벤트와 push 이벤트 모두 대응
        if [ -n "${{ github.event.pull_request.head.ref }}" ]; then
          FULL_BRANCH_NAME=${{ github.event.pull_request.head.ref }}
        else
          FULL_BRANCH_NAME=${GITHUB_REF#refs/heads/}
        fi
  • pull_request 이벤트: github.event.pull_request.head.ref에서 브랜치명을 가져옵니다.
  • push 이벤트: GITHUB_REF 환경 변수에서 refs/heads/ 접두사를 제거하여 브랜치명을 추출합니다.

Release Drafter의 주요 Action Inputs

워크플로우 분리에 사용된 Release Drafter의 주요 Action Inputs를 정리하면 다음과 같습니다.

Input기본값설명
config-namerelease-drafter.yml사용할 설정 파일 경로 (.github/ 기준)
commitish워크플로우 실행 브랜치릴리스 대상 브랜치 또는 커밋. 이 브랜치의 HEAD까지의 변경사항을 릴리스 노트에 포함
disable-releaserfalsetrue로 설정하면 릴리스 노트 생성을 비활성화하고, autolabel 기능만 수행
disable-autolabelerfalsetrue로 설정하면 autolabel을 비활성화하고, 릴리스 노트 생성만 수행
tag-prefix''릴리스 태그의 접두사. 이전 릴리스를 찾을 때 이 접두사로 필터링
filter-by-commitishfalsetrue로 설정하면 이전 릴리스를 찾을 때 commitish와 일치하는 릴리스만 대상으로 함

주의사항

모노레포에서 서비스별 설정 파일 분리

모노레포에서 여러 서비스를 관리하는 경우, 서비스별로 release-drafter-for-{serviceName}.yml 설정 파일을 분리하고, tag-prefix를 서비스별로 다르게 설정하여 릴리스를 구분합니다.

# .github/release-drafter-for-serviceName.yml
tag-prefix: serviceName-v

이렇게 하면 serviceName-v1.2.0 같은 태그에서 이전 릴리스를 찾고, 그 이후의 변경사항만 릴리스 노트에 포함합니다.

release 브랜치 네이밍 컨벤션

release/** 패턴과 Composite Action에서 서비스 이름을 추출하는 로직이 일치해야 합니다. 예를 들어, release/serviceName/1.2.0에서 serviceName를 서비스 이름으로 추출하는 규칙이 있다면, 브랜치 생성 시 이 컨벤션을 반드시 따라야 합니다.

GitHub Actions로 브랜치 이름을 검사하는 방법에 대해서는 다음 링크를 참고하시기 바랍니다.

filter-by-commitish 활용

여러 release 브랜치가 동시에 존재하는 경우, filter-by-commitish: true를 설정하면 이전 릴리스를 찾을 때 해당 release 브랜치에서 생성된 릴리스만 대상으로 합니다. 서로 다른 release 브랜치의 릴리스가 섞이는 것을 방지할 수 있습니다.

마무리

Release Drafter의 commitish를 main에 고정하면, release 브랜치와 main 브랜치의 차이로 인해 릴리스 노트가 부정확해지는 문제가 발생합니다. 워크플로우를 autolabel 전용릴리스 노트 생성 전용으로 분리하고, commitish를 release 브랜치로 동적 설정함으로써 정확한 릴리스 노트를 생성할 수 있습니다.

제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!

앱 홍보

책 홍보

블로그를 운영하면서 좋은 기회가 생겨 책을 출판하게 되었습니다.

아래 링크를 통해 제가 쓴 책을 구매하실 수 있습니다.
많은 분들에게 도움이 되면 좋겠네요.



SHARE
Twitter Facebook RSS