Зачем нужна автоматическая сборка проекта никому объяснять не надо.
В случае со сборкой проектов под Unity это особенно актуально, так как средненький проект, например, под WebGL собирается на рабочей машине 5-7 минут, полностью её завешивая.

Не так давно вышла версия Unity под Linux, что дало принципиальную возможность настроить автоматическую сборку при помощи Gitlab CI (которая основана на docker образах).

Я хочу поделиться своим опытом такой настройки.

Часть первая — собираем докер образ с Unity


Нам понадобится докер-образ, содержащий Unity.

Ниже я приложу готовый докер файл.
Но сначала несколько комментариев, из которых должно стать понятно, зачем в докерфайле такая экзотика, как gnome или tightvncserver.

Итак, основная проблема при использовании докер-образа с юнити «в лоб»: юнити после установки требует ручной активации. Причем работает активация только в графическом режиме. К счастью, делать это нужно только один раз.

Поэтому план такой:

  • Собираем образ с Unity, содержащий графическую среду и VNC-сервер
  • Запускаем из него контейнер
  • Подключаемся к VNC серверу
  • Запускаем Unity вручную и активируем ее
  • Сохраняем контейнер в новый образ, в котором будет содержаться активированная копия unity

Ниже мой докер файл. Он собирает образ для unity5.6
Я не меняю версию Unity на более новую, потому что сам я всё проверял именно с этой версией.
Но вы можете попробовать заменить
beta.unity3d.com/download/6a86e542cf5c/unity-editor_amd64-5.6.1xf1Linux.deb
на другую версию.

Актуальный список можно найти тут:

Dockerfile
FROM ubuntu:16.04
 
RUN apt-get update -qq

