背景
みなさん、GitHub Actionsは使っているでしょうか。最近ではDependaBotを使ってパッケージの最新化を監視し自動的にマージしたりとかなり進んだ機能を持っています。たくさんの機能がありついていけませんが、少しずつ触らないとと思っています。
先日、このツール群を使って、プロジェクトの依存ライブラリを自動的に最新状態に更新するようなワークフロー設定を試みました。設定はプルリクエストの自動作成とマージを通じてDockerイメージのビルドと公開までの一連の全工程を、完全自動化することを目指しました。設定を終え、マージも自動で完了して下記の通り問題ないように見えます。この時点で大分満足しています。
しかし、実際にはマージ後にトリガーするはずのDockerイメージのビルドとプッシュを行うワークフローが、予定通りに起動していないという問題が発生していました。
課題
この問題の原因は、Dependabotによるプルリクエストの自動マージ後に予定されていたDockerのビルドとプッシュのアクションがトリガーされなかったことでした。この問題の根本原因は、GitHub Actionsのセキュリティポリシーにあり、自動化されたプロセスによる無限のループや潜在的な悪用を防ぐための措置のようです。コミュニティでも意見交換と解決方法が共有されていますので、せっかくなのでDockerイメージのプッシュまでやってみます。
解決方法
パーソナルアクセストークンを使用する
GithubActionsでは、標準的にGithubの操作をするためのトークンsecrets.GITHUB_TOKEN
が使用可能で、リポジトリのほとんどの操作が可能です。実際プルリクエストを操作するためにこのトークンを使用しています。しかし、前述の理由からこのトークン経由からの操作はアクションを経由したトリガーとみなされ、他の設定しているワークフローを起動することができません。
そこで、パーソナルアクセストークン(PAT)を使用することで、自動生成されたプルリクエストから他のワークフローをトリガーすることが可能になります。私が試した方法の中で最小限の変更で修正可能でかつ、標準的な回避策としてGitHubによって推奨されています。手順は以下の通りです:
-
GitHubの設定で新しいPATを生成します
-
トークン生成時には以下の権限を有効にします:
-
repo
(プライベートリポジトリの完全な制御) read:org
read:discussion
-
-
生成したトークンをリポジトリのSettings > Secrets and variablesから
REPO_SCOPED_TOKEN
として追加する
-
ワークフローの定義で、
${{ secrets.GITHUB_TOKEN }}
の代わりに${{ secrets.REPO_SCOPED_TOKEN }}
を使用します- name: Approve and enable auto-merge for Dependabot PRs if: | ${{ ( steps.metadata.outputs.package-ecosystem == 'hex' && steps.metadata.outputs.update-type == 'version-update:semver-patch' ) || steps.metadata.outputs.package-ecosystem == 'github-actions' }} run: | gh pr review --approve "$PR_URL" gh pr edit "$PR_URL" -t "(auto merged) $PR_TITLE" gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{ github.event.pull_request.html_url }} PR_TITLE: ${{ github.event.pull_request.title }} GITHUB_TOKEN: ${{ secrets.REPO_SCOPED_TOKEN }} ⭐️変更
-
この後、mainへのpushトリガーを設定しているワークフローが自動的にトリガーします
画像の通り、PATを使用すると、自動生成されたプルリクエストがGitHub Actionsではなく、トークンの所有者である個人ユーザーによって作成されたものと見なされます。そのため、リポジトリのポリシーによっては、このユーザーが変更をリクエストしたりプルリクエストを承認したりすることができなくなる可能性があります。
その他の解決方法
公式サイトでは他にも下記のような解決方法を提供しています。
-
デフォルトのGITHUB_TOKENを使用する方法
- プルリクエストを作成して、手動で一度閉じてから再度開くことで
on: pull_request
ワークフローを起動し、チェックを追加します。誤ってチェックなしでプルリクエストをマージすることを防ぐために、ブランチ保護ルールを使用します。
- プルリクエストを作成して、手動で一度閉じてから再度開くことで
-
SSH(デプロイキー)を使用してプルリクエストブランチをプッシュする方法
- デプロイキーはリポジトリごとに設定できるため、PATを使用するよりも安全だと言えます。しかし、この方法では
on: push
ワークフローのみがトリガーされます。
- デプロイキーはリポジトリごとに設定できるため、PATを使用するよりも安全だと言えます。しかし、この方法では
-
マシンアカウントを使用して、そのフォークからプルリクエストを作成する方法
- これは最も安全な方法です。PATはマシンアカウントのフォークのみにアクセス許可を与えるため、メインリポジトリには影響しません。この方法では
on: pull_request
ワークフローが起動しますが、フォーク内のプッシュイベントによってはon: push
ワークフローは起動しません。
- これは最も安全な方法です。PATはマシンアカウントのフォークのみにアクセス許可を与えるため、メインリポジトリには影響しません。この方法では
-
GitHubアプリを使用してトークンを生成する方法
- GitHubアプリによって生成されたトークンはPATを使用するよりも安全です。GitHubアプリのアクセス権限はより細かく設定でき、アプリがインストールされているリポジトリにのみスコープされます。この方法では
on: push
とon: pull_request
の両方のワークフローがトリガーされます。
- GitHubアプリによって生成されたトークンはPATを使用するよりも安全です。GitHubアプリのアクセス権限はより細かく設定でき、アプリがインストールされているリポジトリにのみスコープされます。この方法では
詳細は下記を確認ください。
さいごに
お疲れ様でした。ワークフローは構成要素が多く全てを把握しづらいと思います。公開リポジトリにdockerイメージ公開までのワークフローをおいていますので参考にしてください。リポジトリは下記のワークフローから構成される、最小限の構成を持っていますので、同じような要件を満たしたい新規プロジェクトの下敷きにしてもいいと思います。
-
dependabot update:
- 毎日ccxtの更新有無を確認
- 更新があればmainへのプルリクエストを発行
-
dependabot auto merge:
- botから発行されたプルリクエストを見つけたらトリガーする
- 自動的にdockerイメージをビルド&テストする
- テストを通過したらプルリクエストを承認・マージする
-
docker build and test:
- mainブランチにpush(変更があった)時にトリガー
- dockerイメージをビルドしてDockerHubにpush
参考