Pages

October 22, 2013

[Unity] Jenkins로 유니티 자동 빌드 세팅하기 (4) - ios 앱 코드 사이닝


이전 포스트들을 통해 살펴본 Jenkins에서 유니티 프로젝트를 빌드하는 과정을 다시 한번 정리하면 다음과 같다.

  1. Jenkins 설치
  2. Jenkins 실행 계정 변경
  3. Jenkins plugin 설치 및 설정
  4. Git repository 접근 설정
  5. Unity 빌드 스크립트 작성
  6. Jenkins build job 생성
    1. Source code Management 세팅
    2. Unity3d 빌드 세팅
    3. Xcode 빌드 세팅
여기까지 iOS앱을 빌드할 수 있는 과정은 모두 커버하였다. 하지만 빌드만 해서는 앱을 배포할 수는 없다. 이번 포스트에서는 빌드된 앱을 배포할 수 있도록 코드 사이닝하는 과정을 커버하고 다음 포스트에서 Testflight이라는 앱배포 서비스를 사용한는 방법을 알아 보도록 하자.

이전 튜토리얼까지 잘 따라왔다면 Jenkins는 git repository에서 프로젝트 파일을 가져와서 해당 빌드잡을 위한 workspace 디렉토리를 생성하고 그 안에 프로젝트 소스와 빌드 파일들을 보관하고 있을 것이다.



원래 Xcode에서 배포를 위한 프로젝트 빌드를 할 때는 배포 버전의 코드 사이닝을 위해 애플 개발자 센터에서 키페어를 요청하고 app id를 등록하고 Distribution provisioning profile을 만드는 과정을 거쳐야 하는데 이 부분에 관련해서는 다른 블로그의 글을 참고하기를 바란다.

Provisioning profile을 생성했다고 가정하고 다음으로 진행하도록 하자.

Xcode에서 Code Signing Identity 지정하기


Jenkins의 현재 작업중인 빌드 프로젝트 workspace내에 유니티의 iOS 버전 빌드로 생성된 Xcode Project 디렉토리에서 Unity-iPhone.xcodeproj 를 클릭하여 Xcode를 열도록 한다.



Xcode의 Project Navigator에서 최상단 프로젝트를 선택하면 Project Info 화면이 Xcode 가운데에 나타난다. 여기서 새로운 Build configuration을 추가하자.  "+" 버튼을 누르고 "Duplicate "Release" Configuration"을 선택하면 된다. 



 지금 단계에서는 앱스토어 배포가 아닌 Ad hoc 배포를 위한 빌드가 필요하므로 "Ad hoc"이라고 이름을 붙였다. 그리고 상단의 Build Settings 를 클릭하고 아래의 "Ad hoc" configuration의 "Code Signing Identity"를 발급받은 배포용 인증서로 변경한다. 앞서 애플 개발자 센터에서 인증서와 Provisioning Profile을 발급 받고 등록을 했다면 여기서 변경할때 선택할 수 있을 것이다.



이제 상단의 Build&Run버튼 옆의 Unity-iPhone이라고 적힌 부분을 눌러 "Edit Scheme" 다이얼로그를 오픈한다. 그리고 Archive부분을 아래와 같이 설정하도록 하자.




이제 프로젝트를 저장하고 Xcode를 닫는다. 닫기 전에 Xcode 상단 메뉴의 Product -> Archive를 선택하여 Archiving이 제대로 되는지 확인해보는 것도 좋겠다.

Xcode 변경 사항이 유지되도록 Unity 빌드 스크립트 수정


앞 단계에서 Xcode 프로젝트에 새로운 Build Configuration을 추가하였고 이제 남은 것은 Jenkins에서 Xcode 빌드를 수행할때 해당 Build configuration으로 빌드가 되도록 하기만 하면 된다. 

그러나 여기서 생기는 이슈는 현재 Xcode Project는 Unity에서 빌드 할 때 생성되는 것이므로 미리 위와 같은 설정을 넣어서 Source code repository에 저장할 수가 없다는 것과 Jenkins에서 해당 빌드 프로젝트를 빌드할 떄 마다 Xcode Project는 삭제되고 Unity에서 새로이 생성된다는 점이다.

이 문제를 해결해보자. 다시 원래의 Unity 프로젝트 소스코드를 열어 Build 스크립트를 아래와 같이 수정한다. 


using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;


