GitHub Projectsで90日より古いdeployment statusが取得できなくなったのは、APIの不具合や権限不足ではなく、GitHubが導入した新しい保持ポリシーによるものです。作成から90日を超えたdeployment statusは自動的に削除され、REST APIとGraphQL APIのどちらからも取得できません。一方、deploymentの現在の状態は維持されます。(The GitHub Blog)
そのため、デプロイの成功・失敗履歴を監査、障害分析、リリース評価に利用している組織は、90日以内に外部ストレージへ保存する仕組みが必要です。実運用では90日ごとではなく、日次または週次でエクスポートし、Webhookによるリアルタイム保存とAPIによる定期照合を組み合わせる方法が安全です。
なお、GitHubは今回の告知で、ストレージ削減や性能改善といった保持期間変更の詳しい理由を公表していません。原因を推測するよりも、クラウドサービス側の新しいデータ保持仕様として対応する必要があります。(The GitHub Blog)
GitHubの90日保持ポリシーで何が変わったのか
2026年7月16日、GitHubはProjectsの高度な検索機能を一般提供すると発表しました。その「Other improvements」の中で、deployment statusに90日間の保持ポリシーを導入したことも案内しています。
変更内容を整理すると、次のとおりです。
| データ | 90日経過後の扱い |
|---|---|
| deploymentの現在の状態 | 今回の変更による影響なし |
| 個々のdeployment status | 自動的に削除 |
| REST APIのstatus一覧 | 90日を超えたstatusは返らない |
GraphQLのDeployment.statuses | 90日を超えたstatusは返らない |
| Projectsで参照する過去のstatus情報 | 長期履歴の保存先としては利用できない |
| 外部保存済みのJSONやデータベース | 自社の保持ポリシーに従って保存可能 |
重要なのは、現在のstateと過去のstatus履歴は別のデータだという点です。
deploymentの現在のstateがSUCCESSで残っていたとしても、過去にQUEUED、IN_PROGRESS、FAILURE、SUCCESSと遷移した時刻や説明、実行者、ログURLまで残るとは限りません。今回削除されるのは、こうした個々の状態遷移を表すdeployment statusです。(The GitHub Blog)
「deployment status」とProjectsの「Status」フィールドは別物
今回の変更で対象になるのは、デプロイ処理に関連付けられたdeployment statusです。GitHub Projectsで利用するカスタムフィールドの「Status」そのものが90日で削除されるという意味ではありません。
混同しやすいデータを整理すると、次のようになります。
| 用語 | 主な用途 | 今回の90日制限 |
|---|---|---|
| Deployment status | デプロイの待機中、実行中、成功、失敗などを記録 | 対象 |
| Deployment state | deploymentの現在の状態 | 今回の変更では維持 |
| ProjectsのStatusフィールド | Backlog、In progress、Doneなどの作業管理 | 対象外 |
| Pull Requestのreview state | 承認、変更要求などのレビュー状況 | 対象外 |
reviews:フィルター | Projects上でPRをレビュー状態により絞り込む | 対象外 |
同じ発表で追加されたreviews:フィルターは、Projects内のPull Requestアイテムをレビュー状態で絞り込むための検索条件です。Reviewersフィールドに記録されたレビュー依頼先と各ユーザーの最新レビューを基に動作しますが、deployment statusの保持期間や復元とは関係ありません。(The GitHub Blog)
対象サービスと互換性条件
今回の変更は、特定のSDKやGitHub CLIのバージョンだけに発生するものではありません。GitHub側でデータそのものが削除されるサーバーサイドの変更です。
| 利用方法 | 影響 | 対応 |
|---|---|---|
| GitHub.com上のGitHub Projects | 影響あり | Projectsを長期履歴の保存先にしない |
| REST API | 影響あり | 90日以内にstatusを取得して外部保存 |
| GraphQL API | 影響あり | Deployment.statusesをページングして外部保存 |
GitHub CLIのgh api | 影響あり | 内部的にRESTまたはGraphQLを使うため同じ結果になる |
| OctokitなどのSDK | 影響あり | SDKを更新しても削除済みstatusは戻らない |
| REST APIのバージョン固定 | 回避不可 | APIヘッダーを旧版にしても削除済みデータは復元できない |
| GitHub Enterprise Server | 告知だけでは判断不可 | 利用中のGHESバージョン別リリースノートを確認 |
REST APIでは、次のエンドポイントでdeployment statusを取得します。
GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses
GraphQLでは、Deploymentオブジェクトのstatusesコネクションを利用します。GitHub公式のGraphQLスキーマでは、Deploymentに現在のstate、最新のlatestStatus、履歴を表すstatusesがそれぞれ定義されています。(GitHub Docs)
GitHub Enterprise Serverについては、今回のGitHub.com向け告知だけを根拠に同じ適用時期だと判断すべきではありません。GHESを利用している場合は、対象バージョンのリリースノートとAPIドキュメントを別途確認してください。
90日超のstatusが見えないときの切り分け方
deployment statusが取得できない原因は、保持期間だけとは限りません。権限不足やページネーションの見落としでも、似た状態になります。
| 症状 | 考えられる原因 | 確認方法 |
|---|---|---|
| 最近のstatusは取得できるが古いものだけない | 90日保持ポリシー | statusの作成時期を確認する |
| 常に30件までしか取得できない | REST APIのデフォルト件数 | per_page=100とページネーションを設定 |
| APIが404を返す | リポジトリ、deployment ID、権限の問題 | URLとトークン権限を確認 |
| GraphQLで一部しか返らない | カーソルページネーション不足 | pageInfo.hasNextPageを確認 |
| deploymentのstateは見えるが履歴がない | 90日保持ポリシーの可能性が高い | statusesと現在のstateを分けて確認 |
| Projectsの検索条件を変えても出ない | 検索フィルターの問題ではない | RESTまたはGraphQLで元データを確認 |
REST APIの一覧取得はデフォルト30件、最大100件です。100件を超える場合は、レスポンスのLinkヘッダーに従って次のページを取得する必要があります。また、非公開リポジトリのdeploymentを取得するトークンには、原則としてリポジトリの「Deployments: read」権限が必要です。(GitHub Docs)
したがって、30件しか表示されない場合は保持期間ではなくページネーション、すべて取得できない場合は権限、古いstatusだけ選択的に消えている場合は90日保持ポリシーを疑うと切り分けやすくなります。
影響を受ける運用と受けにくい運用
影響を受けにくいケース
次のような用途では、現在のdeployment stateが維持されるため、影響は比較的小さくなります。
- 現在、本番環境へのデプロイが成功状態かだけを確認する
- 最新デプロイへのリンクだけをProjectsで参照する
- 直近数週間の開発状況だけを確認する
- 履歴を別のCI/CDツールですでに保存している
影響を受けるケース
一方、次の用途では外部保存が必要です。
- 半年または1年単位でデプロイ成功率を集計する
- 障害発生時に過去の失敗から成功までの遷移を調査する
- 誰が、いつ、どの環境へデプロイしたかを監査する
- リリースごとの説明、environment URL、log URLを残す
- Projectsを使って長期のリリース履歴を可視化する
- デプロイ頻度や変更失敗率を継続的に分析する
- 顧客や監査部門にデプロイ証跡を提出する
deployment statusには、状態だけでなく、作成日時、更新日時、環境名、説明、実行者、environment URL、log URLなどが含まれます。現在のstateだけを残しても、これらの情報を後から再構成することはできません。(GitHub Docs)
推奨する保存構成は「Webhook+定期エクスポート」
長期保存では、次の二段構成が安定します。
GitHub deployment status
│
├─ deployment_status Webhook
│ └─ 発生直後のJSONを外部ストレージへ保存
│
└─ 日次のREST API照合
└─ 取りこぼしを検出して追加保存
Webhookで新しいstatusをすぐ保存する
GitHubのdeployment_status Webhookは、新しいdeployment statusが作成されたときに送信されます。リポジトリ、Organization、GitHub Appで購読でき、payloadにはdeploymentとdeployment statusの情報が含まれます。(GitHub Docs)
Webhook方式には、次の利点があります。
- status作成直後に保存できる
- 90日の期限を意識せず蓄積できる
- 全deploymentをAPIで繰り返し走査する必要がない
- Organization配下の複数リポジトリを集約しやすい
ただし、Webhookだけに依存してはいけません。GitHub公式ドキュメントでは、stateがinactiveのdeployment statusについてはWebhookイベントが発生しないとされています。受信障害や設定ミスによる取りこぼしも考慮し、APIによる定期照合を併用するのが安全です。(GitHub Docs)
REST APIで日次または週次の照合を行う
REST APIでは、最初にdeployment一覧を取得し、各deployment IDに対してstatus一覧を取得します。
GET /repos/{owner}/{repo}/deployments
GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses
Fine-grained personal access tokenやGitHub Appトークンを使用する場合は、対象リポジトリの「Deployments: read」権限を付与します。(GitHub Docs)
90日間保持されるからといって、89日目にまとめて実行する設計は避けるべきです。バッチ停止、認証切れ、ネットワーク障害、休日対応の遅れが重なると、そのまま削除期限を超えるためです。
実務上の目安は次のとおりです。
| 規模・重要度 | 実行間隔 |
|---|---|
| 監査対象の本番環境 | 日次 |
| 一般的な業務システム | 日次または週次 |
| 検証用リポジトリ | 週次 |
| Webhookを併用している大規模Organization | Webhook即時保存+日次照合 |
告知では90日の境界がどの時刻基準で処理されるか、削除バッチがいつ実行されるかまでは示されていません。境界日に依存せず、十分な余裕を持って取得してください。(The GitHub Blog)
REST APIでdeployment statusをJSONLへ保存する例
次のスクリプトは、指定したリポジトリのdeploymentとstatusをJSON Lines形式で保存する例です。初回の退避や、小規模から中規模のリポジトリで利用できます。
#!/usr/bin/env bash
set -euo pipefail
OWNER="${OWNER:?OWNERを設定してください}"
REPO="${REPO:?REPOを設定してください}"
OUT_DIR="${OUT_DIR:-deployment-archive}"
FILE_STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
CAPTURED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
mkdir -p "${OUT_DIR}"
DEPLOYMENTS_FILE="${OUT_DIR}/${OWNER}-${REPO}-deployments-${FILE_STAMP}.jsonl"
STATUSES_FILE="${OUT_DIR}/${OWNER}-${REPO}-statuses-${FILE_STAMP}.jsonl"
: > "${STATUSES_FILE}"
gh api --paginate \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2026-03-10" \
"/repos/${OWNER}/${REPO}/deployments?per_page=100" \
| jq -c '.[]' > "${DEPLOYMENTS_FILE}"
jq -r '.id' "${DEPLOYMENTS_FILE}" | while read -r deployment_id; do
gh api --paginate \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2026-03-10" \
"/repos/${OWNER}/${REPO}/deployments/${deployment_id}/statuses?per_page=100" \
| jq -c \
--arg repository "${OWNER}/${REPO}" \
--arg deployment_id "${deployment_id}" \
--arg captured_at "${CAPTURED_AT}" \
'.[] | . + {
repository_full_name: $repository,
deployment_id: $deployment_id,
captured_at: $captured_at
}' >> "${STATUSES_FILE}"
done
echo "Deployments: ${DEPLOYMENTS_FILE}"
echo "Statuses: ${STATUSES_FILE}"
実行時は、GH_TOKEN、OWNER、REPOを設定します。
export GH_TOKEN="github_pat_xxxxxxxxx"
export OWNER="example-org"
export REPO="example-repository"
bash export-deployment-statuses.sh
公式RESTドキュメントの現行サンプルではX-GitHub-Api-Version: 2026-03-10が使用されています。APIバージョンは将来更新される可能性があるため、運用開始時には最新の公式ドキュメントも確認してください。(GitHub Docs)
このスクリプトが出力したファイルは、ローカルディスクやActionsランナー上に残すだけでは不十分です。処理終了後、必ずS3互換ストレージ、Azure Blob Storage、Google Cloud Storage、社内オブジェクトストレージなどへ転送します。
GitHub Actionsで日次実行する場合の設定例
スクリプトを./scripts/export-deployment-statuses.shに保存した場合、次のようなワークフローで日次実行できます。
name: Archive deployment statuses
on:
schedule:
- cron: "17 2 * * *"
workflow_dispatch:
permissions:
contents: read
deployments: read
jobs:
export:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Export deployment statuses
env:
GH_TOKEN: ${{ github.token }}
OWNER: example-org
REPO: example-repository
run: bash ./scripts/export-deployment-statuses.sh
- name: Upload to external storage
run: |
tar -czf deployment-archive.tgz deployment-archive/
# この部分を組織で利用している
# S3、Azure Blob、GCSなどのアップロード処理に置き換える
./scripts/upload-archive.sh deployment-archive.tgz
GitHub Actionsのartifactは、一時的な受け渡しや処理確認には利用できますが、長期監査データの唯一の保存先にはしない方が安全です。最終保存先には、自社で保持期間、削除防止、暗号化、アクセス制御を管理できる外部ストレージを使用します。
GraphQLで保存する場合のクエリ例
GraphQLを利用しているシステムでは、Deployment.statusesをカーソルページネーションしながら保存します。
query DeploymentStatusArchive($deploymentId: ID!, $cursor: String) {
node(id: $deploymentId) {
... on Deployment {
id
databaseId
commitOid
environment
state
statuses(first: 100, after: $cursor) {
nodes {
id
createdAt
updatedAt
state
description
environment
environmentUrl
logUrl
creator {
login
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
pageInfo.hasNextPageがtrueの間は、endCursorを次のcursorとして再実行します。最初の100件だけ保存して終了すると、status数が多いdeploymentで履歴を取りこぼします。GraphQLのDeploymentには、現在のstateと履歴のstatusesが別フィールドとして用意されています。(GitHub Docs)
外部ストレージに最低限保存すべき項目
単にAPIレスポンスをCSVへ変換するだけでは、後の用途に必要な関連情報が失われることがあります。最低限、次の項目を残してください。
| 項目 | 用途 |
|---|---|
repository_full_name | リポジトリの特定 |
deployment_id | deploymentとの関連付け |
deployment_status_id | 重複排除と更新判定 |
shaまたはcommitOid | 対象コミットの特定 |
ref | ブランチ、タグの特定 |
environment | production、stagingなどの識別 |
state | success、failure、in_progressなど |
description | デプロイ結果の説明 |
creator | statusを作成した主体の確認 |
created_at | status作成時刻 |
updated_at | status更新時刻 |
environment_url | デプロイ先URL |
log_url | 実行ログへの参照 |
captured_at | 自社側で取得した時刻 |
| 生のJSON payload | 将来の項目追加や再解析への備え |
保存時は、deployment_status_idを一意キーとしてupsertできる構造にすると、Webhookと定期バッチの両方から同じstatusを受信しても重複しません。
また、log_urlを保存するだけではログ本文を保存したことにはなりません。リンク先のCI/CDログやartifactにも別の保持期限が設定されている可能性があるため、監査でログ本文が必要なら、ログそのものも別途アーカイブします。
保存先の選び方
| 保存先 | 向いている用途 | 注意点 |
|---|---|---|
| オブジェクトストレージ | 生JSONの長期保存、監査証跡 | 検索や集計には追加処理が必要 |
| リレーショナルデータベース | リポジトリ別、環境別の検索 | スキーマ変更への対応が必要 |
| データウェアハウス | 成功率、頻度、失敗傾向の分析 | 取り込みコストと権限管理が必要 |
| SIEM・ログ基盤 | 障害調査、横断検索、アラート | 長期保存コストを確認する |
| Gitリポジトリ | 小規模なJSONの簡易保存 | 容量増大、機密情報、削除履歴に注意 |
| GitHub Actions artifact | 一時的な処理結果 | 長期保存の最終保管庫には不向き |
実務では、生JSONをオブジェクトストレージへ保存し、分析に必要な項目だけデータベースやデータウェアハウスへ取り込む構成が扱いやすくなります。
運用で失敗しやすいポイント
90日ごとのバッチにする
90日間保持されるからといって、90日間隔で実行すると余裕がありません。日次または週次で実行し、最終成功日時を監視してください。
Webhookだけで完全だと考える
inactive stateではdeployment_status Webhookが発生しません。APIによる定期照合を残す必要があります。(GitHub Docs)
REST APIの先頭30件だけ保存する
REST APIはデフォルト30件です。per_page=100を設定したうえで、全ページを取得してください。(GitHub Docs)
GraphQLの最初のページだけ保存する
statuses(first: 100)だけでは101件目以降を取得できません。pageInfoを確認し、カーソルを更新しながら繰り返します。(GitHub Docs)
current stateだけを保存する
現在の状態だけでは、途中で何回失敗したか、いつ復旧したか、どのログに関連付けられていたかを確認できません。各statusをイベント単位で保存してください。
log URLだけで十分だと考える
リンク先のログが先に削除される可能性があります。監査要件にログ本文が含まれるなら、CI/CD側のログ保持も同時に設計します。
保存成功を監視していない
ワークフローが停止したまま90日を超えると、後から穴を埋められません。少なくとも次の項目を監視します。
- 最終エクスポート成功日時
- 保存したstatus件数
- APIエラー件数
- Webhookの最終受信日時
- 取得した最新statusの
created_at - 前回まで存在したリポジトリが対象外になっていないか
すでに90日を超えたstatusは復元できるのか
GitHub側で削除され、REST APIとGraphQL APIの両方に現れなくなったstatusを、APIオプションや検索フィルターで再表示することはできません。今回の告知でも、削除済みstatusを復元するAPIや保持期間を延長する設定は案内されていません。(The GitHub Blog)
復元できる可能性があるのは、別の場所に情報が残っている場合です。
- GitHub Actionsの過去のworkflow run
- 外部CI/CDサービスの実行履歴
- デプロイツールのログ
- SIEMや監視基盤
- OrganizationまたはリポジトリのWebhook受信ログ
- 過去に取得したRESTまたはGraphQLレスポンス
- リリース管理システムや変更管理票
ただし、これらのログからGitHubのDeploymentStatusオブジェクトを完全に再現できるとは限りません。status ID、作成者、説明、environment URLなどが必要な場合は、削除前のAPIレスポンスを保存しておく必要があります。
今すぐ実施すべき対応
最初に、監査や分析の対象となるリポジトリを一覧化し、現時点で取得できるdeployment statusをすぐにエクスポートします。
次に、日次または週次のREST API取得を設定し、Organization規模で運用する場合はdeployment_status Webhookによる即時保存を追加します。保存先では、生JSONを残しながらdeployment_status_idを一意キーとして重複排除してください。
最後に、エクスポート処理そのものを監視します。90日保持は「90日後に対応すればよい」という猶予ではなく、取得に失敗し続けた場合にデータが失われる最終期限です。Projectsや現在のdeployment stateを長期履歴の代わりにせず、必要な証跡はGitHubの保持期限内に自社管理のストレージへ移すことが重要です。

コメント