アプリケーションをコマンドラインからビルドする

Unityでは Assets/Editor 以下に .cs を置くことでエディタの拡張をすることが出来ます.

それを用いて各種プラットフォーム向けにビルドするスクリプト Build.cs を作成します.

using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;
 
public class BuildScript {
		[MenuItem("Build/Windows Dev Build")]
		public static void WindowsDevelopmentBuild() {
		    Build(BuildTarget.StandaloneWindows64, false);
		    Debug.Log("WindowsDevelopmentBuild one");
		}
 
    [MenuItem("Build/Windows Release Build")]
    public static void WindowsReleaseBuild() {
        Build(BuildTarget.StandaloneWindows64, true);
        Debug.Log("WindowsReleaseBuild Done");
    }
 
    private static void Build(BuildTarget target, bool isReleaseBuild) {
        var scenes = GetScenes(isReleaseBuild);
        var buildPlayerOptions = new BuildPlayerOptions();
        buildPlayerOptions.scenes = scenes;
 
        if (!isReleaseBuild) {
            // テスト時はDevelopmentビルドのオプションを有効にする
            buildPlayerOptions.options = BuildOptions.Development;
        }
 
        var ext = target == BuildTarget.StandaloneWindows64 ? "exe" : "apk";
        
        buildPlayerOptions.locationPathName = $"Build/{Application.productName}.{ext}";
        buildPlayerOptions.target = target;
 
        var report = BuildPipeline.BuildPlayer(buildPlayerOptions);
        var summary = report.summary;
 
        if (summary.result == BuildResult.Succeeded)
            Debug.Log("Build succeeded: " + summary.totalSize + " bytes");
        else if (summary.result == BuildResult.Failed) {
            Debug.LogError("Build failed");
        }
    }
}

これによりアプリケーションをコマンドラインからビルド出来るようになります.

$ path/to/Unity.exe -quit -batchmode -projectPath . \
          -executeMethod BuildScript.WindowsDevelopmentBuild \
          -buildTarget StandaloneWindows64 \
          -setDefaultPlatformTextureFormat astc -nographics -logFile -

インストーラを作成する

ビルドして作成された app.exe のみを配布してもアプリを起動することは出来ません.

Unityアプリの実行には UnityPlayer.dllWinPixEventRuntime.dll 等が必要になります.

zipファイルで全てまとめてもよいのですが, 今回はインストーラとしてまとめて一つの .exe として配布できるようにします. インストーラを作成するために InnoSetup というツールを使用します.

この記事 のテンプレートを参考にさせていただいています.

/UnityApp.iss

; https://github.com/karinharp/InnoSetupTemplateForUnity 
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
 
