[기술정리] Git Actions을 통한 자동 merge CI 세팅 전략 - DDAL-KKAK-DOT/DDALKKAK GitHub Wiki

작성자: 조하은

1. 개요

GitHub Actions를 활용하여 Pull Request에 대한 자동 merge 기능을 구현하는 가이드입니다. 특정 조건(승인 개수, 타겟 브랜치 등)을 만족할 때 자동으로 PR을 병합하여 개발 워크플로우를 효율화할 수 있습니다.

주요 기능

  • 승인 기반 자동 병합: 지정된 수 이상의 승인을 받으면 자동 병합
  • 브랜치 제한: 특정 브랜치(dev-be, dev-fe 등)로의 PR만 대상
  • 안전장치: Draft PR, 작성자 자가 승인 등 예외 상황 처리
  • 상세 로깅: 병합 과정과 결과에 대한 투명한 로그 제공

2. 선택지 비교

방법 1: 기존 액션 + 라벨 방식

# pascalgn/automerge-action의 기본 설정
MERGE_LABELS: "automerge"
MERGE_REQUIRED_APPROVALS: 3

장점

  • 간단한 설정
  • 검증된 액션 사용

단점

  • 라벨을 수동으로 추가해야 함
  • 승인 수 체크가 부정확할 수 있음
  • 디버깅이 어려움

https://github.com/DDAL-KKAK-DOT/DDALKKAK/pull/56

방법 2: 커스텀 스크립트 + 액션 조합 ⭐ 권장

# GitHub Script로 승인 수 직접 계산 후 조건부 병합
steps:
  - name: Check approvals count
    uses: actions/github-script@v7
  - name: Auto merge PR
    if: steps.check-approvals.outputs.should-merge == 'true'

장점

  • 정확한 승인 수 계산
  • 세밀한 조건 제어
  • 투명한 로깅
  • 라벨 불필요

단점

  • 설정이 복잡함
  • YAML 코드가 길어짐

방법 3: 써드파티 액션 사용

다양한 커뮤니티 액션들이 존재하지만, 보안과 안정성 측면에서 공식 액션 조합을 권장합니다.

3. 설정방법

3.1 워크플로우 파일 생성

.github/workflows/automerge.yml 파일을 생성하고 다음 내용을 추가합니다:

name: Auto Merge (3+ approvals, dev-be & dev-fe)

on:
  pull_request_review:
    types: [submitted]

# 동시성 제어: 같은 PR에 대해 하나의 워크플로우만 실행
concurrency:
  group: automerge-${{ github.event.pull_request.number }}
  cancel-in-progress: false

jobs:
  automerge:
    if: |
      github.event.review.state == 'approved' &&
      (github.event.pull_request.base.ref == 'dev-be' || github.event.pull_request.base.ref == 'dev-fe') &&
      github.event.pull_request.draft == false
    runs-on: ubuntu-latest

    steps:
      - name: Check approvals count
        id: check-approvals
        uses: actions/github-script@v7
        with:
          script: |
            const { data: reviews } = await github.rest.pulls.listReviews({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number,
            });
            
            const prAuthor = context.payload.pull_request.user.login;
            
            // 최신 리뷰만 고려 (같은 사용자가 여러 번 리뷰한 경우)
            const latestReviews = {};
            reviews.forEach(review => {
              if (!latestReviews[review.user.login] || 
                  new Date(review.submitted_at) > new Date(latestReviews[review.user.login].submitted_at)) {
                latestReviews[review.user.login] = review;
              }
            });
            
            // PR 작성자 제외하고 승인된 리뷰만 카운트
            const approvals = Object.values(latestReviews).filter(review => 
              review.state === 'APPROVED' && review.user.login !== prAuthor
            ).length;
            
            const approvedReviewers = Object.values(latestReviews)
              .filter(review => review.state === 'APPROVED' && review.user.login !== prAuthor)
              .map(review => review.user.login);
            
            console.log(`PR Author: ${prAuthor}`);
            console.log(`Current approvals (excluding author): ${approvals}`);
            console.log(`Approved by:`, approvedReviewers);
            
            core.setOutput('approvals', approvals);
            core.setOutput('should-merge', approvals >= 3);
            core.setOutput('approved-reviewers', approvedReviewers.join(', '));

      - name: Auto merge PR
        if: steps.check-approvals.outputs.should-merge == 'true'
        uses: pascalgn/[email protected]
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          MERGE_METHOD: squash
          MERGE_LABELS: ""
          MERGE_REQUIRED_APPROVALS: 0
          MERGE_FORKS: false
          MERGE_DELETE_BRANCH: true
          MERGE_COMMIT_MESSAGE: pull-request-title

      - name: Log merge result
        if: steps.check-approvals.outputs.should-merge == 'true'
        run: |
          echo "✅ PR #${{ github.event.pull_request.number }} to ${{ github.event.pull_request.base.ref }} has been auto-merged"
          echo "📊 Total approvals: ${{ steps.check-approvals.outputs.approvals }}"
          echo "👥 Approved by: ${{ steps.check-approvals.outputs.approved-reviewers }}"

      - name: Log insufficient approvals
        if: steps.check-approvals.outputs.should-merge == 'false'
        run: |
          echo "⏳ PR #${{ github.event.pull_request.number }} needs more approvals"
          echo "📊 Current approvals: ${{ steps.check-approvals.outputs.approvals }}/3"
          echo "👥 Approved by: ${{ steps.check-approvals.outputs.approved-reviewers }}"

