기존에는 PR이 열리면, 바로 slack 채널에 리뷰 부탁 메세지를 남겼습니다. 팀원들이 리뷰했는지는 이모지나 댓글로 확인하였습니다.
매번 PR 생성할 때마다 메세지 작성하는 것이 귀찮아서 자동화하기로 했습니다.
자동화 할 작업은 크게 2가지 입니다.
slack webhook은 DM이 보내지지 않아, slack bot을 사용하였습니다.
https://api.slack.com/apps/ 에 들어가서 create new app을 눌러주세요.
버튼을 클릭하면 create an app 모달이 뜹니다. 2개의 선택지 중에서 from scratch를 눌러주세요.
원하는 slack bot 이름과 slack bot을 적용할 workspace를 입력해주세요.
create app을 누르면 한 페이지에 접속하게 되는데요. 왼쪽 사이드 바에 OAuth & Permissions 메뉴를 클릭해주세요.
이제, 슬랙봇이 어떤 동작을 할 지 권한을 정해야 합니다.
Scopes의 Bot Token Scopes에서 아래 그림과 같이 scope를 허용해주세요.
repository의 탭에서 settings > secrets and variables > actions > Repository secrets에서 secrets를 추가해주세요.
{
"github id": {
"name": "실제 이름",
"slackId": "Uxxxxx"
}
}
예를 들어 홍길동의 github id가 "hong"이고, slack id가 "U1234"라면, MEMBER_INFO_MAP은 아래 json 파일을 base64로 인코딩한 값입니다.
{
"hong": {
"name": "홍길동",
"slackId": "U1234"
}
}
위 json 파일을 인코딩하면 아래와 같은 값이 나오고, 이 값을 MEMBER_INFO_MAP에 저장합니다.
ewogICJob25nIjogewogIAkibmFtZSI6ICLtmY3quLjrj5kiLAogICAgInNsYWNrSWQiOiAiVTEyMzQiCiAgfQp9
github actions는 pr open, 리뷰와 같은 github 이벤트를 쉽게 감지할 수 있어서 github actions로 자동화하였습니다.
제가 원하는 flow는 "PR이 열리면, slack 채널에 리뷰 부탁 메세지가 전송된다."입니다.
메세지는 아래 사진처럼 go를 누르면 PR 창이 뜨고, PR의 타이틀과 작성자를 표시하고, 리뷰어들을 멘션합니다.
전체 코드
name: Notify Slack When PR Is Opened
on:
pull_request:
types:
- opened
- reopened
branches:
- develop
jobs:
notify-slack:
runs-on: ubuntu-latest
steps:
- name: Decode memberMap.json from secret
run: |
echo "${{ secrets.MEMBER_INFO_MAP }}" | base64 --decode > memberMap.json
- name: Get PR author and reviewers
id: get_member_info
run: |
# PR 작성자 이름 추출
PR_AUTHOR_GITHUB_ID="${{ github.event.pull_request.user.login }}"
PR_AUTHOR_NAME=$(jq -r --arg id "$PR_AUTHOR_GITHUB_ID" '.[$id].name' memberMap.json)
echo "pr_author_name=$PR_AUTHOR_NAME" >> $GITHUB_OUTPUT
# PR 작성자 제외한 멤버들의 slackId를 멘션으로 모으기
MENTIONS=$(jq -r --arg author "$PR_AUTHOR_GITHUB_ID" 'to_entries | map(select(.key != $author)) | map("<@" + .value.slackId + ">") | join(" ")' memberMap.json)
echo "reviewers_mention=$MENTIONS" >> $GITHUB_OUTPUT
- name: Send Slack message
uses: slackapi/slack-github-action@v2.1.0
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel: ${{ secrets.SLACK_FRONT_CHANNEL_ID }}
text: "리뷰하러 가기 > <${{ github.event.pull_request.html_url }}|go>\n- PR 타이틀: ${{ github.event.pull_request.title }}\n- PR 작성자: ${{ steps.get_member_info.outputs.pr_author_name }}\n- 리뷰어: ${{ steps.get_member_info.outputs.reviewers_mention }}"
unfurl_links: false
unfurl_media: false
하나씩 살펴보겠습니다.
on:
pull_request:
types:
- opened
- reopened
branches:
- develop
jobs:
notify-slack:
runs-on: ubuntu-latest
steps:
steps를 하나씩 살펴보겠습니다.
- name: Decode memberMap.json from secret
run: |
echo "${{ secrets.MEMBER_INFO_MAP }}" | base64 --decode > memberMap.json
우선 MEMBER_INFO_MAP에 저장된 값을 base64로 디코딩하여 memberMap.json 파일에 저장합니다.
- name: Get PR author and reviewers
id: get_member_info
run: |
PR_AUTHOR_GITHUB_ID="${{ github.event.pull_request.user.login }}"
PR_AUTHOR_NAME=$(jq -r --arg id "$PR_AUTHOR_GITHUB_ID" '.[$id].name' memberMap.json)
echo "pr_author_name=$PR_AUTHOR_NAME" >> $GITHUB_OUTPUT
# PR 작성자 제외한 멤버들의 slackId를 멘션으로 모으기
MENTIONS=$(jq -r --arg author "$PR_AUTHOR_GITHUB_ID" 'to_entries | map(select(.key != $author)) | map("<@" + .value.slackId + ">") | join(" ")' memberMap.json)
echo "reviewers_mention=$MENTIONS" >> $GITHUB_OUTPUT
<@U1234>
로 입력하면, 실제 slack 메세지에서 @FE_홍길동
으로 slack 이름으로 멘션됩니다. - name: Send Slack message
uses: slackapi/slack-github-action@v2.1.0
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel: ${{ secrets.SLACK_FRONT_CHANNEL_ID }}
text: "리뷰하러 가기 > <${{ github.event.pull_request.html_url }}|go>\n- PR 타이틀: ${{ github.event.pull_request.title }}\n- PR 작성자: ${{ steps.get_member_info.outputs.pr_author_name }}\n- 리뷰어: ${{ steps.get_member_info.outputs.reviewers_mention }}"
unfurl_links: false
unfurl_media: false
workflow까지 설정했으면, 슬랙봇을 채널에 초대해야 합니다.
채널 상단에 해드셋 아이콘 왼쪽의 버튼을 클릭해주세요.
그럼 모달이 나타납니다.
여기서 통합 탭 클릭 > 앱 추가 클릭하여 만든 slack bot을 추가해주세요.
채널에 슬랙봇을 추가했다면, 이제 PR이 열릴 때마다 slack 채널에 메세지가 아래처럼 전송됩니다.
리뷰어가 PR에 리뷰들 달았으면, 슬랙봇이 개인 DM을 보내도록 하고 싶었습니다.
전체 코드
name: Notify PR Review to Author
on:
pull_request_review:
types: [submitted]
jobs:
notify-author:
runs-on: ubuntu-latest
steps:
- name: Extract Slack IDs and DM Text
id: extract_info
env:
MEMBER_INFO: ${{ secrets.MEMBER_INFO_MAP }}
run: |
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
REVIEWER="${{ github.event.review.user.login }}"
REVIEW_STATE="${{ github.event.review.state }}"
PR_TITLE="${{ github.event.pull_request.title }}"
PR_URL="${{ github.event.pull_request.html_url }}"
# PR 작성자와 리뷰어가 같은 경우, 스킵 표시
if [ "$PR_AUTHOR" = "$REVIEWER" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
exit 0
fi
echo "$MEMBER_INFO" | base64 --decode > memberMap.json
AUTHOR_SLACK_ID=$(jq -r --arg id "$PR_AUTHOR" '.[$id].slackId' memberMap.json)
REVIEWER_SLACK_ID=$(jq -r --arg id "$REVIEWER" '.[$id].slackId' memberMap.json)
if [ -z "$AUTHOR_SLACK_ID" ] || [ -z "$REVIEWER_SLACK_ID" ] || \
[ "$AUTHOR_SLACK_ID" = "null" ] || [ "$REVIEWER_SLACK_ID" = "null" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
exit 0
fi
if [ "$REVIEW_STATE" = "approved" ]; then
TEXT="<@$REVIEWER_SLACK_ID>님이 \"<$PR_URL|$PR_TITLE>\" PR를 approve 하셨습니다!"
elif [ "$REVIEW_STATE" = "changes_requested" ]; then
TEXT="<@$REVIEWER_SLACK_ID>님이 \"<$PR_URL|$PR_TITLE>\"에 변경을 요청하셨습니다!"
else
TEXT="<@$REVIEWER_SLACK_ID>님이 \"<$PR_URL|$PR_TITLE>\"에 코멘트를 남기셨습니다!"
fi
echo "author_slack_id=$AUTHOR_SLACK_ID" >> $GITHUB_OUTPUT
echo "text=$TEXT" >> $GITHUB_OUTPUT
- name: Send Slack DM to PR Author
if: steps.extract_info.outputs.skip != 'true'
uses: slackapi/slack-github-action@v2.1.0
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel: ${{ steps.extract_info.outputs.author_slack_id }}
text: ${{ steps.extract_info.outputs.text }}
PR 리뷰가 submit 되었을 때, workflow가 실행됩니다.
on:
pull_request_review:
types: [submitted]
Extract Slack IDs and DM Text
step을 자세히 살펴보도록 하겠습니다.
우선, 첫 번째로 이벤트에서 제공하는 정보들을 변수에 저장합니다.
run: |
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
REVIEWER="${{ github.event.review.user.login }}"
REVIEW_STATE="${{ github.event.review.state }}"
PR_TITLE="${{ github.event.pull_request.title }}"
PR_URL="${{ github.event.pull_request.html_url }}"
PR 작성자와 리뷰어가 동일할 경우, DM을 보내지 않도록 조건문을 작성해뒀습니다.
# PR 작성자와 리뷰어가 같은 경우, 스킵 표시
if [ "$PR_AUTHOR" = "$REVIEWER" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
exit 0
fi
secrets에 저장되어 있는 멤버 정보를 memberMap.json 파일에 저장하여 PR 작성자와 리뷰어의 slack id를 찾습니다.
만약 찾지 못했다면, DM을 보내지 않도록 합니다.
echo "$MEMBER_INFO" | base64 --decode > memberMap.json
AUTHOR_SLACK_ID=$(jq -r --arg id "$PR_AUTHOR" '.[$id].slackId' memberMap.json)
REVIEWER_SLACK_ID=$(jq -r --arg id "$REVIEWER" '.[$id].slackId' memberMap.json)
if [ -z "$AUTHOR_SLACK_ID" ] || [ -z "$REVIEWER_SLACK_ID" ] || \
[ "$AUTHOR_SLACK_ID" = "null" ] || [ "$REVIEWER_SLACK_ID" = "null" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
exit 0
fi
PR 작성자에게 보낼 DM 내용을 TEXT에 저장합니다.
<@PR작성자>님이 "PR 타이틀" PR를 approve 하셨습니다!
<@PR작성자>님이 "PR 타이틀" 변경을 요청하셨습니다!
<@PR작성자>님이 "PR 타이틀" 변경을 코멘트를 남기셨습니다!
if [ "$REVIEW_STATE" = "approved" ]; then
TEXT="<@$REVIEWER_SLACK_ID>님이 \"<$PR_URL|$PR_TITLE>\" PR를 approve 하셨습니다!"
elif [ "$REVIEW_STATE" = "changes_requested" ]; then
TEXT="<@$REVIEWER_SLACK_ID>님이 \"<$PR_URL|$PR_TITLE>\"에 변경을 요청하셨습니다!"
else
TEXT="<@$REVIEWER_SLACK_ID>님이 \"<$PR_URL|$PR_TITLE>\"에 코멘트를 남기셨습니다!"
fi
echo "author_slack_id=$AUTHOR_SLACK_ID" >> $GITHUB_OUTPUT
echo "text=$TEXT" >> $GITHUB_OUTPUT
text와 author slack id를 다음 step에서 쓰기 위해 $GITHUB_OUTPUT
을 사용합니다.
echo "author_slack_id=$AUTHOR_SLACK_ID" >> $GITHUB_OUTPUT
echo "text=$TEXT" >> $GITHUB_OUTPUT
첫 번째 step에서 id를 작성하고, $GITHUB_OUTPUT
으로 다음 step에서 사용할 변수를 내보냈습니다.
id: extract_info
// ...
echo "author_slack_id=$AUTHOR_SLACK_ID" >> $GITHUB_OUTPUT
echo "text=$TEXT" >> $GITHUB_OUTPUT
만약 다음 step에서 이 변수들을 참조하고 싶다면, steps.extract_info.outputs.변수_이름
으로 쓰시면 됩니다.
이를 이용하여, skip 변수가 true이면 이 step을 진행하지 않습니다.
false라면 slackapi/slack-github-action
을 통해 PR 작성자에게 DM을 보내도록 요청합니다.
if: steps.extract_info.outputs.skip != 'true'
uses: slackapi/slack-github-action@v2.1.0
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel: ${{ steps.extract_info.outputs.author_slack_id }}
text: ${{ steps.extract_info.outputs.text }}
설정하면, PR에 리뷰가 달릴 때마다 PR 작성자에게 DM이 전송됩니다.
https://github.com/naver/notify-pr-review
https://github.com/marketplace/actions/pr-message-to-slack
이미 slack 코드리뷰 자동화한 분들이 많으셨습니다. 위 링크 말고도 많으니 여러분의 상황에 맞게 써주시면 될 것 같습니다!