この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
何も弄ってないのにいきなり動作が変わっていると流石に慌てるものだと思ったこの頃です。
Actionsを利用してのCDを実施していたところ、普段特に問題がなかった箇所でエラーが発生していました。
remote: error: GH006: Protected branch update failed for refs/heads/master.
remote: error: Waiting on code owner review from haoyayoi and/or xxxxxx.
To https://github.com/xxxxxxxx/yyyyyyy
! [remote rejected] master -> master (protected branch hook declined)
何かやらかしがコミットに入っていたのかと思いましたが、動作を追ってみたところWorkflow内で行われているnpm version
によるCommitとPushが原因でした。Tokenは勿論設定済み。「あれ、動いてたはずなんだけど」という思いの元に検索してみたものの、そういう仕様ですとしか言えない記述ばかりが見つかる状態でした。
とりあえずはProtectedを逐一外すことで対処しましたが、事故の発生は不可避です。GitHubのフォーラム等を辿った上で行き着いたActions設定を交えて、ログとして書いておきます。
ブランチの保護には例外が通じない
GitHubでは想定しない変更を抑えるためにBranchを保護することができます。勝手に書き換えられることを防ぐ意味でも重要です。ただ、Actionsを使う上ではやや面倒な状態が成立することもあります。
主に、Workflow上で保護ブランチに直接のCommitやPushを発生させたい場合。そんなことあるっけ、と思われそうなのも確かですが、更新に伴うバージョン番号のインクリメントがありがちなケースです。「作業ブランチをMergeさせるタイミングでやればいいじゃないか」という声も聞こえてきそうです。
まぁ実際そうなのですが、細かい修正をマイナー、本番反映させるタイミングでメジャーバージョンを更新させるようなケースになると話が変わってきます。単純なバージョンのインクリメントにもPull-Request、設定によってはReviewが必要になるわけです。
なお、ブランチの保護を反映のタイミングだけ解除するという手段も見かけました。
I was able to create a workflow that temporarily disables the branch protection and then enables it again. This works fine if you don’t have multiple pull requests merged to master at the same time. And of course this means there is no branch protection for a few seconds.
設定変更についてはいつ行われたのか直ぐには判別し難いため、何か発生した場合に調査が非常に難しくなり、正直おすすめはできません。
以下のスレッドにはGitHub CommunityのStaffからのコメントがあり、手順として妥当なものと思えました。
If we enabled GitHub Actions to push to a protected branch then any collaborator in your repo could push any code to any branch they wanted simply by creating a branch and coding the workflow to push to to some other branch. Using the REST api to merge the PR is the right flow and overtime hopefully there will be actions that make that easier to implement.
ということで、作業用ブランチ作りつつPRも発行し、mergeした後はお好みに処理するためのWorkflowを作ってみました。
Merge用PR発行を機械的に行うWorkflow
今回のポイントは以下の通り。
- workflow_dispatchを介してPRを作成する
- うっかりworkflow_dispatchを多重実行した場合でも有効なPRは一つだけ
作業ブランチを固定にすることで多重実行してしまってもエラーで中断させます。
PR作成用
on:
workflow_dispatch:
branches:
- master
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.sha }}
- name: Set up Node
uses: actions/setup-node@v1
with:
node-version: '10.x'
- name: Git config
run: |
git config --global user.email "xxxxxxx@yyyyyy"
git config --global user.name "GitHub Actions"
- name: Create Release
id: create_branch
run: |
git checkout -b release
git push --set-upstream origin release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update major version
id: update_major_version
run: |
mkdir .git
echo "version_no=$(npm version minor --no-git-tag-version)" >> $GITHUB_ENV
npm version minor
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
working-directory: cdk
- name: Create release PR
id: create_release_pr
uses: peter-evans/create-pull-request@v3
with:
branch: release
base: master
title: "Deploy ${{env.version_no}}"
draft: false
npm version
に対して--no-git-tag-version
とオプションを入れていますが、これはCommitを発生させずにバージョン番号を取るためです。このタイミングではCommitが発生しなかったために次の行にてCommit目的で再実行していますが、万が一を考えて前の行ではCommitを明示的に回避させています。
Merge後のお好み用
delete-merged-branch
をブランチ削除用に使っています。PR作成に用いたcreate-pull-request
にもdelete-branch
なるオプションがあるのですが、同じフロー内でPRのcloseも扱わない場合には動作は期待できないようです。
on:
pull_request:
branches:
- master
types: [closed]
jobs:
test:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true && github.event.pull_request.head.ref == 'release'
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.sha }}
- name: delete branch
uses: SvanBoxel/delete-merged-branch@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Node
uses: actions/setup-node@v1
with:
node-version: '10.x'
- name: Hello
run: echo "Hello"
実際に書いてみると本当に単純なワークフローなんですが、これの確認が結構手間取ります。
実際に動かしてみる
ActionsからPR作成用のWorkflowを実行します。
実行後、Workflowが完了するまで暫く待ちます。
マージします。
Deploy用のWorkflowが動作しました。
Workflow完了後、PRをみてみると作業ブランチの削除も確認できました。
多重実行時
作業ブランチが固定になっているおかげで、ブランチ作成時に重複で失敗して止まっていました。狙い通りです。
あとがき
npm version
で失敗するようになった状況の原因が未だにわかっていないのですが、そもそも出来ていたのがおかしいと考えることにしました。
PR作成用のworkflow_dispatch起点であるWorkflowと、merge後によろしくやってくれるWorkflowの組み合わせは流石に都合よく落ちておらず、やむを得ず作ってみました。意外とベースはシンプルにできた感じです。ベースの動作は確認済みのため、作るのが面倒な場合には今回のサンプルをおすすめします。