3.2 브랜치 보호 규칙 설정

Repository Settings > Branches에서 대상 브랜치(dev-be, dev-fe)에 대한 보호 규칙을 설정합니다:

  1. Require pull request reviews 활성화
  2. Required number of approvals: 3으로 설정
  3. Dismiss stale reviews 활성화 (선택사항)
  4. Require review from code owners 활성화 (선택사항)

3.3 권한 설정

기본 GITHUB_TOKEN으로 충분하지만, 브랜치 보호가 강하게 설정된 경우 개인 액세스 토큰이 필요할 수 있습니다:

  1. GitHub Settings > Developer settings > Personal access tokens 생성
  2. repo 권한 부여
  3. Repository secrets에 토큰 추가
  4. 워크플로우에서 ${{ secrets.YOUR_TOKEN_NAME }} 사용

3.4 커스터마이징 옵션

타겟 브랜치 변경

# 단일 브랜치
github.event.pull_request.base.ref == 'main'

# 여러 브랜치
(github.event.pull_request.base.ref == 'dev-be' || 
 github.event.pull_request.base.ref == 'dev-fe' || 
 github.event.pull_request.base.ref == 'staging')

승인 수 변경

# 2개 승인으로 변경
core.setOutput('should-merge', approvals >= 2);

병합 방식 변경

env:
  MERGE_METHOD: merge    # 일반 병합
  MERGE_METHOD: squash   # 스쿼시 병합
  MERGE_METHOD: rebase   # 리베이스 병합

4. 정리

동작 프로세스

  1. 트리거: PR에 승인 리뷰가 제출됨
  2. 필터링: 승인된 리뷰이면서 대상 브랜치로의 PR인지 확인
  3. 승인 수 계산: GitHub API를 통해 정확한 승인 수 계산
  4. 조건 검사: 3개 이상의 승인을 받았는지 확인
  5. 자동 병합: 조건을 만족하면 squash 병합 실행
  6. 정리: 병합 후 소스 브랜치 삭제

주의사항

  • 동시성: 여러 승인이 동시에 들어와도 안전하게 처리됩니다
  • 중복 리뷰: 같은 사용자의 최신 리뷰만 고려됩니다
  • 작성자 제외: PR 작성자의 승인은 카운트에서 제외됩니다
  • Draft PR: 초안 상태의 PR은 자동 병합되지 않습니다

모니터링

Actions 탭에서 워크플로우 실행 로그를 통해 다음 정보를 확인할 수 있습니다:

  • 현재 승인 수
  • 승인한 사용자 목록
  • 병합 성공/실패 여부
  • 부족한 승인 수

트러블슈팅

병합이 실행되지 않는 경우:

  1. 브랜치 보호 규칙 확인
  2. GitHub Token 권한 확인
  3. 워크플로우 로그에서 승인 수 계산 결과 확인

예상보다 적은 승인 수가 카운트되는 경우:

  1. 작성자 자가 승인 여부 확인
  2. 변경 요청 후 재승인 여부 확인
  3. 중복 리뷰어 존재 여부 확인

이 설정을 통해 안전하고 효율적인 자동 병합 시스템을 구축할 수 있습니다.