GitHubだけでPJ管理を頑張ってみる兄貴 Part1 〜GithubでWBS作ったら、リマインドまで自動化できた話〜
はじめに
初めまして。株式会社フェズで開発のディレクションを担当している増田といいます。
入社して半年、小売様のデータ基盤や販促支援の開発管理を担当しています。
同僚からは何故か兄貴と呼ばれている末っ子です。
想定読者
- PJ管理者になりたての人
- PJ管理したいけど、現状ツールがない人(RedMine、Jiraなど)
なんでこんなことをやり始めたか
フェズに入社して、開発管理をする中で
- As Is
- GitHubでIssue管理をやっている。
- ただ、Issueはあるが、スケジュール管理(特に締切)が管理できていない。
- GitHubでIssue管理をやっている。
- To Be
- DeadLine管理をちゃんとやりたい。
- Issue制でガントチャート・WBS管理やりたい。
- そうした方が、所属するグループの開発管理はスムーズ。
ただ、ツールがない!
という壁に当たりました。どうしよう・・・。
だったら作ってやんよ!
というわけで、GitHubだけでPJ管理を頑張ってみる兄貴の奮闘記です。
DeadLineカスタムフィールド作成&SlackへDL通知
SIや、プロダクトの個別カスタマイズにおいて、DeadLine(以下DL)は重要な概念です。
DeadLineまでに、該当タスクが終わっていないと・・・。
- 後続のタスクスケジュールが壊れてしまう。
- 納品できない。ローンチできない。
等、ビジネスにとってクリティカルなことが起きます。
詳しくはこちら
今まではGitHubとは連動していない別のポータルサイト上でプロジェクトの進捗を管理していましたが、GitHubと非連動なこともあり二重管理になってしまっており、厳密なDL管理もできていない状態でした。
そこで、
- GitHub Issueにカスタムフィールドを追加し、
- GitHub Aciton+PythonでIssue抽出
- Slackでアラートする方式で管理しようと画策しました。
GitHub Issueにカスタムフィールドを追加
-
こんな感じでDLというカスタムフィールドを作りました。
-
作り方
- GitHub PJのTable ViewでAdd Fieldする。
- データ型はdateを選択。
GitHub Aciton+PythonでIssue抽出
続いては、GitHub Actionの準備です。
今回は毎朝9時に明日DLを迎えるIssueを抽出してみるActionを組んでみました。
(GitHub APIで簡単にいけるだろ・・・と思っていた。当時は。)
GitHub APIを使って、Issueを抜いてみた。
と言うわけで当初のコード
Python側
import os
import requests
from datetime import datetime, timedelta
# GitHub APIの設定
GITHUB_API_URL = "https://api.github.com"
REPO = os.getenv("GITHUB_REPOSITORY")
TOKEN = os.getenv("GITHUB_TOKEN")
# 日本時間に合わせた日付の設定
JST = timedelta(hours=9)
today = datetime.utcnow() + JST
tomorrow = (today + timedelta(days=1)).date()
# GitHub APIでIssuesを取得
response = requests.get(
f"{GITHUB_API_URL}/repos/{REPO}/issues",
headers={"Authorization": f"token {TOKEN}"}
)
issues = response.json()
# DeadLineが明日の日付のIssueをフィルタリング
deadline_issues = []
for issue in issues:
print(issue)
# カスタムフィールドのデッドラインを取得
deadline_str = "" # APIに応じてフィールドを変更
if deadline_str:
deadline_date = datetime.strptime(deadline_str, "%Y-%m-%d").date()
if deadline_date == tomorrow:
deadline_issues.append(issue)
# 結果を出力
if deadline_issues:
issue_list = "\n".join([f"- {issue['title']} ({issue['html_url']})" for issue in deadline_issues])
message = f"以下のIssueの締め切りが明日です:\n{issue_list}"
print(f"::set-output name=has_issues::true")
else:
message = "明日の締め切りのIssueはありません。"
print(f"::set-output name=has_issues::true")
GitHubActionのyaml側
name: Notify DeadLine Issues
on:
schedule:
# 日本時間午前9時に実行(UTCで0時)
- cron: '0 0 * * *'
workflow_dispatch: # 手動トリガーのための設定
# 何もしない
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests pyyaml
- name: Run Deadline Checker
id: deadline_checker # ステップにIDを設定
env:
DEVELOPER_GITHUB_TOKEN: ${{ secrets.DEVELOPER_GITHUB_TOKEN }}
run: python .github/workflows/scripts/check_deadlines.py
- name: Send Notification to Slack
uses: rtCamp/action-slack-notify@v2
env:
SLACK_CHANNEL: ${{ secrets.SLACK_DL_ALERT_CHANNEL }}
SLACK_TITLE: "GitHub Issue DLアラート"
SLACK_MESSAGE: ${{ steps.deadline_checker.outputs.message_alert }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
さてさて。うまく動くかな・・・。
動かない。
デバッグした結果、GitHub APIではカスタムフィールドの抽出ができないことが判明しました。
orz
いや、ドキュメント読んでない自分が悪いんだけどね。
どうしたものか・・・。
ちょっと調査をしてみたら、GitHub GraphQL APIなら抽出できることが判明。
早速試してみましょう。
import urllib.parse
import os
import requests
from datetime import datetime, timedelta
# GitHub APIの設定
GITHUB_API_URL = "https://api.github.com/graphql"
REPO = "kamet"
TOKEN = os.getenv("DEVELOPER_GITHUB_TOKEN")
OWNER = os.getenv("GITHUB_REPOSITORY_OWNER")
# GraphQLクエリ
query = """
query ($owner: String!, $repo: String!) {
repository(owner: $owner, name: $repo) {
issues(first: 100, states: OPEN, orderBy: { field: CREATED_AT, direction: DESC }) {
edges {
node {
title
url
projectItems(first: 10) {
nodes {
fieldValueByName(name: "DL") {
... on ProjectV2ItemFieldDateValue {
date
}
}
}
}
}
}
}
}
}
"""
def log_message(message):
print(f"::debug::{message}") # GitHub Actionsのログに出力
# GraphQLリクエストを実行する関数
def run_query(query, variables):
headers = {"Authorization": f"Bearer {TOKEN}"}
response = requests.post(GITHUB_API_URL, json={'query': query, 'variables': variables}, headers=headers)
if response.status_code == 200:
result = response.json()
if result is None:
raise Exception("result")
if 'data' not in result:
raise Exception("result")
if 'repository' not in result['data']:
raise Exception("repo")
return response.json()
else:
raise Exception(f"Query failed with status code {response.status_code}: {response.text}")
# GitHubのIssuesを取得
variables = {"owner": OWNER, "repo": REPO}
result = run_query(query, variables)
log_message("OWNER:" + OWNER)
log_message("REPO:" + REPO)
log_message("result:" + str(result))
# 今日の日付を取得
today = datetime.today().date()
# +1日
tomorrow = today + timedelta(days=1)
deadline_issues = []
# 締め切りが今日のIssueをSlackに送信
for issue_edge in result['data']['repository']['issues']['edges']:
issue = issue_edge['node']
for project_item in issue['projectItems']['nodes']:
deadline_field = project_item['fieldValueByName']
if deadline_field and deadline_field.get('date'):
deadline = datetime.strptime(deadline_field['date'], "%Y-%m-%d").date()
if deadline == tomorrow:
deadline_issues.append(f"{issue['title']}")
log_message("deadline_issues:" + str(deadline_issues))
if deadline_issues:
issue_list = "\n".join(deadline_issues)
log_message("issue_list:" + str(issue_list))
message = f"@group_technology_business_promotion 以下のIssueの締め切りが明日です:\n" + str(issue_list)
log_message("issue_list:" + message)
else:
message = "明日の締め切りのIssueはありません。"
with open(os.getenv('GITHUB_OUTPUT'), 'a') as gh_output:
print(f"message_alert<<EOF\n{message}\nEOF\n", file=gh_output)
これでどうや!
できた!よかった・・・。
と言うわけで、毎日明日のDL Issueを確認できるようになりました。
終わりに
PJ管理をする中で、ツールがない!と言うのはいろんな現場でありました。
その中でエンジニアならではの解決ができたのは、今回が初めてで嬉しい限りです。
それではまた!
(次回はPJ管理自体のことを書こうかなあ)
フェズは、「情報と商品と売場を科学し、リテール産業の新たな常識をつくる。」をミッションに掲げ、リテールメディア事業・リテールDX事業を展開しています。 fez-inc.jp/recruit
Discussion