GitHub Actionsでジョブ実行リポジトリとは別のリポジトリを修正してPRを作る
概要
GitOpsをしていると、アプリケーションコードとインフラコード(Kubernetesマニフェストなど)はリポジトリごと分離して、アプリケーションコードの更新に伴ってインフラコードを修正することになります。この際、デプロイにコンテナを利用していればインフラコード上の修正は使用するコンテナイメージ名の修正になり、CI/CDを使ってこの修正を自動化することも多いと思います。
ここではそんな用途のために、GitHub Actionsで実行されたジョブのリポジトリとは別のリポジトリの内容を修正して、PRを作成するための手法を説明します。なお、ここではOrganizationではなく、個人アカウントでの設定方法についてのみ説明します。
手順
現状の手順は以下の通りになります。
- GitHub Appの作成
- GitHub Appの秘密鍵作成
- GitHub Appのインストール
- GitHub Actionsの構成
GitHub Appの作成
まず、自分のアカウントの Settings
を開きます。
左ペインの Developer Settings
を開きます。
GitHub Apps
が選択されている状態で New GitHub App
を選択します。
以下のパラメータで、GitHub Appを作ります。
- GitHub App Name: 任意の名前
- Homepage URL: 修正先のリポジトリURL
- Webhook: 非Active
- Permissions
- Repository Permissions
- Contents: Read and write
- Pull requests: Read and write
- Repository Permissions
- Where can this GitHub App be installed?: Only on this account
すると、こんな画面になります。App IDは後程GitHub Actionsを構成する際に必要になるので、控えておきます。
GitHub Appの秘密鍵作成
GitHub Appの作成後、下の方に秘密鍵作成用のボタンがあるので、こちらをクリックします。
すると、GitHub Appにアクセスするための秘密鍵が作成され、DLされます。このファイルの中身は後程GitHub Actionsを構成するときに使います。
GitHub Appのインストール
左ペインの Install App
を選択します。
自分のアカウントが表示されると思うので、 Install
をクリックします。
Only select repositories
を選んで、修正先となるリポジトリを選択します。
これでこのアプリに、修正先リポジトリに対してclone・push・PR作成をする権限が付与されました。
GitHub Actionsの構成
ここまで来たら後は実際にGitHub Actionsを構成していきます。まずは最終的な設定内容を見ていきます。
change_image_tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
repository: <修正先リポジトリ>
ref: <修正先ブランチ>
path: <clone先の相対パス>
- run: |
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update && sudo apt install gh -y
- run: <manifest内のimageタグを更新する処理>
working-directory: <clone先の相対パス>
- id: generate_token
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
repository: <修正先リポジトリ>
- run: |
GIT_DIFF=$(git diff --name-only)
if [ -z "$GIT_DIFF" ]; then
echo "nothing to do."
else
git config --unset-all http.https://github.com/.extraheader
git remote add from-actions https://github-actions:${GITHUB_TOKEN}@github.com/<修正先リポジトリ>
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
git checkout -b <任意のブランチ名prefix>-${{ github.sha }} &&
git add .
git commit -m "Update image tag to ${{ github.sha }}" &&
git push from-actions <任意のブランチ名prefix>-${{ github.sha }} &&
gh pr create --base <修正先ブランチ> --head <任意のブランチ名prefix>-${{ github.sha }} --title "from <修正先リポジトリ>: sha=${{ github.sha }}" --body ""
fi
working-directory: <clone先の相対パス>
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
各ステップの詳細を見ていきます。
- uses: actions/checkout@v3
with:
repository: <修正先リポジトリ>
ref: <修正先ブランチ>
path: <clone先の相対パス>
ここは特に不思議なことはないかと思います。manifestの入っているリポジトリをcheckoutしてきているだけです。 path
のディレクトリ内にリポジトリの内容が入るので、後続のジョブでgitコマンドを叩くときは working-directory
に path
の値を入れておきます。
- run: |
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update && sudo apt install gh -y
このステップではgithub cli (gh
コマンド) をインストールしています。内容はほぼ公式のこちらのままです。
- run: <manifest内のimageタグを更新する処理>
working-directory: <clone先の相対パス>
このステップで、実際にmanifest内のimageタグを更新していきます。実際にデプロイするインフラによってこのステップの内容は変わると思います。
- id: generate_token
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
repository: <修正先リポジトリ>
このステップでは事前に作成したGitHub Appに対する GITHUB_TOKEN
を取得します。そのためには事前にactionsのシークレット変数として、GitHub AppのApp IdをAPP_ID
に、秘密鍵の内容をAPP_PRIVATE_KEY
に格納しておいてください。
- run: |
GIT_DIFF=$(git diff --name-only)
if [ -z "$GIT_DIFF" ]; then
echo "nothing to do."
else
git config --unset-all http.https://github.com/.extraheader
git remote add from-actions https://github-actions:${GITHUB_TOKEN}@github.com/<修正先リポジトリ>
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
git checkout -b <任意のブランチ名prefix>-${{ github.sha }} &&
git add .
git commit -m "Update image tag to ${{ github.sha }}" &&
git push from-actions <任意のブランチ名prefix>-${{ github.sha }} &&
gh pr create --base <修正先ブランチ> --head <任意のブランチ名prefix>-${{ github.sha }} --title "from <修正先リポジトリ>: sha=${{ github.sha }}" --body ""
fi
working-directory: <clone先の相対パス>
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
最後のステップでは、直前のジョブで作成したGITHUB_TOKEN
を使って修正先リポジトリにトピックブランチをpush、PRの作成を行います。
ハマりどころとして git config --unset-all http.https://github.com/.extraheader
の一行がないと、actionsのデフォルトでロードされるGITHUB_TOKEN
が利用され、ジョブで作成した値が使われずAccess Deniedとなります。
Discussion