Help us understand the problem. What is going on with this article?

UnityのiOSアプリをアーカイブまでJenkinsで自動化した話

More than 1 year has passed since last update.

最近Resharperを使いたいが為に開発環境をMacからWindowsに変えた結果iOSのビルドが非常に面倒になった。なのでJenkinsを導入してWindows側でビルドからアーカイブまでほぼ全て自動化出来るように環境を整えてみた。

流れとしては

  1. WindowsでGit Push
  2. Mac側でGit Pull
  3. Unityビルド(このときにちょっとXcodeのplistとか書き換える)
  4. Xcodeアーカイブ
  5. Git commit + push

Jenkinsの導入

調べてください★

Jenkinsにジョブの登録

今回はプラグインを使わず素の状態でジョブを作成します。

まずGitから最新の状態のプロジェクトを読み込み

PROJECT_PATH="{プロジェクトの場所}"
cd "${PROJECT_PATH}"
git pull

でUnityのビルド。

/Applications/Unity/Unity.app/Contents/MacOS/Unity \
    -batchmode \
    -quit \
    -logFile ./build.log \
    -projectPath "${PROJECT_PATH}" \
    -executeMethod BuildClass.BuildiOS

でXcodeでアーカイブ

# 全部のルート
ROOT_FOLDER="{ルート}"
# ビルドが入ってるフォルダ
BUILD_FOLDER="{ビルドが入ってるフォルダ名}"

# 最新のビルドを取得
cd "${ROOT_FOLDER}/${BUILD_FOLDER}"
builds=()
for file in *; do
    builds=("${file}" "${builds[@]}")
done

# Scheme
TARGET="Unity-iPhone"
# アーカイブ先
OUTPUT_DIR_ARCHIVE="${ROOT_FOLDER}/build_archive/"
# プロビジョニングプロファイル
PROFILE_PATH="{プロビジョニングプロファイルの場所}"

if [ ! -d ${OUTPUT_DIR_ARCHIVE} ]; then
    mkdir "${OUTPUT_DIR_ARCHIVE}"
fi

xcodebuild -project "${ROOT_FOLDER}/${BUILD_FOLDER}/${builds[0]}/${TARGET}.xcodeproj" \
               -scheme "${TARGET}" \
               archive \
               -archivePath "${OUTPUT_DIR_ARCHIVE}/archive" \
               DEVELOPMENT_TEAM={自分のチームの10桁のコード}

これで指定した場所にarchive.xcarchiveっていうファイルが出来るので、それをダブルクリックして後はアップロードするだけ。

DEVELOPMENT_TEAMやPROFILE_PATHの値は以下参照:
[Xcode] CIなどを使用するときに必要な値を確認する方法

最後にUnityで行った変更点のPush

cd "${PROJECT_PATH}"
git commit -a -m "iOSのビルドに成功しました。"
git push

Unity側

Unityのビルドのところに書いた-executeMethod BuildClass.BuildiOS←これは自分で作成する。

場所はAssets/Editor/BuildClass.cs

BuildClass.cs
using UnityEditor;
using UnityEngine;

public class BuildClass
{

    /// <summary>
    /// iOSのビルドを行います
    /// </summary>
    public static void BuildiOS()
    {
        // バージョンのインクリメント
        var versionString = PlayerSettings.bundleVersion;
        Debug.Log("Version got: " + versionString);
        var verstionStrings = versionString.Split('.');
        int lastVersion;
        if(!int.TryParse(verstionStrings[2], out lastVersion)){
            Debug.Log("[Error!] Failed to increment version.");
            return;
        }
        var newVersion = lastVersion + 1;
        var newVersionStrings = new [] {verstionStrings[0], verstionStrings[1], newVersion.ToString()};
        var newVersionString = string.Join(".", newVersionStrings);
        PlayerSettings.bundleVersion = newVersionString;
        Debug.Log("New Version set: " + PlayerSettings.bundleVersion);

        // Bundleのインクリメント
        var buildString = PlayerSettings.iOS.buildNumber;
        Debug.Log("Build number got: " + buildString);
        int buildNum;
        if(!int.TryParse( buildString, out buildNum)) {
            Debug.Log("[Error!] Failed to increment build version.");
            return;
        }
        PlayerSettings.iOS.buildNumber = (++buildNum).ToString();
        Debug.Log("New Build number set: " + PlayerSettings.iOS.buildNumber);

        // ビルドフォルダの名前
        var buildFolderName = "alpha_" + newVersionString;

        string[] sceneList = {
            "./Assets/Scene/Main.unity",
            "./Assets/Scene/Menu.unity"
        };

        string errorMessage = BuildPipeline.BuildPlayer(
            sceneList,
            "{出力先}" + buildFolderName,
            BuildTarget.iOS,
            BuildOptions.None
        );

        if(!string.IsNullOrEmpty(errorMessage)){
            Debug.LogError("[Error!]" + errorMessage);
        } else {
            Debug.Log("[Success!]");
        }
    }
}

めんどくさいので中身の解説は省略。とりあえずバージョンのインクリメントとかそういうのをここで自動化できる。ただ僕の場合これだけだとTestFlightで内容を更新する際に審査が必要になるので、バージョンを一定にしたりとplistの編集が必要だったので、PostProcessBuildも書いてる。

これの場所はAssets/Editor/XcodeSettingsPostProcesser.cs

XcodeSettingsPostProcesser.cs
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditor.iOS.Xcode;
using UnityEditor.Callbacks;
using System.Collections;

public class XcodeSettingsPostProcesser
{
    [PostProcessBuildAttribute (0)]
    public static void OnPostprocessBuild (BuildTarget buildTarget, string pathToBuiltProject)
    {
        // iOS以外のプラットフォームは処理を行わない
        if (buildTarget != BuildTarget.iOS) return;

        // Plist変更
        string plistpath = Path.Combine(pathToBuiltProject, "Info.plist");
        PlistDocument plist = new PlistDocument();
        plist.ReadFromFile(plistpath);

        // iOS10対策
        plist.root.SetString("NSCameraUsageDescription", "Unity/Admob Dependency");
        // Testflightの審査を端折る為
        plist.root.SetString("CFBundleShortVersionString", "0.0.5");

        // plist反映
        plist.WriteToFile(plistpath);
    }
}

以上

Unity側で少し内容を書き換えてるので更新の前にコンフリクトを解消しないと行けなかったりしてまだ面倒なところはあるけど、とりあえずビルドさせて風呂入って、戻ったら実機テストしてOKだったらアップロード、みたいな感じでだいぶ楽になった。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした