서론
저희 회사의 개발팀은 Git에 코드를 푸시하고 사내 메신저인 네이버 웍스를 통해 공유를 합니다. 이 과정에서 많은 귀찮음을 느끼기도 하고, 누락이 되는 등 문제가 있다고 생각하였습니다. 이러한 비효율적인 업무 프로세스를 개선시키고자 GitHub Action과 네이버 웍스 API를 연동해 Git 푸시 시 자동으로 메시지를 보내는 웹훅을 구현했습니다.
네이버 웍스 API 준비
네이버 웍스 API를 사용하기 위해서는 어드민 계정 혹은 개발자 권한이 있는 계정이 필요합니다. 이 글에서는 어드민 계정을 이용해 진행합니다.
앱 추가
1. https://dev.worksmobile.com/kr/console/openapi/v2/app/list/view
2. API 2.0 > 앱 추가
3. OAuth Scopes > 관리 > 아래 3개 항목 선택 후 저장
- bot
- bot.message
- bot.read
4. Service Account > 발급
5. Private Key > 발행/재발급
6. Client ID, Client Secret, Service Account, Private Key 저장
Bot 추가
1. Bot > 등록하기
단체 채팅방에 봇을 사용하려면 Bot 정책 - 조직/그룹, 1:N 메시지방 초대 가능을 체크해야 합니다.
Bot 연동
1. https://admin.worksmobile.com/service/bot
2. 우측 Bot 추가 클릭
3. 위에서 생성한 Bot 체크 후 Bot 추가 클릭
4. 1번 페이지로 이동 후 추가한 봇 클릭 > 수정
5. 공개 설정 활성화 후 저장
6. Bot No. 저장
API를 사용해 메세지를 보내려면 아래의 Bot No. 가 필요합니다.
채팅방에 Bot 추가
Python 코드
import base64
from Crypto.PublicKey import RSA
from Crypto.Util import asn1
import datetime
import jwt
from datetime import datetime, timedelta
from urllib.parse import quote
import requests
import os
# 네이버웍스 API PRIVATE KEY
PRIVATE_KEY = ""
# 네이버 웍스 채널 ID
CHANNEL_ID = ""
# 네이버 웍스 봇 ID
BOT_ID = ""
# 네이버 웍스 Client ID
CLIENT_ID = ""
# 네이버 웍스 Client Secret
CLIENT_SECRET = ""
# 네이버 웍스 API 서버 토큰 발급
def get_server_token():
headers = {
"alg": "RS256",
"typ": "JWT"
}
iat = datetime.utcnow()
exp = iat + timedelta(minutes=10)
assertion = jwt.encode({
"iss": "cv821ebW6avTOS7tzMcf",
"sub": "y3fln.serviceaccount@potatowoong.by-works.com",
"iat": iat,
"exp": exp
}, PRIVATE_KEY, algorithm="RS256", headers=headers)
return assertion
# 네이버 웍스 API ACCESS TOKEN 발급
def get_access_token():
access_token_data = {'assertion': get_server_token(),
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'scope': 'bot'}
req = requests.post('https://auth.worksmobile.com/oauth2/v2.0/token', data=access_token_data)
return req.json().get('access_token')
# 커밋 메세지 생성
def get_commit_message(arr):
message = [{
"type": "text",
"text": arr[0],
"weight": "bold",
"size": "md",
"align": "start",
"wrap": True
}, ] # 커밋 설명
if len(arr) > 1:
for description in arr[2:]:
message.append({
"type": "text",
"text": description,
"weight": "regular",
"size": "xs",
"align": "start",
"margin": "sm",
"wrap": True
})
return message
# 네이버 웍스 채널 메세지 생성
def get_chat_message(repository, commit_summarize, commit_message, branch_name, pushed_by, commit_sha, commit_link):
return {
"content": {
"type": "flex",
"altText": f'[{repository}]\n {commit_summarize}',
"contents": {
"type": "carousel",
"contents": [
{
"type": "bubble",
"size": "mega",
"header": {
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": repository,
"size": "lg",
"color": "#00c73c",
"weight": "bold"
}
],
"backgroundColor": "#ffffff"
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "vertical",
"contents": commit_message,
"margin": "xl"
},
{
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": "Branch",
"size": "sm",
"color": "#858f89",
"flex": 3,
"margin": "md"
},
{
"type": "text",
"text": branch_name.split('/')[-1],
"size": "sm",
"margin": "md",
"flex": 5
}
]
},
{
"type": "box",
"layout": "baseline",
"contents": [
{
"type": "text",
"text": "Author",
"size": "sm",
"color": "#858f89",
"flex": 3,
"margin": "md"
},
{
"type": "text",
"text": pushed_by,
"size": "sm",
"margin": "md",
"flex": 5
}
],
"margin": "sm"
},
{
"layout": "baseline",
"type": "box",
"contents": [
{
"text": "Commit",
"type": "text",
"flex": 3,
"margin": "md",
"size": "sm",
"color": "#858f89"
},
{
"text": commit_sha,
"action": {
"type": "uri",
"label": "",
"uri": commit_link,
},
"type": "text",
"flex": 5,
"margin": "md",
"size": "sm",
"decoration": "underline",
}
],
"margin": "sm"
}
],
"margin": "xxl",
"width": "260px",
"alignItems": "center",
"paddingAll": "6px"
}
]
}
}
]
}
}
}
if __name__ == '__main__':
access_token = get_access_token() # ACCESS TOKEN
branch_name = os.environ['BRANCH_NAME'] # 브랜치 이름
commit_arr = os.environ['COMMIT_MESSAGE'].split('\n') # 커밋 배열
commit_message = get_commit_message(commit_arr) # 커밋 메세지
pushed_by = os.environ['PUSHED_BY'] # 푸시한 사람
repository = os.environ['REPOSITORY'].split('/')[-1] # 레포지토리 이름
commit_sha = os.environ['COMMIT_SHA'][: 6] # 커밋 SHA
commit_link = os.environ['COMMIT_LINK'] # 커밋 링크
chat_headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {access_token}'
}
chat_data = get_chat_message(repository, commit_arr[0], commit_message, branch_name.split('/')[-1], pushed_by,
commit_sha, commit_link)
# 메세지 전송
requests.post(f'https://www.worksapis.com/v1.0/bots/{BOT_ID}/channels/{CHANNEL_ID}/messages', headers=chat_headers, json=chat_data)
위 파이썬 코드를 작성해 Git에 올립니다. GitHub Action에서 절대 경로를 이용해 접근 가능하기 때문에 원하는 위치에 올려줍니다.
코드 설명
get_server_token() : 네이버 웍스 API 사용을 위한 JWT Token을 발행받는 함수입니다.
get_access_token() : 발급받은 JWT Token을 이용해 Access Token을 발행받는 함수입니다.
get_commit_message() : commit summary와 commit description을 이용해 커밋 메시지를 획득하는 함수입니다.
get_chat_message() : 원하는 형식의 채팅 메세지를 만듭니다.
os.environ : github-action에서 획득한 환경변수를 가져옵니다.
github-actions.yml
name: github-actions # Workflow의 이름
on: # 이벤트 트리거 설정
push: # push 이벤트 발생 시
branches: # 다음 브랜치에 대해
- main # main 브랜치
- develop # develop 브랜치
jobs: # Job 정의
build: # Job 이름
runs-on: ubuntu-latest # Job이 실행될 환경
steps: # Job이 실행될 단계들
- name: Check out repository
uses: actions/checkout@v2 # GitHub 액션 사용 (저장소 체크아웃)
- name : Cache Python
uses: actions/cache@v2 # GitHub 액션 사용 (캐싱)
with: # 입력 매개변수 설정
path: ~/.local # 캐싱할 경로
key: ${{ runner.os }}-python-${{ hashFiles('**/*.py') }} # 캐싱 키 설정
- name: Cache Python dependencies
uses: actions/cache@v2 # GitHub 액션 사용 (캐싱)
with: # 입력 매개변수 설정
path: ~/.cache/pip # 캐싱할 경로
key: ${{ runner.os }}-pip-${{ hashFiles('**/*.lock') }} # 캐싱 키 설정
restore-keys: | # 복원 키 설정
${{ runner.os }}-pip- # 캐싱 복원 키
- name: Set up Python
uses: actions/setup-python@v2 # GitHub 액션 사용 (Python 설정)
with: # 입력 매개변수 설정
python-version: '3.9' # Python 버전 설정
- name: Install dependencies
run: | # pip 업그레이드, 종속성 설치
python -m pip install --upgrade pip
pip install pycryptodome cryptography requests pyjwt
- name: Run Python script
run: python main.py # Python 스크립트 실행
env: # 환경 변수 설정
BRANCH_NAME: ${{ github.ref }} # 브랜치 이름
COMMIT_MESSAGE: ${{ github.event.head_commit.message }} # 커밋 메시지
PUSHED_BY : ${{ github.actor }} # 푸시한 사용자
REPOSITORY: ${{ github.repository }} # 저장소 이름
COMMIT_SHA: ${{ github.sha }} # 커밋 해시
COMMIT_LINK: https://github.com/${{ github.repository }}/commit/${{ github.sha }} # 커밋 링크
위 github-actions.yml 파일을 .github/workflows 경로에 넣어줍니다.
메시지 봇 결과
개발 후 코드를 Git에 푸시하면 아래와 같은 형식으로 메시지가 옵니다.