RUN env DEBIAN_FRONTEND=noninteractive apt-get -qq install -y  ubuntu-desktop gnome-panel gnome-settings-daemon metacity nautilus gnome-terminal --no-install-recommends curl gconf-service lib32gcc1 lib32stdc++6 libasound2 libc6 libc6-i386 libcairo2 libcap2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libfreetype6 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libgl1-mesa-glx libglib2.0-0 libglu1-mesa libgtk2.0-0 libnspr4 libnss3 libpango1.0-0 libstdc++6 libx11-6 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxtst6 zlib1g debconf npm xdg-utils lsb-release libpq5 xvfb git vim xrdp tightvncserver     && rm -rf /var/lib/apt/lists/* && apt-get purge
 
RUN echo "Downloading Unity3D installer...."     && mkdir /app     && curl -o /app/unity_editor.deb "http://beta.unity3d.com/download/6a86e542cf5c/unity-editor_amd64-5.6.1xf1Linux.deb"     && echo "Unity3D installer downloaded."     # To make a "min" version of this image build, omit this dpkg line. It saves a lot of space, but you'll need to dpkg it yourself when you use the image.
    && dpkg -i /app/unity_editor.deb && ln -s /opt/Unity/Editor/Unity /usr/local/bin/unity && rm -rf /app
 
 
RUN mkdir -p .vnc .cache/unity3d .local/share/unity3d
 
ENV USER root
 
ADD xstartup /root/.vnc/xstartup
COPY runUnity.sh /root

RUN chmod 755 /root/.vnc/xstartup && chmod 755 /root/runUnity.sh && sed -i -e 's/\r//g' /root/.vnc/xstartup && sed -i -e 's/\r//g' /root/runUnity.sh

# VNC
EXPOSE 5901

ENTRYPOINT ["bash"]


А вот два скрипта, которые используются этим докерфайлом:

xstartup
#!/bin/sh
 
export XKL_XMODMAP_DISABLE=1
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
 
[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
 
gnome-panel &
gnome-settings-daemon &
metacity &
nautilus &
gnome-terminal &


runUnity.sh
#!/bin/sh
xvfb-run --server-args="-screen 0 1024x768x24" /opt/Unity/Editor/Unity -batchmode -logfile -force-opengl -quit -projectPath /project -executeMethod WebGLBuilder.build

Шаг 1


Складываем эти три файла в одну директорию.

Шаг 2


Собираем докером образ (он будет содержать неактивированную копию Unity)
Я делал так:

docker build -t softaria/unity3d .

Шаг 3


Запускаем контейнер из этого образа:

docker run --privileged --cap-add=SYS_ADMIN -ti -p "5901:5901" --entrypoint=bash softaria/unity3d

Порт нам нужен, чтобы достучаться до VNC сервера.

Зачем нужны привилегии я уже не помню, но без них оно не работает. В качестве entrypoint указываем bash чтобы сразу зайти в запущенный контейнер.

Шаг 4


Теперь внутри контейнера нужно запустить vnc сервер:

vncserver :1

Оно попросит придумать пароль. Ок — придумываем.



Как видно на скриншоте, сервер пишет путь до своего лог файла.
Можно заглянуть в этот лог, чтобы убедиться, что стартовал gnome (впрочем, можно и не заглядывать).

Шаг 5


Теперь нам нужен VNC клиент. Любой.
Например, вот этот

Заходим на свой сервер (надо указывать IP + port) например:

123.123.133.123:5901

Порт будет именно 5901. А вот IP – ваш.

Шаг 6


Запускаеи через gnome меню Unity
Логинимся, и активируем лицензию.



Выходим

Шаг 7


Останавливаем vncserver (естественно, внутри докер контейнера — там же, где его запускали)

vncserver -kill :1

Это стоит сделать, чтобы не засорять наш будущий образ с активированной Unity всяким ненужным барахлом.

Шаг 8


Теперь на машине, где запущен докер контейнер открываем другой терминал. Нам нужно сохранить новый докер-образ из работающего контейнера.

Для этого не выходя из контейнера, в другом терминале находим этот контейнер, просто запустив docker ps и копируем себе ид этого контейнера.

Далее сохраняем контейнер в новый образ:

docker commit {ид-контейнера} softaria/unity3d:licensed

Теперь у нас есть докер образ с активированной Unity.
Можно залить его в registry.

Проверяем образ


После этого шага стоит проверить работоспособность образа вне gitlab.

Сделать это можно так:

Пусть в /root/myProject лежат исходники нашего проекта, которые надо собрать.
Мы знаем (или сейчас узнаем), что для сборки из командной строки в исходники проекта нужно поместить специальный класс, конфигурирующий сборку.

Поместить его надо в Assets/Editor

И называться класс и его метод должны именно так, как в моем примере ниже (поскольку эти имена упоминались в нашем скрипте runUnity.sh. Помните :“-executeMethod WebGLBuilder.build” ?)

public class WebGLBuilder {

    static void build()
{
         //Здесь нужно указать все пути к вашим сценам
        string[] scenes = { "Assets/main.unity" };
//Второй аргумент — название папки в которую будет помещена сборка, Третий — тип сборки (в нашем случае WebGL, четвертый обычно None)
BuildPipeline.BuildPlayer(scenes, "WebGL-Dist", BuildTarget.WebGL, BuildOptions.None);
    }
}

Далее запускаем конейнер из нашего образа так:

docker run --privileged --cap-add=SYS_ADMIN -v "/root/myProject:/project" softaria/unity3d:licensed /root/runUnity.sh

И убеждаемся, что сборка работает.

Если сборка заработала, переходим к следующему пункту:

Часть вторая. Сборка проекта в Gitlab.CI


Ниже я приведу свой .gitlab-ci.yml

Но сначала снова несколько комментариев — почему он такой безобразный нетривиальный.

Попытка сделать сборку на основе только что собранного образа (softaria/unity3d:licenced)
провалилась — возникала ошибка "/usr/bin/sh: /usr/bin/sh: cannot execute binary file gitlab"
Точных ее причин я уже не помню, но простым и работающим решением оказалось использовать стандартный image – docker, а свой образ запускать уже внутри него.

Второй проблемой оказалась невозможность передать свои исходники внутрь контейнера с Unity при помощи volume. Внутри гитлаба volumes просто не заработали. Возможно, тут есть какой-то workaround, но мне было лень разбираться проще скопировать исходники внутрь контейнера с юнити, а потом забрать результат обратно.

Не претендую на элегантность этих решений, но вот вам
работающий вариант сборки:
stages:
  - build
 
build:
  image: docker
  stage: build
  before_script:
    #логинимся в локальный докер регистри
    - docker login -u $CI_REGISTRY_USER -p $CI_BUILD_TOKEN $CI_REGISTRY
  script:
#забираем оттуда образ с юнити
- docker pull docker.softaria.com/games/unity:licensed
#создаем из образа контейнер, но не запускаем его пока
- docker create --name ${CI_BUILD_REF}_lines --privileged --cap-add=SYS_ADMIN --entrypoint=bash docker.softaria.com/games/unity:licensed /root/runUnity.sh
# копируем исходники в контейнер
- docker cp $(pwd) ${CI_BUILD_REF}_lines:/project
#запускаем контейнер
- docker start -a -i ${CI_BUILD_REF}_lines
#на всякий случай чистим локальную директорию
- rm -rf nginx/client
#копируем собранный проект в почищенную локальную директорию
- docker cp ${CI_BUILD_REF}_lines:/project/WebGL-Dist/ nginx/client/
#две следующие строки специфичны для моего проекта — они создают новый докер образ на основе образа nginx, который содержит только что созданную сборку и складывают этот образ в локальный регистри. Вы можете их просто убрать (тогда результатом сборки будет только артифакт) или заменить на что-то свое.
    - docker build -t $CI_REGISTRY_IMAGE --build-arg build_name=$CI_PIPELINE_ID nginx
    - docker push $CI_REGISTRY_IMAGE
  after_script:
    - docker rm ${CI_BUILD_REF}_lines
    - docker logout $CI_REGISTRY
  tags:
    - docker
  artifacts:
    name: "${CI_BUILD_REF_NAME}_${CI_BUILD_REF}"
    expire_in: 2 weeks
    paths:
      - nginx/client
  only:
- master

На этом все. Если возникнут вопросы, постараюсь ответить в комментариях.

Комментарии (2)