깃허브에 남은 PR을 공휴일이 아닌 월-금 동안 매일 오전 9시에 자동으로 슬랙으로 알려주는 봇을 만들고 싶었습니다.
슬랙봇을 만듭니다.
권한은 이렇게 두개 넣었는데 chat:write만 넣어도 되는 것 같습니다.
위에 참고 블로그에는 call:write 권한을 넣으라 되어있는데 chat:write권한이 있어야 합니다.
깃허브 토큰을 얻어옵니다.
저는 토큰 종류는 클래식 버전으로 만들었고 권한은 repo, user만 체크해줬습니다.
아까 만든 슬랙봇을 슬랙에 추가합니다.
슬랙 하단에 '앱'부분에 '앱 추가'를 눌러서 아까 만든 봇을 추가합니다.
봇 이름 우클릭 > 세부정보 보기 > 이앱을 채널에 추가를 클릭해 원하는 채널에 봇을 추가합니다.
슬랙 채널 아이디를 얻습니다. 하단에 앱 채널 아이디가 아니라 봇이 추가되길 원하는 채널의 아이디 입니다.
채널 우클릭 > 채널 세부정보 보기 > 하단에 채널 ID를 확인합니다.
컴퓨터 아무폴더에 해당 파이썬 파일을 만듭니다.
#공휴일 체크 해서 공휴일 아닌 경우에만 슬랙봇 실행하는 파일
from datetime import datetime, timedelta, timezone
import requests
import json
from pandas import json_normalize
from github import Github
class KoreaHolidays:
def __init__(self):
pass
def get_holidays(self):
today_year = datetime.today().year
KEY = "3번에서 얻은 공공데이터포털 토큰"
url = (
"http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getRestDeInfo?_type=json&numOfRows=50&solYear="
+ str(today_year)
+ "&ServiceKey="
+ str(KEY)
)
response = requests.get(url)
if response.status_code == 200:
json_ob = json.loads(response.text)
holidays_data = json_ob["response"]["body"]["items"]["item"]
dataframe = json_normalize(holidays_data)
return dataframe["locdate"].to_list()
else:
return []
def today_is_holiday(self):
_today = datetime.now().strftime("%Y%m%d")
holidays = self.get_holidays()
is_holiday = False
if int(_today) in holidays:
is_holiday = True
return is_holiday
def _get_total_pull_requests(repo):
count = 0
pull_requests_list = []
# 현재 열려있는 PR 목록들을 가져온다.
for pull in repo.get_pulls(state="opened", sort="updated"):
pr_comments_count = pull.review_comments
if pr_comments_count != 0:
# 리뷰가 진행중인 PR인 경우는 목록에서 제외
pass
else:
count += 1
pull_requests_list.append((repo.name, pull))
return count, pull_requests_list
def _send_slack(msg: str):
response = requests.post(
"https://slack.com/api/chat.postMessage",
headers={"Authorization": "Bearer 1번에서 얻은 슬랙봇 토큰"},
data={"channel": "5번에서 얻은 채널아이디", "text": msg},
)
### print(response.json()) # Slack API 응답 출력해서 확인해보기
def _make_pr_link_with_no(repo_name: str, pr_no: int) -> str:
link = f"https://github.com/{repo_name}/pull/{pr_no}"
return link
def set_pull_requests_tags():
g = Github("2번에서 얻은 깃헙 토큰")
repos = [repo for repo in g.get_user().get_repos()]
total_cnt = 0
total_pulls = []
for repo in repos: # 모든 저장소에 대해 실행
cnt, pulls = _get_total_pull_requests(repo)
total_cnt += cnt
total_pulls.extend(pulls)
# 모든 저장소의 PR을 집계한 후 메시지를 생성
pr_msg_to_slack = (
f"<!here> 👋🏻 총 {total_cnt}개의 Pull Request가 리뷰를 기다리고 있어요! :eyes:\n"
)
today = datetime.now(timezone.utc)
for repo_name, pull in total_pulls:
pr_link = _make_pr_link_with_no(repo_name, pull.number)
pr_title = pull.title
pr_created_at = pull.created_at
days_diff = (today - pr_created_at).days
if days_diff >= 2:
pr_msg_to_slack += f"🚨"
pr_msg_to_slack += f"D+{days_diff} [{repo_name}] {pr_title}: {pr_link}\n"
_send_slack(pr_msg_to_slack) # 집계된 PR 정보를 슬랙에 전송
if __name__ == "__main__":
koreaHolidays = KoreaHolidays()
if not koreaHolidays.today_is_holiday():
set_pull_requests_tags()
else:
print("Today is a holiday. The script will not run.")
만약 파일 이름이 pr_bot.py 라고 한다면 터미널에 'python3 pr_bot.py'를 입력해 실행해봅니다.
이때 오늘이 공휴일이 아니라면 슬랙에 pr내용이 잘 담겨서 알림이 잘 가야합니다.
on:
workflow_dispatch: //이 코드를 넣으면 깃헙액션 페이지에서 테스트 가능
schedule:
- cron: "0 0 * * * MON-FRI" //크론 돌릴 시간 - 월-금 오전 9시
jobs:
run_script:
name: Run Script
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.12.3' //파이썬 버전
- name: Install dependencies
run: pip install -r requirements.txt //필요한 디펜던시 버전을 정의한 파일
//이 파일은 루트 디렉토리에 저장
//파일 얻는 법 > 해당 파이썬 스크립트가 있는 폴더에서 이 명령어 수행 sudo python3 -m pip freeze > requirements.txt
//파일에는 버전만 정의되어있어야한다.(아래참고)
- name: Run script
run: python pr_bot.py //루트 디렉토리에 저장
이 파일을 .github/workflows/manual.yml (파일 이름은 마음대로)에 직접 만들거나
깃헙 페이지 내 Actions 버튼 클릭 > New workflow > Manual workflow를 클릭해 생성합니다.
main브랜치 루트 디렉토리에 실행시키고자 하는 파이썬 스크립트를 푸시합니다.
여기서는 6번 코드인 pr_bot.py가 됩니다.
main브랜치 루트 디렉토리에 실행시키고자 하는 파이썬 스크립트가 필요로하는 디펜던시와 버전을 정의한 파일을 푸시합니다.
파일이름은 'requirements.txt'로 합니다.
이파일을 얻는 방법은 6번 위치로 가서 터미널에 'pip freeze > requirements.txt'를 실행시킵니다. 만약 pip not found 에러가 발생하면 'sudo python3 -m pip freeze > requirements.txt'로 진행합니다.
해당 명령어를 실행하면 requirements파일이 얻어집니다.
이 파일에는 아래 코드조각처럼 디펜던시와 버전만 정의되어야 합니다.
certifi==2024.2.2
cffi==1.16.0
charset-normalizer==3.3.2
cryptography==42.0.7
Deprecated==1.2.14
idna==3.7
numpy==1.26.4
pandas==2.2.2
pycparser==2.22
PyGithub==2.3.0
PyJWT==2.8.0
PyNaCl==1.5.0
python-dateutil==2.9.0.post0
pytz==2024.1
requests==2.32.2
six==1.16.0
typing_extensions==4.12.0
tzdata==2024.1
urllib3==2.2.1
wheel==0.43.0
wrapt==1.16.0
참고로 공휴일체크하지 않고 슬랙에 pr내용 보내는 파이썬 스크립트 버전은 아래와 같습니다.
from github import Github
import requests
# First create a Github instance:
# using an access token
g = Github("깃헙토큰")
# Then play with your Github objects:
repos = [repo for repo in g.get_user().get_repos()]
#레포지토리 이름 프린트해서 깃헙 잘 연동되었나 확인
#for repo in repos:
# print(repo.name)
def _get_total_pull_requests(repo):
count = 0
pull_requests_list = []
# 현재 열려있는 PR 목록들을 가져온다.
for pull in repo.get_pulls(state="open", sort="updated"):
pr_comments_count = pull.review_comments
if pr_comments_count != 0:
# 리뷰가 진행중인 PR인 경우는 목록에서 제외
pass
else:
count += 1
pull_requests_list.append((repo.name,pull))
return count, pull_requests_list
def _send_slack(msg: str):
response = requests.post(
"https://slack.com/api/chat.postMessage",
headers={"Authorization": "Bearer 슬랙토큰"},
data={"channel": "채널아이디", "text": msg},
)
### print(response.json()) # Slack API 응답 출력해서 확인해보기
def _make_pr_link_with_no(repo_name:str, pr_no: int) -> str:
link = f"https://github.com/{repo_name}/pull/{pr_no}"
return link
def set_pull_requests_tags():
total_cnt = 0
total_pulls = []
for repo in repos: # 모든 저장소에 대해 실행
cnt, pulls = _get_total_pull_requests(repo)
total_cnt += cnt
total_pulls.extend(pulls)
# 모든 저장소의 PR을 집계한 후 메시지를 생성
pr_msg_to_slack = (
f"<!here> 👋🏻 총 {total_cnt}개의 Pull Request가 리뷰를 기다리고 있어요! :eyes:\n"
)
for repo_name, pull in total_pulls:
pr_link = _make_pr_link_with_no(repo_name, pull.number)
pr_title = pull.title
pr_msg_to_slack += f"[{repo_name}] {pr_title}:{pr_link}\n"
_send_slack(pr_msg_to_slack) # 집계된 PR 정보를 슬랙에 전송
set_pull_requests_tags()
참고 블로그
https://devocean.sk.com/blog/techBoardDetail.do?ID=165255
https://tasddc.tistory.com/160
https://uipath.tistory.com/200