Pages

October 10, 2013

[Unity] Jenkins로 유니티 자동 빌드 세팅하기 (1)

진행중인 유니티 프로젝트의 CI (Continuous Integration)를 위해 Jenkins를 설치하고 설정하는 방법을 알아보자. 우리 프로젝트는 버전관리툴로 git을 사용중이며 remote repository는 Bitbucket을 사용중이고 젠킨스는 맥OS 버전을 설치를 할 예정이다.

프로젝트의 빌드머신으로는 맥미니를 쓰고 있다. 빌드머신이 맥미니여야 하는 이유는 유니티에서 iOS버전의 앱을 빌드하려면 Xcode가 필요하기 때문이다. (Xcode는 맥OS버전만 존재한다.)

 젠킨스 홈페이지로 가서 패키지를 다운받자. 다행스럽게도 Mac OS X전용 패키지가 존재하여 인스톨은 굉장히 단순해졌다.

젠킨스는 /Applications/Jenkins 에 인스톨된다.

인스톨 후 http://localhost:8080 페이지를 열면 아래와 같이 젠킨스의 첫 화면을 볼 수 있다.

Jenkins의 첫 화면

플러그인 설치


젠킨스는 수많은 서드파티 플러그인을 사용할 수 있어서 더 유용한데 플러그인을 설치하는 방법은 다음 페이지에 잘 설명이 되어 있다. 언제 부터인지 모르겠지만 젠킨스 대쉬보드상에서 플러그인을 설치할 수 있도록 페이지가 제공되어 플러그인 검색및 설치가 엄청 편해졌다.


일단 지금 꼭 설치해야 하는 플러그인은 Git 플러그인, Bitbucket OAuth 플러그인과 유니티 빌드 플러그인이다.

Git 플러그인은 젠킨스가 외부 git 저장소로부터 git을 통해 파일을 받아 오는데 사용되고,
Bitbucket OAuth 플러그인은 Bitbucket 의 계정 로그인 정보를 Jenkins 로그인 시스템과 통합하는데 사용된다. 그리고 유니티 빌드 플러그인은 유니티 프로젝트를 커맨드라인으로 빌드하기 위해 필요하다.

사용가능한 플러그인 페이지(http://localhost:8080/pluginManager/available)에서 Git Plugin, Bitbucket OAuth Plugin과 Unity3dBuilder Plugin을 찾아서 체크하고 설치 후 젠킨스를 리스타트 해준다. (Install하는 페이지에서 Restart Jenkins when installation is complete and no jobs are running 를 체크하면 자동으로 인스톨 완료후 자동 리스타트됨)

플러그인 설치



Unity3dBuilder Plugin 설정


이 플러그인을 설치하지 않아도 커맨드라인에서 유니티프로젝트를 빌드하는 것은 가능하다 그러나 이 플러그인을 사용하여 얻는 가장 큰 이점은 빌드가 진행되는 동안 유니티에서 생성되는 로그를 확인할 수 있다는 점이다. 그 외에도 자잘한 기능들이 더 있다.

젠킨스 대시보드 상에서 Manage Jenkins -> Configure System -> Unity 3d 탭을 확인하여 Unity3d 에 대한 설정을 아래 스크린샷과 같이 추가해준다. 시스템에 여러 버전의 Unity3d가 설치되어 있는 경우 복수의 유니티설정을 추가할 수 있다.


Unity3dBuilder 플러그인 설정


Bitbucket 로그인 연동하기


아래 플러그인 메인 페이지에 Bitbucket 로그인을 연동하는 방법이 있으니 절차대로 따라하면 문제없이 Bitbucket OAuth 인증으로 Jenkins를 이용할 수 있다.

https://wiki.jenkins-ci.org/display/JENKINS/Bitbucket+OAuth+Plugin

Git 사용을 위한 준비


일단 Git 원격 저장소에서 파일들을 받아오기 위해서는 Key Pair를 생성할 필요가 있다.
Jenkins를 설치하면서 시스템에 생성된 jenkins 계정을 통해 Git repository에 접근하는 것이기 때문에 해당 계정의 Key Pair를 생성하여 Bitbucket(혹은 GitHub)에 등록해야 한다.
터미널을 열고 다음과 같이 jenkins 계정으로 접근하여 jenkins 계정 홈디렉토리에서 key pair를 생성하자.


/Users/Shared $ sudo su - jenkins
MBP-25:~ jenkins$ pwd
/Users/Shared/Jenkins
MBP-25:~ jenkins$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/Shared/Jenkins/.ssh/id_rsa):
Created directory '/Users/Shared/Jenkins/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/Shared/Jenkins/.ssh/id_rsa.
Your public key has been saved in /Users/Shared/Jenkins/.ssh/id_rsa.pub.
The key fingerprint is:
38:f4:8b:75:b1:1d:94:90:57:20:fb:73:83:1a:d7:49 jenkins@MBP-25.local
The key's randomart image is:
+--[ RSA 2048]----+
|          ooo+.  |
|          .+o    |
|      .   o.. E  |
|     . o   = = . |
|      o S + * =  |
|       + o + o . |
|      . . .      |
|                 |
|                 |
+-----------------+


