Add additional build targets (uwp and tvOS)

Adjustments to build scripts to not require win10 sdk when not needed (tvOS)
Platform-based prereq setup
Setup image tags for the new platforms with errors if building on the wrong base os
Rename test-project-il2cpp to test-project-windows to be used for all windows based project building (IL2CPP backend selected instead of mono)
Fix tests to be platform based
pull/305/head
Andrew Kahr 2021-12-26 14:05:08 -08:00
parent 9228ae394f
commit 52bb48dd5c
36 changed files with 488 additions and 207 deletions

View File

@ -71,7 +71,7 @@ jobs:
name: Build Ubuntu (${{ matrix.unityVersion }}) name: Build Ubuntu (${{ matrix.unityVersion }})
path: build path: build
retention-days: 14 retention-days: 14
buildForAllPlatformsWindows: buildForAllPlatformsWindows:
name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }} name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
runs-on: windows-2019 runs-on: windows-2019
@ -79,12 +79,14 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
projectPath: projectPath:
- test-project-il2cpp - test-project-windows
unityVersion: unityVersion:
- 2020.3.24f1 - 2020.3.24f1
targetPlatform: targetPlatform:
- StandaloneWindows64 # Build a Windows 64-bit standalone. - StandaloneWindows64 # Build a Windows 64-bit standalone.
- WSAPlayer # Build a UWP App
- tvOS # Build an Apple TV XCode project
steps: steps:
########################### ###########################
# Checkout # # Checkout #

460
dist/index.js vendored
View File

