5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AI時代にGitHubを最強のToDo管理秘書にする(ProjectV2API編)

5
Last updated at Posted at 2025-11-09

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つの種類があります:

  1. ユーザーレベルの ProjectV2: https://github.com/users/{username}/projects/{number}
  2. 組織レベルの 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 の作成手順

  1. GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)
  2. 「Generate new token (classic)」をクリック
  3. 必要なスコープを選択:
    • repo: リポジトリへのアクセス
    • project: ProjectV2 へのアクセス(ユーザーレベル)
  4. トークンを生成し、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 の設定

  1. GitHub Settings → Developer settings → Personal access tokens → Fine-grained tokens
  2. 「Generate new token」をクリック
  3. Repository permissions で以下を設定:
    • Actions (read)
    • Administration (read)
    • Metadata (read - required)
    • Secrets, Variables, Webhooks, Workflows (必要に応じて)
  4. Organization permissions で以下を設定:
    • Projects (read and write) ← 重要
  5. トークンを生成し、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.ymlPROJECT_OWNER_KIND に応じて必要な PAT を明示使用します(ユーザー=Classic、組織=Fine-grained)。GITHUB_TOKEN へのフォールバックは行いません
  • エラーメッセージは「Could not resolve to a ProjectV2...」または「must have admin rights to view this project」等、トークンの種別や権限により異なります。Project の URL(ユーザー/組織、番号)が正しいかも併せて確認してください

参考リンク


実験コード(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 スコープを付与、Secret PAT_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 を自動選択)
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?