깃허브에 남은 PR을 공휴일이 아닌 월-금 동안 매일 오전 9시에 자동으로 슬랙으로 알려주는 봇을 만들고 싶었습니다.
슬랙봇을 만듭니다.
권한은 이렇게 두개 넣었는데 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