passphrase는 따로 입력하지 않았다. 이렇게 생성한 public키 정보를 Bitbucket에 등록하도록 하자. 키를 등록하는 과정은 Bitbucket의 경우 내 어카운트 아이콘 클릭 -> ManageAccount -> SSH Keys메뉴를 선택 후 Add Key 버튼을 클릭하여 추가할 수 있다.

Bitbucket에 public key 추가하기


이후 아까 열었던 터미널 화면에서 등록한 public key를 통해 테스트 삼아 git repository를 clone해보자. (튜토리얼을 위해 테스트 저장소를 하나 만들고 유니티 프로젝트 파일들을 commit 해두었다.)


MBP-25:~ jenkins$ mkdir temp
MBP-25:~ jenkins$ cd temp
MBP-25:temp jenkins$ git clone git@bitbucket.org:anonymous/unity_ci_test.git
Cloning into 'unity_ci_test'...
The authenticity of host 'bitbucket.org (131.103.20.168)' can't be established.
RSA key fingerprint is 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'bitbucket.org,131.103.20.168' (RSA) to the list of known hosts.
remote: Counting objects: 67, done.
remote: Compressing objects: 100% (67/67), done.
Receiving objects:  52% (35/67), 3.99 MiB | 89.00 KiB/s



파일들을 잘 받아 오는 것을 확인할 수 있다. 이렇게 테스트함으로써 'Are you sure...' 메세지로 자동화 과정이 멈추는 것을 방지할 수 있기도 하다.

그리고  다음과 같이 jenkins유저인 상태에서 git 사용자의 identity도 설정해야 프로젝트 파일을 체크할 때 발생하는 에러를 방지할 수 있다.
 git config --global user.email "you@example.com"
 git config --global user.name "Your Name"

Unity3dBuilder 플러그인을 사용하기 위한 코드 추가


Unity3dBuilder 플러그인은 유니티 프로젝트내에 특별히 프로젝트 빌드를 위한 Editor Class가 추가되어 있음을 가정하고 구현이 되어 있다.

아래 C# 코드를 현재 빌드하려는 프로젝트내의 Asset 폴더아래 Editor폴더를 만들고 적당한 이름을 붙여 생성하자.


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


class ProjectBuilder {
    static string[] SCENES = FindEnabledEditorScenes();
    static string APP_NAME = "YourProject";

    [MenuItem ("Custom/CI/Build Android")]
    static void PerformAndroidBuild ()
    {
         string target_filename = APP_NAME + ".apk";
         GenericBuild(SCENES, target_filename, BuildTarget.Android ,BuildOptions.None);
    }

 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_filename, BuildTarget build_target, BuildOptions build_options)
    {
        EditorUserBuildSettings.SwitchActiveBuildTarget(build_target);
        string res = BuildPipeline.BuildPlayer(scenes, target_filename, build_target, build_options);
        if (res.Length > 0) {
            throw new Exception("BuildPlayer failure: " + res);
        }
    }
}


해당 빌드 코드는 커맨드라인에서 Jenkins가 직접 불러 빌드 과정을 수행하게 된다. 따라서 이 스크립트내에 여러가지 빌드 과정에서 필요한 절차를 추가하여 진행중인 프로젝트에 맞도록 Customize하도록 한다.


