보투게더 팀의 코드리뷰 규칙은 크게 3가지 정도가 있습니다.
코드리뷰 완료
, 리뷰 반영 완료
등의 메시지를 슬랙에 남긴다.위의 규칙 중 첫번째는 깃허브 레포의 Settings에서 상세히 설정해주면 되지만, 2, 3번과 같은 규칙은 자동화하려면 Github Actions와 Slack API를 활용해야 했습니다.
더불어 2번의 10시간
이라는 기준이, 단순히 PR 올린 시간에 10시간 더한 결과가 아니었습니다. 보투게더 팀은 워라밸을 중시하기 때문에, 근무시간이평일 오전 10시 ~ 오후 10시
로 정해져 있습니다.
주말에는 좀 쉬자!! 코드리뷰 No~~
금요일 오후와 주말에 pr을 올리면, 마감시간은 월요일 오전 10시에 10을 더해서 월요일 20시
가 됩니다.
예를 들어 목요일 새벽 2시
에 pr을 올리면, 목요일 오전 10시에 10시간을 더해서 목요일 20시
가 코드리뷰 마감시간이 됩니다.
수요일 오전 9시 30분 -> 마감시간: 수요일 오후 7시 30분
목요일 오전 10시 30분 -> 마감시간: 목요일 오후 8시 30분
목요일 13시 30분 -> 마감시간: 금요일 오전 11시 30분
목요일 15시 30분 -> 마감시간: 금요일 13시 30분
금요일 15시 30분 -> 마감시간: 월요일 13시 30분
금요일 21시 30분 -> 마감시간: 월요일 19시 30분
토요일 오전 10시 30분 -> 마감시간: 월요일 20시 00분
일요일 18시 30분 -> 마감시간: 월요일 20시 00분
월요일 오전 10시 30분 -> 마감시간: 월요일 20시 30분
이런 계산을 머리로 직접 해서, 아래처럼 슬랙에 올리는데.. 계속 하다 보면 익숙해질 수 있겠지만 두 달 해보니까 자동화 꼭 해보고 싶은데?
라는 생각이 들더라구요😅🤔
🔼보투게더 팀의 기존 코드리뷰 모습 (슬랙에 마감시간 계산해서 올림)
또 코드리뷰를 했다는 댓글(리뷰 완
등)을 달려고 할 때, pr에 대한 댓글을 찾아서 스레드로 달고 있는데 이런 과정이 다소 귀찮고 불편하다고 느꼈습니다.
이렇게 사소하게 매뉴얼한 부분도 편리하게 자동화한다면, 코드리뷰 효율도 올라간다고 생각했구요!!
따라서 아래의 과정들을 거쳐 코드리뷰 마감시간을 알려주는 슬랙 봇
, 코멘트를 달면 슬랙에 메시지를 보내는 기능
2가지를 만들어봤습니다😀
Github Actions 란?
▶ CI(지속 통합) 또는 CD(지속 배포)와 같은 자동화를 위한 툴로, workflow에서 반복적으로 처리되는 여러 작업(job)에 대한 매커니즘을 구성할 수 있습니다.
Webhook 이란?
▶ Github에서 특정 이벤트들이 발생하면, 외부 웹서버로 알림들이 전달되도록 하는 방법
✔ 특정 이벤트들의 예시는 아래와 같아요.
1) 코드가 repo 에서 push 되는 경우
2) pr이 open된 경우
3) Github Pages 사이트가 배포된 경우
4) 새로운 멤버가 팀에 합류한 경우
우리 팀은 pr을 올렸을 때 어떤 workflow가 일어나길 원하기 때문에, Webhook event는 pull request
가 됩니다. 따라서 Github Workflow Trigger에 대한 Github Docs를 참고하여 구성해봤습니다.
frontend-pr-deadline-slack-bot.yml
로 Github Actions을 트리거한 로직은 아래와 같습니다.
name: Notify Pull Request Deadline (FE)
on:
pull_request:
types:
- opened // open된 pr인 경우
branches: ['dev'] // 다른 브랜치에서 dev 브랜치를 향하는 경우
paths:
- 'frontend/**' // frontend 폴더에서 변경사항이 일어난 경우
jobs:
pull_request_open:
runs-on: ubuntu-latest
name: New pr to repo
steps:
- name: Checkout code
uses: actions/checkout@v2 // Github repo에 올려둔 코드를 CI 서버로 내려받은 후에 특정 branch로 전환하는 행위
- name: Set environment variable // github 환경변수 세팅
run: echo "PR_CREATED_AT_UTC=${{ github.event.pull_request.created_at }}" >> $GITHUB_ENV // .env 파일에 환경변수 생성
- name: Convert UTC to KST // pr 생성 시간 포맷을 UTC에서 KST(한국 기준)로 변경
run: |
UTC_TIME=$PR_CREATED_AT_UTC
KST_TIME=$(date -u -d "$UTC_TIME 9 hour" "+%Y-%m-%dT%H:%M:%SZ")
echo "PR_CREATED_AT_KST=$KST_TIME" >> $GITHUB_ENV // .env 파일에 환경변수 생성
이제 PR 생성 시간(한국 기준)을 가지고 코드리뷰 마감시간을 계산해봅시다. 들어가며
에서 설명했듯이 우리 팀만의 컨벤션을 적용해서 복잡한 로직을 구성하려면,, 대체 yml 파일만으로 어떻게 구현해야 하지??
걱정이 많았으나.... 역시나 yml 문법만으로 복잡한 수학적 계산을 하는 것은 살짝 한계가 있었습니다.
마감시간 계산하는 로직은 javascript로 하고, 계산된 결과값만 yml 파일에서 가져와서 활용해보자!
🔽calculatePRDeadline.js
function calculatePRDeadline(prCreatedAtKST) {
const prCreatedAt = new Date(String(prCreatedAtKST));
const prCreatedMinute = prCreatedAt.getUTCMinutes();
const prCreatedHour = prCreatedAt.getUTCHours();
const prCreatedDate = prCreatedAt.getUTCDate();
const prCreatedDay = prCreatedAt.getUTCDay();
const prCreatedMonth = prCreatedAt.getUTCMonth() + 1; // getUTCMonth()는 0부터 시작하므로 1을 더해줍니다.
const isFridayAfternoon = prCreatedDay === 5 && prCreatedHour >= 22; // 금요일 오후 10시 이후 (금요일: 5, 오후 12시: 12)
const isWeekend = prCreatedDay === 6 || prCreatedDay === 0; // 주말인 경우
const isMondayMorning = prCreatedDay === 1 && prCreatedHour < 10; // 월요일 오전 10시 이전 (월요일: 1, 오전 10시: 10)
// 주어진 근무시간(월요일 오전 10시~금요일 오후 10시) 내에 올린 pr인지 판별
const isNotWorkingTime = isFridayAfternoon || isWeekend || isMondayMorning;
let nextDay = new Date(prCreatedAt);
nextDay.setUTCDate(prCreatedDate + 1); // 다음 날의 날짜를 설정합니다.
const nextDayDate = nextDay.getUTCDate();
const nextDayMonth = nextDay.getUTCMonth() + 1;
let nextWeekMonday = new Date(prCreatedAt);
const daysUntilMonday = 8 - prCreatedDay;
nextWeekMonday.setUTCDate(
prCreatedDay === 0 ? prCreatedDate + 1 : prCreatedDate + daysUntilMonday
);
const nextWeekMondayDate = nextWeekMonday.getUTCDate();
const nextWeekMondayMonth = nextWeekMonday.getUTCMonth() + 1;
const isFriday = prCreatedDay === 5;
if (isNotWorkingTime)
return `${nextWeekMondayMonth}월 ${nextWeekMondayDate}일 20시 00분`;
if (prCreatedHour < 10 && prCreatedHour > 0)
return `${prCreatedMonth}월 ${prCreatedDate}일 20시 00분`;
else if (prCreatedHour === 22 || prCreatedHour === 23)
return `${nextDayMonth}월 ${nextDayDate}일 20시 00분`;
else if (prCreatedHour >= 12)
return `${isFriday ? nextWeekMondayMonth : nextDayMonth}월 ${
isFriday ? nextWeekMondayDate : nextDayDate
}일 ${prCreatedHour - 2}시 ${prCreatedMinute}분`;
else
return `${isFriday ? nextWeekMondayMonth : prCreatedMonth}월 ${
isFriday ? nextWeekMondayDate : prCreatedDate
}일 ${prCreatedHour + 10}시 ${prCreatedMinute}분`;
}
console.log(
`::set-output name=DEADLINE::${calculatePRDeadline(
process.env.PR_CREATED_AT_KST
)}`
); // yml 파일에서 github env로 생성한 환경변수
이제 calculatePRDeadline이 return 하는 마감시간 값을, yml 파일에서 가져와 사용하려면 어떻게 해야 할까요?
마지막에 console.log
로 DEADLINE 이라는 key 값에, 마감시간 value를 담아서 출력하면 됩니다.
또 yml 파일에 아래의 코드를 추가해줍니다.
- name: Calculate deadline
id: deadline
run: node .github/workflows/scripts/calculatePRDeadline.js
env:
PR_CREATED_AT_KST: ${{ env.PR_CREATED_AT_KST }}
Slack App(Bot) 생성하기
Slack App(Bot)을 생성하고 보투게더 워크스페이스에 설치, 특정 채널에 초대해줍니다.
슬랙봇을 워크스페이스에 성공적으로 추가했으면, 마지막으로 yml에 아래의 코드를 추가해줍니다.
- name: Send Slack notification When FE PR
uses: slackapi/slack-github-action@v1.24.0 // slack api 를 이용하기
with:
channel-id: ${{ secrets.SLACK_FE_CHANNEL }} # Slack 채널 ID
payload: |
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<${{ steps.remove_https.outputs.pr_url }}|${{ github.event.pull_request.title }}>\n코드리뷰 마감시간: ${{ steps.deadline.outputs.DEADLINE }}"
}
}
]
} // id: deadline 에서 calculatePRDeadline.js가 출력하는 DEADLINE 값 가져오기
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} # Github Secret에 설정한 Slack 토큰 값
slack-github-actions
API에 대한 자세한 설명 및 공식 문서는 요 repo에서 확인하실 수 있습니다😀
github repo 에서 Settings-Secrets-Actions secrets and variables 로 들어가보면, New Repository Secret
이라는 버튼이 보입니다.
이 버튼을 눌러 새로운 환경변수를 추가해줍시다.
채널 ID는 어떻게 알 수 있나요?
▶ 브라우저로 슬랙 워크스페이스 들어가면 url의 마지막 파라미터가 해당 슬랙 채널 ID 값입니다!
보투게더 팀은
1. 프론트엔드 코드리뷰 채널 ID (SLACK_FE_CHANNEL),
2. 백엔드 코드리뷰 채널 ID (SLACK_BE_CHANNEL),
3. 슬랙봇 토큰 (슬랙 홈페이지에서 Copy한 Bot Token, SLACK_TOKEN)
이렇게 3가지의 Github Secret을 추가해주었습니다!
그러면 아래와 같이 추가해준 환경변수 목록을 볼 수 있습니다~
이제 yml 파일 전체 코드를 살펴봅시다.
name: Notify Pull Request Deadline (FE)
on:
pull_request:
types:
- opened
branches: ['dev']
paths:
- 'frontend/**'
jobs:
pull_request_open:
runs-on: ubuntu-latest
name: New pr to repo
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Remove "https://" from PR URL
id: remove_https
run: |
PR_URL="${{ github.event.pull_request.html_url }}"
PR_URL="http://${PR_URL#https://}"
echo "::set-output name=pr_url::$PR_URL"
- name: Set environment variable
run: echo "PR_CREATED_AT_UTC=${{ github.event.pull_request.created_at }}" >> $GITHUB_ENV
- name: Convert UTC to KST
run: |
UTC_TIME=$PR_CREATED_AT_UTC
KST_TIME=$(date -u -d "$UTC_TIME 9 hour" "+%Y-%m-%dT%H:%M:%SZ")
echo "PR_CREATED_AT_KST=$KST_TIME" >> $GITHUB_ENV
- name: Calculate deadline
id: deadline
run: node .github/workflows/scripts/calculatePRDeadline.js
env:
PR_CREATED_AT_KST: ${{ env.PR_CREATED_AT_KST }}
- name: Send Slack notification When FE PR
uses: slackapi/slack-github-action@v1.24.0
with:
channel-id: ${{ secrets.SLACK_FE_CHANNEL }} # Slack 채널 ID
payload: |
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<${{ steps.remove_https.outputs.pr_url }}|${{ github.event.pull_request.title }}>\n코드리뷰 마감시간: ${{ steps.deadline.outputs.DEADLINE }}"
}
}
]
}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} # Slack 토큰
위 코드에서 별도로 추가한 코드가 있는데요,
- name: Remove "https://" from PR URL
id: remove_https
run: |
PR_URL="${{ github.event.pull_request.html_url }}"
PR_URL="http://${PR_URL#https://}"
echo "::set-output name=pr_url::$PR_URL"
PR의 url을 https
를 http
로 바꿔주는 코드입니다. 슬랙에서는 링크를 치면 링크에 대한 미리보기가 아래처럼 뜨는데, 링크가 공간을 많이 잡아먹어서 메시지를 찾기가 힘들더라구요.. 그런데 링크가 http로 시작하는 경우에는 미리보기가 생기지 않아서, 일부러 http로 바꿔주었습니다!👍
repo에서 Actions 탭을 눌러서 Worflow(Webhook event)가 잘 trigger되는지 확인할 수 있습니다.
지금까지는 슬랙 스레드에 리뷰 완!
또는 피드백 반영했습니다~
등의 댓글을 남겼었는데요, 스레드를 찾아서 위로 스크롤하기 불편하다고 느꼈습니다.
pr에 코멘트만 남기면, 자동으로 슬랙에 알림에 가도록 하면 어떨까? 라는 생각이 들었고 코멘트 봇 만들기 라는 글을 참고하여 아래와 같이 yml 파일을 작성했습니다.
frontend-pr-comment.yml
name: Pull request comment (FE)
on:
issue_comment:
types: [created, edited, deleted]
jobs:
pull_request_comment:
# This job only runs for pull request comments
if: ${{ github.event.issue.pull_request }}
runs-on: ubuntu-latest
steps:
- name: Send Slack notification When Review Completed
if: contains(github.event.comment.body, 'review-complete') # check the comment if it contains the keywords
uses: slackapi/slack-github-action@v1.24.0
with:
channel-id: ${{ secrets.SLACK_FE_CHANNEL }} # Slack 채널 ID
payload: |
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "리뷰 완료했습니다👍\n<${{ github.event.comment.html_url }}|리뷰어의 코멘트 확인하러 가기>"
}
}
]
}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} # Slack 토큰
- name: Send Slack notification When Re-review Requested
if: contains(github.event.comment.body, 'review-request') # check the comment if it contains the keywords
uses: slackapi/slack-github-action@v1.24.0
with:
channel-id: ${{ secrets.SLACK_FE_CHANNEL }} # Slack 채널 ID
payload: |
{
"text": "",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "리뷰 반영 최종 완료!✅ 확인 부탁드립니다😃\n<${{ github.event.comment.html_url }}|피드백 반영 확인하러 가기>"
}
}
]
}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} # Slack 토큰
issue_comment
라는 Webhook event를 활용해서 worflow를 trigger 하는 로직인데요, 아래 이미지처럼 pr에 review-complete
라는 댓글을 달아보면...
Actions 탭에서 workflow가 잘 일어나고 있네요👍👍
github와 slack API 를 활용하여 슬랙 알림 자동화를 구현해봤는데요! 코드리뷰 효율을 올릴 수 있어 정말 뿌듯하네요😆 슬랙봇 구현하면서 평소에 미루던 github actions 공부도 제대로 할 수 있었고, yml 문법도 배워볼 수 있었습니다👍
하나의 yml 파일에서, frontend, backend 폴더의 변경 사항에 따라 분기를 나눠서 슬랙 알림을 보내보려고 시도했는데, yml 문법에 익숙하지 않아 if를 활용해서 분기를 나누는 것에 실패했습니다..😂
따라서 frontend-pr-comment.yml
, backend-pr-comment.yml
이렇게 2개의 파일로 나눴는데요, 하나의 파일에서도 분기 처리할 수 있는 방법을 좀 더 알아보면 좋겠네요🤔😀 (혹시 아시는 분이 계신다면 댓글 달아주시면 감사하겠습니다!!)
https://github.com/woowacourse-teams/2023-votogether/tree/dev/.github/workflows
최고에요!