（検証中）GitHub Webhookを使った操作自動ロールバックシステムの構築手順のメモ

※以下はClaude 3.7 Sonnetで聞いてみただけの記事なので未検証です。
これから検証するためのメモです。これを見て試して失敗しても責任は負いかねますので
ご了承ください。

このガイドでは、GitHubで何らかの操作（例：イシューの作成）が行われたときに自動的にそれを検知し、その操作を取り消す（ロールバック）するシステムをAWS Lambda、API Gateway、GitHub Webhookを使って構築する方法を詳しく説明します。

## 目次
1. [GitHub個人アクセストークンの作成](#1-github個人アクセストークンの作成)
2. [Lambda関数の作成](#2-lambda関数の作成)
3. [API Gatewayの設定](#3-api-gatewayの設定)
4. [GitHub Webhookの設定](#4-github-webhookの設定)
5. [テスト方法](#5-テスト方法)
6. [トラブルシューティング](#6-トラブルシューティング)

## 1. GitHub個人アクセストークンの作成

GitHub APIを呼び出すには、アクセストークンが必要です。

1. **GitHubにログイン**: あなたのGitHubアカウントにログインします
2. **設定ページに移動**: 右上のプロフィールアイコンをクリックし、「Settings」を選択
3. **開発者設定に移動**: 左側のメニューの下部にある「Developer settings」をクリック
4. **個人アクセストークンを選択**: 「Personal access tokens」→「Tokens (classic)」をクリック
5. **新しいトークンを生成**: 「Generate new token」→「Generate new token (classic)」をクリック
6. **トークンの設定**:
   - Token name: `GitHubWebhookRollback`など分かりやすい名前
   - Expiration: 有効期限（まずは30日程度に設定）
   - Select scopes: 最低限以下の権限が必要
     - `repo` （リポジトリへのフルアクセス権）
   - 「Generate token」ボタンをクリック
7. **トークンをコピー**: 生成されたトークンをコピーして安全な場所に保存（この画面を閉じると二度とトークン全体を見ることができません）

## 2. Lambda関数の作成

1. **AWSコンソールにログイン**: AWSマネジメントコンソールにログインします
2. **Lambda画面に移動**: サービス一覧から「Lambda」を選択
3. **関数の作成**:
   - 「関数の作成」ボタンをクリック
   - 「一から作成」を選択
   - 関数名: `GitHubWebhookHandler`
   - ランタイム: `Node.js 20.x`
   - アーキテクチャ: `x86_64`
   - 「関数の作成」ボタンをクリック

4. **Lambda関数のコードを記述**:
   - 表示されるコードエディタに以下のコードを入力:

```javascript
const https = require('https');

// 環境変数からGitHubのアクセストークンを取得
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;

exports.handler = async (event) => {
    try {
        // リクエストボディをパース
        const webhookPayload = JSON.parse(event.body);
        console.log('Received webhook payload:', JSON.stringify(webhookPayload));
        
        // イベントタイプを確認
        const githubEvent = event.headers['X-GitHub-Event'] || event.headers['x-github-event'];
        console.log('GitHub Event:', githubEvent);
        
        // リポジトリ情報とアクション情報を取得
        const repo = webhookPayload.repository.full_name;
        const action = webhookPayload.action;
        console.log(`Repository: ${repo}, Action: ${action}`);
        
        // 適切なロールバックアクションを実行
        let result;
        if (githubEvent === 'issues' && action === 'opened') {
            // イシューが作成された場合、そのイシューを閉じる
            const issueNumber = webhookPayload.issue.number;
            result = await closeIssue(repo, issueNumber);
            console.log(`Closed issue #${issueNumber}`);
        } else {
            console.log(`No rollback action defined for event '${githubEvent}' with action '${action}'`);
            result = { message: 'No rollback action performed' };
        }
        
        // 成功レスポンスを返す
        return {
            statusCode: 200,
            body: JSON.stringify({
                message: 'Webhook processed successfully',
                result: result
            })
        };
    } catch (error) {
        // エラーログ
        console.error('Error processing webhook:', error);
        
        // エラーレスポンスを返す
        return {
            statusCode: 500,
            body: JSON.stringify({
                message: 'Error processing webhook',
                error: error.message
            })
        };
    }
};

// GitHubのイシューを閉じる関数
async function closeIssue(repo, issueNumber) {
    const options = {
        hostname: 'api.github.com',
        path: `/repos/${repo}/issues/${issueNumber}`,
        method: 'PATCH',
        headers: {
            'User-Agent': 'AWS-Lambda',
            'Content-Type': 'application/json',
            'Authorization': `token ${GITHUB_TOKEN}`,
            'Accept': 'application/vnd.github.v3+json'
        }
    };
    
    const data = JSON.stringify({
        state: 'closed'
    });
    
    return new Promise((resolve, reject) => {
        const req = https.request(options, (res) => {
            let responseBody = '';
            
            res.on('data', (chunk) => {
                responseBody += chunk;
            });
            
            res.on('end', () => {
                if (res.statusCode >= 200 && res.statusCode < 300) {
                    resolve(JSON.parse(responseBody));
                } else {
                    reject(new Error(`GitHub API responded with status code ${res.statusCode}: ${responseBody}`));
                }
            });
        });
        
        req.on('error', (error) => {
            reject(error);
        });
        
        req.write(data);
        req.end();
    });
}
```

5. **環境変数の設定**:
   - Lambda関数の「設定」タブをクリック
   - 「環境変数」セクションを選択し、「編集」ボタンをクリック
   - 新しい環境変数を追加:
     - キー: `GITHUB_TOKEN`
     - 値: 先ほど生成したGitHubアクセストークン
   - 「保存」ボタンをクリック

6. **タイムアウトの設定**:
   - Lambda関数の「設定」タブで、「一般設定」セクションの「編集」ボタンをクリック
   - タイムアウトを10秒に設定（デフォルトの3秒だと足りない場合があります）
   - 「保存」ボタンをクリック

7. **コードのデプロイ**
   - 「コード」タブから「Deploy」ボタンをクリック

## 3. API Gatewayの設定

1. **API Gatewayコンソールに移動**: AWSコンソールのサービス一覧から「API Gateway」を選択
2. **APIの作成**:
   - 「APIを作成」ボタンをクリック
   - 「REST API」を選択し、「構築」ボタンをクリック
   - 「REST API」が選択されていることを確認
   - API名: `GitHubWebhookAPI`
   - 「APIの作成」ボタンをクリック

3. **リソースの作成**:
   - 「リソースの作成」ボタンをクリック
   - リソース名: `webhook`
   - リソースパス: `/webhook`
   - 「CORS（Cross-Origin Resource Sharing）の有効化」はチェックしない
   - 「リソースの作成」ボタンをクリック

4. **メソッドの作成**:
   - 新しく作成した `/webhook` リソースを選択
   - 「メソッドの作成」ボタンをクリック
   - ドロップダウンから「POST」を選択し、チェックマークをクリック
   - 統合タイプ: 「Lambda関数」
   - Lambda関数: 先ほど作成した `GitHubWebhookHandler` を入力（入力中に候補が表示されます）
   - 「メソッドを作成」ボタンをクリック
   - 「Lambda関数に権限を追加することを確認します」というポップアップが表示されたら「OK」をクリック

5. **メソッドレスポンスの設定**:
   - 「メソッドレスポンス」をクリック
   - 「レスポンス 200」を編集
   - 「ヘッダーの追加」をクリック
   - 「Access-Control-Allow-Origin」と入力し、「保存」ボタンをクリック

6. **統合レスポンスの設定**:
   - 「統合レスポンス」をクリック
   - 「デフォルト-レスポンス」を編集
   - 「ヘッダーマッピング」セクションで「Access-Control-Allow-Origin」の値に「'*'」と入力（シングルクォートを含む）
   - 「保存」ボタンをクリック

7. **APIのデプロイ**:
   - 「APIをデプロイ」を選択
   - デプロイされるステージ: 「新しいステージ」
   - ステージ名: `prod`
   - 「デプロイ」ボタンをクリック

8. **エンドポイントURLの取得**:
   - デプロイされたAPIの情報が表示される画面で、「URLを呼び出す」に記載されたURLをメモ
   - このURLは次のステップのGitHub Webhook設定で使用します 
   - 例: `https://abcd1234.execute-api.us-east-1.amazonaws.com/prod/webhook`
※webhookを末尾に足す

## 4. GitHub Webhookの設定

1. **対象GitHubリポジトリに移動**: WebhookでイベントをキャッチしたいGitHubリポジトリのページに移動
2. **設定ページに移動**: リポジトリページの上部メニューから「Settings」タブをクリック
3. **Webhookセクションに移動**: 左側のメニューから「Webhooks」を選択
4. **新しいWebhookを追加**:
   - 「Add webhook」ボタンをクリック
   - Payload URL: API GatewayのエンドポイントURL（`/webhook`まで含む）を入力
   - Content type: `application/json` を選択
   - Secret: 空のままでも良いですが、セキュリティを強化したい場合は秘密のトークンを入力し、Lambda関数でも対応する必要があります
   - どのイベントでWebhookを起動するか: `Let me select individual events` を選択
   - 「Issues」にチェックを入れる（イシュー作成イベントをキャッチする場合）
   - 「Active」にチェックが入っていることを確認
   - 「Add webhook」ボタンをクリック

## 5. テスト方法

1. **イシューを作成**:
   - GitHubリポジトリページに移動
   - 「Issues」タブをクリック
   - 「New issue」ボタンをクリック
   - タイトルと説明を入力して「Create」ボタンをクリック

2. **結果の確認**:
   - イシューが作成された後、すぐに閉じられる（ロールバックされる）はずです
   - イシューリストに戻ると、作成したイシューが「Closed」状態になっていることが確認できます

3. **Lambdaログの確認**:
   - AWSコンソールのLambda関数ページに移動
   - 「モニタリング」タブをクリック
   - 「ログ」をクリック（CloudWatchログに移動します）
   - 最新のログストリームをクリックして、処理の詳細を確認

4. **GitHub Webhookの配信履歴確認**:
   - リポジトリの「Settings」→「Webhooks」に移動
   - 設定したWebhookをクリック
   - 「Recent Deliveries」セクションでWebhookの配信履歴と結果を確認できます

## 6. トラブルシューティング

### Webhook呼び出しが成功しない場合:
1. **API GatewayのCORSを確認**: 統合レスポンスで正しくCORSヘッダーが設定されているか確認
2. **Lambda関数の権限を確認**: API GatewayからLambda関数を呼び出す権限が付与されているか確認
3. **GitHubのWebhook設定を確認**: ペイロードURLやコンテンツタイプが正しく設定されているか確認
4. **Lambdaのログを確認**: CloudWatchログでエラーメッセージを確認
5. **GitHub APIの権限を確認**: 個人アクセストークンに十分な権限が付与されているか確認

### イシューが閉じられない場合:
1. **GitHubトークンの有効性を確認**: トークンが有効であることを確認（期限切れでないか）
2. **Lambda関数のタイムアウトを確認**: 処理が完了する前にタイムアウトしていないか確認
3. **Webhookペイロードの構造を確認**: Lambda関数内でWebhookペイロードが正しくパースされているか確認

## 拡張方法

このシステムは、イシューのクローズ（取り消し）だけでなく、さまざまなGitHubイベントに対応できるよう拡張できます：

1. **プルリクエストの自動クローズ**: 特定の条件下でプルリクエストを自動的に閉じる
2. **コメントの自動追加**: イシューやプルリクエストに自動的にコメントを追加
3. **特定のラベルの追加/削除**: 条件に基づいてラベルを追加または削除
4. **承認者の自動割り当て**: プルリクエストに特定の承認者を自動的に割り当て

Lambda関数のコードを修正することで、これらの機能を実装できます。