概要
コードレビューにかかる時間の可視化に向けて、最初にレビュー依頼した時および approve された時の日時を Notion のタスクチケットに自動で記録する仕組みを Github Actions + Notion API で実装しました。今回はその手順を書いていこうと思います。
手順
Notion準備
インテグレーションを作成する
以下のページから Notion のインテグレーションを作成してください。「新しいインテグレーション」をクリックし、インテグレーション名と関連ワークスペースを選択して「保存」を押すことで保存できます。
保存後の設定画面にて ntn_ で始まるインテグレーションシークレットを確認できるのでコピーしてください。Github Actions にて使用するので、Github の設定画面の Secrets and variables > Actions のシークレットに保存してください(シークレット名は何でも大丈夫です。ここでは NOTION_TOKEN とします)。
データベースとインテグレーションを接続する
タスクチケットを管理しているデータベースの Notion テーブルを開いてください。まず、URL 内にあるデータベースID部分をコピーしてください(クエリパラメータ ? の手前にある文字列がデータベースIDです)。これを Github Actions のシークレットに保存してください(ここでは NOTION_DATABASE_ID とします)。
次に、Notion テーブルの右上の三点リーダーから「接続」をクリックして先ほど作成したインテグレーションを選択し、インテグレーションを接続してください。
データベースにプロパティを追加する
レビュー開始日時およびレビュー完了日時を記録するプロパティをデータベースに追加します。名前は何でも大丈夫ですが、ここでは「レビュー開始日時」「レビュー完了日時」という名前にします。種類はどちらも日付タイプで作成してください。
Github Actionsのワークフロー実装
Notion と連携させたいリポジトリにてワークフローを作成します。今回は
-
TASK-1234 あああのように TASK-XXX(XXX 部分は更新したい Notion タスクチケットの ID)で始まるタイトルで作成したプルリクを対象とする - 初回レビュー依頼時に「レビュー開始日時」プロパティに現在日時を記録(既にプロパティに日時が記録されている場合はスキップ)
- プルリクが approve された時に「レビュー完了日時」プロパティに現在日時を記録(既にプロパティに日時が記録されている場合は日時を更新)
するワークフローを作成したいと思います。
以下の内容で yml ファイルを作成してください。
name: Connect Notion Task with GitHub PR
on:
pull_request:
types:
- review_requested
- edited
pull_request_review:
types:
- submitted
env:
NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }} # ntn_から始まるNotionのトークン
DATABASE_ID: ${{ secrets.NOTION_DATABASE_ID }} # NotionページURLの先頭に付くID
NOTION_REVIEW_START_DATE_PROPERTY_NAME: 'レビュー開始日時' # レビュー開始日時を記録するプロパティ名
NOTION_REVIEW_APPROVED_DATE_PROPERTY_NAME: 'レビュー完了日時' # レビュー完了日時を記録するプロパティ名
jobs:
connect-notion-task-with-github-pr:
runs-on: ubuntu-22.04
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
# TASK-XXX で始まるタイトルのプルリクにて、初回レビュー依頼時にNotionにレビュー開始日時を記録する(既に日付が設定されている場合はスキップ)
- name: Install jq
if: github.event.action == 'review_requested'
id: install_jq_requested
run: sudo apt-get update && sudo apt-get install -y jq
- name: Extract Notion ID from PR title
if: github.event.action == 'review_requested'
id: extract_notion_id_requested
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
# TASK-XXX のようなパターンから数値部分のみを抽出
if [[ "${PR_TITLE}" =~ TASK-([0-9]+) ]]; then
NOTION_ID="${BASH_REMATCH[1]}"
echo "抽出されたNotion ID: $NOTION_ID"
echo "VALUE=$NOTION_ID" >> $GITHUB_OUTPUT
else
echo "PRタイトルからNotion IDが見つかりませんでした: $PR_TITLE"
echo "VALUE=" >> $GITHUB_OUTPUT
fi
- name: Find Notion page by ID
id: find_page_requested
if: steps.extract_notion_id_requested.outputs.VALUE != ''
run: |
SEARCH_RESPONSE=$(curl -s -X POST "https://api.notion.com/v1/databases/${{ env.DATABASE_ID }}/query" \
-H "Authorization: Bearer ${{ env.NOTION_TOKEN }}" \
-H "Content-Type: application/json" \
-H "Notion-Version: 2022-06-28" \
-d "{
\"filter\": {
\"property\": \"ID\",
\"unique_id\": {
\"equals\": ${{ steps.extract_notion_id_requested.outputs.VALUE }}
}
}
}")
echo "検索レスポンス: $SEARCH_RESPONSE"
# エラーチェック
if echo "$SEARCH_RESPONSE" | jq -e '.object == "error"' > /dev/null; then
echo "検索エラー: $(echo "$SEARCH_RESPONSE" | jq -r '.message')"
exit 1
fi
PAGE_ID=$(echo "$SEARCH_RESPONSE" | jq -r '.results[0].id // empty')
if [ -n "$PAGE_ID" ]; then
echo "見つかったページID: $PAGE_ID"
echo "VALUE=$PAGE_ID" >> $GITHUB_OUTPUT
# レビュー開始日時プロパティの現在値を取得
REVIEW_START_DATE_VALUE=$(echo "$SEARCH_RESPONSE" | jq -r ".results[0].properties[\"${{ env.NOTION_REVIEW_START_DATE_PROPERTY_NAME }}\"].date.start // empty")
echo "現在のレビュー開始日時: $REVIEW_START_DATE_VALUE"
echo "CURRENT_VALUE=$REVIEW_START_DATE_VALUE" >> $GITHUB_OUTPUT
else
echo "対象のNotionページが見つかりませんでした"
exit 1
fi
- name: Check if review start date is already set
id: check_existing_date_requested
if: steps.find_page_requested.outputs.VALUE != ''
run: |
CURRENT_VALUE="${{ steps.find_page_requested.outputs.CURRENT_VALUE }}"
if [ -z "$CURRENT_VALUE" ] || [ "$CURRENT_VALUE" = "null" ]; then
echo "レビュー開始日時が未設定のため、更新を実行します"
echo "SHOULD_UPDATE=true" >> $GITHUB_OUTPUT
else
echo "レビュー開始日時が既に設定されています: $CURRENT_VALUE"
echo "更新をスキップします"
echo "SHOULD_UPDATE=false" >> $GITHUB_OUTPUT
fi
- name: Get current datetime
if: steps.check_existing_date_requested.outputs.SHOULD_UPDATE == 'true'
id: get_datetime_requested
run: |
# 日本時間でISO 8601形式の日時を取得(タイムゾーンオフセット付き)
CURRENT_DATETIME=$(TZ=Asia/Tokyo date +'%Y-%m-%dT%H:%M:%S+09:00')
echo "現在日時(JST): $CURRENT_DATETIME"
echo "VALUE=$CURRENT_DATETIME" >> $GITHUB_OUTPUT
- name: Update Notion page with review start date
if: steps.check_existing_date_requested.outputs.SHOULD_UPDATE == 'true' && steps.get_datetime_requested.outputs.VALUE != ''
id: update_notion_review_start_date
run: |
echo "ページを更新中: ${{ steps.find_page_requested.outputs.VALUE }}"
echo "日時: ${{ steps.get_datetime_requested.outputs.VALUE }}"
UPDATE_RESPONSE=$(curl -s -X PATCH "https://api.notion.com/v1/pages/${{ steps.find_page_requested.outputs.VALUE }}" \
-H "Authorization: Bearer ${{ env.NOTION_TOKEN }}" \
-H "Content-Type: application/json" \
-H "Notion-Version: 2022-06-28" \
-d "{
\"properties\": {
\"${{ env.NOTION_REVIEW_START_DATE_PROPERTY_NAME }}\": {
\"date\": {
\"start\": \"${{ steps.get_datetime_requested.outputs.VALUE }}\"
}
}
}
}")
# エラーチェック
if echo "$UPDATE_RESPONSE" | jq -e '.object == "error"' > /dev/null; then
echo "❌ 更新エラー: $(echo "$UPDATE_RESPONSE" | jq -r '.message')"
echo "レスポンス詳細: $UPDATE_RESPONSE"
exit 1
else
echo "✅ レビュー開始日時を更新しました"
echo "📅 日時: ${{ steps.get_datetime_requested.outputs.VALUE }}"
echo "📄 ページID: ${{ steps.find_page_requested.outputs.VALUE }}"
fi
# TASK-XXX で始まるタイトルのプルリクにて、approve時にNotionにレビュー完了日時を記録する(既に日付が設定されている場合は更新する)
- name: Install jq
if: github.event.review.state == 'approved'
id: install_jq_approved
run: sudo apt-get update && sudo apt-get install -y jq
- name: Extract Notion ID from PR title
if: github.event.review.state == 'approved'
id: extract_notion_id_approved
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
# TASK-XXX のようなパターンから数値部分のみを抽出
if [[ "${PR_TITLE}" =~ TASK-([0-9]+) ]]; then
NOTION_ID="${BASH_REMATCH[1]}"
echo "抽出されたNotion ID: $NOTION_ID"
echo "VALUE=$NOTION_ID" >> $GITHUB_OUTPUT
else
echo "PRタイトルからNotion IDが見つかりませんでした: $PR_TITLE"
echo "VALUE=" >> $GITHUB_OUTPUT
fi
- name: Find Notion page by ID
id: find_page_approved
if: steps.extract_notion_id_approved.outputs.VALUE != ''
run: |
SEARCH_RESPONSE=$(curl -s -X POST "https://api.notion.com/v1/databases/${{ env.DATABASE_ID }}/query" \
-H "Authorization: Bearer ${{ env.NOTION_TOKEN }}" \
-H "Content-Type: application/json" \
-H "Notion-Version: 2022-06-28" \
-d "{
\"filter\": {
\"property\": \"ID\",
\"unique_id\": {
\"equals\": ${{ steps.extract_notion_id_approved.outputs.VALUE }}
}
}
}")
echo "検索レスポンス: $SEARCH_RESPONSE"
# エラーチェック
if echo "$SEARCH_RESPONSE" | jq -e '.object == "error"' > /dev/null; then
echo "検索エラー: $(echo "$SEARCH_RESPONSE" | jq -r '.message')"
exit 1
fi
PAGE_ID=$(echo "$SEARCH_RESPONSE" | jq -r '.results[0].id // empty')
if [ -n "$PAGE_ID" ]; then
echo "見つかったページID: $PAGE_ID"
echo "VALUE=$PAGE_ID" >> $GITHUB_OUTPUT
# レビュー完了日時プロパティの現在値を取得
REVIEW_APPROVED_DATE_VALUE=$(echo "$SEARCH_RESPONSE" | jq -r ".results[0].properties[\"${{ env.NOTION_REVIEW_APPROVED_DATE_PROPERTY_NAME }}\"].date.start // empty")
echo "現在のレビュー完了日時: $REVIEW_APPROVED_DATE_VALUE"
echo "CURRENT_VALUE=$REVIEW_APPROVED_DATE_VALUE" >> $GITHUB_OUTPUT
else
echo "対象のNotionページが見つかりませんでした"
exit 1
fi
- name: Get current datetime
if: steps.extract_notion_id_approved.outputs.VALUE != ''
id: get_datetime_approved
run: |
# 日本時間でISO 8601形式の日時を取得(タイムゾーンオフセット付き)
CURRENT_DATETIME=$(TZ=Asia/Tokyo date +'%Y-%m-%dT%H:%M:%S+09:00')
echo "現在日時(JST): $CURRENT_DATETIME"
echo "VALUE=$CURRENT_DATETIME" >> $GITHUB_OUTPUT
- name: Update Notion page with review approved date
if: steps.get_datetime_approved.outputs.VALUE != ''
id: update_notion_review_approved_date
run: |
echo "ページを更新中: ${{ steps.find_page_approved.outputs.VALUE }}"
echo "日時: ${{ steps.get_datetime_approved.outputs.VALUE }}"
UPDATE_RESPONSE=$(curl -s -X PATCH "https://api.notion.com/v1/pages/${{ steps.find_page_approved.outputs.VALUE }}" \
-H "Authorization: Bearer ${{ env.NOTION_TOKEN }}" \
-H "Content-Type: application/json" \
-H "Notion-Version: 2022-06-28" \
-d "{
\"properties\": {
\"${{ env.NOTION_REVIEW_APPROVED_DATE_PROPERTY_NAME }}\": {
\"date\": {
\"start\": \"${{ steps.get_datetime_approved.outputs.VALUE }}\"
}
}
}
}")
# エラーチェック
if echo "$UPDATE_RESPONSE" | jq -e '.object == "error"' > /dev/null; then
echo "❌ 更新エラー: $(echo "$UPDATE_RESPONSE" | jq -r '.message')"
echo "レスポンス詳細: $UPDATE_RESPONSE"
exit 1
else
echo "✅ レビュー完了日時を更新しました"
echo "📅 日時: ${{ steps.get_datetime_approved.outputs.VALUE }}"
echo "📄 ページID: ${{ steps.find_page_approved.outputs.VALUE }}"
fi
プルリクを作成してレビュー依頼を出すと、「レビュー開始日時」プロパティに現在日時が記録されます。再レビュー依頼時など、既にプロパティに日付が記録されている場合はスキップされます。
プルリクが approve された時に「レビュー完了日時」プロパティに現在日時が記録されます。再 approve した場合など、既にプロパティに日付が記録されている場合は日時が更新されます。
今回は TASK-XXX で始まる名前でプルリクを作成した場合に発火するようにしましたが、正規表現部分を変えればここの条件は変更可能です。
まとめ
今回はレビュー開始日時およびレビュー完了日時を記録できるようにしましたが、Notion API を使えば任意のプロパティの値を更新できるので他にもやれることはありそうです。ぜひ色々試してみてください。
参考



