GitHub Actions × Unity3D 持续交付实践

GitHub Actions × Unity3D 持续交付实践

技术 2111 字 / 4 分钟

期末考完回到家了,很庆幸这学期又没有挂科。从今以后就是不挂科的大茄子了! 在校备考这段时间,收到一条关于 Asteroid 的 issue #3,说实话挺气的。开源无可避免地会遇到伸手党或者不愿自己尝试的人,作为开发者只能被动地把软件做得尽可能简单,来减少他们提问题的机会。 每当遇到不看文档不动手直接上来问的,我都会想起下面这则笑话:

软件工程师努力设计出最大最好的连白痴都会使用的程序;而宇宙在拼命制造最大最好的白痴。到目前为止,宇宙是胜利者。

关于 Asteroid

Asteroid 是我给 Cardinal 平台搭配上的一个实时攻击态势大屏。使用 Unity3D + C# 开发,现已开源在 GitHub。在配好 GitHub Actions 之前,用户是通过 clone 代码到本地,再在本地使用 Unity 2019.1.0f2 打开,自行编译打包。然而一个 Unity 大小高达几十 Gb,对于只是想进行编译而不二次开发的用户来说是个很大的麻烦。因此 Release 发版时的自动编译打包很有必要。

本文将从 Asteroid 项目的实践经历,来讲述使用 GitHub Actions 实现 Unity3D 项目的编译打包。

申请 Unity Licnese

我们将使用 webbertakken/unity-builder 这个 Action 模块来编译 Unity 项目,其实现原理是使用了 gableroux/unity3d 这个 Docker 镜像。其去除了 Unity 的图形化界面,只含有一个 Unity 命令行,可以执行 Unity 相关命令。 但其本身还是一个 Unity 软件的客户端,因此需要使用 Unity 许可证进行激活才能使用。 我在本地电脑上进行 Unity 开发时,是通过 Unity Hub 自动获取许可证。Unity 许可证是与机器的硬件 ID 与 Unity 版本相绑定的。因此如果我们想在 GitHub Actions 上运行 Unity,则需要使用 GitHub Actions 机器硬件 ID 所对应的许可证进行激活。 而所有的 GitHub Actions 虚拟机都拥有相同的硬件 ID,因此我们只需要触发 GitHub Actions 在它的机器上生成 Unity 许可证即可。

GitHub 新建一个 Repository,随便起个名字,比如:unity-activation-file。在仓库目录下新建文件.github/workflow/activation.yml编写一个 GitHub Actions 用于生成许可证。

name: Acquire activation file
on: [push]
jobs:
  activation:
    name: Request manual activation file 🔑
    runs-on: ubuntu-latest
    steps:
        # Request manual activation file
        - name: Request manual activation file
          id: getManualLicenseFile
          uses: webbertakken/[email protected]
          with:
            unityVersion: 2019.1.0f2
        # Upload artifact (Unity_v20XX.X.XXXX.alf)
        - name: Expose as artifact
          uses: actions/upload-artifact@v1
          with:
            name: ${{ steps.getManualLicenseFile.outputs.filePath }}
            path: ${{ steps.getManualLicenseFile.outputs.filePath }}
上文中提到 Unity 许可证与硬件 ID 和 Unity 版本相匹配,因此请将配置中的`unityVersion`改成你所期望的 Unity 版本(一般为你本地的 Unity 版本),接下来的编译也会在该版本的 Unity 上进行。

push 上去后就会触发该 Actions 运行。运行结束后我们可以在 Artifacts 下载到形如Unity_v2019.1.0f2.alf的文件。这就是我们接下来网页端手动激活需要提供的文件。

访问 https://license.unity.cn/manual 进行手动激活。

有坑注意 在访问上方 URL 时会需要你登录 Unity 账号。而每个 Unity 账号只能激活一张个人版免费许可证,这往往就是我们本地开发时所使用的那一张,若再次激活则会报错许可证数量超过上限。 这里建议各位再去注册一个 Unity 账号,用于激活 GitHub Actions 的这张许可证。
登录后,选择我们刚刚从 Artifacts 下载的.alf文件,Next 之后便可以下载得到Unity_v2019.x.ulf文件,这就是我们所需要的 Unity 许可证。保存好它!

