コンテンツにスキップ

GitHub Actions Script API実践活用ガイド

この記事は朝の記事のフォローアップです

朝の記事: GitHub Actions自己修復型ワークフロー実装ガイド

この記事の対象者

  • GitHub Actions Script APIを本格運用で活用したい上級者

ゴール

  • REST/GraphQL APIの使い分け戦略を身につける
  • 認証・権限・レート制限への対処法を実装する
  • エラーハンドリングとロギングのベストプラクティスを確立する

アーキテクチャ概要

GitHub Actions内でのScript APIは3つの実行コンテキストで動作する:

Workflow Context → Script Action → GitHub API
     ↓               ↓              ↓
  - GITHUB_TOKEN   - octokit/rest  - Rate Limits
  - Repository     - context obj   - Permissions
  - Event payload  - Error handling - Response format

実装ステップ

ステップ1: API選択戦略とコンテキスト設定

RESTとGraphQLの特性を理解し、用途に応じて使い分ける。

- name: Smart API Usage Pattern
  uses: actions/github-script@v7
  with:
    github-token: ${{ secrets.PAT_TOKEN }}
    script: |
      // REST: シンプルなCRUD操作
      const { data: issues } = await github.rest.issues.listForRepo({
        owner: context.repo.owner,
        repo: context.repo.repo,
        state: 'open',
        labels: 'bug'
      });

      // GraphQL: 複雑なクエリと関連データ取得
      const query = `
        query($owner: String!, $repo: String!) {
          repository(owner: $owner, name: $repo) {
            pullRequests(first: 10, states: OPEN) {
              nodes {
                number
                title
                author { login }
                reviews(first: 5) {
                  nodes { state author { login } }
                }
              }
            }
          }
        }
      `;

      const gqlResult = await github.graphql(query, {
        owner: context.repo.owner,
        repo: context.repo.repo
      });

ステップ2: 権限エスカレーションとトークン管理

個人アクセストークン(PAT)とGITHUB_TOKENの使い分けを実装する。

- name: Permission-aware API Operations
  uses: actions/github-script@v7
  env:
    ADMIN_TOKEN: ${{ secrets.ADMIN_PAT }}
  with:
    script: |
      // 権限チェック関数
      async function checkPermissions(requiredScope) {
        try {
          const { data: user } = await github.rest.users.getAuthenticated();
          const { data: perms } = await github.rest.repos.getCollaboratorPermissionLevel({
            owner: context.repo.owner,
            repo: context.repo.repo,
            username: user.login
          });

          const hasPermission = perms.permission === 'admin' || 
                               perms.permission === requiredScope;

          if (!hasPermission) {
            // フォールバック: Admin PAT使用
            const adminOctokit = github.getOctokit(process.env.ADMIN_TOKEN);
            return adminOctokit;
          }
          return github;
        } catch (error) {
          core.setFailed(`Permission check failed: ${error.message}`);
        }
      }

      // 使用例: Branch Protection設定
      const apiClient = await checkPermissions('admin');
      await apiClient.rest.repos.updateBranchProtection({
        owner: context.repo.owner,
        repo: context.repo.repo,
        branch: 'main',
        required_status_checks: {
          strict: true,
          contexts: ['build', 'test']
        },
        enforce_admins: false
      });

ステップ3: レート制限対応とバッチ処理最適化

API制限に対する段階的バックオフ戦略を実装する。

- name: Rate-limit Resilient Processing
  uses: actions/github-script@v7
  with:
    script: |
      class RateLimitHandler {
        constructor(octokit, options = {}) {
          this.github = octokit;
          this.maxRetries = options.maxRetries || 3;
          this.baseDelay = options.baseDelay || 1000;
        }

        async executeWithRetry(apiCall, retryCount = 0) {
          try {
            const result = await apiCall();

            // レート制限情報をログ
            const remaining = result.headers['x-ratelimit-remaining'];
            if (remaining && parseInt(remaining) < 100) {
              const resetTime = new Date(result.headers['x-ratelimit-reset'] * 1000);
              console.log(`⚠️  Rate limit low: ${remaining} remaining, resets at ${resetTime}`);
            }

            return result;
          } catch (error) {
            if (error.status === 403 && error.message.includes('rate limit')) {
              if (retryCount < this.maxRetries) {
                const delay = this.baseDelay * Math.pow(2, retryCount);
                console.log(`Rate limited. Retrying in ${delay}ms... (${retryCount + 1}/${this.maxRetries})`);

                await new Promise(resolve => setTimeout(resolve, delay));
                return this.executeWithRetry(apiCall, retryCount + 1);
              }
            }
            throw error;
          }
        }
      }

      // 使用例: 大量イシュー処理
      const handler = new RateLimitHandler(github);
      const issues = await handler.executeWithRetry(async () => {
        return github.rest.issues.listForRepo({
          owner: context.repo.owner,
          repo: context.repo.repo,
          per_page: 100,
          state: 'all'
        });
      });

ベンチマーク比較

API種別処理速度レート制限複雑クエリ推奨用途
REST API高速5000req/hourCRUD操作
GraphQL API中速5000points/hour関連データ取得
GitHub App最高速15000req/hour大規模処理

失敗パターンと回避策

症状原因回避策
403 Permission deniedGITHUB_TOKEN権限不足PAT使用またはApp認証
404 Not Foundプライベートリポジトリアクセスrepo権限スコープ追加
Rate limit exceededAPI呼び出し過多バッチ化と段階バックオフ
Invalid GraphQL queryクエリ構文エラースキーマ検証ツール使用

自動化拡張案

  • API使用量監視: CloudWatch/DataDogでレート制限アラート設定
  • キャッシュ戦略: Redis/Memcachedで頻繁アクセスデータ保存
  • ログ集約: ELKスタックでAPI実行履歴の分析・可視化
  • テスト自動化: API呼び出しのモック化とリグレッション検証
  • 権限監査: 定期的なトークン権限チェックと自動更新

次のステップ