TL;DR;
- プルリク中に動作確認のため開発環境にデプロイしたい
- プルリクに特定のコメントを入れたらデプロイされるって機能があったら便利では!?
- プルリクコメントに反応するGitHub Actionsを定義して実現した
コード例はこちら。
https://gist.github.com/gimKondo/1cac9b7133d6eb279ccaff4f5ec7324a
背景
開発していて、プルリクのレビュー承認前にちょっと開発環境にデプロイして動作を確認したいことがあります。
もちろん、ローカルからデプロイする手段を作っても良いのですが、GCP, Firebase, AWSなどクラウド環境へのデプロイのための認証情報を開発者に配るのは面倒です。
すべてGitHubからのデプロイにしてしまえば、デプロイのための秘密鍵はsecretsに集約できます。
GitHubからデプロイする手段としては、GitHub Actionsが用意されています。
しかし、GitHub ActionsはJenkinsのようにユーザがボタンをポチっと押したらデプロイする、というような「手動」の操作はできません。
とはいえ、GitHub上での様々なイベントをハンドリングして起動することはできます。
ということで、プルリクエクストにコメントを付けることでデプロイする仕組みを作ってみました。
実装イメージ
こんなコメントを書いたら
デプロイが走り、こんな風に
結果が通知されるものを作ります(この例ではデプロイ失敗してますね )。
方法
コードを見てもらうのが早いでしょう。
!github deploy-feature
とコメントを打つと、デプロイが走るActionsがこちらになります。
name: deploy dev from feature by pull request
# (0) issueのコメント生成をハンドリング
on:
issue_comment:
types:
- created
jobs:
deploy:
# add comment of pull request && comment is KEYWORD
if: (github.event.issue.pull_request != null) && github.event.comment.body == '!github deploy-feature'
runs-on: ubuntu-latest
steps:
# (1) github-scriptを使って、プルリクコメントが付けられたプルリクのマージ元ブランチ名を取得
- uses: actions/github-script@v2
id: set-target-branch
with:
github-token: ${{secrets.GITHUB_TOKEN}}
result-encoding: string
script: |
const pull_request = await github.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
})
return pull_request.data.head.ref
# (2) (1)で取得したブランチをチェックアウト
- uses: actions/checkout@v2
with:
ref: ${{ steps.set-target-branch.outputs.result }}
# (3) チェックアウトしたブランチをデプロイ
- name: Deploy
# ---------------------------
# ここにデプロイの処理を書く
# ---------------------------
run: echo "Deploy Action is here"
# (4) せっかくなのでSlackへも通知
- name: Slack Notification
uses: homoluctus/slatify@master
if: always()
with:
type: ${{ job.status }}
job_name: "Deploy (${{ steps.set-target-branch.outputs.result }})"
mention: "here"
mention_if: "failure"
url: ${{ secrets.SLACK_WEBHOOK_URL }}
commit: true
token: ${{ secrets.GITHUB_TOKEN }}
一応、簡単な説明も書いておきます。
(0) issueのコメント生成をハンドリング
ポイント1です。
GitHub Actionsにはプルリクのレビューをトリガーとするための pull_request_review
があるのですが、これはコード内へのレビューコメントのみをトリガーにします。
https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-review-event-pull_request_review
プルリク本体へのコメントは issue_comment
に反応するようになっているのです。
これはGitHubが内部的にプルリクをissueとして扱っていることに起因するのではないかと思ってます。
(余談: 個人的には、これは間違った抽象化だと思っています。歴史的経緯やissueとプルリクを同じ通し番号で扱うための実装上の都合等はあるのでしょうが)
(1) github-scriptを使って、プルリクコメントが付けられたプルリクのマージ元ブランチ名を取得
ポイント2です。
(1)で書いた通り、このアクションで取れるのはissueのコメントイベントなので、プルリクの詳細情報は取れません。
具体的に言うと、プルリクのマージ元ブランチが取れないのです。
しかし、プルリクのIDは取れます。
このIDを使ってGitHubのAPIを呼び出すことでプルリクの詳細情報を取得し、マージ元ブランチ名を取り出します。
curl等でREST APIを使っても良いのですが、github-scriptを使うと便利です。
ドキュメントは充実しているとは言い難いので、それなりに試行錯誤になるかもしれません(量はあるが未整理)。
なお、GitHub Actionsのissue_commentの説明を見ると、イベントで取れるパラメータについてはissue commentsのREST APIの説明を参照するように書いてあります。
しかし、そちらのドキュメントも全情報を網羅しているわけではありません。
(実際、issue_commentからプルリクのIDを取得できることはドキュメントには書かれていませんでした)
あまり気の進む方法ではないですが、パラメータにどんな情報が含まれるかは
- name: Dumb event
run: echo "${{ github.event }}"
のようにして、古き悪しきprintfデバッグをして調べるしかなさそうです。
(2) (1)で取得したブランチをチェックアウト
特に語る事はありません。
取得したブランチをチェックアウトするのみです。
(3) チェックアウトしたブランチをデプロイ
普段のデプロイ処理をここに書くのみです。
(4) せっかくなのでSlackへも通知
私はSlatifyを使って結果をSlackに通知しています。
どのブランチからデプロイされたか分かると便利かと思いjob_nameを
"Deploy (${{ steps.set-target-branch.outputs.result }})"
のように設定しています。
おまけ
featureをデプロイした後にdev(※開発の統合ブランチ。developという名前の方が一般的かも)に戻したいこともあります。
そんなときのために !github deploy-dev
と打つと、devをデプロイするActionも用意しておくと便利です。
そのためコードも乗せておきます。
name: deploy dev to rollback dev by pull request
on:
issue_comment:
types:
- created
jobs:
deploy:
# add comment of pull request && comment is KEYWORD
if: (github.event.issue.pull_request != null) && github.event.comment.body == '!github deploy-dev'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: dev
- uses: ./.github/actions/test
- name: Deploy
run: echo "Deploy Action"
- name: Slack Notification
uses: homoluctus/slatify@master
if: always()
with:
type: ${{ job.status }}
job_name: "Deploy to Dev (${{ github.event.comment.body }})"
mention: "here"
mention_if: "failure"
url: ${{ secrets.SLACK_WEBHOOK_URL }}
commit: true
token: ${{ secrets.GITHUB_TOKEN }}