@kyanny's blog

My thoughts, my life. Views/opinions are my own.

getsentry/action-github-app-token の注意点

GitHub App の installation access token 発行に必要な処理をカプセル化している action-github-app-token という便利なアクションが Marketplace で配布されているが、GitHub App を複数のアカウントにインストールしている場合にハマることがある。

ハマった状況: テスト用の GitHub App に repo contents read/write permission を付与して、自分のユーザーアカウントと organization アカウントにインストール済みの状態で、以下のようなワークフローを実行すると、checkout-user-private-repo ジョブが常に失敗する(Not Found というエラー)。不思議なのは、このワークフローを開発していた当初はこのジョブは成功していて、checkout-org-private-repo ジョブを追加したりと開発を進める過程でいつの間にか失敗するようになった。失敗し始めてからは、checkout-org-private-repo ジョブをコメントアウトしても依然として失敗し続ける。

jobs:
  checkout-user-private-repo:
    runs-on: ubuntu-latest
    steps:
      - name: my-app-install token
        id: my-app
        uses: getsentry/action-github-app-token@v3
        with:
          app_id: ${{ vars.APP_ID }}
          private_key: ${{ secrets.APP_PRIVATE_KEY }}
      - name: Checkout private repo
        uses: actions/checkout@v4
        with:
          repository: ${{ github.event.inputs.repo }}
          token: ${{ steps.my-app.outputs.token }}

  checkout-org-private-repo:
    runs-on: ubuntu-latest
    steps:
      - name: my-app-install token
        id: my-app
        uses: getsentry/action-github-app-token@v3
        with:
          app_id: ${{ vars.APP_ID }}
          private_key: ${{ secrets.APP_PRIVATE_KEY }}
      - name: Checkout private repo
        uses: actions/checkout@v4
        with:
          repository: ${{ github.event.inputs.org_repo }}
          token: ${{ steps.my-app.outputs.token }}

この謎の挙動は action-github-app-token アクションの実装に起因している。具体的にはたぶんここ。GitHub App の installation のリストを取得して、そのリストの先頭の installation id を使うようになっている。つまり、どの installation に対する installation access token を発行するかは、installation のリストの並び順に依存する。

なぜ先ほどのジョブが途中から失敗するようになったのか、これで説明がつく。この GitHub App を、まず最初に自分のユーザーアカウントにインストールした。その時点では installations[0] はユーザーアカウントへの installation だったので、ユーザーアカウントのリポジトリにアクセスできる installation access token が発行され、ジョブはうまく動いていた。次にこの GitHub App を organization アカウントにインストールした。この時点で installation[0] は organization への installation に変わったため、発行される installation access token は organization のリポジトリにはアクセスできるがユーザーアカウントのリポジトリにはアクセスできなくなった。ワークフローを編集しても installation のリストの順番は変わらないので、ジョブはずっと失敗し続けた。

試しに organization への installation を suspend してワークフローを再実行すると、This installation has been suspended という非常にわかりやすいエラーメッセージが表示された。organization への installation を削除してワークフローを再実行すると、無事に checkout-user-private-repo ジョブが成功した。

getsentry/action-github-app-token は GitHub App のトークンを発行するアクションの中でポピュラーなものの一つで、一番人気の GitHub App token や GitHub 謹製の Create GitHub App Token と比べて使い方が簡単なので気に入っていたが、簡単なぶん細かい制御が効かないという落とし穴があり、今回見事にハマった次第。

そもそも一つの GitHub App を複数のアカウントにインストールして使い回すこと自体がバッドプラクティスなのかも、と書こうとして、いやいや GitHub App ってそういう使い方をするためのものでしょ?と思い直した。これはやっぱり実装が雑、もといシンプルな用途のためのアクションなので、その辺を理解して使うべきということだな。

UPDATE:

やはりというか、すでに同様の問題が報告されていた。

で、scope というパラメータでインストールしたアカウントを指定すれば、installation[0] 決め打ちではなく正しい installation を参照するようになるので、この問題を回避できるとのこと。ソースを読み直すと確かにそういう実装になっていた。

インストールしたスコープがリポジトリ単位の場合にまだバグがあるが、プルリクエストが何年もマージされていない。

つい最近も同じハマり方をした人がいて修正のプルリクエストを送っていた。なんか見覚えあるアイコン・ユーザー名だなと思ったら同僚だった(普段直接の絡みはないが)。