🥴

GitHub Actionsを使って、PRがマージされたらNotionタスクを更新する

に公開

NotionにはGitHub連携機能があり、Pull Requestの状態が変化したらタスクのステータスを自動で更新などが可能である。
※ Notionは2025年5月に料金体系を改訂しており、上記機能の利用にはビジネスプランが必要。

私は会社の環境に加えて個人でもNotionを利用しているが、もちろんビジネスプランではないので、ありものを組み合わせて多少それっぽいことができるようにしたので備忘がてら記録しておく。

実現したフロー

最低限、PRがマージされたらタスクをクローズしたかったので、以下の通りとした。

もともと(Notionの機能として提供されていたほうの)GitHubプルリクエスト型プロパティを使っていたので、
ほぼ同様の運用となるよう、タスクにPRのURLを書き込む(上図Write PR URL)ようにしておき、それをもとにタスクを検索(同Search task)するようにした。

実現方法

トリガー

GitHub Actionsワークフローのpull_requestイベントのclosedを使用した。(cf.)

処理の内容

コードは以下の通り。

name: Sync status to Notion

on:
  pull_request:
    types:
      - closed

jobs:
  sync_notion:
    runs-on: ubuntu-24.04
    if: github.event.pull_request.merged == true
    steps:
      - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
        with:
          script: |
            const notionToken = process.env.NOTION_TOKEN;
            const databaseId = process.env.NOTION_DB_ID;
            const prUrl = context.payload.pull_request.html_url;
            
            console.log(`Processing merged PR: ${prUrl}`);
            
            // NotionのDB検索
            const searchResponse = await fetch(`https://api.notion.com/v1/databases/${databaseId}/query`, {
              method: 'POST',
              headers: {
                'Authorization': `Bearer ${notionToken}`,
                'Notion-Version': '2022-06-28',
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                filter: {
                  property: 'Pull Request', // URL型のPull Requestプロパティを検索
                  url: {
                    equals: prUrl
                  }
                }
              })
            });
            
            if (!searchResponse.ok) {
              const errorText = await searchResponse.text();
              throw new Error(`Notion search failed: ${searchResponse.status} ${errorText}`);
            }
            
            const searchData = await searchResponse.json();
            console.log(`Found ${searchData.results.length} matching pages`);
            
            // 検索結果が0件なら処理を終了
            if (searchData.results.length === 0) {
              console.log('No matching pages found. Exiting.');
              return;
            }
            
            // 各ページのステータスを「完了」に更新
            for (const page of searchData.results) {
              const updateResponse = await fetch(`https://api.notion.com/v1/pages/${page.id}`, {
                method: 'PATCH',
                headers: {
                  'Authorization': `Bearer ${notionToken}`,
                  'Notion-Version': '2022-06-28',
                  'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                  properties: {
                    'ステータス': {
                      status: {
                        name: '完了'
                      }
                    }
                  }
                })
              });
              
              if (!updateResponse.ok) {
                const errorText = await updateResponse.text();
                console.error(`Failed to update page ${page.id}: ${updateResponse.status} ${errorText}. To see page detail, visit: ${page.url}`);
              } else {
                console.log(`Successfully updated page ${page.id}`);
              }
            }
        env:
          NOTION_TOKEN: ${{ secrets.NOTION_TOKEN_PR_SYNC }}
          NOTION_DB_ID: ${{ secrets.NOTION_TASK_DB_ID }}

上述の通り、あらかじめPRのURLをタスクに紐付けているので、それをもとにNotionDBからページを検索し、
取得できた場合は追加で更新APIを呼び出してステータスを「完了」にしている。

NotionのAPIトークン(およびなんとなくでタスクDBのID)は環境変数経由としている。

地味にハマったポイント: Notion標準のPull RequestプロパティはAPI検索には不向き

フローの説明において(Notionの機能として提供されていたほうの)GitHubプルリクエスト型プロパティについて触れたが、
今回の自前処理においては、下記の理由から使わず、別途URL型のプロパティを用いている。

  1. 検索APIでの利用に不向き
    GUI上ではそこまで差を感じないが、API経由でGitHubプルリクエスト型プロパティの中身を見たところ Relation 型となっていた。
    Relation型には以下のような形式となるため、GitHub Actionsで把握している情報ではページ検索が困難であった。
{
  id: 'abc',
  type: 'relation',
  relation: [ { id: '1234-abcd-5678-efgh' } ],
  has_more: false
}
  1. そもそもプラン外機能のためのプロパティなのでこの先使えるかすら怪しい
    私個人の利用ではビジネスプランにする予定はない(※)ので、GitHub連携はプラン外の機能である。
    そのため、いまは名残でDBに残っているGitHubプルリクエスト型プロパティもある日突然項目ごと消えても不思議ではない。
    ※ プラン改定前にNotion AIを契約しており、ビジネスプランにしなくてもNotionAIは使えているのも理由

軽い思いつきでやった&初めてGitHub Actionsを触った割には特に詰まることもなく、半日程度の作業で完成した。
(相談に乗ってもらった@ShoppingJawsとClaude codeがいなかったらこうはいかなかった。圧倒的感謝)

本家の機能ほどこだわらなければ、すぐできる割にそこそこ便利なのでオススメです。

Discussion