#define MyAppUUID "com.idh.choivr"
#define MyAppName "choivr"
#define MyAppVersion "0.1"
#define MyAppPublisher ""
#define MyAppURL ""
#define MyAppExeName "choivr.exe"
#define ResourceDir "Build"
#define OutputDir "Build"
#define OutputBaseFilename "choivr_setup"
 
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={#MyAppUUID}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
OutputDir={#OutputDir}
OutputBaseFilename={#OutputBaseFilename}
Compression=lzma
SolidCompression=yes
 
[Languages]
Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl"
 
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
 
[Files]
Source: "{#ResourceDir}\{#MyAppName}.exe"; DestDir: "{app}\app"; Flags: ignoreversion; Permissions: everyone-full
Source: "{#ResourceDir}\UnityPlayer.dll"; DestDir: "{app}\app"; Flags: ignoreversion; Permissions: everyone-full
Source: "{#ResourceDir}\WinPixEventRuntime.dll"; DestDir: "{app}\app"; Flags: ignoreversion; Permissions: everyone-full
Source: "{#ResourceDir}\UnityCrashHandler64.exe"; DestDir: "{app}\app"; Flags: ignoreversion; Permissions: everyone-full
Source: "{#ResourceDir}\{#MyAppName}_Data\*"; DestDir: "{app}\app\{#MyAppName}_Data"; Flags: ignoreversion recursesubdirs createallsubdirs; Permissions: everyone-full
Source: "{#ResourceDir}\MonoBleedingEdge\*"; DestDir: "{app}\app\MonoBleedingEdge"; Flags: ignoreversion recursesubdirs createallsubdirs; Permissions: everyone-full
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
 
[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\app\{#MyAppExeName}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\app\{#MyAppExeName}"; Tasks: desktopicon
 
[Run]
Filename: "{app}\app\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
 
[Dirs]
Name: "{app}\app"; Permissions: everyone-full

インストーラの作成は以下コマンドにより行います.

$ path/to/ISCC.exe UnityApp.iss

Self-Hostedランナーでreleaseブランチマージ時にリリースする

Unity Editorはライセンス認証が必要な関係でUnity Cloud Buildを使用せず自前でCI/CD上でビルドさせようとすると非常に面倒くさいです.

今回はGitHub Actionのself-hostedランナーでビルドすることによりその問題を回避します.

以下が release-* ブランチにPRを出し、マージされた際にPRの内容からリリースノートを作成してWindowsのDevelopmentビルドとAndroidのDevelopmentビルドをリリースするWorkflowです.

まだバージョンをつける等のことは出来ていません.

ワークフロー

  1. feature/xxx で作業をする
  2. release-x.x.x ブランチを作成する
  3. feature/xxx → release-x.x.x にPRを出す
  4. マージされたらCI/CDが走る
  5. CDが成功したら master にマージされ release-x.x.x タグがつく
name: Release from PR
on:
  pull_request:
    branches:
      - 'release-*'
    types: [ closed ]
jobs:
  build:
    if: github.event.pull_request.merged == true
    runs-on: self-hosted
    steps:
    - name: Checkout
      uses: actions/checkout@v2
      with:
        lfs: true
        clean: false
    - name: Extract branch name
      uses: mdecoleman/pr-branch-name@1.0.0
      id: extract_branch
      with:
        repo-token: ${{ secrets.GITHUB_TOKEN }}
    - name: Envs
      shell: bash
      # <https://zenn.dev/uta_mory/articles/14e358a2dbf6e47400dc>
      run: |
        echo "UNITY_PATH=path/to/Unity.exe" >> $GITHUB_ENV
        echo "INSTALLER_CC_PATH=path/to/ISCC.exe" >> $GITHUB_ENV
    - name: Unity Android Build
      shell: bash
      run: |
        "${UNITY_PATH}" -quit -batchmode -projectPath . \
          -executeMethod BuildScript.AndroidReleaseBuild \
          -buildTarget Android \
          -setDefaultPlatformTextureFormat astc -nographics -logFile -
    - name: Unity Windows Build
      shell: bash
      run: |
        "${UNITY_PATH}" -quit -batchmode -projectPath . \
          -executeMethod BuildScript.WindowsDevelopmentBuild \
          -buildTarget StandaloneWindows64 \
          -setDefaultPlatformTextureFormat astc -nographics -logFile -
    - name: Create Installer
      shell: bash
      run: |
        "${INSTALLER_CC_PATH}" UnityApp.iss
         powershell -c Compress-Archive -Path "Build/choivr_setup.exe" -DestinationPath "Build/choivr.zip"
    - name: Get body from PullRequest
      id: pr-body
      uses: actions/github-script@v2
      with:
        github-token: ${{secrets.GITHUB_TOKEN}}
        result-encoding: string
        script: |
          const result = await github.pulls.get({
            owner: context.repo.owner,
            repo: context.repo.repo,
            pull_number: context.issue.number
          })
          return result.data.body
    - name: Create ReleaseNote
      id: create_release
      if: startsWith(steps.extract_branch.outputs.branch, 'release')
      uses: actions/create-release@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        tag_name: ${{ steps.extract_branch.outputs.branch }}
        release_name: Release ${{ steps.extract_branch.outputs.branch }}
        body: |
          ${{ steps.pr-body.outputs.result }}
        draft: false
        prerelease: true
    - name: Upload Android Debug Build
      uses: actions/upload-release-asset@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        upload_url: ${{ steps.create_release.outputs.upload_url }}
        asset_path: Build/choivr.apk
        asset_name: choivr.apk
        asset_content_type: application/zip
    - name: Upload Windows Debug Build
      uses: actions/upload-release-asset@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        upload_url: ${{ steps.create_release.outputs.upload_url }}
        asset_path: Build/choivr.zip
        asset_name: choivr_win.zip
        asset_content_type: application/zip
  1. Unityプロジェクトを管理するときは Git LFS を使用しているはずなのでcheckout時に lfs: true を設定, clean: false にすると毎回のビルド時間を短縮できる
  2. 各step間で環境変数を共有する方法は ::set-env:: によるものが非推奨になったので $GITHUB_ENV へリダイレクトする方法に
  3. $GITHUB_ENV に設定する値はエスケープしなくてよい(ハマった)
  4. 以前は Artifact にビルドしたものをおいていたが 1GB の制限に引っかかりお金がかかってしまったのでrelease機能を使うことに
  5. .exe は直接 asset_content_type に指定できないのでzipとかにする

参考文献