igsr5 のブログ

呟きたい時に呟きます

GitHub Projects (v2) で個人の Issue/PR 管理をエンジニアリングする

先日、GitHub Projects (v2) を利用して個人の Issue/PR 管理を良い感じに自動化したのでその時の話をします。

ことのきっかけ

一般的なプラクティスかどうかは分かりませんがこの世の中には、個人専用の GitHub Projects を作り、そこで自身の Issue や PR を追加して管理している人たちがいます。

進行中のプロジェクト、突如発生するサブタスクなどに付随して、関わる Issue/PR の数はどんどん肥大化していきます。そんな問題に対して GitHub Projects による Issue/PR 管理はは人間の認知負荷を下げることに役立ちます。また筆者も 1 年ほど前から GitHub Projects で Issue/PR を管理しています。

しかしながら、実際に 1 年ほど運用してみると、今度は GitHub Projects 運用の複雑化が目立つようになってしまいました。例えば GitHub Projects への Issue/PR の追加漏れやステータス更新の手間、フィールド数の肥大化などの問題が度々見られていました。散乱する Issue/PR たちの辛みに比べればまだマシですが、やはり微妙感は漂います。

そこで「このまま GitHub Projects を腐らせておくのは勿体無い!」という気持ちのもと、個人用 GitHub Projects のリファクタリングを実施することにしました。

前提: Projects で管理したい Issue/PR

次の章に進む前に、自分が GitHub Projects で管理したいと思っている Issue/PR の種別を示します。Why には踏み込みませんが、今回の取り組みではこれらの Issue/PR 管理が生産性高く行える形を目指します。

  • 作業中の Issue
  • 作業中の PR
    • 実装中の PR
    • レビュー待ちの PR
    • Approve された PR
  • レビュー中の PR
    • 未レビューの PR
    • レビューを返した PR
    • Approve 済みの PR
  • 完了済みの Issue/PR

やったこと

ある程度の問題整理をしたのち、今回の取り組みでは以下の問題の解決にあたることにしました。

  1. 運用の認知負荷を下げるために、不要なステータスやカスタムフィールドを整理したい
  2. レビュー漏れ等のリスクを防ぐために、Projects への追加漏れ・ステータス更新漏れの発生を防ぎたい
  3. 継続的な運用のために、限りなく Projects の操作 (mutation) を自動化したい
  4. 継続的な運用のために、この先アイテム上限数 (1200) に達しないようする

それぞれ具体的にやることは以下の通りです。

No. やること
1 フィールド設計
2, 3 Projects 操作の自動化
4 Archiving items の利用

フィールド設計

まずは「前提: Projects で管理したい Issue/PR」たちをどのように分類するかを考えます。

  • 作業中の Issue
  • 作業中の PR
    • 実装中の PR
    • レビュー待ちの PR
    • Approve された PR
  • レビュー中の PR
    • 未レビューの PR
    • レビューを返した PR
    • Approve 済みの PR
  • 完了済みの Issue/PR

つまりフィールド設計ですが、アイテムの状態 8 個をそのままフラットに扱うメリットはなかったので、今回は "カテゴリー" と "ステータス" という二つの概念を導入してその積でアイテムの状態を決定することとします。

用語 定義
カテゴリー アイテム(Issue/PR)が属する大まかな分類。作業の性質に基づいて分けられる。
ステータス アイテム(Issue/PR)の進行状況を示す。
  • カテゴリー
    • Issues: 作業中の Issue
    • PRs: 作業中の PR
    • Review PRs:レビューリクエストを受けた PR
  • ステータス
    • In Progress: 何かしらの作業中 (未着手を含む)
    • In Pending: 何かしらの待ち状態
    • Complete: 完了可能になった状態

これらのフィールドを用いて、アイテムの状態は以下のように考えます。

カテゴリー(下) x ステータス(上) Issues PRs Review PRs
In Progress 作業中の Issue 作業中の PR 未レビューの PR
In Pending レビュー待ちの PR 未レビューの PR
Complete Approve 済みの PR Approve 済みの PR

