Unityエンジニアの森田です。今回は前回書いた記事の続きで、CI/CDのちょっとした改善をした話を書いていきます。
課題
以下の記事で書いたように、ミラティブでは複数のアプリのバージョンを運用しており、それに合わせてアセットも複数のバージョンを管理しています。 tech.mirrativ.stream
アセットの更新がアセットのFix用ブランチとして運用しているmasterブランチに取り込まれると、その更新をリリース用のブランチにそれを取り込まなければいけないのですが、この作業をその都度デザイナーからエンジニアに依頼する形になっていました。またバージョンごとに作られたリリース用ブランチ全てに取り込まなければいけないため、とても煩わしいです。
そのため今回はmasterブランチにアセットの更新が入った時点で各リリース用ブランチに自動的にマージされる仕組みをGitHub Actionsで作成しました。
実装
今回求められたのは以下の点です
- masterブランチに入った変更は全てのreleaseブランチに反映したい
- 下位バージョンのreleaseブランチに入った変更は全ての上位バージョンのreleaseブランチにも反映したい(逆はしない)
これを手短に実現するために、以下の2つのworkflowを作成することにしました。
- masterへのマージをトリガーに、存在するreleaseブランチの中で最も下位のブランチに自動的にマージするworkflow
- 任意のreleaseブランチへのマージをトリガーに、上位のreleaseブランチ全てに自動的にマージするworkflow
1のworkflowでmasterを最下位バージョンのreleaseブランチにマージするのは、2のworkflowで最下位バージョンの変更が全てのreleaseブランチにも反映されるため、結果的にmasterブランチの変更が全てのreleaseブランチに反映されるためです。
1. masterへのマージをトリガーに、存在するreleaseブランチの中で最も下位のブランチに自動的にマージするworkflow
name: Auto merge master to oldest release on: push: branches: - master jobs: merge: runs-on: ubuntu-latest continue-on-error: true steps: - name: Generate github token id: generate_token uses: tibdex/github-app-token@v1 with: app_id: ${{ secrets.BOT_APP_ID }} private_key: ${{ secrets.BOT_PRIVATE_KEY }} - name: Checkout uses: actions/checkout@v3 with: fetch-depth: '0' token: ${{ steps.generate_token.outputs.token }} env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - name: Set e-mail and username run: | git config --global user.email "github.actions@mirrativ.co.jp" git config --global user.name "GitHub Action" - name: Merge master to oldest release run: | git fetch list=($(git branch -a | grep remotes/origin/release/ | sed s@remotes/origin/release/@\@ | sort )) git checkout release/${list[0]} git merge master git push origin release/${list[0]} git checkout develop git merge master git push origin develop env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
このworkflowは単純で、masterへのマージを起点に、git branchとgrepで取得したreleaseブランチをsortして、一番下位のバージョンのreleaseブランチにmasterをマージしているだけです。注意するのはworkflow上でマージする際にGITHUB_TOKENの環境変数にパーソナルトークンを設定することです。GitHub Actionsでは意図しないworkflowの再帰実行を防ぐためにこのような制限がかかっていますが、今回下位バージョンのブランチへの変更を上位バージョンのブランチ全てに反映させたいため、パーソナルトークンを使う必要があります。
しかし会社のリポジトリで個人のパーソナルトークンに依存するのはよろしくないので、今回は社内GitHub Appを作成し、tibdex/github-app-token@v1というアクションを使ってパーソナルトークンを生成するようにしました。
2. 任意のreleaseブランチへのマージをトリガーに、上位のreleaseブランチ全てに自動的にマージするworkflow
name: Auto Merge Releases on: push: branches: - release/* workflow_dispatch: jobs: get-branch-list: runs-on: ubuntu-latest outputs: branch_list: ${{ steps.branch_list.outputs.release_list }} steps: - name: Checkout uses: actions/checkout@v3 - name: Release branch list id: branch_list run: | git fetch temp_list=$(git branch -a | grep remotes/origin/release/ | sed s@remotes/origin/release/@\"@ | sed s/$/\",/ | sort ) list=$(echo $temp_list | sed s/,$// ) echo "release_list=[ $list ]" >> $GITHUB_OUTPUT merge-release-branches: needs: get-branch-list runs-on: ubuntu-latest continue-on-error: true strategy: fail-fast: false matrix: value: ${{ fromJson(needs.get-branch-list.outputs.branch_list) }} steps: - name: Current branch version id: current_version run: | version=$(echo $GITHUB_REF_NAME | sed s@release/@@ ) echo "value=$version" >> $GITHUB_OUTPUT - name: Checkout if: matrix.value > steps.current_version.outputs.value uses: actions/checkout@v3 with: fetch-depth: '0' - name: Set e-mail and username if: matrix.value > steps.current_version.outputs.value run: | git config --global user.email "github.actions@mirrativ.co.jp" git config --global user.name "GitHub Action" - name: Merge branch if: matrix.value > steps.current_version.outputs.value run: | git fetch origin release/${{ matrix.value }} git checkout release/${{ matrix.value }} git merge ${{ github.ref_name }} git push origin release/${{ matrix.value }} - name: Generate github token id: generate_token if: matrix.value > steps.current_version.outputs.value uses: tibdex/github-app-token@v1 with: app_id: ${{ secrets.BOT_APP_ID }} private_key: ${{ secrets.BOT_PRIVATE_KEY }} - name: Kick AssetBundle Build if: success() && matrix.value > steps.current_version.outputs.value uses: benc-uk/workflow-dispatch@v1 with: workflow: AssetBundle Build Second token: ${{ steps.generate_token.outputs.token }} inputs: '{ "version" : "${{ matrix.value }}" }'
このworkflowではreleaseブランチのバージョンを取得するjobと、マージを行うjobの2つに分かれています。複数バージョン運用されていることがほとんどなので、matrixを使って並列でマージを行うためにjobを分けています。
バージョンを取得する部分は1つ目のworkflowで行ったものと変わりありませんが、set-outputを使ってマージを行うjobにバージョンのリストを渡しています。GitHub Actions組み込みのfromJson()
を使ってmatrixの値に展開するため、バージョンのリストは[ x.x, x.x, x.x]
のようにJSONの配列形式で渡しています。
fromJson()
でリストを展開した後、$GITHUB_REF_NAMEから現在のreleaseブランチのバージョンを取得し、各stepsのifでバージョン比較して上位バージョンのブランチ以外はスキップするようにします。またマージ後は再びパーソナルトークンを発行し、アセットバンドルビルドを行う他のworkflowを実行して自動的にアセットバンドルビルドが行われるようにしています。
設定が簡単だったので、今回は他のworkflowを起動するのにbenc-uk/workflow-dispatchというActionを使用しています。
結果
たったこれだけの設定ですが、アセットの更新が行われると自動的に各バージョンに反映&アセットバンドルビルドが行われるようになったため、エンジニアがいちいち作業を止めなくてもよくなりました。
We are hiring!
ミラティブでは一緒に作ってくれるUnityエンジニアを募集しているので、お気軽にお声がけください! www.mirrativ.co.jp speakerdeck.com