🛠️

GitHub Actionsで別リポジトリーにAPIクライアントのPRを作る

に公開

GA technologiesでソフトウェアエンジニアをしている相川です。

私の所属しているチームでは、メインのプロダクト上でOpenAPI Generatorを用いて生成したAPIクライアントを別リポジトリーにコピーして配布しているのですが、これを自動化できないかということで今回試してみることにしました。

実現したいこと

  • OpenAPI仕様ファイルを元に、RubyのAPIクライアントを自動生成
  • 生成したAPIクライアントを別リポジトリーにプッシュ
  • バージョン番号を含むブランチとPRを自動作成

アーキテクチャー概要

[プロダクトリポジトリー]
  ├── .api-client-version(トリガーファイル)
  ├── docs/api_spec/openapi.yaml
  └── .github/workflows/generate_api_client.yml
                  ↓
         GitHub Actions実行
                  ↓
    OpenAPI Generator でクライアント生成
                  ↓
[APIクライアントリポジトリー]
  └── 生成されたRuby Gem(自動生成されるPRによって更新)

成果物: generate_api_client.yml

on:
  push:
    branches:
      - develop
    paths:
      - '.api-client-version'
jobs:
  generate_api_client:
    name: APIクライアントを生成する
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/create-github-app-token@v2
        id: app-token
        with:
          app-id: ${{ vars.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}
          owner: ${{ github.repository_owner }}
          repositories: |
            api_client_repo
      - uses: actions/checkout@v5
        with:
          token: ${{ steps.app-token.outputs.token }}
          repository: ga-tech/api_client_repo
          path: client
          persist-credentials: false
      - uses: actions/setup-node@v5
        with:
          node-version: 24.x
      - uses: actions/setup-java@v5
        with:
          distribution: oracle
          java-version: 21
      - name: Install OpenAPI Generator
        run: npm install @openapitools/openapi-generator-cli -g
      - name: Set OpenAPI Generator version
        run: npx openapi-generator-cli version-manager set 7.11.0
      - name: Get current version and increment
        id: get-new-version
        run: |
          NEW_VERSION=$(cat .api-client-version | tr -d '\n')
          echo "new-version=$NEW_VERSION" >> $GITHUB_OUTPUT
      - name: Generate API client
        run: openapi-generator-cli generate -i docs/api_spec/openapi.yaml -g ruby --additional-properties=gemName=api_client_name,gemVersion=${{ steps.get-new-version.outputs.new-version }},library=faraday -o ./client/
      - name: Get GitHub App User ID
        id: get-user-id
        run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
        env:
          GH_TOKEN: ${{ steps.app-token.outputs.token }}
      - name: Commit and push changes
        run: |
          cd client
          git checkout -b v${{ steps.get-new-version.outputs.new-version }}
          git config --global user.name "${{ steps.app-token.outputs.app-slug }}[bot]"
          git config --global user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com"
          git add .
          git commit -m "v${{ steps.get-new-version.outputs.new-version }}"
          git remote set-url origin https://${{ steps.app-token.outputs.app-slug }}[bot]:${{ steps.app-token.outputs.token }}@github.com/ga-tech/api_client_repo.git
          git push origin v${{ steps.get-new-version.outputs.new-version }}
          gh pr create \
            --repo ga-tech/api_client_repo \
            --base main \
            --head v${{ steps.get-new-version.outputs.new-version }} \
            --title "v${{ steps.get-new-version.outputs.new-version }}" \
            --body "v${{ steps.get-new-version.outputs.new-version }}"
        env:
          GH_TOKEN: ${{ steps.app-token.outputs.token }}

実装のポイント

GitHub Appを使った別リポジトリーへのアクセス

GitHub ActionsのデフォルトのGITHUB_TOKENには、別リポジトリーへの書き込み権限がありません。そこで今回はGitHub Appを作成し、actions/create-github-app-tokenアクションを使用してトークンを生成しました。

- uses: actions/create-github-app-token@v2
  id: app-token
  with:
    app-id: ${{ vars.APP_ID }}
    private-key: ${{ secrets.APP_PRIVATE_KEY }}
    owner: ${{ github.repository_owner }}
    repositories: |
      api_client_repo

GitHub Appの設定手順は以下の通りです。

  1. GitHub Appを作成(Settings → Developer settings → GitHub Apps)
  2. 必要な権限を設定(Contents: Read and write, Pull requests: Read and write)
  3. 対象リポジトリーにAppをインストール
  4. 操作したいリポジトリーを選択(今回はapi_client_repo)
  5. App IDとPrivate Keyをシークレットに登録

バージョン管理ファイルによるトリガー制御

.api-client-versionファイルを更新することで、ワークフローを明示的にトリガーできます。

on:
  push:
    branches:
      - develop
    paths:
      - '.api-client-version'

ちなみに、OpenAPI仕様ファイルにもバージョンは設定できますが、こちらを使うと生成されるファイルの差分が非常に多岐にわたってしまうため、利便性を優先してこうした仕組みにしました。

OpenAPI Generatorの設定

OpenAPI Generatorのバージョンを固定することで、生成結果の一貫性を保ちます。また、このバージョンも変更するとファイルの差分が多岐にわたってしまうという背景もあります。

- name: Install OpenAPI Generator
  run: npm install @openapitools/openapi-generator-cli -g
- name: Set OpenAPI Generator version
  run: npx openapi-generator-cli version-manager set 7.11.0

GitHub App Botとしてのコミット

GitHub Appの正規のBot IDとメールアドレスを取得し、コミット作成者として設定します。これにより、GitHubのUIでBotアイコンが正しく表示されます。

- name: Get GitHub App User ID
  id: get-user-id
  run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"

- name: Commit and push changes
  run: |
    git config --global user.name "${{ steps.app-token.outputs.app-slug }}[bot]"
    git config --global user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com"

PR自動作成

GitHub CLIを使用してPRを作成します。バージョン番号をブランチ名、タイトル、本文に使用しています。

gh pr create \
  --repo ga-tech/api_client_repo \
  --base main \
  --head v${{ steps.get-new-version.outputs.new-version }} \
  --title "v${{ steps.get-new-version.outputs.new-version }}" \
  --body "v${{ steps.get-new-version.outputs.new-version }}"

実行例

  1. .api-client-version1.0.0から1.1.0に更新
  2. developブランチにプッシュ
  3. GitHub Actionsが実行される
  4. api_client_repoリポジトリーに以下が作成される:
    • ブランチ: v1.1.0
    • PR: タイトル「v1.1.0」
    • コミット: Botユーザーによる自動生成コード

実装時の注意点

Java環境が必要

OpenAPI Generatorの実行にはJava 11以上が必要です。

- uses: actions/setup-java@v5
  with:
    distribution: oracle
    java-version: 21

persist-credentials: false の設定

別リポジトリーのチェックアウト時に、GitHub App tokenを使用するためpersist-credentials: falseを設定します。

- uses: actions/checkout@v5
  with:
    token: ${{ steps.app-token.outputs.token }}
    repository: ga-tech/api_client_repo
    path: client
    persist-credentials: false

重複PRの防止

同じブランチ名でPRが既に存在する場合、gh pr createはエラーになります。必要に応じて、既存のPRチェックやブランチ削除のロジックを追加しても良いかもしれません。

まとめ

GitHub Actionsを活用することで、OpenAPI仕様ファイルからAPIクライアントを自動生成し、別リポジトリーにPRを作成するワークフローを実現できました。これにより、手動作業が削減されたこともありますが、チームメンバー各自がそれぞれOpenAPI Generatorをインストールする必要が無くなったということも大きいと思います。

重複PRの防止をはじめ、作られたPRのタイトルと内容がバージョンだけで素っ気ないなどの課題はありますが、その辺は追々改善していければと考えています。

参考リンク

株式会社GA technologies

Discussion