자동화 빌드를 위한 Job 생성하기

Job 생성 화면
이제 자동화를 위한 Jenkins Job을 생성해보자. 먼저 위의 스샷과 같이 New Job을 클릭후 나오는 페이지에서  unity_test_build라는 Job을 생성하고 아래와 같이 Source Code Management 에 Git을 선택후 저장소 URL을 입력하였다. 위의 단계 중 잘못 된 부분이 있으면 페이지에 빨간 글씨로 경고가 뜰 것이다. (소스 코드 저장소에 접근을 할 수 없다는 둥...)


Git Repository 설정
그 다음에 Branches to build 란에는 빌드를 할 branch를 쓰면 되는데 기본은 모든 브랜치를 빌드하는 옵션이다. "origin/master" 라고 쓰면 master branch만 빌드하게 된다.

그리고 이제 Unity3dBuilder 플러그인으로 유니티 프로젝트 빌드 설정을 추가한다.
Unity3dBuilder 플러그인 설정때 추가했던 유니티설정을 선택하고 유니티 커맨드라인으로 넘길 파라메터를 입력한다.

-quit -batchMode -executeMethod ProjectBuilder.PerformAndroidBuild


유니티 커맨드 라인 빌드 설정


자 이제 단순하게나마 Job 설정을 마쳤다. 이제 젠킨스 대시보드로 돌아가 빌드를 수행할 시간.

Job등록 상태


참고로 Job 설정에 관한 자세한 내용은 Jenkins 위키 페이지도 참고하도록 하자.


무한 빌드?!!



대시보드에서 해당 Job을 선택하고 왼쪽의 Build Now 메뉴를 클릭. 간단한 테스트 프로젝트이고 그래픽 리소스도 없기 때문에 순식간에 빌드가 끝나야 정상이다. 그러나..

빌드 진행중


세월아~네월아~ 빌드가 끝나질 않는다. 무슨일이 일어난걸까..



진행되고 있는 빌드 히스토리의 현재 진행되고 있는 빌드를 눌러 Console Output을 확인해보자.

빌드 상태 확인


빌드 콘솔 로그 확인


콘솔에 찍힌 로그를 통해서 보건데 아마도 "_RegisterApplication(), FAILED TO establish the default connection to the WindowServer, _CGSDefaultConnection() is NULL." 이 부분이 문제인것 같다.

구글을 통해서 알아본 결과.. MacOS 버전의 유니티는 커맨드라인 빌드를 실행할때 유니티가 시스템 윈도우 서비스에 접근을 해야 하는데 Jenkins는 LaunchDaemon으로 실행되었기 때문에 이 Jenkins가 유니티를 실행하면서 WindowServer 시스템에 접근할 수 있는 권한을 가지고 있지 않기 때문에 발생한 문제이다.. 골치가 아프기 시작한다.

다음 포스팅에서 이어서 문제를 해결해보도록 해야겠다.




3 comments:

  1. 좋은 정리 감사합니다. 추가로 삽질을 한 부분이 있어 덧붙여 봅니다.
    jenkins unity build 과정에서 android SDK 경로를 계속 Unity prompt UI가 뜨면서 물어보는 문제가 있었습니다.
    Unity Preference에서 설정도 해보고 수동 빌드도 해보고 했는데 계속 물어보더군요.

    http://forum.unity3d.com/threads/unity-command-line-doesnt-find-android-sdk.162976/
    위 링크 참조 하여 PerformAndroidBuild() 수행할 때 EditorPrefs.SetString("AndroidSdkRoot", androidSdkPath);
    으로 android SDK 경로를 지정하도록 추가 해서 해결 했습니다.
    android SDK 경로는 jenkins에서 환경변수로 추가하고
    C# 에서 환경변수를 읽도록 했습니다.

    var androidSdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK_ROOT");
    if (androidSdkPath == null) {
    androidSdkPath = "/Users/vm/Documents/qlton/android-sdk-macosx";
    }
    EditorPrefs.SetString("AndroidSdkRoot", androidSdkPath);

    ReplyDelete
  2. 최신버전 유니티도 적용되나여 이거???

    ReplyDelete