507 lines
16 KiB
TypeScript
507 lines
16 KiB
TypeScript
import AWSBuildPlatform from './aws-build-platform';
|
|
import * as core from '@actions/core';
|
|
import { BuildParameters } from '..';
|
|
import RemoteBuilderNamespace from './remote-builder-namespace';
|
|
import RemoteBuilderSecret from './remote-builder-secret';
|
|
import { RemoteBuilderProviderInterface } from './remote-builder-provider-interface';
|
|
import Kubernetes from './kubernetes-build-platform';
|
|
import RemoteBuilderEnvironmentVariable from './remote-builder-environment-variable';
|
|
const repositoryFolder = 'repo';
|
|
const buildVolumeFolder = 'data';
|
|
const cacheFolder = 'cache';
|
|
const cacheFolderFull = `/${buildVolumeFolder}/${cacheFolder}`;
|
|
|
|
class RemoteBuilder {
|
|
static RemoteBuilderProviderPlatform: RemoteBuilderProviderInterface;
|
|
private static buildParams: BuildParameters;
|
|
private static defaultSecrets: RemoteBuilderSecret[];
|
|
private static buildId: string;
|
|
private static branchName: string;
|
|
private static buildPathFull: string;
|
|
private static builderPathFull: string;
|
|
private static steamPathFull: string;
|
|
private static repoPathFull: string;
|
|
private static projectPathFull: string;
|
|
private static libraryFolderFull: string;
|
|
static SteamDeploy: boolean = process.env.STEAM_DEPLOY !== undefined || false;
|
|
private static readonly defaultGitShaEnvironmentVariable = [
|
|
{
|
|
name: 'GITHUB_SHA',
|
|
value: process.env.GITHUB_SHA || '',
|
|
},
|
|
];
|
|
|
|
static async build(buildParameters: BuildParameters, baseImage) {
|
|
RemoteBuilder.buildId = RemoteBuilderNamespace.generateBuildName(
|
|
RemoteBuilder.readRunNumber(),
|
|
buildParameters.platform,
|
|
);
|
|
RemoteBuilder.buildParams = buildParameters;
|
|
RemoteBuilder.setupBranchName();
|
|
RemoteBuilder.setupFolderVariables();
|
|
RemoteBuilder.setupDefaultSecrets();
|
|
try {
|
|
RemoteBuilder.setupBuildPlatform();
|
|
|
|
await this.RemoteBuilderProviderPlatform.setupSharedBuildResources(
|
|
this.buildId,
|
|
this.buildParams,
|
|
this.branchName,
|
|
this.defaultSecrets,
|
|
);
|
|
|
|
this.buildPathFull = `/${buildVolumeFolder}/${this.buildId}`;
|
|
this.builderPathFull = `${this.buildPathFull}/builder`;
|
|
this.steamPathFull = `${this.buildPathFull}/steam`;
|
|
this.repoPathFull = `${this.buildPathFull}/${repositoryFolder}`;
|
|
this.projectPathFull = `${this.repoPathFull}/${this.buildParams.projectPath}`;
|
|
this.libraryFolderFull = `${this.projectPathFull}/Library`;
|
|
|
|
await RemoteBuilder.SetupStep();
|
|
await RemoteBuilder.BuildStep(baseImage);
|
|
await RemoteBuilder.CompressionStep();
|
|
await RemoteBuilder.UploadArtifacts();
|
|
if (this.SteamDeploy) {
|
|
await RemoteBuilder.DeployToSteam();
|
|
}
|
|
await this.RemoteBuilderProviderPlatform.cleanupSharedBuildResources(
|
|
this.buildId,
|
|
this.buildParams,
|
|
this.branchName,
|
|
this.defaultSecrets,
|
|
);
|
|
} catch (error) {
|
|
await RemoteBuilder.handleException(error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
private static setupFolderVariables() {
|
|
this.buildPathFull = `/${buildVolumeFolder}/${this.buildId}`;
|
|
this.builderPathFull = `${this.buildPathFull}/builder`;
|
|
this.steamPathFull = `${this.buildPathFull}/steam`;
|
|
this.repoPathFull = `${this.buildPathFull}/${repositoryFolder}`;
|
|
this.projectPathFull = `${this.repoPathFull}/${this.buildParams.projectPath}`;
|
|
this.libraryFolderFull = `${this.projectPathFull}/Library`;
|
|
}
|
|
|
|
private static async SetupStep() {
|
|
core.info('Starting step 1/4 clone and restore cache)');
|
|
|
|
const lfsDirectory = `${this.repoPathFull}/.git/lfs`;
|
|
const testLFSFile = `${this.projectPathFull}/Assets/LFS_Test_File.jpg`;
|
|
|
|
const repo = `https://${this.buildParams.githubToken}@github.com/game-ci/unity-builder.git`;
|
|
const repo2 = `https://${this.buildParams.githubToken}@github.com/game-ci/steam-deploy.git`;
|
|
const repo3 = `https://${this.buildParams.githubToken}@github.com/${process.env.GITHUB_REPOSITORY}.git`;
|
|
|
|
const purgeRemoteCache = process.env.PURGE_REMOTE_BUILDER_CACHE !== undefined;
|
|
const initializeSourceRepoForCaching = `${this.builderPathFull}/dist/remote-builder/cloneNoLFS.sh "${this.repoPathFull}" "${repo3}" "$GITHUB_SHA" "${testLFSFile}"`;
|
|
const handleCaching = `${this.builderPathFull}/dist/remote-builder/handleCaching.sh "${cacheFolderFull}" ${this.libraryFolderFull}" "${lfsDirectory}" "${purgeRemoteCache}"`;
|
|
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
|
this.buildId,
|
|
'alpine/git',
|
|
[
|
|
` printenv
|
|
#
|
|
apk update -q
|
|
apk add unzip zip git-lfs jq tree -q
|
|
#
|
|
mkdir -p ${this.buildPathFull}
|
|
mkdir -p ${this.builderPathFull}
|
|
mkdir -p ${this.repoPathFull}
|
|
mkdir -p ${this.steamPathFull}
|
|
#
|
|
echo ' '
|
|
echo 'Cloning utility repositories for remote builder'
|
|
git clone -q --branch "remote-builder/unified-providers" ${repo} ${this.builderPathFull}
|
|
echo 'Cloned ${repo}'
|
|
git clone -q ${repo2} ${this.steamPathFull}
|
|
echo 'Cloned ${repo2}'
|
|
#
|
|
echo ' '
|
|
echo 'Initializing source repository for cloning with caching of LFS files'
|
|
${initializeSourceRepoForCaching}
|
|
export LFS_ASSETS_HASH="$(cat ${this.repoPathFull}/.lfs-assets-id)"
|
|
${process.env.DEBUG ? '#' : ''}echo ' '
|
|
${process.env.DEBUG ? '#' : ''}echo 'Large File before LFS caching and pull'
|
|
${process.env.DEBUG ? '#' : ''}ls -alh "${lfsDirectory}"
|
|
echo ' '
|
|
echo 'Source repository initialized'
|
|
echo ' '
|
|
echo 'Checking cache for the Unity project Library and git LFS files'
|
|
${handleCaching} "$LFS_ASSETS_HASH"
|
|
${process.env.DEBUG ? '#' : ''}echo 'Caching complete'
|
|
${process.env.DEBUG ? '#' : ''}echo ' '
|
|
${process.env.DEBUG ? '#' : ''}echo 'Large File after LFS caching and pull'
|
|
${process.env.DEBUG ? '#' : ''}ls -alh "${lfsDirectory}"
|
|
echo ' '
|
|
#
|
|
${process.env.DEBUG ? '#' : ''}tree -L 4 "${this.buildPathFull}"
|
|
${process.env.DEBUG ? '#' : ''}ls -lh "${buildVolumeFolder}"
|
|
echo ' '
|
|
#
|
|
`,
|
|
],
|
|
`/${buildVolumeFolder}`,
|
|
`/${buildVolumeFolder}/`,
|
|
RemoteBuilder.defaultGitShaEnvironmentVariable,
|
|
this.defaultSecrets,
|
|
);
|
|
}
|
|
|
|
private static async BuildStep(baseImage: any) {
|
|
const buildSecrets = new Array();
|
|
RemoteBuilder.readBuildSecrets(buildSecrets);
|
|
core.info('Starting part 2/4 (build unity project)');
|
|
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
|
this.buildId,
|
|
baseImage.toString(),
|
|
[
|
|
`
|
|
printenv
|
|
cp -r "${this.builderPathFull}/dist/default-build-script/" "/UnityBuilderAction"
|
|
cp -r "${this.builderPathFull}/dist/entrypoint.sh" "/entrypoint.sh"
|
|
cp -r "${this.builderPathFull}/dist/steps/" "/steps"
|
|
chmod -R +x "/entrypoint.sh"
|
|
chmod -R +x "/steps"
|
|
/entrypoint.sh
|
|
ls -lh
|
|
`,
|
|
],
|
|
`/${buildVolumeFolder}`,
|
|
`/${this.repoPathFull}`,
|
|
RemoteBuilder.readBuildEnvironmentVariables(),
|
|
buildSecrets,
|
|
);
|
|
}
|
|
|
|
private static async CompressionStep() {
|
|
core.info('Starting step 3/4 build compression');
|
|
// Cleanup
|
|
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
|
this.buildId,
|
|
'alpine',
|
|
[
|
|
`
|
|
apk update -q
|
|
apk add zip -q
|
|
apk add tree -q
|
|
cd "${this.libraryFolderFull}"
|
|
zip -r "lib-${this.buildId}.zip" "${this.libraryFolderFull}"
|
|
mv "lib-${this.buildId}.zip" "${cacheFolderFull}/lib"
|
|
cd "${this.projectPathFull}"
|
|
ls -lh "${this.projectPathFull}"
|
|
zip -r "build-${this.buildId}.zip" "${this.projectPathFull}/${RemoteBuilder.buildParams.buildPath}"
|
|
mv "build-${this.buildId}.zip" "/${buildVolumeFolder}/${this.buildId}/build-${this.buildId}.zip"
|
|
`,
|
|
],
|
|
`/${buildVolumeFolder}`,
|
|
`/${buildVolumeFolder}`,
|
|
[
|
|
{
|
|
name: 'GITHUB_SHA',
|
|
value: process.env.GITHUB_SHA || '',
|
|
},
|
|
],
|
|
this.defaultSecrets,
|
|
);
|
|
core.info('compression step complete');
|
|
}
|
|
|
|
private static async UploadArtifacts() {
|
|
core.info('Starting step 4/4 upload build to s3');
|
|
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
|
this.buildId,
|
|
'amazon/aws-cli',
|
|
[
|
|
`
|
|
aws s3 cp ${this.buildId}/build-${this.buildId}.zip s3://game-ci-storage/
|
|
# no need to upload Library cache for now
|
|
# aws s3 cp "/${buildVolumeFolder}/${cacheFolder}/$branch/lib-${this.buildId}.zip" "s3://game-ci-storage/"
|
|
${this.SteamDeploy ? '#' : ''} rm -r ${this.buildId}
|
|
`,
|
|
],
|
|
`/${buildVolumeFolder}`,
|
|
`/${buildVolumeFolder}/`,
|
|
RemoteBuilder.readUploadArtifactEnvironmentVariables(),
|
|
RemoteBuilder.readUploadArtifactsSecrets(),
|
|
);
|
|
}
|
|
|
|
private static async DeployToSteam() {
|
|
core.info('Starting steam deployment');
|
|
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
|
this.buildId,
|
|
'cm2network/steamcmd:root',
|
|
[
|
|
`
|
|
ls
|
|
ls /
|
|
cp -r /${buildVolumeFolder}/${this.buildId}/steam/action/entrypoint.sh /entrypoint.sh;
|
|
cp -r /${buildVolumeFolder}/${this.buildId}/steam/action/steps/ /steps;
|
|
chmod -R +x /entrypoint.sh;
|
|
chmod -R +x /steps;
|
|
/entrypoint.sh;
|
|
rm -r /${buildVolumeFolder}/${this.buildId}
|
|
`,
|
|
],
|
|
`/${buildVolumeFolder}`,
|
|
`/${buildVolumeFolder}/${this.buildId}/steam/action/`,
|
|
[
|
|
{
|
|
name: 'GITHUB_SHA',
|
|
value: process.env.GITHUB_SHA || '',
|
|
},
|
|
],
|
|
RemoteBuilder.readDeployToSteamSecrets(),
|
|
);
|
|
}
|
|
|
|
private static setupBuildPlatform() {
|
|
switch (this.buildParams.remoteBuildCluster) {
|
|
case 'aws':
|
|
core.info('Building with AWS');
|
|
this.RemoteBuilderProviderPlatform = new AWSBuildPlatform(this.buildParams);
|
|
break;
|
|
default:
|
|
case 'k8s':
|
|
core.info('Building with Kubernetes');
|
|
this.RemoteBuilderProviderPlatform = new Kubernetes(this.buildParams);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static readRunNumber() {
|
|
const runNumber = process.env.GITHUB_RUN_NUMBER;
|
|
if (!runNumber || runNumber === '') {
|
|
throw new Error('no run number found, exiting');
|
|
}
|
|
return runNumber;
|
|
}
|
|
|
|
private static setupBranchName() {
|
|
const defaultBranchName =
|
|
process.env.GITHUB_REF?.split('/')
|
|
.filter((x) => {
|
|
x = x[0].toUpperCase() + x.slice(1);
|
|
return x;
|
|
})
|
|
.join('') || '';
|
|
this.branchName =
|
|
process.env.REMOTE_BUILDER_CACHE !== undefined ? process.env.REMOTE_BUILDER_CACHE : defaultBranchName;
|
|
}
|
|
|
|
private static setupDefaultSecrets() {
|
|
this.defaultSecrets = [
|
|
{
|
|
ParameterKey: 'GithubToken',
|
|
EnvironmentVariable: 'GITHUB_TOKEN',
|
|
ParameterValue: this.buildParams.githubToken,
|
|
},
|
|
{
|
|
ParameterKey: 'branch',
|
|
EnvironmentVariable: 'branch',
|
|
ParameterValue: this.branchName,
|
|
},
|
|
{
|
|
ParameterKey: 'buildPathFull',
|
|
EnvironmentVariable: 'buildPathFull',
|
|
ParameterValue: this.buildPathFull,
|
|
},
|
|
{
|
|
ParameterKey: 'projectPathFull',
|
|
EnvironmentVariable: 'projectPathFull',
|
|
ParameterValue: this.projectPathFull,
|
|
},
|
|
];
|
|
}
|
|
|
|
private static readBuildSecrets(buildSecrets: any[]) {
|
|
buildSecrets.push(...this.defaultSecrets);
|
|
|
|
if (process.env.UNITY_LICENSE)
|
|
buildSecrets.push({
|
|
ParameterKey: 'UnityLicense',
|
|
EnvironmentVariable: 'UNITY_LICENSE',
|
|
ParameterValue: process.env.UNITY_LICENSE,
|
|
});
|
|
|
|
if (process.env.UNITY_EMAIL)
|
|
buildSecrets.push({
|
|
ParameterKey: 'UnityEmail',
|
|
EnvironmentVariable: 'UNITY_EMAIL',
|
|
ParameterValue: process.env.UNITY_EMAIL,
|
|
});
|
|
|
|
if (process.env.UNITY_PASSWORD)
|
|
buildSecrets.push({
|
|
ParameterKey: 'UnityPassword',
|
|
EnvironmentVariable: 'UNITY_PASSWORD',
|
|
ParameterValue: process.env.UNITY_PASSWORD,
|
|
});
|
|
|
|
if (process.env.UNITY_SERIAL)
|
|
buildSecrets.push({
|
|
ParameterKey: 'UnitySerial',
|
|
EnvironmentVariable: 'UNITY_SERIAL',
|
|
ParameterValue: process.env.UNITY_SERIAL,
|
|
});
|
|
|
|
if (this.buildParams.androidKeystoreBase64)
|
|
buildSecrets.push({
|
|
ParameterKey: 'AndroidKeystoreBase64',
|
|
EnvironmentVariable: 'ANDROID_KEYSTORE_BASE64',
|
|
ParameterValue: this.buildParams.androidKeystoreBase64,
|
|
});
|
|
|
|
if (this.buildParams.androidKeystorePass)
|
|
buildSecrets.push({
|
|
ParameterKey: 'AndroidKeystorePass',
|
|
EnvironmentVariable: 'ANDROID_KEYSTORE_PASS',
|
|
ParameterValue: this.buildParams.androidKeystorePass,
|
|
});
|
|
|
|
if (this.buildParams.androidKeyaliasPass)
|
|
buildSecrets.push({
|
|
ParameterKey: 'AndroidKeyAliasPass',
|
|
EnvironmentVariable: 'AWS_ACCESS_KEY_ALIAS_PASS',
|
|
ParameterValue: this.buildParams.androidKeyaliasPass,
|
|
});
|
|
}
|
|
|
|
private static readUploadArtifactsSecrets() {
|
|
return [
|
|
{
|
|
ParameterKey: 'AWSAccessKeyID',
|
|
EnvironmentVariable: 'AWS_ACCESS_KEY_ID',
|
|
ParameterValue: process.env.AWS_ACCESS_KEY_ID || '',
|
|
},
|
|
{
|
|
ParameterKey: 'AWSSecretAccessKey',
|
|
EnvironmentVariable: 'AWS_SECRET_ACCESS_KEY',
|
|
ParameterValue: process.env.AWS_SECRET_ACCESS_KEY || '',
|
|
},
|
|
...this.defaultSecrets,
|
|
];
|
|
}
|
|
|
|
private static readDeployToSteamSecrets(): RemoteBuilderSecret[] {
|
|
return [
|
|
{
|
|
EnvironmentVariable: 'INPUT_APPID',
|
|
ParameterKey: 'appId',
|
|
ParameterValue: process.env.APP_ID || '',
|
|
},
|
|
{
|
|
EnvironmentVariable: 'INPUT_BUILDDESCRIPTION',
|
|
ParameterKey: 'buildDescription',
|
|
ParameterValue: process.env.BUILD_DESCRIPTION || '',
|
|
},
|
|
{
|
|
EnvironmentVariable: 'INPUT_ROOTPATH',
|
|
ParameterKey: 'rootPath',
|
|
ParameterValue: RemoteBuilder.buildParams.buildPath,
|
|
},
|
|
{
|
|
EnvironmentVariable: 'INPUT_RELEASEBRANCH',
|
|
ParameterKey: 'releaseBranch',
|
|
ParameterValue: process.env.RELEASE_BRANCH || '',
|
|
},
|
|
{
|
|
EnvironmentVariable: 'INPUT_LOCALCONTENTSERVER',
|
|
ParameterKey: 'localContentServer',
|
|
ParameterValue: process.env.LOCAL_CONTENT_SERVER || '',
|
|
},
|
|
{
|
|
EnvironmentVariable: 'INPUT_PREVIEWENABLED',
|
|
ParameterKey: 'previewEnabled',
|
|
ParameterValue: process.env.PREVIEW_ENABLED || '',
|
|
},
|
|
...this.defaultSecrets,
|
|
];
|
|
}
|
|
|
|
private static readBuildEnvironmentVariables(): RemoteBuilderEnvironmentVariable[] {
|
|
return [
|
|
{
|
|
name: 'ContainerMemory',
|
|
value: this.buildParams.remoteBuildMemory,
|
|
},
|
|
{
|
|
name: 'ContainerCpu',
|
|
value: this.buildParams.remoteBuildCpu,
|
|
},
|
|
{
|
|
name: 'GITHUB_WORKSPACE',
|
|
value: `/${buildVolumeFolder}/${this.buildId}/${repositoryFolder}/`,
|
|
},
|
|
{
|
|
name: 'PROJECT_PATH',
|
|
value: this.buildParams.projectPath,
|
|
},
|
|
{
|
|
name: 'BUILD_PATH',
|
|
value: this.buildParams.buildPath,
|
|
},
|
|
{
|
|
name: 'BUILD_FILE',
|
|
value: this.buildParams.buildFile,
|
|
},
|
|
{
|
|
name: 'BUILD_NAME',
|
|
value: this.buildParams.buildName,
|
|
},
|
|
{
|
|
name: 'BUILD_METHOD',
|
|
value: this.buildParams.buildMethod,
|
|
},
|
|
{
|
|
name: 'CUSTOM_PARAMETERS',
|
|
value: this.buildParams.customParameters,
|
|
},
|
|
{
|
|
name: 'BUILD_TARGET',
|
|
value: this.buildParams.platform,
|
|
},
|
|
{
|
|
name: 'ANDROID_VERSION_CODE',
|
|
value: this.buildParams.androidVersionCode.toString(),
|
|
},
|
|
{
|
|
name: 'ANDROID_KEYSTORE_NAME',
|
|
value: this.buildParams.androidKeystoreName,
|
|
},
|
|
{
|
|
name: 'ANDROID_KEYALIAS_NAME',
|
|
value: this.buildParams.androidKeyaliasName,
|
|
},
|
|
];
|
|
}
|
|
|
|
private static readUploadArtifactEnvironmentVariables() {
|
|
return [
|
|
{
|
|
name: 'GITHUB_SHA',
|
|
value: process.env.GITHUB_SHA || '',
|
|
},
|
|
{
|
|
name: 'AWS_DEFAULT_REGION',
|
|
value: process.env.AWS_DEFAULT_REGION || '',
|
|
},
|
|
];
|
|
}
|
|
|
|
private static async handleException(error: unknown) {
|
|
core.error(JSON.stringify(error, undefined, 4));
|
|
core.setFailed('Remote Builder failed');
|
|
await this.RemoteBuilderProviderPlatform.cleanupSharedBuildResources(
|
|
this.buildId,
|
|
this.buildParams,
|
|
this.branchName,
|
|
this.defaultSecrets,
|
|
);
|
|
}
|
|
}
|
|
export default RemoteBuilder;
|