class ProjectBuilder {
    static string[] SCENES = FindEnabledEditorScenes();
    static string TARGET_DIR = "build";

    [MenuItem ("Custom/CI/Build iOS Debug")]
    static void PerformiOSDebugBuild ()
    {
         BuildOptions opt = BuildOptions.SymlinkLibraries |
                            BuildOptions.Development |
                            BuildOptions.ConnectWithProfiler |
                            BuildOptions.AllowDebugging |
                            BuildOptions.Development |
                            BuildOptions.AcceptExternalModificationsToPlayer;         

         PlayerSettings.iOS.sdkVersion = iOSSdkVersion.DeviceSDK; 
         PlayerSettings.iOS.targetOSVersion = iOSTargetOSVersion.iOS_4_3;
         PlayerSettings.statusBarHidden = true;

         char sep = Path.DirectorySeparatorChar;
         string buildDirectory = Path.GetFullPath(".") + sep + TARGET_DIR;

         string BUILD_TARGET_PATH = TARGET_DIR + "/ios";
         Directory.CreateDirectory(BUILD_TARGET_PATH);

         GenericBuild(SCENES, BUILD_TARGET_PATH, BuildTarget.iPhone, opt);
    }

    private static string[] FindEnabledEditorScenes() {
        List<string> EditorScenes = new List<string>();
        foreach(EditorBuildSettingsScene scene in EditorBuildSettings.scenes) {
           if (!scene.enabled) continue;
           EditorScenes.Add(scene.path);
        }

        return EditorScenes.ToArray();
    }

    static void GenericBuild(string[] scenes, string target_path, BuildTarget build_target, BuildOptions build_options)
    {
        EditorUserBuildSettings.SwitchActiveBuildTarget(build_target);
        string res = BuildPipeline.BuildPlayer(scenes, target_path, build_target, build_options);
        if (res.Length > 0) {
            throw new Exception("BuildPlayer failure: " + res);
        }
    }
}



앞에서 보았던 스크립트 코드와 똑같은것 같은데 딱 한가지 바뀐것은 BuildOptions에 BuildOptions.AcceptExternalModificationsToPlayer 옵션을 추가한 것이다. 이 옵션의 의미는 Unity에서 프로젝트를 iOS버전으로 빌드할때 현재 이미 빌드된 프로젝트 파일들이 있을 경우 Replace하지 말고 Merge하라는 의미이다. 따라서 이 옵션을 추가함으로써 윗단계에서 프로젝트 파일을 수정한 내용이 사라지지 않고 남아 있을 수 있다.

그러나 문제는 가장 처음 유니티에서 빌드 할 때 위의 옵션을 추가하고 스크립트를 실행하여 빌드하려고 하면 에러가 발생한다. 이유는 Merge할 대상이 없기 때문이다. 개인적인 의견으로는 유니티에서 좀 더 스마트하게 체크해서 없으면 일반 적인 빌드로 프로젝트 파일들을 만들어 낼 수 있게 되었으면 더 좋았을 텐데 아쉽다.

어쨋든 위와 같이 유니티 빌드 스크립트를 수정하고 git에 commit하고 push하여 중앙 소스 저장소에 수정내용이 반영되도록 한다. 그래야 다음번 Jenkins빌드 Job이 시작될때 수정된 빌드 스크립트가 사용되고 Jenkins workspace에서 변경한 Xcode프로젝트가 초기화되지 않는다.

Jenkins Xcode plugin 설정 수정 및 빌드

자 이제 거의 마지막 단계이다. Jenkins workspace 내의 Xcode 프로젝트를 수정하여 원하는 인증서로 코드 사이닝이 될 수 있도록 Build configuration을 새로 생성하였다. 이제 Jenkins Xcode plugin이 빌드 할때 해당 Build Configuration을 사용하도록만 설정하면 된다.

Jenkins build job의 Configuration을 아래와 같이 수정하도록 하자.



이렇게 설정하고 Jenkins build job을 실행하고 Console log를 확인해보면 설정된 Build configuration으로 빌드가 완료되었음을 확인할 수 있다.



이상으로 iOS 앱이 원하는 인증서로 코드 사이닝되어 빌드 되는 과정을 알아 보았다.

다음에는 Testflight이라는 서비스를 이용해서 팀원혹은 테스터들에게 Ad hoc빌드된 앱을 쉽게 배포하는 방법을 알아 보도록 하겠다.



1 comment: