Implemented logic for windows based docker builds. Moved dockerfiles and scripts to platform specific folders.

pull/305/head
Andrew Kahr 2021-12-18 21:16:35 -08:00
parent a3e783f430
commit 373d987823
15 changed files with 376 additions and 10 deletions

View File

@ -39,11 +39,13 @@ namespace UnityBuilderAction
// Set version for this build // Set version for this build
VersionApplicator.SetVersion(options["buildVersion"]); VersionApplicator.SetVersion(options["buildVersion"]);
VersionApplicator.SetAndroidVersionCode(options["androidVersionCode"]);
// Apply Android settings // Apply Android settings
if (buildPlayerOptions.target == BuildTarget.Android) if (buildPlayerOptions.target == BuildTarget.Android)
{
VersionApplicator.SetAndroidVersionCode(options["androidVersionCode"]);
AndroidSettings.Apply(options); AndroidSettings.Apply(options);
}
// Execute default AddressableAsset content build, if the package is installed. // Execute default AddressableAsset content build, if the package is installed.
// Version defines would be the best solution here, but Unity 2018 doesn't support that, // Version defines would be the best solution here, but Unity 2018 doesn't support that,

View File

@ -11,9 +11,9 @@ LABEL "homepage"="http://github.com/webbertakken/unity-actions"
LABEL "maintainer"="Webber Takken <webber@takken.io>" LABEL "maintainer"="Webber Takken <webber@takken.io>"
ADD default-build-script /UnityBuilderAction ADD default-build-script /UnityBuilderAction
ADD steps /steps ADD platforms/ubuntu/steps /steps
RUN chmod -R +x /steps RUN chmod -R +x /steps
ADD entrypoint.sh /entrypoint.sh ADD platforms/ubuntu/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh RUN chmod +x /entrypoint.sh
RUN ls RUN ls

View File

View File

View File

@ -0,0 +1,18 @@
ARG IMAGE
FROM $IMAGE
LABEL "com.github.actions.name"="Unity - Builder"
LABEL "com.github.actions.description"="Build Unity projects for different platforms."
LABEL "com.github.actions.icon"="box"
LABEL "com.github.actions.color"="gray-dark"
LABEL "repository"="http://github.com/webbertakken/unity-actions"
LABEL "homepage"="http://github.com/webbertakken/unity-actions"
LABEL "maintainer"="Webber Takken <webber@takken.io>"
ADD default-build-script c:\UnityBuilderAction
ADD platforms/windows/steps c:\steps
ADD platforms/windows/entrypoint.ps1 c:\entrypoint.ps1
RUN ls
ENTRYPOINT ["powershell", "c:/entrypoint.ps1"]

View File

@ -0,0 +1,14 @@
# First we activate Unity
& "c:\steps\activate.ps1"
# Next we import the registry keys that point Unity to the win 10 sdk
reg import c:\regkeys\winsdk.reg
# Now we register the visual studio installation so Unity can find it
regsvr32 C:\ProgramData\Microsoft\VisualStudio\Setup\x64\Microsoft.VisualStudio.Setup.Configuration.Native.dll
# Now we can build our project
& "c:\steps\build.ps1"
# Finally free the seat for the activated license
& "c:\steps\return_license.ps1"

View File

@ -0,0 +1,6 @@
# Activates Unity
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -batchmode -quit -nographics `
-username $Env:UNITY_USER `
-password $Env:UNITY_PASS `
-serial $Env:UNITY_SERIAL `
-logfile | Out-Host

View File

@ -0,0 +1,147 @@
#
# Set project path
#
$Env:UNITY_PROJECT_PATH="$Env:GITHUB_WORKSPACE\$Env:PROJECT_PATH"
Write-Output "$('Using project path "')$($Env:UNITY_PROJECT_PATH)$('".')"
#
# Display the name for the build, doubles as the output name
#
Write-Output "$('Using build name "')$($Env:BUILD_NAME)$('".')"
#
# Display the build's target platform;
#
Write-Output "$('Using build target "')$($Env:BUILD_TARGET)$('".')"
#
# Display build path and file
#
Write-Output "$('Using build path "')$($Env:BUILD_PATH)$('" to save file "')$($Env:BUILD_FILE)$('".')"
$Env:BUILD_PATH_FULL="$Env:GITHUB_WORKSPACE\$Env:BUILD_PATH"
$Env:CUSTOM_BUILD_PATH="$Env:BUILD_PATH_FULL\$Env:BUILD_FILE"
#
# Set the build method, must reference one of:
#
# - <NamespaceName.ClassName.MethodName>
# - <ClassName.MethodName>
#
# For example: `BuildCommand.PerformBuild`
#
# The method must be declared static and placed in project/Assets/Editor
#
if ($Env:BUILD_METHOD)
{
# User has provided their own build method.
# Assume they also bring their own script.
Write-Output "$('Using build method "')$($Env:BUILD_METHOD)$('".')"
}
else
{
# User has not provided their own build command.
#
# Use the script from this action which builds the scenes that are enabled in
# the project.
#
Write-Output "Using built-in build method."
# Create Editor directory if it does not exist
if(-Not (Test-Path -Path $Env:UNITY_PROJECT_PATH\Assets\Editor))
{
# We use -Force to suppress output, doesn't overwrite anything
New-Item -ItemType Directory -Force -Path $Env:UNITY_PROJECT_PATH\Assets\Editor
}
# Copy the build script of Unity Builder action
Copy-Item -Path "c:\UnityBuilderAction" -Destination $Env:UNITY_PROJECT_PATH\Assets\Editor -Recurse
# Set the Build method to that of UnityBuilder Action
$Env:BUILD_METHOD="UnityBuilderAction.Builder.BuildProject"
# Verify recursive paths
Get-ChildItem -Path $UNITY_PROJECT_PATH\Assets\Editor -Recurse
}
#
# Pre-build debug information
#
Write-Output ""
Write-Output "###########################"
Write-Output "# Custom parameters #"
Write-Output "###########################"
Write-Output ""
Write-Output "$('"')$($Env:CUSTOM_PARAMETERS)$('"')"
Write-Output ""
Write-Output "###########################"
Write-Output "# Current build dir #"
Write-Output "###########################"
Write-Output ""
Write-Output "$('Creating "')$($Env:BUILD_PATH_FULL)$('" if it does not exist.')"
if (-Not (Test-Path -Path $Env:BUILD_PATH_FULL))
{
mkdir "$Env:BUILD_PATH_FULL"
}
Get-ChildItem $Env:BUILD_PATH_FULL
Write-Output ""
Write-Output "###########################"
Write-Output "# Project directory #"
Write-Output "###########################"
Write-Output ""
Get-ChildItem $Env:UNITY_PROJECT_PATH
#
# Build
#
Write-Output ""
Write-Output "###########################"
Write-Output "# Building project #"
Write-Output "###########################"
Write-Output ""
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -quit -batchmode -nographics `
-projectPath $Env:UNITY_PROJECT_PATH `
-executeMethod $Env:BUILD_METHOD `
-buildTarget $Env:BUILD_TARGET `
-customBuildTarget $Env:BUILD_TARGET `
-customBuildPath $Env:CUSTOM_BUILD_PATH `
-buildVersion $Env:VERSION `
$Env:CUSTOM_PARAMETERS `
-logfile | Out-Host
# Catch exit code
$Env:BUILD_EXIT_CODE=$?
# Display results
if ($Env:BUILD_EXIT_CODE -eq 0)
{
Write-Output "Build Succeeded!"
} else
{
Write-Output "$('Build failed, with exit code ')$($Env:BUILD_EXIT_CODE)$('"')"
}
# TODO: Determine if we need to set permissions on any files
#
# Results
#
Write-Output ""
Write-Output "###########################"
Write-Output "# Build output #"
Write-Output "###########################"
Write-Output ""
Get-ChildItem $Env:BUILD_PATH_FULL
Write-Output ""

View File

@ -0,0 +1,6 @@
# Returns the active Unity license
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -batchmode -quit -nographics `
-username $Env:UNITY_USER `
-password $Env:UNITY_PASS `
-returnlicense `
-logfile | Out-Host

View File

@ -2,7 +2,7 @@ import path from 'path';
class Action { class Action {
static get supportedPlatforms() { static get supportedPlatforms() {
return ['linux']; return ['linux', 'win32'];
} }
static get isRunningLocally() { static get isRunningLocally() {
@ -30,7 +30,16 @@ class Action {
} }
static get dockerfile() { static get dockerfile() {
return `${Action.actionFolder}/Dockerfile`; const currentPlatform = process.platform;
switch(currentPlatform)
{
case "linux":
return `${Action.actionFolder}/platforms/ubuntu/Dockerfile`;
case "win32":
return `${Action.actionFolder}/platforms/windows/Dockerfile`;
default:
throw new Error(`No Dockerfile for currently unsupported platform: ${currentPlatform}`);
}
} }
static get workspace() { static get workspace() {

View File

@ -1,5 +1,7 @@
import { exec } from '@actions/exec'; import { exec } from '@actions/exec';
import { fstat } from 'fs';
import ImageTag from './image-tag'; import ImageTag from './image-tag';
const fs = require('fs');
class Docker { class Docker {
static async build(buildParameters, silent = false) { static async build(buildParameters, silent = false) {
@ -43,7 +45,10 @@ class Docker {
chownFilesTo, chownFilesTo,
} = parameters; } = parameters;
const command = `docker run \ switch(process.platform)
{
case "linux":
const linuxRunCommand = `docker run \
--workdir /github/workspace \ --workdir /github/workspace \
--rm \ --rm \
--env UNITY_LICENSE \ --env UNITY_LICENSE \
@ -95,7 +100,133 @@ class Docker {
${sshAgent ? '--volume /home/runner/.ssh/known_hosts:/root/.ssh/known_hosts:ro' : ''} \ ${sshAgent ? '--volume /home/runner/.ssh/known_hosts:/root/.ssh/known_hosts:ro' : ''} \
${image}`; ${image}`;
await exec(command, undefined, { silent }); await exec(linuxRunCommand, undefined, { silent });
break;
case "win32":
var unitySerial = "";
if (!process.env.UNITY_SERIAL)
{
//No serial was present so it is a personal license that we need to convert
if (!process.env.UNITY_LICENSE)
{
throw new Error(`Missing Unity License File and no Serial was found. If this
is a personal license, make sure to follow the activation
steps and set the UNITY_LICENSE GitHub secret or enter a Unity
serial number inside the UNITY_SERIAL GitHub secret.`)
}
unitySerial = this.getSerialFromLicenseFile(process.env.UNITY_LICENSE);
} else
{
unitySerial = process.env.UNITY_SERIAL!;
}
if (!(process.env.UNITY_EMAIL && process.env.UNITY_PASSWORD))
{
throw new Error(`Unity email and password must be set for windows based builds`);
}
await this.setupWindowsRun();
this.validateWindowsPrereqs();
const windowsRunCommand = `docker run \
--workdir c:/github/workspace \
--rm \
--env UNITY_LICENSE \
--env UNITY_LICENSE_FILE \
--env UNITY_EMAIL \
--env UNITY_PASSWORD \
--env UNITY_SERIAL="${unitySerial}" \
--env UNITY_VERSION="${version}" \
--env USYM_UPLOAD_AUTH_TOKEN \
--env PROJECT_PATH="${projectPath}" \
--env BUILD_TARGET="${platform}" \
--env BUILD_NAME="${buildName}" \
--env BUILD_PATH="${buildPath}" \
--env BUILD_FILE="${buildFile}" \
--env BUILD_METHOD="${buildMethod}" \
--env VERSION="${buildVersion}" \
--env ANDROID_VERSION_CODE="${androidVersionCode}" \
--env ANDROID_KEYSTORE_NAME="${androidKeystoreName}" \
--env ANDROID_KEYSTORE_BASE64="${androidKeystoreBase64}" \
--env ANDROID_KEYSTORE_PASS="${androidKeystorePass}" \
--env ANDROID_KEYALIAS_NAME="${androidKeyaliasName}" \
--env ANDROID_KEYALIAS_PASS="${androidKeyaliasPass}" \
--env ANDROID_TARGET_SDK_VERSION="${androidTargetSdkVersion}" \
--env ANDROID_SDK_MANAGER_PARAMETERS="${androidSdkManagerParameters}" \
--env CUSTOM_PARAMETERS="${customParameters}" \
--env CHOWN_FILES_TO="${chownFilesTo}" \
--env GITHUB_REF \
--env GITHUB_SHA \
--env GITHUB_REPOSITORY \
--env GITHUB_ACTOR \
--env GITHUB_WORKFLOW \
--env GITHUB_HEAD_REF \
--env GITHUB_BASE_REF \
--env GITHUB_EVENT_NAME \
--env GITHUB_WORKSPACE=/github/workspace \
--env GITHUB_ACTION \
--env GITHUB_EVENT_PATH \
--env RUNNER_OS \
--env RUNNER_TOOL_CACHE \
--env RUNNER_TEMP \
--env RUNNER_WORKSPACE \
--env GIT_PRIVATE_TOKEN="${gitPrivateToken}" \
--volume "${runnerTempPath}/_github_home":"c:/root" \
--volume "${runnerTempPath}/_github_workflow":"c:/github/workflow" \
--volume "${workspace}":"c:/github/workspace" \
--volume "c:/regkeys":"c:/regkeys" \
--volume "C:/Program Files (x86)/Microsoft Visual Studio":"C:/Program Files (x86)/Microsoft Visual Studio" \
--volume "C:/Program Files (x86)/Windows Kits":"C:/Program Files (x86)/Windows Kits" \
--volume "C:/ProgramData/Microsoft/VisualStudio":"C:/ProgramData/Microsoft/VisualStudio" \
${image}`;
await exec(windowsRunCommand, undefined, { silent });
break;
default:
throw new Error(`Can't run docker on unsupported host platform`);
}
}
//Setup prerequisite files for a windows-based docker run
static async setupWindowsRun(silent = false) {
//Need to export registry keys that point to the location of the windows 10 sdk
const makeRegKeyFolderCommand = "mkdir c:/regkeys";
await exec(makeRegKeyFolderCommand, undefined, {silent});
const exportRegKeysCommand = "echo Y| reg export \"HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0\" c:/regkeys/winsdk.reg";
await exec(exportRegKeysCommand, undefined, {silent});
}
static validateWindowsPrereqs() {
//Check for Visual Studio on runner
if (!(fs.existsSync("C:/Program Files (x86)/Microsoft Visual Studio") && fs.existsSync("C:/ProgramData/Microsoft/VisualStudio")))
{
throw new Error(`Visual Studio Installation not found at default location.
Make sure the runner has Visual Studio installed in the
default location`);
}
//Check for Windows 10 SDK on runner
if(!fs.existsSync("C:/Program Files (x86)/Windows Kits"))
{
throw new Error(`Windows 10 SDK not found in default location. Make sure
the runner has a Windows 10 SDK installed in the default
location.`);
}
}
static getSerialFromLicenseFile(license)
{
const startKey = `<DeveloperData Value="`;
const endKey = `"/>`;
let startIndex = license.indexOf(startKey) + startKey.length;
if (startIndex < 0)
{
throw new Error(`License File was corrupted, unable to locate serial`);
}
let endIndex = license.indexOf(endKey, startIndex);
//We substring off the first character as it is a garbage value
return atob(license.substring(startIndex, endIndex)).substring(1);
} }
} }

View File

@ -35,6 +35,7 @@ class ImageTag {
webgl: 'webgl', webgl: 'webgl',
mac: 'mac-mono', mac: 'mac-mono',
windows: 'windows-mono', windows: 'windows-mono',
windowsIl2cpp: 'windows-il2cpp',
linux: 'base', linux: 'base',
linuxIl2cpp: 'linux-il2cpp', linuxIl2cpp: 'linux-il2cpp',
android: 'android', android: 'android',
@ -44,7 +45,7 @@ class ImageTag {
} }
static getTargetPlatformToImageSuffixMap(platform, version) { static getTargetPlatformToImageSuffixMap(platform, version) {
const { generic, webgl, mac, windows, linux, linuxIl2cpp, android, ios, facebook } = ImageTag.imageSuffixes; const { generic, webgl, mac, windows, windowsIl2cpp, linux, linuxIl2cpp, android, ios, facebook } = ImageTag.imageSuffixes;
const [major, minor] = version.split('.').map((digit) => Number(digit)); const [major, minor] = version.split('.').map((digit) => Number(digit));
// @see: https://docs.unity3d.com/ScriptReference/BuildTarget.html // @see: https://docs.unity3d.com/ScriptReference/BuildTarget.html
@ -52,8 +53,32 @@ class ImageTag {
case Platform.types.StandaloneOSX: case Platform.types.StandaloneOSX:
return mac; return mac;
case Platform.types.StandaloneWindows: case Platform.types.StandaloneWindows:
// Unity versions before 2019.3 do not support il2cpp
// Can only build windows-il2cpp on a windows based system
if (process.platform == "win32")
{
if (major >= 2020 || (major === 2019 && minor >= 3)) {
return windowsIl2cpp;
} else
{
throw new Error(`Windows-based builds are only supported on 2019.3.X+ versions of Unity.
If you are trying to build for windows-mono, please use a Linux based OS.`)
}
}
return windows; return windows;
case Platform.types.StandaloneWindows64: case Platform.types.StandaloneWindows64:
// Unity versions before 2019.3 do not support il2cpp
// Can only build windows-il2cpp on a windows based system
if (process.platform == "win32")
{
if (major >= 2020 || (major === 2019 && minor >= 3)) {
return windowsIl2cpp;
} else
{
throw new Error(`Windows-based builds are only supported on 2019.3.X+ versions of Unity.
If you are trying to build for windows-mono, please use a Linux based OS.`)
}
}
return windows; return windows;
case Platform.types.StandaloneLinux64: { case Platform.types.StandaloneLinux64: {
// Unity versions before 2019.3 do not support il2cpp // Unity versions before 2019.3 do not support il2cpp
@ -101,7 +126,15 @@ class ImageTag {
} }
get tag() { get tag() {
return `${this.version}-${this.builderPlatform}`.replace(/-+$/, ''); if (ImageTag.getTargetPlatformToImageSuffixMap(this.platform, this.version) === ImageTag.imageSuffixes.windowsIl2cpp)
{
//Windows based image tags are prefixed with windows-
return `windows-${this.version}-${this.builderPlatform}`.replace(/-+$/, '');
}
else
{
return `${this.version}-${this.builderPlatform}`.replace(/-+$/, '');
}
} }
get image() { get image() {