GitHub Actionsでブランチに合わせてリリース用PRを自動生成する
きっかけ
スプリントで実装した内容をリリースする際、PRを毎回手作業で作成しているのですが、「ちょっと手間だな。自動化したいな〜」と思っていました。
年末になって時間ができたので、折角なら勉強も兼ねてアクションを自作しようと思います。
要件
- developにマージされたら、stageへのPRが自動で生成されること
- stageにマージされたら、mainへのPRが自動で生成されること
- PRのタイトルやボディが指定できること
- 既にリリース用PRが存在する場合はスキップすること
完成形はこちら
いきなりですが、完成形のアクションはこちらになります。
name: Create a pull request for release.
on:
push:
branches: [ stage, develop ]
jobs:
create-release-pr:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v2
# リリース用PRが既に存在するかどうかをチェック
- name: Check if pr exists
id: check_pr
run: |
pr_title=${{ (github.ref == 'refs/heads/stage' && 'Stage') || 'Develop' }}
base_branch=${{ (github.ref == 'refs/heads/stage' && 'main') || 'stage' }}
echo "::set-output name=count::$(gh pr list -S ${pr_title}' in:title' -B $base_branch | wc -l)"
echo "::set-output name=pr_title::$pr_title"
echo "::set-output name=base_branch::$base_branch"
# リリース用PRを作成
- name: Create release pr
if: ${{ steps.check_pr.outputs.count == 0 }}
run: |
gh pr create -B ${{ steps.check_pr.outputs.base_branch }} -t ${{ steps.check_pr.outputs.pr_title }} -b ""
内容について順に確認していきましょう。
developにマージされた際、自動でstageへのPRを作成する
マージされたタイミングでPRを作成したいです。
hubコマンドを使う方法もあるのですが、今回はGitHub CLIを使っていこうと思います。
GitHub CLIでPRを作成するコマンドは以下です。
gh pr create [flags]
詳しくは公式ドキュメントに記載されているので、気になる方はそちらをご確認ください。
まずはシンプルなアクションを作成してみました。
name: Create a pull request for release.
on:
push:
branches: [ develop ]
jobs:
create-release-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Create release pr
run: gh pr create -B "stage" -t "Develop" -b ""
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ここでdevelopブランチにプッシュされた際に発火するようにしています。
push:
branches: [ develop ]
続いて、GitHub CLIコマンドでPRを新規作成していきましょう。
ここではstageブランチに対して、タイトルは"Develop"、ボディが空のPRを作成するようにしています。
- name: Create release pr
run: gh pr create -B "stage" -t "Develop" -b ""
-b, --body <string>
Body for the pull request
B, --base <branch>
The branch into which you want your code merged
-t, --title <string>
Title for the pull request
最後は環境変数の設定です。
GitHub CLIを使うにはログインが必要なので、ここで設定をしています。
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
実際に作成されたPRがこちら。
ちなみに、Draft用のオプションもありますが、個人(GitHub Free)のプライベートリポジトリではドラフトPRの作成はできません。
▶︎ Run gh pr create -B "stage" -d -t "Develop" -b ""
pull request create failed: GraphQL: Draft pull requests are not supported in this repository. (createPullRequest)
Error: Process completed with exit code 1.
Draft pull requests are available in public repositories with GitHub Free for organizations and legacy per-repository billing plans, and in public and private repositories with GitHub Team, GitHub Enterprise Server 2.17+, and GitHub Enterprise Cloud.
GitHub Teamなどを使っている場合は、以下のように -d
オプションを付与するとDraft状態でPRを作成できます。
- name: Create draft pr
run: gh pr create -d -B "stage" -t "Develop" -b ""
d, --draft
Mark pull request as a draft
これで最低限PRは作成できるのですが、これだけだとdevelop→stageへのPRしか作成できません。
実際のリリースではstage→main、main→releaseなど、他にもパターンがあると思います。
ここは動的に変えられるようにしたいところです。
また、先ほどのアクションでは既にPRが存在している場合にエラーとなってしまいます。
▶︎ Run gh pr create -B "stage" -t "Develop" -b ""
a pull request for branch "develop" into branch "stage" already exists:
{PRのURL}
Error: Process completed with exit code 1.
これらの問題を解消するため、もう少し工夫をしていきましょう。
PRのマージ先を動的に切り替える
PRのマージ先を判定するには、このアクションがどのブランチで実行されたのかを知らなければなりません。
ブランチ名を取得する方法はいくつかありますが、今回は github.ref
を使いましょう。
github.ref
の中には refs/heads/{hoge}
という形でブランチ名が格納されています。
github.ref == 'refs/heads/stage'
などとすることで、ブランチ名の判別が可能です。
他にもスマートな取得の仕方もありますので、お好みでどうぞ〜
実行されたブランチがstageかどうかを判定し、マージ先を切り替える処理は以下となります。
ついでにRPのタイトルも切り替えるようにしました。
- name: Check if pr exists
id: check_pr
run: |
pr_title=${{ (github.ref == 'refs/heads/stage' && 'Stage') || 'Develop' }}
base_branch=${{ (github.ref == 'refs/heads/stage' && 'main') || 'stage' }}
echo "::set-output name=pr_title::$pr_title"
echo "::set-output name=base_branch::$base_branch"
- name: Create release pr
run: |
gh pr create -B ${{ steps.check_pr.outputs.base_branch }} -t ${{ steps.check_pr.outputs.pr_title }} -b ""
以下の箇所がPRのタイトルとマージ先のブランチを判別している処理です。
pr_title=${{ (github.ref == 'refs/heads/stage' && 'Stage') || 'Develop' }}
base_branch=${{ (github.ref == 'refs/heads/stage' && 'main') || 'stage' }}
ここでは三項演算子のように処理を記載しています。
({条件式} && {TRUEの場合の処理}) || {FALSEの場合の処理})
のように実行されるので便利です。
今回は、ブランチがstageだった場合はpr_title
にStage
を、base_branch
にはmain
を格納するようにしています。
また、 ::set-output name={name}::{value}
で次のステップで使用するパラメータを引き継げるように設定しています。
これでPRのマージ先を動的に変更できるようになりました。
既にリリース用PRが存在する場合はスキップする
最後は既にPRが存在している場合にエラーとなってしまう件の対応です。
事前にリリース用のPRが存在しているかどうかをチェックし、既に存在している場合はPR作成をスキップするように変更しましょう。
GitHub CLIにはPRの一覧を取得・検索するコマンドがありますので、それを使います。
gh pr list [flags]
今回想定するリリース用PRの条件は以下です。
- stage→mainへのPR、かつタイトルは
Stage
- develop→stageのPR、かつタイトルは
Develop
検索条件としてはタイトルとPRの向き先でいけそうなので、以下のオプションを使います。
B, --base <string>
Filter by base branch
-S, --search <query>
Search pull requests with query
クエリの詳しい書き方については以下のページをご確認ください。
というわけで、先ほど作成したアクションに追加したものがこちらになります。
- name: Check if pr exists
id: check_pr
run: |
pr_title=${{ (github.ref == 'refs/heads/stage' && 'Stage') || 'Develop' }}
base_branch=${{ (github.ref == 'refs/heads/stage' && 'main') || 'stage' }}
echo "::set-output name=count::$(gh pr list -S ${pr_title}' in:title' -B $base_branch | wc -l)"
echo "::set-output name=pr_title::$pr_title"
echo "::set-output name=base_branch::$base_branch"
- name: Create release pr
if: ${{ steps.check_pr.outputs.count == 0 }}
run: |
gh pr create -B ${{ steps.check_pr.outputs.base_branch }} -t ${{ steps.check_pr.outputs.pr_title }} -b ""
以下の箇所がPRの検索処理になります。
echo "::set-output name=count::$(gh pr list -S ${pr_title}' in:title' -B $base_branch | wc -l)"
GitHub CLIでPRの一覧を取得すると以下のように出力されるので、その行数を wc -l
でカウントしています。
あとは取得したカウント数が0の場合(リリース用PRが存在しない場合)のみ、PR作成処理を実行するようにif文を追加しました。
if: ${{ steps.check_pr.outputs.count == 0 }}
これで完成です!
以下が実際にPR作成処理がスキップされた際のログになります。
Create release pr
のstepがスキップできていますね。
作成処理の方も問題なしです。
おわりに
実際に自分でアクションを作ってみることでGitHub Actionsの理解が深まりました。(あと純粋に楽しかった!)
小さな改善ではありましたが、このような改善を積み重ねて、来年は業務の効率化をもっと進めていきたいと思います!
今回作ったアクションを再掲して終了です。
name: Create a pull request for release.
on:
push:
branches: [ stage, develop ]
jobs:
create-release-pr:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v2
# リリース用PRが既に存在するかどうかをチェック
- name: Check if pr exists
id: check_pr
run: |
pr_title=${{ (github.ref == 'refs/heads/stage' && 'Stage') || 'Develop' }}
base_branch=${{ (github.ref == 'refs/heads/stage' && 'main') || 'stage' }}
echo "::set-output name=count::$(gh pr list -S ${pr_title}' in:title' -B $base_branch | wc -l)"
echo "::set-output name=pr_title::$pr_title"
echo "::set-output name=base_branch::$base_branch"
# リリース用PRを作成
- name: Create release pr
if: ${{ steps.check_pr.outputs.count == 0 }}
run: |
gh pr create -B ${{ steps.check_pr.outputs.base_branch }} -t ${{ steps.check_pr.outputs.pr_title }} -b ""
参考にさせていただいた記事
素晴らしい記事をありがとうございました!
Discussion