GitHub Actions용 체인지로그 체크
4분 분량
체인지로그를 수작업으로 유지하는 모든 팀은 같은 사건 뒤에 같은 대화를 나눈 적이 있다. 릴리스가 항목 없이 나갔고, 누군가 왜냐고 묻고, 정직한 답은 그것을 썼을 사람이 바쁘게 움직이고 있었고 체인지로그 단계가 오직 기억 속에만 존재했다는 것이다. 체인지로그 자동화는 파이프라인이 안전하게 자동화할 수 있는 것과 여전히 사람이 필요한 것을 다룬다. CI에서의 체인지로그 체크는 이 문제의 나머지 절반이다. 쓰기를 자동화해도 애초에 아무도 그것을 실행할 의무가 없다면 도움이 되지 않기 때문이다. 대부분의 팀은 이미 풀 리퀘스트 체크를 GitHub Actions에서 돌리고 있으므로, 이 체크도 거기에 자리 잡는다.
“사람들에게 항목을 추가해달라고 부탁한다”는 왜 예측 가능한 패턴으로 실패하는가
풀 리퀘스트 안의 다른 모든 것과 관심을 두고 경쟁하며, 건너뛰어도 즉각적인 결과가 없는 유일한 부분이기 때문이다. 테스트는 시끄럽게 실패하며 머지를 막는다. 빠진 체인지로그 항목은 아무것도 막지 않으므로, 누군가 서두르는 순간, 실제로는 대부분의 시간에 패배한다. 기억으로 강제되는 정책은 예상할 수 있는 바로 그 속도로 저하된다. 모두가 동의한 첫 몇 주는 괜찮다가, 신경 쓰던 사람이 휴가를 가거나 팀을 옮기는 순간 조용히 버려진다.
체인지로그 항목을 위한 CI 체크는 실제로 무엇을 검증하는가
글쓰기의 품질이 아니라 항목이 존재하고 형식이 올바른지 여부만이며, 이는 사람의 머릿속이 아니라 CI에서 실행되는 체인지로그 체크에 맞는 범위다. 흔한 형태는, 체크가 PR의 diff를 보고 changeset 디렉터리 안의 새 파일(Changesets와 비슷한 도구들이 쓰는 패턴)이나 체인지로그 파일 안의 변경된 줄 둘 중 하나를 요구하고, 둘 다 없으면 빌드를 실패시킨다. 항목이 실제로 무엇을 말하는지에 대한 검토는 언제나 그래왔던 곳, 즉 코드 리뷰에서 계속 이루어진다. 그 판단은 스크립트가 할 일이 아니기 때문이다.
| CI 체크가 검증하는 것 | 검증하지 않는 것 |
|---|---|
| diff에 changeset이나 체인지로그 줄이 존재한다 | 표현이 명확한지 |
| 항목이 모노레포에서 올바른 패키지를 참조한다 | 변경이 애초에 항목을 받을 만한지 |
| 파일이 구문적으로 유효하다 (frontmatter, JSON 형태) | 항목이 영향에 대해 정직한지 |
모든 PR이 이것을 필요로 하는가, 아니면 일부 변경은 면제되는가
일부는 면제되며, 면제 목록이야말로 이런 시스템이 실제로 구축되거나 버려지는 지점이다. 눈에 띄는 효과가 없는 의존성 업데이트, 테스트만 바뀌는 변경, 동작 변화 없는 내부 리팩터링, 이 중 어느 것도 체인지로그를 읽는 누구도 신경 쓰지 않는 것을 위해 기여자가 체인지로그 항목을 지어내도록 강요해서는 안 된다. 효과적인 패턴은 기여자가 적용할 수 있는(no-changelog-needed) 라벨이나 플래그이며, 파일 없이 CI 체크를 만족시키고, PR을 승인하는 사람이 검토하므로 면제 자체가 항목이 통과했을 것과 같은 검토를 통과하게 된다.
긴급 핫픽스 같은 정당한 예외는 어떻게 되는가
게이트는 배포가 아니라 머지에 있다. 진짜 시간 압박 아래 있는 핫픽스는 CI 체크가 완성된 문단이 아니라 의도로 만족되는 한, 자리 표시자 항목이나 후속 티켓으로 머지할 수 있다. 일부 팀은 다음 릴리스 컷 전에 메인테이너가 다듬는 한 줄짜리 스텁을 받아들인다. 게이트가 절대 허용해서는 안 되는 것은 그 단계를 조용히 건너뛰는 것이다. 잊혀진 스텁은 한 번도 존재한 적 없는 항목보다 작은 실패이며, 스텁은 적어도 누군가 나중에 찾을 수 있는 흔적을 남기기 때문이다.
# .github/workflows/changelog-check.yml
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
changelog:
if: >-
!contains(github.event.pull_request.labels.*.name,
'no-changelog-needed')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # the diff needs the base branch
- name: Require changelog entry
run: |
base="origin/${{ github.base_ref }}"
if ! git diff --name-only "$base"...HEAD \
| grep -q '^\.changeset/'; then
echo "No changeset. Add one, or have a maintainer"
echo "apply the no-changelog-needed label."
exit 1
fi
이 체크가 실제 PR을 막기 시작하기 전에, 체크 자체가 올바른지 어떻게 아는가
먼저 버릴 수 있는 브랜치를 대상으로 스크래치 풀 리퀘스트를 열어본다. changeset이 있는 것 하나, 없는 것 하나, 면제 라벨을 단 것 하나를 만들어 세 가지 모두 다른 사람의 작업에 이 체크가 적용되기 전에 예상한 결과가 나오는지 확인한다. 조건이 거꾸로 쓰여서 모든 PR을 통과시키는, 즉 실패 개방형 체인지로그 체크는 아예 체크가 없는 것보다 나쁘다. 존재하지 않는 커버리지처럼 보이기 때문이다. 같은 파일에 대해 workflow_dispatch를 수동으로, 최근에 머지된 PR 몇 개를 대상으로 돌려보는 것만으로도 실제 풀 리퀘스트 없이 이런 실수 대부분을 잡아낼 수 있다.
같은 아이디어가 GitHub Actions 바깥에서도 통하는가
형태는 그대로 옮겨가고, 문법만 달라진다. GitLab CI는 같은 규칙을 GitHub Actions의 if 대신 $CI_MERGE_REQUEST_LABELS를 확인하는 job rules 블록으로 표현하며, 필수 머지 리퀘스트 승인이 면제 검토 단계를 대신할 수 있다. 이 글이 설명하는 체크가 GitHub Actions인 이유는 이 글을 읽는 대부분의 팀이 이미 그 플랫폼을 쓰고 있기 때문일 뿐, 그 밑에 깔린 요구사항, 즉 부탁받은 관행이 아니라 머신이 검증하는 게이트라는 것은 CI가 머지 전에 돌아가는 모든 곳에서 동일하다.
이것은 모노레포에서도 똑같이 작동하는가
한 조각이 더 필요하다. 항목이 어느 패키지를 위한 것인가다. 모노레포 체인지로그는 패키지가 독립적으로 릴리스되는 순간 저장소 전체를 위한 단일 파일이 작동을 멈추는 이유를 다룬다. CI 체크는 같은 요구사항을 물려받는다. 패키지를 명시하지 않는 changeset은 올바른 체인지로그가 업데이트될 것이라는 유용한 증거가 아니라, diff 어딘가에서 어떤 파일이 바뀌었다는 증거일 뿐이다. 이를 위해 만들어진 도구들(JavaScript 생태계에서는 Changesets가 흔하다)은 changeset이 만들어지는 바로 그 순간 기여자에게 영향받는 패키지와 semver 상승을 선택하게 하므로, CI 체크는 나중에 추론하는 대신 두 정보를 공짜로 얻는다.
FAQ
CI 체크는 머지를 막아야 하는가, 아니면 경고만 해야 하는가? 막아야 한다. 경고는 기능적으로 정중하게 부탁하는 것과 동일하며, 그것은 이미 실패했다. 면제 라벨은 진짜 경고만 필요한 경우조차 같은 엄격한 게이트를 통과하는 정당한 경로를 갖도록 정확히 그 목적으로 존재한다.
면제 라벨이 올바르게 적용되었는지는 누가 검토하는가? PR을 승인하는 사람이, 어차피 이미 하고 있는 검토의 일부로서. 라벨은 절대 스스로 적용되고 검토되지 않은 채 남아서는 안 된다. 그렇지 않으면 게이트가 막으려던 바로 그 조용한 우회로가 되어버린다.
CI에서 이를 의무화하는 것이 체인지로그 자동화 파이프라인의 필요성을 대체하는가? 아니다, 그것을 먹여 살린다. 체인지로그 자동화는 구조화된 항목을 페이지, 피드, 이메일로 바꾸는 것을 다룬다. CI 체크는 애초에 자동화할 그 구조화된 항목들이 존재하도록 보장하는 것이다.
먼저 구축할 가치가 있는 이것의 가장 작은 버전은 무엇인가? 지정된 체인지로그 디렉터리 아래 어떤 파일도 변경되지 않았다면 실패하는 단일 체크와, 하나의 면제 라벨이다. 패키지별 라우팅과 모노레포를 위한 semver 추론은 나중에 와도 된다. 핵심 습관, 항목이 존재하거나 누군가 명시적으로 필요 없다고 말했거나, 이것이야말로 첫날부터 가질 가치가 있는 것이다.
이 글의 기술적 내용은 독립적으로 검토되지 않았습니다. 잘못된 부분이 있으면 알려 주세요. 바로잡겠습니다.