编写 GitHub Actions

在你的 Unity 项目仓库下新建文件.github/workflows/build.yml

name: Build project

on:
    release:
        types: [published]

env:
  UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}

jobs:
  buildForSomePlatforms:
    name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        buildName:
          - Asteroid
        projectPath:
          - ./
        unityVersion:
          - 2019.1.0f2
        targetPlatform:
          - StandaloneOSX # Build a macOS standalone (Intel 64-bit).
          - StandaloneWindows # Build a Windows standalone.
          - StandaloneWindows64 # Build a Windows 64-bit standalone.
          - StandaloneLinux64 # Build a Linux 64-bit standalone.

    steps:
      - uses: actions/checkout@v2
        with:
          lfs: true

      - uses: webbertakken/[email protected]
        with:
          buildName: ${{ matrix.buildName }}
          projectPath: ${{ matrix.projectPath }}
          unityVersion: ${{ matrix.unityVersion }}
          targetPlatform: ${{ matrix.targetPlatform }}

      - uses: actions/upload-artifact@v1
        with:
          name: Build
          path: build

      - name: Zip Binary
        run: zip -r Asteroid_${{ matrix.targetPlatform }}.zip build/${{ matrix.targetPlatform }}/*

      - name: Upload Release Asset
        id: upload-release-asset 
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
        with:
          upload_url: ${{ github.event.release.upload_url }}
          asset_path: ./Asteroid_${{ matrix.targetPlatform }}.zip
          asset_name: Asteroid_${{ matrix.targetPlatform }}.zip
          asset_content_type: application/zip 

这里有几点需要注意一下:

  1. 在项目 Secrets 下添加 UNITY_LICENSE ,内容为 Unity_v2019.x.ulf 文件的内容;添加 GH_TOKEN ,内容为你的 GitHub Token。不知道 GitHub 抽了什么风,key 里面不能带 GITHUB,因此只能写成 GH
  2. 注意 unityVersion 版本号填写正确,与你申请的 Unity 许可证时的版本相同。
  3. targetPlatform 为编译的目标平台,我这里只有 Windows 32/64、macOS、Linux 四种,更多平台参见: https://github.com/webbertakken/unity-builder/blob/master/src/model/platform.js#L7
  4. 最后我是直接将文件 zip 打包放到 Release 里了(注意修改下我的 zip 文件名),你也可以选择只上传到 Artifact。
有坑注意 该 GitHub Actions 的触发条件是当有 release publish 时触发。GitHub Actions release event trigger 下细分为 `published` `unpublished` `created` `edited` `deleted` `prereleased` `released` 七种。若我们的触发条件只写`on: release`,则在发布 Release 时会同时触发`published`,`created`,`released`,从而启动三个并发执行的 GitHub Actions 任务! 最后运行的慢一点的任务会因为向 Release 中上传已经存在的文件而报错失败。(但是最后结果是各个平台的 zip 文件都在,只是 CI 的状态是失败的)

对于 Asteroid,每个平台的编译时间大约在 6 分钟左右。这已经很不错了。

由于操作系统的特殊性,对于 Linux、macOS 系统,执行二进制文件前需要chmod +x赋予执行权限。 macOS:chmod +x [PATH-TO-ARTIFACT]/StandaloneOSX/StandaloneOSX.app/Contents/MacOS/*,详见unity-builder #77

最后再说几句

Cardinal 以及其附属的项目,我到现在已经维护了大半年了。这半年来,收获了不少,也认识了很多人。 在 Cardinal README 的下方,明确写了禁止用于商业用途。但最近听说有公司因为 Cardinal 使用了 Apache 协议,因此认为可以商业使用。关于补充协议是否有效这些我也不懂。这件事说白了其实就是我开源的东西被别人反手一波几万拿去卖了恰烂钱。😒

嘛,这项目说白了也不是啥高精尖的技术,最初开源也只是想能多交朋友。这波是朋友没交几个,反而自添烦恼。😅