배경
사내에서 커밋과 PR 내역이 한 채널에 모두 섞여있었다. 그리고 GitHub 아이디와 슬랙 이름이 매칭되지 않아 PR이 오픈되어도 리뷰어에게 알림이 울리지 않았다. 그래서 팀 채널에서 직접 리뷰어를 멘션한 뒤 리뷰를 부탁하는 글을 작성해야 했다. 나는 이 과정이 코드 리뷰 활성화를 어렵게 만드는 허들이라고 생각했다. 따라서 PR을 위한 채널을 만들고 슬랙봇으로 자동화를 통해 허들을 낮추고자 하였다.
자동화 과정에서 GitHub Actions를 적극적으로 사용하였다. Github Actions를 사용하면, GitHub 하나의 플랫폼 내에서 모든 과정을 수행할 수 있다. 그리고 .github/workflows 디렉토리에 워크플로우 yaml 파일만 작성하면 별도의 세팅과 설치가 필요없이 간단하게 기능을 사용할 수 있다. 따라서 다른 팀원이 쉽게 슬랙봇의 기능을 수정하거나 추가할 수 있다고 생각했다.
먼저 슬랙봇의 기능을 살펴보자면, PR이 오픈되면 슬랙에 리뷰어를 멘션한다. 더 나아가 리뷰어가 PR에 코멘트를 달면, 해당 PR 스레드에 자동으로 댓글이 남겨지도록 했다.
PR과 코멘트에 대한 슬랙 알림
GitHub 아이디와 슬랙 이름은 다르다. 따라서 매핑해주는 과정이 필요한데, 먼저 GitHub 아이디를 Key로 슬랙의 멤버 아이디를 Value로 하여 Json 파일을 생성했다. 슬랙의 멤버 아이디는 프로필에서 확인할 수 있다.
사내 슬랙에서 백엔드, 프론트엔드 유저 그룹이 있다. 백엔드 전체를 리뷰어로 멘션하려면 백엔드 유저 그룹을 멘션하면 된다. 따라서 사용자 그룹 아이디에 대해서도 Json 파일을 생성했다.
특정 멤버를 리뷰어로 멘션하고 싶으면 PR에 리뷰어로 요청했고 특정 유저 그룹을 리뷰어로 멘션하고 싶으면 아래와 같이 PR에 라벨을 적용하도록 했다.
그리고 Json 파일을 읽어서 변환하는 자바스크립트 코드를 작성했다. PR의 리뷰어 리스트와 라벨 리스트를 받아 각각 멤버와 유저 그룹 멘션으로 변환했다.
const workers = JSON.parse(fs.readFileSync('.github/mention_to_slack.json'));
const convertMentionUserGroupList = (users, labels) => {
// 깃허브 아이디를 받아 멤버 멘션으로 변환
const userMention = users.map((user) => {
const { login } = user;
if (login in workers) {
return `<@${workers[login]}>`;
}
return ``
});
// PR 라벨을 유저 그룹 멘션으로 변환
const groupMention = labels.map((label) => {
const { name } = label;
if (name in workers) {
return `<!subteam^${workers[name]}>`;
}
return ``;
})
// 두 배열을 합친 뒤, 빈 문자열은 제거한 후 결합
return [...userMention, ...groupMention].filter(Boolean).join(', ');
}
이렇게 작성된 코드를 워크플로우에서 실행시킨 뒤, 슬랙 API 실행시켜서 PR에 대한 스레드를 생성한다. 이렇게 생성한 PR 스레드에 코멘트를 달기 위해선 스레드의 타임스탬프가 필요하다. 따라서 타임스탬프를 어딘가에 저장해 두어야 한다.
나는 아티팩트(Artifacts)에 저장해두었다. 아티팩트를 사용하면 워크플로우 간에 데이터를 공유하고 워크플로우가 완료되면 데이터를 저장할 수 있다. 그리고 이렇게 보관된 아티팩트가 자동으로 만료되기 전에 다운로드할 수 있다. GitHub Actions 문서에 따르면 아티팩트는 기본적으로 90일 동안 GitHubsms에 보존된다.
먼저 PR이 오픈되거나 재오픈될 때 실행되는 PR 슬랙 알림 워크플로우를 살펴보자. 워크플로우를 살펴보면, slack에 PR 오픈 알림을 보내고 스레드의 타임스탬프를 thread_ts.txt 형태로 저장한다. 그리고 아티팩트 이름을 PR 아이디로 지정한 뒤 업로드를 했다.
다음으로 PR에 코멘트가 달렸을 때 실행되는 코멘트 슬랙 알림 워크플로우를 살펴보자. 워크플로우를 살펴보면, PR 알림 워크플로우에서 실행된 아티팩트를 다운로드 한다. 이렇게 다운로드된 아티팩트에서 타임스탬프를 읽어 slack의 PR 스레드에 댓글을 올린다.
Repository Dispatch로 워크플로우 재사용
하나의 레포지토리가 아닌 여러 레포지토리에 대해서 PR 알림 자동화를 이뤄내야 한다. 각 레포지토리에 PR 슬랙 알림을 전송하는 워크플로우가 위치하는 것도 방법이겠지만, 알림 탬플릿이 변경되거나 슬랙봇에 기능이 추가된다면 모든 레포지토리의 워크플로우를 수정해야 한다. 따라서 PR 슬랙 알림 워크플로우는 하나의 레포지토리에서 관리되고 다른 레포지토리가 이 워크플로우를 trigger하도록 했다.
기본적으로 GitHub Actions는 자신의 저장소의 위치한 워크플로우만 실행시킬 수 있다. 따라서 다른 저장소의 워크플로우를 trigger 하려면 Repository Dispatch를 사용해야 한다.
다음과 같이 PR이 오픈되거나 재오픈될 때 pr-review-request-noti 라는 이벤트가 발행된다. 그리고 이 이벤트를 수신하는 워크플로우는 이벤트를 발행한 레포지토리의 PR이나 리뷰 정보를 알지 못한다. 따라서 이벤트를 발행할 때 client_payload 에 PR이나 리뷰 정보를 실어야 한다.
후기
Github Actions를 통해 간단하게 간략하게 PR 알림 자동화를 구현했다. 그 외에도 평일 오전 11시에 코드 리뷰가 필요한 PR 리스트업하여 알림, 매주 금요일 오후 2시에 이번 주 PR과 리뷰에 대한 통계 일림 기능도 추가했다. 현재까지도 팀원들의 니즈를 받아 틈틈이 추가 기능을 개발하고 있다.
처음엔 사내에서 협업 도구로 슬랙을 가장 많이 사용하기에 슬랙봇을 만들었다. 만들기 전, 여러 슬랙봇 디자인을 살펴보았고 슬랙봇을 통해 다양한 자동화를 이뤄낼 수 있음을 알았다.
참고
'프로젝트 > 트러블슈팅' 카테고리의 다른 글
@RedisHash 주의해서 사용하기 (2) | 2024.11.10 |
---|---|
[트러블 슈팅] 인덱스 컨디션 푸시다운, 인덱스를 이용한 정렬, 커버링 인덱스로 슬로우 쿼리 튜닝하기 (1) | 2024.02.13 |
[트러블 슈팅] n + 1 문제를 IN절로 해결하기 (0) | 2024.02.12 |
[트러블 슈팅] 리팩토링을 통한 복잡했던 모듈 구조를 단순화 (0) | 2024.02.06 |
[트러블 슈팅] 레이어별 멀티 모듈 적용 (과도한 모듈 분리로 실패🤪) (0) | 2024.01.15 |