Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

GitHub의 Rulesets로 main 브랜치의 푸시 막기

GitHub도 새로운 기능이 계속 나오고 있고 자주 쓰는 설정이면서도 필요할 때만 설정하고 또 잊고 있다 보니 헷갈려서 간단히 정리를 해보게 되었다.

일반적으로 Pull Request를 이용해서 협업하는 경우 main같은 기본 브랜치는 직접 푸시하는 걸 막고 Pull Request를 이용해서 main에 커밋을 머지하도록 하는 것이 일반적이다.

기존에는 저장소의 Branch protection을 이용해서 이러한 설정을 하곤 했다.

사용자 삽입 이미지

2년 전에 Rulesets라는 기능이 나온 뒤로는 이 기능을 주로 밀기 때문에 지금은 이 기능을 classic branch protection rule이라고 부르고 있다. 레거시니까 앞으로는 Rulesets의 사용을 권장한다는 얘기다.

작년에 이 Repository Rules 기능에 대해서도 글을 적은 적이 있지만 이때는 새로운 기능이기도 했고, 당시의 관심사에 따라 ORG 관리자 차원에서 저장소에 규칙을 지정하려는 방법으로 이 기능을 살펴봤기에 이 글에서는 Repository Rulestes로 main 브랜치를 저장하는 방법을 살펴본다.

사용자 삽입 이미지

저장소 Rulesets는 Settings의 Rules 메뉴에서 생성할 수 있는데 여기서는 main 브랜치에 규칙을 지정할 것이므로 New branch ruleset을 사용한다.

사용자 삽입 이미지

규칙의 이름을 정하고 Enforcement 상태를 지정하면 되는데 저장소 수준에서는 활성화와 비활성화밖에 없다. Org 수준에서 지정할 때는 Enterprise 요금제라면 평가 모드로 설정해서 영향을 주지 않고 테스트할 수 있지만 저장소 수준에서는 그럴 수 없다.

Bypass 목록을 이 규칙에서 제외할 대상을 지정할 수 있는데 role, team, app만 되기 때문에 사용자별로 제외할 수는 없다. main 브랜치를 보호하더라도 특수한 상황에 바로 main에 머지해야 하는 상황이 필요할 수 있는데(개인적으로 권장하진 않는다) 이럴 때 사용할 수 있다.

사용자 삽입 이미지

대상 브랜치를 여기서는 기본 브랜치인 main에 대해 설정할 것이므로 기본 브랜치를 설정한다. 물론 pattern을 이용해서 main을 지정해도 되지만 편의상 기본 브랜치를 사용했다.

사용자 삽입 이미지

브랜치 Ruleset에는 기본적으로 Restrict deletions와 Block force pushes가 켜져 있다. 원하면 끌 수 있지만 기본 브랜치를 삭제하거나 여기에 포스 푸시를 하는 것은 일반적인 상황이 아니므로 이 또한 차단한다.

사용자 삽입 이미지

main 브랜치에 직접 푸시하지 못하도록 막기 위해 Require a pull request before merging을 켜준다. 이 하위에는 다양한 옵션이 있다.

  • Required approvals를 0 이상으로 지정하면 Pull Request가 꼭 누군가의 리뷰를 받도록 할 수 있다. 팀 내에 리뷰 규칙에 따라 승인자의 수를 지정하면 된다.
  • Dismiss stale pull request approvals when new commits are pushed는 리뷰를 받은 후에 새로운 커밋을 올리는 경우에 기존 승인을 다시 취소하는 기능이다. 리뷰를 엄격하게 한다면 필요할 수도 있지만 일반적으로는 내용을 안 고치고 리베이스만 하는 경우도 많고 이런 경우에 PR이 머지되는 속도를 너무 늦추기 때문에 나는 잘 사용하지 않는다.
  • Require review from specific teams는 원래는 못 보던 기능인데 최근에 새로 생긴 기능인 거 같다. 이 기능을 키면 특정 파일 패턴에 대해서 리뷰할 팀을 지정할 수 있는데 써보진 않았지만, 요즘처럼 모노레포로 여러 팀이 한 저장소를 쓰거나 특정 기능에 대해서는 꼭 특정 팀의 리뷰가 필요하다고 할 때 유용해 보인다.
  • Require review from Code Owners를 지정하면 해당 저장소의 코드 오너들의 승인을 꼭 필요로 하게 된다. CODEOWNERS 파일로 관리할 수 있는 저장소의 코드 오너들 외에도 팀이나 회사의 다른 사람도 승인할 수 있기 때문에 코드 오너들은 꼭 PR을 확인하게 하는 용도로 사용하는 편이다. CODEOWNERS 파일 내에서도 파일 패턴으로 다양하게 지정할 수 있고 리뷰어로도 자동으로 지정되어서 이쪽 방법을 더 선호하는 편이다.
  • Require approval of the most recent reviewable push는 푸시한 사람 외에 다른 사람의 승인을 꼭 필요로 하는 기능으로 fork 방식으로 협업하지 않고 대부분의 회사에서 하듯이 업스트림 저장소에 작업 브랜치를 만들어서 Pull Request를 올릴 때 이미 승인 받은 다른 Pull Request에 커밋을 추가해서 머지하게 할 수도 있으므로 이를 막을 수 있는 기능이다. 하지만 팀 내에서 이 정도까지 엄격하게 할 필요가 있나 싶긴 하다.
  • Require conversation resolution before merging는 리뷰를 받은 내용을 해결 처리하지 않으면 머지하지 못하게 하는 기능인데 이것도 마찬가지로 확실하긴 하지만 속도가 느려져서 선호하지 않는다.
  • Automatically request Copilot code review는 코파일럿 리뷰를 사용하고 있다면 켜도 좋을 옵션이다.
  • Allowed merge methods는 팀 내에서 합의한 머지 방식을 선택하면 되는데 요즘은 Squash 머지를 선호하는 편이다.

사용자 삽입 이미지

해당 Ruleset을 저장하면 규칙이 등록된다. 당연히 여러 개 만드는 것도 가능하다.

$ git push origin main
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 10 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 920 bytes | 920.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0), pack-reused 0
remote: error: GH013: Repository rule violations found for refs/heads/main.
remote: Review all repository rules at https://github.com/outsideris/ruleset-test/rules?ref=refs%2Fheads%2Fmain
remote:
remote: - Changes must be made through a pull request.
remote:
To github.com:outsideris/ruleset-test.git
 ! [remote rejected] main -> main (push declined due to repository rule violations)
error: failed to push some refs to 'github.com:outsideris/ruleset-test.git'

이제 main 브랜치에 직접 push하면 저장소 규칙을 위반(GH013: Repository rule violations found for refs/heads/main)했다고 푸시가 거절되는 것을 볼 수 있다.

사용자 삽입 이미지

Pull Request를 올렸을 때도 Ruleset에 정의한 대로 1개의 리뷰를 받지 못한 상황에서는 머지가 막혀 있는 것을 볼 수 있다.

2025/09/07 19:42 2025/09/07 19:42