@ -88,7 +88,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
const path_1 = __importDefault(__webpack_require__(85622)); const path_1 = __importDefault(__webpack_require__(85622));
class Action { class Action {
static get supportedPlatforms() { static get supportedPlatforms() {
return ['linux']; return ['linux', 'win32'];
} }
static get isRunningLocally() { static get isRunningLocally() {
return process.env.RUNNER_WORKSPACE === undefined; return process.env.RUNNER_WORKSPACE === undefined;
@ -109,7 +109,15 @@ class Action {
return `${Action.rootFolder}/dist`; return `${Action.rootFolder}/dist`;
} }
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() {
return process.env.GITHUB_WORKSPACE; return process.env.GITHUB_WORKSPACE;
@ -311,10 +319,10 @@ class Cache {
if (action_1.default.isRunningLocally) { if (action_1.default.isRunningLocally) {
return; return;
} }
core.warning(` core.warning(`
Library folder does not exist. Library folder does not exist.
Consider setting up caching to speed up your workflow, Consider setting up caching to speed up your workflow,
if this is not your first build. if this is not your first build.
`); `);
} }
} }
@ -343,6 +351,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
const exec_1 = __webpack_require__(71514); const exec_1 = __webpack_require__(71514);
const image_tag_1 = __importDefault(__webpack_require__(57648)); const image_tag_1 = __importDefault(__webpack_require__(57648));
const fs = __webpack_require__(35747);
class Docker { class Docker {
static build(buildParameters, silent = false) { static build(buildParameters, silent = false) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
@ -360,7 +369,9 @@ class Docker {
static run(image, parameters, silent = false) { static run(image, parameters, silent = false) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const { version, workspace, runnerTempPath, platform, projectPath, buildName, buildPath, buildFile, buildMethod, buildVersion, androidVersionCode, androidKeystoreName, androidKeystoreBase64, androidKeystorePass, androidKeyaliasName, androidKeyaliasPass, androidTargetSdkVersion, androidSdkManagerParameters, customParameters, sshAgent, gitPrivateToken, chownFilesTo, } = parameters; const { version, workspace, runnerTempPath, platform, projectPath, buildName, buildPath, buildFile, buildMethod, buildVersion, androidVersionCode, androidKeystoreName, androidKeystoreBase64, androidKeystorePass, androidKeyaliasName, androidKeyaliasPass, androidTargetSdkVersion, androidSdkManagerParameters, customParameters, sshAgent, gitPrivateToken, chownFilesTo, } = 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 \
@ -411,9 +422,163 @@ class Docker {
${sshAgent ? `--volume ${sshAgent}:/ssh-agent` : ''} \ ${sshAgent ? `--volume ${sshAgent}:/ssh-agent` : ''} \
${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}`;
yield exec_1.exec(command, undefined, { silent }); yield exec_1.exec(linuxRunCommand, undefined, { silent });
break;
}
case 'win32': {
let 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`);
}
yield this.setupWindowsRun(platform);
this.validateWindowsPrereqs(platform);
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}`;
//Note: When upgrading to Server 2022, we will need to move to just "program files" since VS will be 64-bit
yield exec_1.exec(windowsRunCommand, undefined, { silent });
break;
}
default:
throw new Error(`Can't run docker on unsupported host platform`);
}
}); });
} }
//Setup prerequisite files/folders for a windows-based docker run
static setupWindowsRun(platform, silent = false) {
return __awaiter(this, void 0, void 0, function* () {
const makeRegKeyFolderCommand = 'mkdir c:/regkeys';
yield exec_1.exec(makeRegKeyFolderCommand, undefined, { silent });
switch (platform) {
//These all need the Windows 10 SDK
case 'StandaloneWindows':
this.generateWinSDKRegKeys(silent);
break;
case 'StandaloneWindows64':
this.generateWinSDKRegKeys(silent);
break;
case 'WSAPlayer':
this.generateWinSDKRegKeys(silent);
break;
}
});
}
static generateWinSDKRegKeys(silent = false) {
return __awaiter(this, void 0, void 0, function* () {
//Export registry keys that point to the location of the windows 10 sdk
const exportWinSDKRegKeysCommand = 'echo Y| reg export "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0" c:/regkeys/winsdk.reg';
yield exec_1.exec(exportWinSDKRegKeysCommand, undefined, { silent });
});
}
static validateWindowsPrereqs(platform) {
//We run different checks for different platforms
switch (platform) {
case 'StandaloneWindows':
this.checkForVisualStudio();
this.checkForWin10SDK();
break;
case 'StandaloneWindows64':
this.checkForVisualStudio();
this.checkForWin10SDK();
break;
case 'WSAPlayer':
this.checkForVisualStudio();
this.checkForWin10SDK();
break;
case 'tvOS':
this.checkForVisualStudio();
break;
}
}
static checkForWin10SDK() {
//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 checkForVisualStudio() {
//Note: When upgrading to Server 2022, we will need to move to just "program files" since VS will be 64-bit
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`);
}
}
static getSerialFromLicenseFile(license) {
const startKey = `<DeveloperData Value="`;
const endKey = `"/>`;
const startIndex = license.indexOf(startKey) + startKey.length;
if (startIndex < 0) {
throw new Error(`License File was corrupted, unable to locate serial`);
}
const endIndex = license.indexOf(endKey, startIndex);
//We slice off the first 4 characters as they are garbage values
return Buffer.from(license.slice(startIndex, endIndex), 'base64').toString('binary').slice(4);
}
} }
exports.default = Docker; exports.default = Docker;
@ -487,23 +652,48 @@ class ImageTag {
webgl: 'webgl', webgl: 'webgl',
mac: 'mac-mono', mac: 'mac-mono',
windows: 'windows-mono', windows: 'windows-mono',
windowsIl2cpp: 'windows-il2cpp',
wsaplayer: 'universal-windows-platform',
linux: 'base', linux: 'base',
linuxIl2cpp: 'linux-il2cpp', linuxIl2cpp: 'linux-il2cpp',
android: 'android', android: 'android',
ios: 'ios', ios: 'ios',
tvos: 'appletv',
facebook: 'facebook', facebook: 'facebook',
}; };
} }
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, wsaplayer, linux, linuxIl2cpp, android, ios, tvos, 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
switch (platform) { switch (platform) {
case platform_1.default.types.StandaloneOSX: case platform_1.default.types.StandaloneOSX:
return mac; return mac;
case platform_1.default.types.StandaloneWindows: case platform_1.default.types.StandaloneWindows:
// Can only build windows-il2cpp on a windows based system
if (process.platform === 'win32') {
// Unity versions before 2019.3 do not support il2cpp
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_1.default.types.StandaloneWindows64: case platform_1.default.types.StandaloneWindows64:
// Can only build windows-il2cpp on a windows based system
if (process.platform === 'win32') {
// Unity versions before 2019.3 do not support il2cpp
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_1.default.types.StandaloneLinux64: { case platform_1.default.types.StandaloneLinux64: {
// Unity versions before 2019.3 do not support il2cpp // Unity versions before 2019.3 do not support il2cpp
@ -519,13 +709,19 @@ class ImageTag {
case platform_1.default.types.WebGL: case platform_1.default.types.WebGL:
return webgl; return webgl;
case platform_1.default.types.WSAPlayer: case platform_1.default.types.WSAPlayer:
return windows; if (process.platform !== 'win32') {
throw new Error(`WSAPlayer can only be built on a windows base OS`);
}
return wsaplayer;
case platform_1.default.types.PS4: case platform_1.default.types.PS4:
return windows; return windows;
case platform_1.default.types.XboxOne: case platform_1.default.types.XboxOne:
return windows; return windows;
case platform_1.default.types.tvOS: case platform_1.default.types.tvOS:
return windows; if (process.platform !== 'win32') {
throw new Error(`tvOS can only be built on a windows base OS`);
}
return tvos;
case platform_1.default.types.Switch: case platform_1.default.types.Switch:
return windows; return windows;
// Unsupported // Unsupported
@ -549,7 +745,15 @@ class ImageTag {
} }
} }
get tag() { get tag() {
return `${this.version}-${this.builderPlatform}`.replace(/-+$/, ''); //We check the host os so we know what type of the images we need to pull
switch (process.platform) {
case 'win32':
return `windows-${this.version}-${this.builderPlatform}`.replace(/-+$/, '');
case 'linux':
return `${this.version}-${this.builderPlatform}`.replace(/-+$/, '');
default:
break;
}
} }
get image() { get image() {
return `${this.repository}/${this.name}`.replace(/^\/+/, ''); return `${this.repository}/${this.name}`.replace(/^\/+/, '');
@ -874,14 +1078,14 @@ class Kubernetes {
command: [ command: [
'/bin/sh', '/bin/sh',
'-c', '-c',
`apk update; `apk update;
apk add git-lfs; apk add git-lfs;
export GITHUB_TOKEN=$(cat /credentials/GITHUB_TOKEN); export GITHUB_TOKEN=$(cat /credentials/GITHUB_TOKEN);
cd /data; cd /data;
git clone https://github.com/${process.env.GITHUB_REPOSITORY}.git repo; git clone https://github.com/${process.env.GITHUB_REPOSITORY}.git repo;
git clone https://github.com/webbertakken/unity-builder.git builder; git clone https://github.com/webbertakken/unity-builder.git builder;
cd repo; cd repo;
git checkout $GITHUB_SHA; git checkout $GITHUB_SHA;
ls`, ls`,
], ],
volumeMounts: [ volumeMounts: [
@ -910,13 +1114,13 @@ class Kubernetes {
command: [ command: [
'bin/bash', 'bin/bash',
'-c', '-c',
`for f in ./credentials/*; do export $(basename $f)="$(cat $f)"; done `for f in ./credentials/*; do export $(basename $f)="$(cat $f)"; done
cp -r /data/builder/action/default-build-script /UnityBuilderAction cp -r /data/builder/action/default-build-script /UnityBuilderAction
cp -r /data/builder/action/entrypoint.sh /entrypoint.sh cp -r /data/builder/action/entrypoint.sh /entrypoint.sh
cp -r /data/builder/action/steps /steps cp -r /data/builder/action/steps /steps
chmod -R +x /entrypoint.sh; chmod -R +x /entrypoint.sh;
chmod -R +x /steps; chmod -R +x /steps;
/entrypoint.sh; /entrypoint.sh;
`, `,
], ],
resources: { resources: {
@ -992,8 +1196,8 @@ class Kubernetes {
command: [ command: [
'bin/bash', 'bin/bash',
'-c', '-c',
`cd /data/builder/action/steps; `cd /data/builder/action/steps;
chmod +x /return_license.sh; chmod +x /return_license.sh;
/return_license.sh;`, /return_license.sh;`,
], ],
}, },
@ -1264,25 +1468,25 @@ class AWSBuildEnvironment {
// throw new Error('Method not implemented.'); // throw new Error('Method not implemented.');
// } // }
static getParameterTemplate(p1) { static getParameterTemplate(p1) {
return ` return `
${p1}: ${p1}:
Type: String Type: String
Default: '' Default: ''
`; `;
} }
static getSecretTemplate(p1) { static getSecretTemplate(p1) {
return ` return `
${p1}Secret: ${p1}Secret:
Type: AWS::SecretsManager::Secret Type: AWS::SecretsManager::Secret
Properties: Properties:
Name: !Join [ "", [ '${p1}', !Ref BUILDID ] ] Name: !Join [ "", [ '${p1}', !Ref BUILDID ] ]
SecretString: !Ref ${p1} SecretString: !Ref ${p1}
`; `;
} }
static getSecretDefinitionTemplate(p1, p2) { static getSecretDefinitionTemplate(p1, p2) {
return ` return `
- Name: '${p1}' - Name: '${p1}'
ValueFrom: !Ref ${p2}Secret ValueFrom: !Ref ${p2}Secret
`; `;
} }
static insertAtTemplate(template, insertionKey, insertion) { static insertAtTemplate(template, insertionKey, insertion) {
@ -1293,8 +1497,8 @@ class AWSBuildEnvironment {
static setupCloudFormations(CF, buildUid, stackName, image, entrypoint, commands, mountdir, workingdir, secrets) { static setupCloudFormations(CF, buildUid, stackName, image, entrypoint, commands, mountdir, workingdir, secrets) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const logid = nanoid_1.customAlphabet(remote_builder_constants_1.default.alphabet, 9)(); const logid = nanoid_1.customAlphabet(remote_builder_constants_1.default.alphabet, 9)();
commands[1] += ` commands[1] += `
echo "${logid}" echo "${logid}"
`; `;
const taskDefStackName = `${stackName}-${buildUid}`; const taskDefStackName = `${stackName}-${buildUid}`;
let taskDefCloudFormation = this.readTaskCloudFormationTemplate(); let taskDefCloudFormation = this.readTaskCloudFormationTemplate();
@ -1705,60 +1909,60 @@ class RemoteBuilder {
core.info('Starting step 1/4 clone and restore cache)'); core.info('Starting step 1/4 clone and restore cache)');
yield aws_build_platform_1.default.runBuild(buildUid, buildParameters.awsStackName, 'alpine/git', [ yield aws_build_platform_1.default.runBuild(buildUid, buildParameters.awsStackName, 'alpine/git', [
'-c', '-c',
`apk update; `apk update;
apk add unzip; apk add unzip;
apk add git-lfs; apk add git-lfs;
apk add jq; apk add jq;
# Get source repo for project to be built and game-ci repo for utilties # Get source repo for project to be built and game-ci repo for utilties
git clone https://${buildParameters.githubToken}@github.com/${process.env.GITHUB_REPOSITORY}.git ${buildUid}/${repositoryDirectoryName} -q git clone https://${buildParameters.githubToken}@github.com/${process.env.GITHUB_REPOSITORY}.git ${buildUid}/${repositoryDirectoryName} -q
git clone https://${buildParameters.githubToken}@github.com/game-ci/unity-builder.git ${buildUid}/builder -q git clone https://${buildParameters.githubToken}@github.com/game-ci/unity-builder.git ${buildUid}/builder -q
git clone https://${buildParameters.githubToken}@github.com/game-ci/steam-deploy.git ${buildUid}/steam -q git clone https://${buildParameters.githubToken}@github.com/game-ci/steam-deploy.git ${buildUid}/steam -q
cd /${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/ cd /${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/
git checkout $GITHUB_SHA git checkout $GITHUB_SHA
cd /${efsDirectoryName}/ cd /${efsDirectoryName}/
# Look for usable cache # Look for usable cache
if [ ! -d ${cacheDirectoryName} ]; then if [ ! -d ${cacheDirectoryName} ]; then
mkdir ${cacheDirectoryName} mkdir ${cacheDirectoryName}
fi fi
cd ${cacheDirectoryName} cd ${cacheDirectoryName}
if [ ! -d "${branchName}" ]; then if [ ! -d "${branchName}" ]; then
mkdir "${branchName}" mkdir "${branchName}"
fi fi
cd "${branchName}" cd "${branchName}"
echo '' echo ''
echo "Cached Libraries for ${branchName} from previous builds:" echo "Cached Libraries for ${branchName} from previous builds:"
ls ls
echo '' echo ''
ls "/${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/${buildParameters.projectPath}" ls "/${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/${buildParameters.projectPath}"
libDir="/${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/${buildParameters.projectPath}/Library" libDir="/${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/${buildParameters.projectPath}/Library"
if [ -d "$libDir" ]; then if [ -d "$libDir" ]; then
rm -r "$libDir" rm -r "$libDir"
echo "Setup .gitignore to ignore Library folder and remove it from builds" echo "Setup .gitignore to ignore Library folder and remove it from builds"
fi fi
echo 'Checking cache' echo 'Checking cache'
# Restore cache # Restore cache
latest=$(ls -t | head -1) latest=$(ls -t | head -1)
if [ ! -z "$latest" ]; then if [ ! -z "$latest" ]; then
echo "Library cache exists from build $latest from ${branchName}" echo "Library cache exists from build $latest from ${branchName}"
echo 'Creating empty Library folder for cache' echo 'Creating empty Library folder for cache'
mkdir $libDir mkdir $libDir
unzip -q $latest -d $libDir unzip -q $latest -d $libDir
# purge cache # purge cache
${process.env.PURGE_REMOTE_BUILDER_CACHE === undefined ? '#' : ''} rm -r $libDir ${process.env.PURGE_REMOTE_BUILDER_CACHE === undefined ? '#' : ''} rm -r $libDir
else else
echo 'Cache does not exist' echo 'Cache does not exist'
fi fi
# Print out important directories # Print out important directories
echo '' echo ''
echo 'Repo:' echo 'Repo:'
ls /${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/ ls /${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/
echo '' echo ''
echo 'Project:' echo 'Project:'
ls /${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/${buildParameters.projectPath} ls /${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/${buildParameters.projectPath}
echo '' echo ''
echo 'Library:' echo 'Library:'
ls /${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/${buildParameters.projectPath}/Library/ ls /${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/${buildParameters.projectPath}/Library/
echo '' echo ''
`, `,
], `/${efsDirectoryName}`, `/${efsDirectoryName}/`, [ ], `/${efsDirectoryName}`, `/${efsDirectoryName}/`, [
{ {
@ -1817,13 +2021,13 @@ class RemoteBuilder {
core.info('Starting part 2/4 (build unity project)'); core.info('Starting part 2/4 (build unity project)');
yield aws_build_platform_1.default.runBuild(buildUid, buildParameters.awsStackName, baseImage.toString(), [ yield aws_build_platform_1.default.runBuild(buildUid, buildParameters.awsStackName, baseImage.toString(), [
'-c', '-c',
` `
cp -r /${efsDirectoryName}/${buildUid}/builder/dist/default-build-script/ /UnityBuilderAction; cp -r /${efsDirectoryName}/${buildUid}/builder/dist/default-build-script/ /UnityBuilderAction;
cp -r /${efsDirectoryName}/${buildUid}/builder/dist/entrypoint.sh /entrypoint.sh; cp -r /${efsDirectoryName}/${buildUid}/builder/dist/entrypoint.sh /entrypoint.sh;
cp -r /${efsDirectoryName}/${buildUid}/builder/dist/steps/ /steps; cp -r /${efsDirectoryName}/${buildUid}/builder/dist/steps/ /steps;
chmod -R +x /entrypoint.sh; chmod -R +x /entrypoint.sh;
chmod -R +x /steps; chmod -R +x /steps;
/entrypoint.sh; /entrypoint.sh;
`, `,
], `/${efsDirectoryName}`, `/${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/`, [ ], `/${efsDirectoryName}`, `/${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/`, [
{ {
@ -1887,15 +2091,15 @@ class RemoteBuilder {
// Cleanup // Cleanup
yield aws_build_platform_1.default.runBuild(buildUid, buildParameters.awsStackName, 'alpine', [ yield aws_build_platform_1.default.runBuild(buildUid, buildParameters.awsStackName, 'alpine', [
'-c', '-c',
` `
apk update apk update
apk add zip apk add zip
cd Library cd Library
zip -r lib-${buildUid}.zip .* zip -r lib-${buildUid}.zip .*
mv lib-${buildUid}.zip /${efsDirectoryName}/${cacheDirectoryName}/${branchName}/lib-${buildUid}.zip mv lib-${buildUid}.zip /${efsDirectoryName}/${cacheDirectoryName}/${branchName}/lib-${buildUid}.zip
cd ../../ cd ../../
zip -r build-${buildUid}.zip ${buildParameters.buildPath}/* zip -r build-${buildUid}.zip ${buildParameters.buildPath}/*
mv build-${buildUid}.zip /${efsDirectoryName}/${buildUid}/build-${buildUid}.zip mv build-${buildUid}.zip /${efsDirectoryName}/${buildUid}/build-${buildUid}.zip
`, `,
], `/${efsDirectoryName}`, `/${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/${buildParameters.projectPath}`, [ ], `/${efsDirectoryName}`, `/${efsDirectoryName}/${buildUid}/${repositoryDirectoryName}/${buildParameters.projectPath}`, [
{ {
@ -1911,11 +2115,11 @@ class RemoteBuilder {
core.info('Starting step 4/4 upload build to s3'); core.info('Starting step 4/4 upload build to s3');
yield aws_build_platform_1.default.runBuild(buildUid, buildParameters.awsStackName, 'amazon/aws-cli', [ yield aws_build_platform_1.default.runBuild(buildUid, buildParameters.awsStackName, 'amazon/aws-cli', [
'-c', '-c',
` `
aws s3 cp ${buildUid}/build-${buildUid}.zip s3://game-ci-storage/ aws s3 cp ${buildUid}/build-${buildUid}.zip s3://game-ci-storage/
# no need to upload Library cache for now # no need to upload Library cache for now
# aws s3 cp /${efsDirectoryName}/${cacheDirectoryName}/${branchName}/lib-${buildUid}.zip s3://game-ci-storage/ # aws s3 cp /${efsDirectoryName}/${cacheDirectoryName}/${branchName}/lib-${buildUid}.zip s3://game-ci-storage/
${this.SteamDeploy ? '#' : ''} rm -r ${buildUid} ${this.SteamDeploy ? '#' : ''} rm -r ${buildUid}
`, `,
], `/${efsDirectoryName}`, `/${efsDirectoryName}/`, [ ], `/${efsDirectoryName}`, `/${efsDirectoryName}/`, [
{ {
@ -1946,15 +2150,15 @@ class RemoteBuilder {
core.info('Starting steam deployment'); core.info('Starting steam deployment');
yield aws_build_platform_1.default.runBuild(buildUid, buildParameters.awsStackName, 'cm2network/steamcmd:root', [ yield aws_build_platform_1.default.runBuild(buildUid, buildParameters.awsStackName, 'cm2network/steamcmd:root', [
'-c', '-c',
` `
ls ls
ls / ls /
cp -r /${efsDirectoryName}/${buildUid}/steam/action/entrypoint.sh /entrypoint.sh; cp -r /${efsDirectoryName}/${buildUid}/steam/action/entrypoint.sh /entrypoint.sh;
cp -r /${efsDirectoryName}/${buildUid}/steam/action/steps/ /steps; cp -r /${efsDirectoryName}/${buildUid}/steam/action/steps/ /steps;
chmod -R +x /entrypoint.sh; chmod -R +x /entrypoint.sh;
chmod -R +x /steps; chmod -R +x /steps;
/entrypoint.sh; /entrypoint.sh;
rm -r /${efsDirectoryName}/${buildUid} rm -r /${efsDirectoryName}/${buildUid}
`, `,
], `/${efsDirectoryName}`, `/${efsDirectoryName}/${buildUid}/steam/action/`, [ ], `/${efsDirectoryName}`, `/${efsDirectoryName}/${buildUid}/steam/action/`, [
{ {

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,9 @@
# First we activate Unity # First we activate Unity
& "c:\steps\activate.ps1" & "c:\steps\activate.ps1"
# Next we import the registry keys that point Unity to the win 10 sdk # Next we import any necessary registry keys, ie: location of windows 10 sdk
reg import c:\regkeys\winsdk.reg # No guarantee that there will be any necessary registry keys, ie: tvOS
Get-ChildItem -Path c:\regkeys -File | Foreach {reg import $_.fullname}
# Now we register the visual studio installation so Unity can find it # 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 regsvr32 C:\ProgramData\Microsoft\VisualStudio\Setup\x64\Microsoft.VisualStudio.Setup.Configuration.Native.dll

View File

@ -1,5 +1,4 @@
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'); const fs = require('fs');
@ -45,9 +44,8 @@ class Docker {
chownFilesTo, chownFilesTo,
} = parameters; } = parameters;
switch(process.platform) switch (process.platform) {
{ case 'linux': {
case "linux":
const linuxRunCommand = `docker run \ const linuxRunCommand = `docker run \
--workdir /github/workspace \ --workdir /github/workspace \
--rm \ --rm \
@ -102,32 +100,29 @@ class Docker {
await exec(linuxRunCommand, undefined, { silent }); await exec(linuxRunCommand, undefined, { silent });
break; break;
case "win32": }
var unitySerial = ""; case 'win32': {
if (!process.env.UNITY_SERIAL) let unitySerial = '';
{ if (!process.env.UNITY_SERIAL) {
//No serial was present so it is a personal license that we need to convert //No serial was present so it is a personal license that we need to convert
if (!process.env.UNITY_LICENSE) if (!process.env.UNITY_LICENSE) {
{
throw new Error(`Missing Unity License File and no Serial was found. If this throw new Error(`Missing Unity License File and no Serial was found. If this
is a personal license, make sure to follow the activation is a personal license, make sure to follow the activation
steps and set the UNITY_LICENSE GitHub secret or enter a Unity steps and set the UNITY_LICENSE GitHub secret or enter a Unity
serial number inside the UNITY_SERIAL GitHub secret.`) serial number inside the UNITY_SERIAL GitHub secret.`);
} }
unitySerial = this.getSerialFromLicenseFile(process.env.UNITY_LICENSE); unitySerial = this.getSerialFromLicenseFile(process.env.UNITY_LICENSE);
} else } else {
{
unitySerial = process.env.UNITY_SERIAL!; unitySerial = process.env.UNITY_SERIAL!;
} }
if (!(process.env.UNITY_EMAIL && process.env.UNITY_PASSWORD)) if (!(process.env.UNITY_EMAIL && process.env.UNITY_PASSWORD)) {
{
throw new Error(`Unity email and password must be set for windows based builds`); throw new Error(`Unity email and password must be set for windows based builds`);
} }
await this.setupWindowsRun(); await this.setupWindowsRun(platform);
this.validateWindowsPrereqs(); this.validateWindowsPrereqs(platform);
const windowsRunCommand = `docker run \ const windowsRunCommand = `docker run \
--workdir c:/github/workspace \ --workdir c:/github/workspace \
@ -180,53 +175,95 @@ class Docker {
--volume "C:/Program Files (x86)/Windows Kits":"C:/Program Files (x86)/Windows Kits" \ --volume "C:/Program Files (x86)/Windows Kits":"C:/Program Files (x86)/Windows Kits" \
--volume "C:/ProgramData/Microsoft/VisualStudio":"C:/ProgramData/Microsoft/VisualStudio" \ --volume "C:/ProgramData/Microsoft/VisualStudio":"C:/ProgramData/Microsoft/VisualStudio" \
${image}`; ${image}`;
//Note: When upgrading to Server 2022, we will need to move to just "program files" since VS will be 64-bit
await exec(windowsRunCommand, undefined, { silent }); await exec(windowsRunCommand, undefined, { silent });
break; break;
}
default: default:
throw new Error(`Can't run docker on unsupported host platform`); throw new Error(`Can't run docker on unsupported host platform`);
} }
} }
//Setup prerequisite files for a windows-based docker run //Setup prerequisite files/folders for a windows-based docker run
static async setupWindowsRun(silent = false) { static async setupWindowsRun(platform, silent = false) {
//Need to export registry keys that point to the location of the windows 10 sdk const makeRegKeyFolderCommand = 'mkdir c:/regkeys';
const makeRegKeyFolderCommand = "mkdir c:/regkeys"; await exec(makeRegKeyFolderCommand, undefined, { silent });
await exec(makeRegKeyFolderCommand, undefined, {silent}); switch (platform) {
const exportRegKeysCommand = "echo Y| reg export \"HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0\" c:/regkeys/winsdk.reg"; //These all need the Windows 10 SDK
await exec(exportRegKeysCommand, undefined, {silent}); case 'StandaloneWindows':
this.generateWinSDKRegKeys(silent);
break;
case 'StandaloneWindows64':
this.generateWinSDKRegKeys(silent);
break;
case 'WSAPlayer':
this.generateWinSDKRegKeys(silent);
break;
}
} }
static validateWindowsPrereqs() { static async generateWinSDKRegKeys(silent = false) {
//Check for Visual Studio on runner //Export registry keys that point to the location of the windows 10 sdk
if (!(fs.existsSync("C:/Program Files (x86)/Microsoft Visual Studio") && fs.existsSync("C:/ProgramData/Microsoft/VisualStudio"))) const exportWinSDKRegKeysCommand =
{ 'echo Y| reg export "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0" c:/regkeys/winsdk.reg';
throw new Error(`Visual Studio Installation not found at default location. await exec(exportWinSDKRegKeysCommand, undefined, { silent });
Make sure the runner has Visual Studio installed in the }
default location`);
}
static validateWindowsPrereqs(platform) {
//We run different checks for different platforms
switch (platform) {
case 'StandaloneWindows':
this.checkForVisualStudio();
this.checkForWin10SDK();
break;
case 'StandaloneWindows64':
this.checkForVisualStudio();
this.checkForWin10SDK();
break;
case 'WSAPlayer':
this.checkForVisualStudio();
this.checkForWin10SDK();
break;
case 'tvOS':
this.checkForVisualStudio();
break;
}
}
static checkForWin10SDK() {
//Check for Windows 10 SDK on runner //Check for Windows 10 SDK on runner
if(!fs.existsSync("C:/Program Files (x86)/Windows Kits")) if (!fs.existsSync('C:/Program Files (x86)/Windows Kits')) {
{
throw new Error(`Windows 10 SDK not found in default location. Make sure throw new Error(`Windows 10 SDK not found in default location. Make sure
the runner has a Windows 10 SDK installed in the default the runner has a Windows 10 SDK installed in the default
location.`); location.`);
} }
} }
static getSerialFromLicenseFile(license) static checkForVisualStudio() {
{ //Note: When upgrading to Server 2022, we will need to move to just "program files" since VS will be 64-bit
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`);
}
}
static getSerialFromLicenseFile(license) {
const startKey = `<DeveloperData Value="`; const startKey = `<DeveloperData Value="`;
const endKey = `"/>`; const endKey = `"/>`;
let startIndex = license.indexOf(startKey) + startKey.length; const startIndex = license.indexOf(startKey) + startKey.length;
if (startIndex < 0) if (startIndex < 0) {
{
throw new Error(`License File was corrupted, unable to locate serial`); throw new Error(`License File was corrupted, unable to locate serial`);
} }
let endIndex = license.indexOf(endKey, startIndex); const endIndex = license.indexOf(endKey, startIndex);
//We substring off the first character as it is a garbage value //We slice off the first 4 characters as they are garbage values
return atob(license.substring(startIndex, endIndex)).substring(1); return Buffer.from(license.slice(startIndex, endIndex), 'base64').toString('binary').slice(4);
} }
} }

View File

@ -48,8 +48,14 @@ describe('ImageTag', () => {
describe('toString', () => { describe('toString', () => {
it('returns the correct version', () => { it('returns the correct version', () => {
const image = new ImageTag({ version: '2099.1.1111', platform: some.platform }); const image = new ImageTag({ version: '2099.1.1111', platform: some.platform });
switch (process.platform) {
expect(image.toString()).toStrictEqual(`${defaults.image}:2099.1.1111-0`); case 'win32':
expect(image.toString()).toStrictEqual(`${defaults.image}:windows-2099.1.1111-0`);
break;
case 'linux':
expect(image.toString()).toStrictEqual(`${defaults.image}:2099.1.1111-0`);
break;
}
}); });
it('returns customImage if given', () => { it('returns customImage if given', () => {
const image = new ImageTag({ const image = new ImageTag({
@ -64,13 +70,27 @@ describe('ImageTag', () => {
it('returns the specific build platform', () => { it('returns the specific build platform', () => {
const image = new ImageTag({ version: '2019.2.11f1', platform: 'WebGL' }); const image = new ImageTag({ version: '2019.2.11f1', platform: 'WebGL' });
expect(image.toString()).toStrictEqual(`${defaults.image}:2019.2.11f1-webgl-0`); switch (process.platform) {
case 'win32':
expect(image.toString()).toStrictEqual(`${defaults.image}:windows-2019.2.11f1-webgl-0`);
break;
case 'linux':
expect(image.toString()).toStrictEqual(`${defaults.image}:2019.2.11f1-webgl-0`);
break;
}
}); });
it('returns no specific build platform for generic targetPlatforms', () => { it('returns no specific build platform for generic targetPlatforms', () => {
const image = new ImageTag({ platform: 'NoTarget' }); const image = new ImageTag({ platform: 'NoTarget' });
expect(image.toString()).toStrictEqual(`${defaults.image}:2019.2.11f1-0`); switch (process.platform) {
case 'win32':
expect(image.toString()).toStrictEqual(`${defaults.image}:windows-2019.2.11f1-0`);
break;
case 'linux':
expect(image.toString()).toStrictEqual(`${defaults.image}:2019.2.11f1-0`);
break;
}
}); });
}); });
}); });

View File

@ -36,16 +36,31 @@ class ImageTag {
mac: 'mac-mono', mac: 'mac-mono',
windows: 'windows-mono', windows: 'windows-mono',
windowsIl2cpp: 'windows-il2cpp', windowsIl2cpp: 'windows-il2cpp',
wsaplayer: 'universal-windows-platform',
linux: 'base', linux: 'base',
linuxIl2cpp: 'linux-il2cpp', linuxIl2cpp: 'linux-il2cpp',
android: 'android', android: 'android',
ios: 'ios', ios: 'ios',
tvos: 'appletv',
facebook: 'facebook', facebook: 'facebook',
}; };
} }
static getTargetPlatformToImageSuffixMap(platform, version) { static getTargetPlatformToImageSuffixMap(platform, version) {
const { generic, webgl, mac, windows, windowsIl2cpp, linux, linuxIl2cpp, android, ios, facebook } = ImageTag.imageSuffixes; const {
generic,
webgl,
mac,
windows,
windowsIl2cpp,
wsaplayer,
linux,
linuxIl2cpp,
android,
ios,
tvos,
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
@ -53,30 +68,26 @@ 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 // Can only build windows-il2cpp on a windows based system
if (process.platform == "win32") if (process.platform === 'win32') {
{ // Unity versions before 2019.3 do not support il2cpp
if (major >= 2020 || (major === 2019 && minor >= 3)) { if (major >= 2020 || (major === 2019 && minor >= 3)) {
return windowsIl2cpp; return windowsIl2cpp;
} else } else {
{ throw new Error(`Windows-based builds are only supported on 2019.3.X+ versions of Unity.
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.`);
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 // Can only build windows-il2cpp on a windows based system
if (process.platform == "win32") if (process.platform === 'win32') {
{ // Unity versions before 2019.3 do not support il2cpp
if (major >= 2020 || (major === 2019 && minor >= 3)) { if (major >= 2020 || (major === 2019 && minor >= 3)) {
return windowsIl2cpp; return windowsIl2cpp;
} else } else {
{ throw new Error(`Windows-based builds are only supported on 2019.3.X+ versions of Unity.
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.`);
If you are trying to build for windows-mono, please use a Linux based OS.`)
} }
} }
return windows; return windows;
@ -94,13 +105,19 @@ class ImageTag {
case Platform.types.WebGL: case Platform.types.WebGL:
return webgl; return webgl;
case Platform.types.WSAPlayer: case Platform.types.WSAPlayer:
return windows; if (process.platform !== 'win32') {
throw new Error(`WSAPlayer can only be built on a windows base OS`);
}
return wsaplayer;
case Platform.types.PS4: case Platform.types.PS4:
return windows; return windows;
case Platform.types.XboxOne: case Platform.types.XboxOne:
return windows; return windows;
case Platform.types.tvOS: case Platform.types.tvOS:
return windows; if (process.platform !== 'win32') {
throw new Error(`tvOS can only be built on a windows base OS`);
}
return tvos;
case Platform.types.Switch: case Platform.types.Switch:
return windows; return windows;
// Unsupported // Unsupported
@ -126,14 +143,14 @@ class ImageTag {
} }
get tag() { get tag() {
if (ImageTag.getTargetPlatformToImageSuffixMap(this.platform, this.version) === ImageTag.imageSuffixes.windowsIl2cpp) //We check the host os so we know what type of the images we need to pull
{ switch (process.platform) {
//Windows based image tags are prefixed with windows- case 'win32':
return `windows-${this.version}-${this.builderPlatform}`.replace(/-+$/, ''); return `windows-${this.version}-${this.builderPlatform}`.replace(/-+$/, '');
} case 'linux':
else return `${this.version}-${this.builderPlatform}`.replace(/-+$/, '');
{ default:
return `${this.version}-${this.builderPlatform}`.replace(/-+$/, ''); break;
} }
} }