2022年にこんなqiitaを書いたらバズりました。
🔗GitHubを最強のToDo管理ツールにする
その後、GitHub公式に「Projects」機能が登場し、さらに高度なカンバン方式が使えるProjectV2がリリースされたのですが、あまりきちんと使われていない感じがします。
(そうでなければNotionに移行する人が出たりはしないはず)
そして、いまだにこの記事にはアクセスやコメントがつきます。
実は私自身もまだToDo管理はこのGitHub "MyToDO"というリポジトリで行っていることもあり、Isssueは400を超えています。
AI時代にさまざまな改善を施しているのですが、そのうち公開します。
(どうせ書くならノーコードの民にもわかりやすい記事にしたいですね!)
こんな感じでGitHub IssuesとActionsとGoogleカレンダーとSlackを統合してプロジェクトマネージャーと秘書がこんな感じで動いています。
その改善の中で、GitHub側のAPIで「ProjectV2」が使えるはずなのですが、めっちゃつまづく要素があった上に、ネット上にはほとんど情報がなく、Codexや Cursorなどのコーディングエージェントが毎回騙されるので、実験コードとともにTIPSをまとめておきます。
GitHub ProjectV2 へのアクセス: Fine-grained PAT vs Classic PAT
GitHub Actions から ProjectV2 にアクセスする際の PAT(Personal Access Token)の選択と設定方法についてまとめます。
TL;DR
- ProjectsV2 には「ユーザー所有」と「Organization 所有」の2種類がある
- ユーザー所有は Classic PAT 必須(
project必須、repo推奨)。Fine‑grained は仕様上アクセス不可 - Organization 所有は Fine‑grained PAT(Org: Projects RW、Repo: Metadata R)で操作可能
- 本リポの Workflow は
PROJECT_OWNER_KINDで自動分岐(user=Classic、org=Fine‑grained)。GITHUB_TOKEN へのフォールバックはしない - 手動実験は
workflow_dispatch入力(owner_kind/login/number)を指定して実行、一覧取得ステップで存在確認できる
実験日 2025-11-09
直近の実行結果と検証ログを踏まえて内容を補強しました(.github/workflows/test-project-v2-pat.yml で再現可能)。結論は以下のとおりです:
-
GITHUB_TOKENはユーザーレベルの ProjectV2 にアクセス不可(取得できず失敗する) - Fine-grained PAT はユーザーレベルの ProjectV2 にアクセス不可(仕様制限)
- Classic PAT はユーザーレベルの ProjectV2 へアクセス可能(成功を確認)
- 組織レベルの ProjectV2 は Fine-grained PAT で
Projects: read/writeを付ければアクセス可能
再現手順(GitHub Actions)
- Fine-grained の検証:
gh workflow run "Test ProjectV2 PAT Access" -f token_type=fine-grained - Classic の検証:
gh workflow run "Test ProjectV2 PAT Access" -f token_type=classic
最小権限の目安(改訂)
- Classic PAT(ユーザープロジェクトに追加):
project(必須)。Issue/PR も扱うならrepoを併用 - Fine-grained PAT(組織プロジェクトに追加): Organization permissions の Projects: Read and write、Repository permissions の Metadata: Read は必須
問題: ProjectV2 へのアクセス権限エラー
GitHub Actions のワークフローから ProjectV2 にアクセスしようとすると、以下のようなエラーが発生することがあります:
Could not resolve to a ProjectV2 with the number 4.
Number4 #4 というのはプロジェクトの番号です。迂闊に増やすとどんどん増えるので注意。
このエラーは、使用しているトークンに ProjectV2 への適切なアクセス権限がない場合に発生します。
ProjectV2 の種類と PAT の制限
GitHub の ProjectV2 には2つの種類があります:
-
ユーザーレベルの ProjectV2:
https://github.com/users/{username}/projects/{number} -
組織レベルの ProjectV2:
https://github.com/orgs/{orgname}/projects/{number}
Fine-grained PAT の制限事項
GitHub の公式ドキュメントによると、Fine-grained PAT には以下の制限があります:
Fine-grained personal access tokens limitations
- Using fine-grained personal access token to access Projects owned by a user account.
つまり、Fine-grained PAT ではユーザーレベルの ProjectV2 にアクセスできません。
組織レベルの ProjectV2 の場合
組織レベルの ProjectV2 にアクセスする場合は、Fine-grained PAT で organization_projects 権限(read/write)を付与することでアクセス可能です。
解決策
オプション1: Classic PAT を使用(ユーザーレベルの ProjectV2 の場合)
ユーザーレベルの ProjectV2 にアクセスする場合は、Classic PAT が必要です。
Classic PAT の作成手順
- GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)
- 「Generate new token (classic)」をクリック
- 必要なスコープを選択:
-
repo: リポジトリへのアクセス -
project: ProjectV2 へのアクセス(ユーザーレベル)
-
- トークンを生成し、Secret として保存
ワークフローでの使用例(ユーザープロジェクト)
jobs:
add-to-project:
runs-on: ubuntu-latest
steps:
- name: Set token (Classic PAT required)
id: token
run: |
if [ -z "${{ secrets.PAT_MYTODO_PROJECT_TOKEN_CLASSIC }}" ]; then
echo "::error::Classic PAT (PAT_MYTODO_PROJECT_TOKEN_CLASSIC) is required for user-level ProjectV2";
exit 1
fi
echo "token=${{ secrets.PAT_MYTODO_PROJECT_TOKEN_CLASSIC }}" >> $GITHUB_OUTPUT
echo "Using Classic PAT"
- name: Add item to ProjectV2
uses: actions/github-script@v7
with:
github-token: ${{ steps.token.outputs.token }}
script: |
const query = `query($login:String!, $number:Int!){
user(login:$login) { projectV2(number:$number) { id } }
}`;
const res = await github.graphql(query, { login: 'kaitas', number: 4 });
// ...
オプション2: 組織レベルの ProjectV2 に切り替える
組織レベルの ProjectV2 を使用する場合は、Fine-grained PAT でアクセス可能です。
Fine-grained PAT の設定
- GitHub Settings → Developer settings → Personal access tokens → Fine-grained tokens
- 「Generate new token」をクリック
- Repository permissions で以下を設定:
- Actions (read)
- Administration (read)
- Metadata (read - required)
- Secrets, Variables, Webhooks, Workflows (必要に応じて)
-
Organization permissions で以下を設定:
- Projects (read and write) ← 重要
- トークンを生成し、Secret として保存
ワークフローでの使用例
jobs:
add-to-project:
runs-on: ubuntu-latest
steps:
- name: Add item to ProjectV2
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PAT_MYTODO_PROJECT_TOKEN }}
script: |
// 組織レベルの場合は user → organization に変更
const query = `query($login:String!, $number:Int!){
organization(login:$login) { projectV2(number:$number) { id } }
}`;
const res = await github.graphql(query, { login: 'orgname', number: 4 });
// ...
実験結果
実際に2つの PAT で実験した結果:
Fine-grained PAT (PAT_MYTODO_PROJECT_TOKEN)
- 権限: Actions (read), Variables (read), Administration (read), Metadata (read), Webhooks (read), Secrets (read), Workflows (read/write)
- 結果: ユーザーレベルの ProjectV2 にアクセス不可(GitHub の制限事項)
-
エラーメッセージ:
Could not resolve to a ProjectV2 with the number 4.
Classic PAT (PAT_MYTODO_PROJECT_TOKEN_CLASSIC)
-
スコープ:
admin:enterprise,admin:org,admin:org_hook,project,repo,user,workflow - 結果: ユーザーレベルの ProjectV2 にアクセス可能 ✅
- 成功: ProjectV2 の ID とタイトルを取得可能
GITHUB_TOKEN(ワークフロー既定トークン)
-
権限: ワークフロー内
permissions次第だが、ユーザーレベルの ProjectV2 にはアクセス不可 - 結果: ユーザーレベルの ProjectV2 にアクセス不可(取得できず失敗)
- 備考: リポジトリコンテキスト外(ユーザーの ProjectV2)は参照権限が与えられないため。ユーザープロジェクトに対しては Classic PAT を使用する
まとめ: Classic PATが最強だった
| ProjectV2 の種類 | Fine-grained PAT | Classic PAT |
|---|---|---|
| ユーザーレベル | ❌ アクセス不可(制限事項) | ✅ アクセス可能 |
| 組織レベル | ✅ organization_projects 権限で可能 |
✅ アクセス可能 |
推奨事項
- ユーザーレベルの ProjectV2: Classic PAT を使用
-
組織レベルの ProjectV2: Fine-grained PAT で
organization_projects権限を付与(より安全)
セキュリティ上の注意
- Classic PAT は広範囲の権限を持つため、可能な限り Fine-grained PAT を使用することを推奨
- ただし、ユーザーレベルの ProjectV2 にアクセスする必要がある場合は、Classic PAT が必要
- トークンは必ず Secret として保存し、リポジトリに直接記述しないこと
運用上の補足(既存ワークフローからの学び)
-
.github/workflows/add-to-project.ymlはPROJECT_OWNER_KINDに応じて必要な PAT を明示使用します(ユーザー=Classic、組織=Fine-grained)。GITHUB_TOKEN へのフォールバックは行いません - エラーメッセージは「
Could not resolve to a ProjectV2...」または「must have admin rights to view this project」等、トークンの種別や権限により異なります。Project の URL(ユーザー/組織、番号)が正しいかも併せて確認してください
参考リンク
- Fine-grained personal access tokens limitations
- GitHub GraphQL API - ProjectV2
- GitHub Actions - Using secrets
実験コード(Projects 一覧取得 + ターゲット確認)
ワークフローに workflow_dispatch を追加し、一覧取得のみを実行できる実験ステップを追加しました。
dispatch 入力(抜粋)
on:
workflow_dispatch:
inputs:
owner_kind:
description: 'Project owner type (user or org)'
default: 'user'
type: choice
options: [user, org]
login:
description: 'User/Org login name'
default: 'kaitas'
number:
description: 'Project number'
default: '4'
list_first:
description: 'How many projects to list'
default: '20'
perform_add:
description: 'Also add the triggering item to the project (false = list only)'
default: 'false'
一覧取得ステップ(抜粋)
- name: List ProjectV2 (experiment)
id: list_projects
uses: actions/github-script@v7
with:
github-token: ${{ steps.token.outputs.token }}
script: |
const login = process.env.PROJECT_LOGIN;
const number = parseInt(process.env.PROJECT_NUMBER, 10);
const ownerKind = (process.env.PROJECT_OWNER_KIND || 'user').toLowerCase();
const first = parseInt(process.env.LIST_FIRST || '20', 10);
let nodes = [];
if (ownerKind === 'org') {
const q = `query($login:String!, $first:Int!){
organization(login:$login) {
projectsV2(first:$first) { nodes { id title number closed } }
}
}`;
const res = await github.graphql(q, { login, first });
nodes = res?.organization?.projectsV2?.nodes ?? [];
} else {
const q = `query($login:String!, $first:Int!){
user(login:$login) {
projectsV2(first:$first) { nodes { id title number closed } }
}
}`;
const res = await github.graphql(q, { login, first });
nodes = res?.user?.projectsV2?.nodes ?? [];
}
const target = nodes.find(p => p?.number === number);
core.setOutput('target_found', String(!!target));
core.setOutput('target_id', target?.id || '');
core.setOutput('target_title', target?.title || '');
実行コマンド(GitHub CLI)
gh workflow run "Add items to GitHub ProjectV2" \
-f owner_kind=user -f login=kaitas -f number=4 -f list_first=20 -f perform_add=false
注: PAT_MYTODO_PROJECT_TOKEN_CLASSIC(ユーザー)/PAT_MYTODO_PROJECT_TOKEN(組織)の Secrets を事前に設定してください。ローカル変更が未 push の場合、dispatch できません。
実験(2025-11-09 実施)
補助検証として、既存のテスト用ワークフロー(.github/workflows/test-project-v2-pat.yml)を実行。
- 実行:
gh workflow run "Test ProjectV2 PAT Access" -f token_type=classic - 結果(ログ抜粋):
Testing Classic PAT access to ProjectV2...Attempting to access: user=kaitas, project=4❌ ProjectV2 not found
考察(可能性)
- Classic PAT が
projectスコープを含んでいない - Project の番号が誤っている、または対象ログインが異なる
- Project の可視性/権限(閲覧者でない)により参照不可
実行(listing-only)
- 実行:
gh workflow run "Add items to GitHub ProjectV2" -f owner_kind=user -f login=kaitas -f number=4 -f list_first=20 -f perform_add=false - Run URL: https://github.com/kaitas/my-todo/actions/runs/19204699301
- 結果: ワークフロー成功。
List ProjectV2 (experiment)の出力でターゲット検出✅ Target project found: #4 id=PVT_kwHOAE37tM4Am4ON title="MyToDo"- 追加処理はスキップ(listing only)
次アクション
- ブラウザで実URLを確認し、所有者/番号(
/users/<login>/projects/<number>)を再確認 - Classic PAT を再生成し
projectスコープを付与、SecretPAT_MYTODO_PROJECT_TOKEN_CLASSICを更新 - 上記の「一覧取得(experiment)」で実在プロジェクトの番号一覧を取得し、ターゲット番号の突合を行う
- 必要に応じて
ACTIONS_STEP_DEBUG=trueを Secrets に設定してステップログの詳細を有効化
素朴な疑問「ProjectsV2 は Organization 専用」なのか?
結論: いいえ。実はProjectsV2 には「ユーザー所有」と「Organization 所有」の2種類があります。
- ユーザー所有(
/users/<login>/projects/<number>)- UI では通常通り利用可能
- API(Actions/GraphQL)で操作する場合は Classic PAT が必要
- 必須スコープ:
project(+Issue/PR を扱うならrepo推奨)
- 必須スコープ:
- Fine-grained PAT ではアクセス不可(仕様制限)
- Organization 所有(
/orgs/<org>/projects/<number>)- API から操作可能(Fine-grained PAT で OK)
- 必要権限: Org「Projects: Read and write」+ Repo「Metadata: Read」
つまり?推奨される運用は?
- 自動化・最小権限で運用したい場合は Organization プロジェクトを利用(Fine-grained PAT で安全に運用)
- ユーザープロジェクトを使う場合は Classic PAT を Secret に保存して使用(本リポの Workflow は
PROJECT_OWNER_KINDに応じて Classic/Fine-grained を自動選択)