S3とGitHub WorkflowsでAPI仕様書を自動公開してみる

に公開

⚠️内部向けの公開を意図しており、外部向けに公開するにはCloudFront経由でアクセスさせるような仕組みが必要になろうかと思います。Amplifyなんていうのもあるようです。


OpenAPIで記述したAPI仕様書を、GitHub Workflowsを使って自動でビルドし、S3の静的ウェブサイトホスティング機能で公開する仕組みです。

前提条件

  • 内部の開発者向けに公開することを目的とし、S3バケットへのアクセスはIPアドレス等で制限されているものとします。
  • API仕様書のデザインにはRedocを利用しています。

構成図

今回構築するシステムの全体像は以下の通りです。

<br>

  1. 開発者がファイルを編集し、GitHubにプッシュします。
  2. プッシュをトリガーにGitHub Actionsのワークフローが実行されます。
  3. OIDCを利用してAWSの一時的な認証情報を取得します。
  4. Redocで生成されたHTMLファイルをS3バケットにアップロードします。
  5. 開発者はS3のURLにアクセスし、API仕様書を閲覧します。

ステップ1: S3バケットの準備 (前提)

まず、生成した静的ファイルをホスティングするためのS3バケットを事前に用意しておきます。
静的ウェブサイトホスティングを有効化したS3バケットを作成してください。インデックスドキュメントは index.html に設定します。
内部からのアクセスのみを許可するため、必要に応じてバケットポリシーでIPアドレス制限をかけるなどしておきます。

このバケットは、GitHub Actionsからファイルをアップロードする先になります。


ステップ2: GitHubとAWSのOIDC連携設定

次に、GitHub ActionsがS3を安全に操作できるように、OIDCを設定します。これにより、アクセスキーをSecretsに保存せずにすみます。

  1. IAM IDプロバイダの作成:

    • AWSのIAMコンソールで「IDプロバイダ」を開き、「プロバイダを追加」をクリックします。
    • プロバイダのタイプで OpenID Connect を選択します。
    • プロバイダのURLに https://token.actions.githubusercontent.com を入力します。
    • 「サムプリントを取得」をクリックします。
    • 対象者(Audience)に sts.amazonaws.com を入力して、プロバイダを追加します。
  2. IAMロールの作成:

    • IAMコンソールで「ロール」を開き、「ロールを作成」をクリックします。
    • 信頼されたエンティティのタイプとして「カスタム信頼ポリシー」を選択し、以下のJSONを貼り付けます。

    信頼ポリシーの例:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Federated": "arn:aws:iam::<YOUR_AWS_ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
                },
                "Action": "sts:AssumeRoleWithWebIdentity",
                "Condition": {
                    "StringEquals": {
                        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                    },
                    "StringLike": {
                        "token.actions.githubusercontent.com:sub": "repo:<YOUR_GITHUB_ORG>/<YOUR_REPOSITORY_NAME>:*"
                    }
                }
            }
        ]
    }
    
    • <YOUR_AWS_ACCOUNT_ID>, <YOUR_GITHUB_ORG>, <YOUR_REPOSITORY_NAME> を実際の値に置き換えてください。* の部分を ref:refs/heads/main のように特定のブランチに限定することも可能です。StringLikeで定義すること、ワイルドカードにしておくことで手動実行時にも好きにブランチを決められます。
  3. 許可ポリシーのアタッチ:

    • 次に、このロールにアタッチする許可ポリシーを作成します。
    • 以下のJSONを参考に、S3バケットへの操作を許可するポリシーを作成します。
    • aws s3 sync--deleteを使うためDeleteObjectも付与しています。

    許可ポリシーの例:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "s3:PutObject",
                    "s3:GetObject",
                    "s3:ListBucket",
                    "s3:DeleteObject"
                ],
                "Resource": [
                    "arn:aws:s3:::<YOUR_BUCKET_NAME>",
                    "arn:aws:s3:::<YOUR_BUCKET_NAME>/*"
                ]
            }
        ]
    }
    
    • 作成した許可ポリシーを、先ほど作成したIAMロールにアタッチします。
    • 最後に、作成したIAMロールのARNを控えておきます。

ステップ3: GitHub Workflowsの構築

リポジトリにGitHub Actionsのワークフローファイルを作成します。

リポジトリのルートに .github/workflows/deploy-api-docs.yml という名前でファイルを作成し、以下の内容を記述します。

name: Deploy API Docs to S3

# ワークフローのトリガー設定
on:
  # mainブランチにpushされた時
  push:
    branches:
      - main
  # 手動実行を可能にする
  workflow_dispatch:

# OIDC認証に必要な権限
permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Get branch name
        id: get_branch
        run: |
          BRANCH_NAME=$(echo "${{ github.ref }}" | sed -e 's,refs/heads/,,')
          if [ "$BRANCH_NAME" = "main" ]; then
            echo "S3_PATH=latest" >> $GITHUB_OUTPUT
          else
            echo "S3_PATH=$BRANCH_NAME" >> $GITHUB_OUTPUT
          fi

      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      # package.jsonに設定しているものを呼ぶだけにしています
      - name: Build HTML with Redoc
        run: |
          npm run generate

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::<YOUR_AWS_ACCOUNT_ID>:role/<YOUR_IAM_ROLE_NAME>
          aws-region: ap-northeast-1 # 東京リージョンの例

      # aws s3 cp でも十分かと思います。その場合はDeleteObjectアクションは不要です。
      - name: Deploy to S3
        run: |
          aws s3 sync <生成したファイル> s3://${{ secrets.S3_BUCKET_NAME }}/${{ steps.get_branch.outputs.S3_PATH }} --delete

リポジトリのSecrets設定:

ワークフロー内でS3バケット名を直接記述するのを避けるため、GitHubリポジトリのSecretsに登録します。

  • 以下のSecretを登録します。
    • S3_BUCKET_NAME: ステップ1で作成したS3バケット名

実行と確認

ワークフローをデフォルトブランチにマージしたら、あとは実行するだけです。

  • 自動実行: main ブランチにコミットをプッシュすると、自動的にワークフローが実行され、http://<バケット名>.s3-website.../<リージョン>.amazonaws.com/latest/ に最新の仕様書が公開されます。
  • 手動実行:
    1. リポジトリの「Actions」タブを開きます。
    2. 左のサイドバーから「Deploy API Docs to S3」ワークフローを選択します。
    3. 「Run workflow」ドロップダウンをクリックします。
    4. デプロイしたいブランチ名を選択し、「Run workflow」ボタンを押します。
    5. 完了後、http://<バケット名>.s3-website.../<リージョン>.amazonaws.com/<ブランチ名>/ でプレビューが確認できます。

まとめ

今回は、AWS S3とGitHub Workflowsを連携させ、API仕様書を自動で公開する仕組みを構築しました。
内部向けの資料を展開するにはそこそこ便利です。外部向けはちょっと考える必要がありますが、静的なファイルとして生成される系はこの感じで公開できるため、誰かに意見を聞きたいときも説明が楽になりました。

Discussion