※ は今回は利用しない状態をさします。なお「完了済みの Issue/PR」については問題 4 (アイテム上限数) の考慮を行うために別対応を行うのでここでは除外しています。

完了済みアイテムについて

GitHub Projects には追加できるアイテム数に上限があります (2023/10 時点で 1200 個)。業務で普段使いしていると大体半年~1 年で上限に達してしまうので継続的に Projects を利用していくためには不要になったアイテムから棚卸をしないといけません。

とはいえ完全に GitHub Projects から削除してしまうと後に掘り起こす際に不便なので、今回は Delete ではなく Archive を利用することで対応しました。Archive にしておけば専用の Archive Item ページから Issue/PR を検索することも出来るので便利です。

参考: Archiving items automatically - GitHub Docs

自動化

状態遷移図

自動化にあたり、まずはフィールド設計で導いた "カテゴリー" × "ステータス" の 8 状態 +α の状態遷移を整理しました。以下がその結果です。 ここでは前章で挙げた 8 状態に加えて「Projects 未追加」「Archive 予定 (1 日後に Archive されるアイテム)」の 2 状態を考慮しています。

Issues/PR の状態遷移図

こうして見ると全ての状態遷移は何かしらの機械判定が可能なものになっています。機械判定可能ということは自動化も可能であると推測出来るので、ひとまずはこの状態遷移図を主軸として自動化を試ることにします。

実装

GitHub Projects には公式が用意するワークフロー制御の機構が存在しますが、残念ながら今回実現したい自動化の要件を満たす機能は Auto Archiving を除くと他に 1 つもありません。さらに次点で有力そうな Webhook を活用する方法もまた、自身が管理ユーザーではない Organization に対する Webhook URL 登録が出来なかったため見送っています。

ここまで来ると、もはや残された道は GitHub API を利用して 0 から自分で状態遷移を実装を作ることくらいしかありません。こればかりは現状だとどうしようもないので、今回は自身で今回の要件を満たすステートマシンを実装することにしました。なおステートレスに実装できないか PoC を作ってみましたが、流石に状態・状態遷移の数が多くて複雑だったため諦めてステートマシンの実装を行うことにしました。

実装の詳細についてはそれ単体で文量が爆発してしまうためここでは割愛させていただきます。

作ってみて

執筆時点では PoC 段階が終わったフェーズでまだ本実装には進んでいませんが、PoC の成果物もそれなりに活用可能な出来にはなったので実際に業務で利用することにしました。実際に使用してみると全ての Issue/PR が自動で管理される世界はとても快適でした。

また、今回の取り組みによって今まで増え続けていたいくつものフィールドが消えて、"カテゴリー" と "ステータス" のみシンプルな構造になりました。フィールド設計がシンプルに保てているのでボードを作成する際にも条件の構築が行いやすく体験がよかったです。

さらに、これは考えていなかったのですが、いざ自動化実装を動かしてみると、自分が放置していた大量の過去 Issue/PR たちが掘り起こされました。これらを特別に非表示にするオプションは導入していない (導入する予定もない) ので、プロジェクトの利便性を上げるためには必然的に不要になった Issue/PR を close することになりましたが、これがめちゃくちゃ良かったです。軽く棚卸しただけでも 50 個弱の Issue/PR が close 出来たので驚いています (反省)。

おまけ

  • 自動化は Cloud Run と Cloud Scheduler を使っています
    • 平日の9時-18時のあいだ、3分毎に GitHub API V3 を叩いて GitHub Projects の状態を更新しています
    • ロジック自体は Golang で記述
    • GitHub Api V3 の search api の rate limit に引っかからないように頻度を調整しています
  • 節約 月2,000円 → 月750円
    • 初めは時間帯制限を設けず平日の24時間フルで Cloud Scheduler を回していましたが、Cloud Run の CPU Allocation Time が増えすぎて月 2,000 円ほどかかっていたので実行タイミングを減らして節約しました。