はじめに
株式会社QualiArtsでUnityエンジニアをしている山本優威です。
本記事は、QualiArts Advent Calender 2023 の24日目の記事になります。
今回はQualiArtsでのGitHub Actionsへの取り組みや、実際に私がアプリビルドをGitHub Actionsで動作(2023/12/20時点)するようにしましたので、その紹介になります。
QualiArtsでのGitHub Actionsへの取り組み
QualiArtsでは今までGitHub Actionsを使って開発に活用をしてきました。
こちらの弊社の田村が執筆した記事に詳細が載っていますが、主に
- PullRequestの自動テスト
- 定期的なコードのクリーンアップ
- 開発バージョン間の自動マージ
- PullRequestのラベル付与
などで活用しています。
また最近の取り組みとして、Jenkinsで動作させていたアプリビルドやUnityのアセットバンドルビルドなどをGitHub Actionsに移行させる動きがあります。
今回はその一環でアプリビルドをJenkinsからGitHub Actionsに移行させた話にもなります。
GitHub Actionsを使ったアプリビルドについて
動機
先程取り組みについて紹介したところでも記載がありますが、従来QualiArtsではJenkinsというCIツールを使ってアプリビルドをしてきました。
ですが、使っていくうちに様々な問題が出てきたため、GitHub Actionsに移行することにしました。
理由につきましては紹介した弊社の田村の記事から引用させていただきます。
大きな問題の1つは、ジョブ(GitHub Actionsでいうワークフロー)の設定が大変という点です。 Jenkinsの設定はGUIで行われるため、気軽に設定可能という利点があるものの、コードで管理するのが難しく、履歴を追うのが難しかったり再利用性に乏しかったりします。 また、設定の変更をレビューするようなフローも作りづらいため、その設定について作った人しか知らないという属人化が発生しがちです。
もう1つ大きな問題は、管理が大変という点です。 Jenkinsはセキュリティの観点から継続的にアップデートが必要ですが、多数のプラグインがインストールされたJenkins環境では、プラグインの依存バージョンの問題などからアップデートは簡単ではありません。
このような問題を解消できるかもしれない策として、GitHub Actionsの利用を進めています。 特にワークフローの設定に関しては、YAML形式でGitHub上で管理されるのに加えて、再利用も可能なため、Jenkinsにおける問題を解消できそうだと手応えを感じています。
GitHub Actions化
動作の流れとコードをお見せした方がイメージが付きやすいと思うので、図解した処理の流れと完成形のYAMLコードを載せておきます。
任意で修正する必要がある箇所や修正点はコメントアウトで記載してあるので、参考にしてみてください。
name: Build-iOS or Android
on:
workflow_dispatch:
inputs:
environment:
description: 'Environment'
required: true
type: choice
default: 'Develop'
options:
- Custom
- Develop
- Staging
- Release
appCenterName:
description: 'App Centerのアプリ名'
required: true
type: string
default: 'hoge.hoge.hoge'
jsonProperties:
description: 'json Properties'
required: false
type: string
default: '{"property1":"hoge","property2":"1","property3":"false"}'
env:
TARGET_PLATFORM: iOS or Android
jobs:
build:
name: App Build
runs-on: [ ] # 任意のランナーを選択
environment: ${{ inputs.environment }}
steps:
# Inject
- name: Inject
id: inject-data
run: |
# Json型のデータからEnvironment・Outputを設定できるようにする
# 例
jsonProperties='${{ inputs.jsonProperties }}'
property1=$(echo ${jsonProperties} | jq -r .property1)
property2=$(echo ${jsonProperties} | jq -r .property2)
property3=$(echo ${jsonProperties} | jq -r .property3)
echo "PROPERTY1=${property1}" >> $GITHUB_OUTPUT
echo "PROPERTY2=${property2}" >> $GITHUB_ENV
echo "PROPERTY3=${property3}" >> $GITHUB_ENV
# Checkout
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
clean: true
submodules: recursive
persist-credentials: false
ssh-key: ${{ secrets.SSH_KEY }} # 任意のSSHキーを設定
# Cache Library
- name: Cache Library
uses: actions/cache@v3
with:
path: Library
key: Library-${{ hashFiles('Assets/**', 'Packages/**','ProjectSettings/**') }}
restore-keys: Library-
# Unity Build
- name: UnityBuild
run: |
UNITY_PATH={ # UnityのPathを指定 }/Unity
UNITY_PATH \
-buildTarget ${{ env.TARGET_PLATFORM }} \
-quit \
-batchmode \
-nographics \
-silent-crashes \
-logFile - \
-executeMethod BuildScript.PerformBuild \ # 実行するクラス.関数を設定
-projectPath ./
# XCode Build
# iOSビルドの場合はここでXCodeビルドをする
# Check AppCenter
- name: Check AppCenter
id: appcenter
run: appcenter -v
continue-on-error: true
# Install AppCenter CLI
- name: Install appcenter-cli
if: ${{ steps.appcenter.outcome == 'failure' }}
run: npm install -g appcenter-cli
# Upload to App Center
- name: Upload to App Center
env:
APPCENTER_ACCESS_TOKEN: ${{ secrets.APPCENTER_ACCESS_TOKEN }}
APPCENTER_UPLOAD_FILE: ${{ env.PROJECT_PATH }}/Builds/Android.apk # iOSの場合はまた別のファイルパス
APPCENTER_APPLICATION: ${{ inputs.appCenterName }}
APPCENTER_DISTRIBUTION_GROUP: ${{ var.distributionGroup }}
run: |
appcenter \
distribute \
release \
--disable-telemetry \
--debug \
--silent \
--file "${APPCENTER_UPLOAD_FILE}" \
--app "${APPCENTER_APPLICATION}" \
--group "${APPCENTER_DISTRIBUTION_GROUP}" \
-R "${{ format('{0}/release_note.txt', github.workspace) }}" # ReleaseNoteがあれば指定
お見せできない部分も多いため、簡易的なコードになってしまいましたが雰囲気はこんな感じの流れで記載し、必要な部分を追記しながら書いていくようにすれば動くようになると思います。
また実際にはiOSとAndroidで別のyamlにしていたり、証明書やプロジェクト単位で必要なInject、設定をビルド前にしていたりします。
Jenkinsからの移行ということもあり、Jenkinsもyamlで記述する関係上コードで変更した点は多くなく、GitHub特有のPathや書き方に変更しています。
工夫点
Input制限の突破
GitHub Actionsではinputで宣言できる上限が決まっていて、その個数は10になります。
様々なパラメータを必要とするアプリビルドでは10個では足りない可能性があり、今回はinputの1つを使ってjson形式でstringでパラメータを一部受け取るようにしています。
on:
workflow_dispatch:
inputs:
jsonProperties:
description: 'json Properties'
required: false
type: string
default: '{"property1":"hoge","property2":"1","property3":"false"}'
jobs:
build:
name: App Build
runs-on: [ ] # 任意のランナーを選択
environment: ${{ inputs.environment }}
steps:
# Inject
- name: Inject
id: inject-data
run: |
# Json型のデータからEnvironment・Outputを設定できるようにする
# 例
jsonProperties='${{ inputs.jsonProperties }}'
property1=$(echo ${jsonProperties} | jq -r .property1)
property2=$(echo ${jsonProperties} | jq -r .property2)
property3=$(echo ${jsonProperties} | jq -r .property3)
echo "PROPERTY1=${property1}" >> $GITHUB_OUTPUT
echo "PROPERTY2=${property2}" >> $GITHUB_ENV
echo "PROPERTY3=${property3}" >> $GITHUB_ENV
展開方法は少し複雑ですが、inputsのjsonPropertiesからstringを受け取り、
jsonProperties='${{ inputs.jsonProperties }}'
下記のコードのようにすればjsonのpropertyに対応するvalueが受け取れるようになります。
property=$(echo ${jsonProperties} | jq -r .jsonで指定したproperty)
あとはそのjob内で受け取ったvalueを使用できるようにOutputやEnvにすれば良いです。
echo "PROPERTY=${property1}" >> $GITHUB_OUTPUT
json型のinputにしたことによる影響の回避
上記の工夫でjson型のinputを追加したことにより、GitHub Actionsからそのjsonの内容を変更しようとした場合に難易度の向上とニューマンエラーの可能性が出てきました。
そこでQualiArtsではSlackアプリを使用してGUIを用意することにより、json型入力の影響を回避することにしました。
また他のメリットとしては他の職種の方もアプリビルドをすることがあるため、GitHubを開かなくて良くなるようにもしています。
アプリのHomeとModalの見た目
アプリの導入についてはこちらの記事を参考にさせていただきました。
上記の方法を用いてアプリを作成し、アプリビルド用のHomeとModalはこんな感じにしました。
各ビルド環境とプラットフォーム毎にボタンを用意し、そのボタンを押すと対応するModalが開かれる仕組みになっています。
例だとAndroidのModalが開かれており、パラメータを入力後「実行」ボタンを押すことによりワークフローのdispatchイベントが呼ばれます。
また誰でもアプリビルドをできてしまうとそれはそれで問題になってしまうので、Homeが開かれたタイミングやモーダルを開くタイミングでSlackのUserIdを取得し、実行できるユーザーかどうかをチェックするようにもしています。
実行できるユーザーのホワイトリストはSlackのUserGroupを利用することにより実現していますので、Slack完結型にもなっています。
まとめ
今回はQualiArtsのGitHub Actionsへの取り組みとGitHub Actionsでのアプリビルドの方法について簡単に紹介させていただきました。
GitHub Actionsを活用したアプリビルド自体はそこまで実装難易度が高い訳でもないので是非参考にして試してみてください!