Refactor state and steps out of cloud-runner main class

pull/310/head
Frostebite 2021-10-04 23:13:26 +01:00
parent 1ce7383d4a
commit 11fd1dccbf
12 changed files with 1032 additions and 687 deletions

806
dist/index.js vendored
View File

@ -535,7 +535,7 @@ class AWSBuildEnvironment {
StackName: taskDefStackName, StackName: taskDefStackName,
}).promise()).StackResources; }).promise()).StackResources;
const baseResources = (yield CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources; const baseResources = (yield CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources;
// TODO: offer a parameter to decide if you want the guarenteed shutdown or fastest startup time possible // TODO: offer a parameter to decide if you want the guaranteed shutdown or fastest startup time possible
return { return {
taskDefStackName, taskDefStackName,
taskDefCloudFormation, taskDefCloudFormation,
@ -963,6 +963,461 @@ class CloudRunnerNamespace {
exports.default = CloudRunnerNamespace; exports.default = CloudRunnerNamespace;
/***/ }),
/***/ 32877:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CloudRunnerState = void 0;
const image_environment_factory_1 = __importDefault(__webpack_require__(25145));
class CloudRunnerState {
static readBuildEnvironmentVariables() {
return [
{
name: 'ContainerMemory',
value: CloudRunnerState.buildParams.cloudRunnerMemory,
},
{
name: 'ContainerCpu',
value: CloudRunnerState.buildParams.cloudRunnerCpu,
},
{
name: 'GITHUB_WORKSPACE',
value: `/${CloudRunnerState.buildVolumeFolder}/${CloudRunnerState.buildGuid}/${CloudRunnerState.repositoryFolder}/`,
},
{
name: 'PROJECT_PATH',
value: CloudRunnerState.buildParams.projectPath,
},
{
name: 'BUILD_PATH',
value: CloudRunnerState.buildParams.buildPath,
},
{
name: 'BUILD_FILE',
value: CloudRunnerState.buildParams.buildFile,
},
{
name: 'BUILD_NAME',
value: CloudRunnerState.buildParams.buildName,
},
{
name: 'BUILD_METHOD',
value: CloudRunnerState.buildParams.buildMethod,
},
{
name: 'CUSTOM_PARAMETERS',
value: CloudRunnerState.buildParams.customParameters,
},
{
name: 'BUILD_TARGET',
value: CloudRunnerState.buildParams.platform,
},
{
name: 'ANDROID_VERSION_CODE',
value: CloudRunnerState.buildParams.androidVersionCode.toString(),
},
{
name: 'ANDROID_KEYSTORE_NAME',
value: CloudRunnerState.buildParams.androidKeystoreName,
},
{
name: 'ANDROID_KEYALIAS_NAME',
value: CloudRunnerState.buildParams.androidKeyaliasName,
},
];
}
static getHandleCachingCommand() {
return `${CloudRunnerState.builderPathFull}/dist/cloud-runner/handleCaching.sh "${CloudRunnerState.cacheFolderFull}" "${CloudRunnerState.libraryFolderFull}" "${CloudRunnerState.lfsDirectory}" "${CloudRunnerState.purgeRemoteCaching}"`;
}
static getCloneNoLFSCommand() {
return `${CloudRunnerState.builderPathFull}/dist/cloud-runner/cloneNoLFS.sh "${CloudRunnerState.repoPathFull}" "${CloudRunnerState.targetBuildRepoUrl}"`;
}
static getCloneBuilder() {
return `git clone -q ${CloudRunnerState.CloudRunnerBranch} ${CloudRunnerState.unityBuilderRepoUrl} ${CloudRunnerState.builderPathFull}`;
}
static readRunNumber() {
const runNumber = process.env.GITHUB_RUN_NUMBER;
if (!runNumber || runNumber === '') {
throw new Error('no run number found, exiting');
}
return runNumber;
}
static setupFolderVariables() {
CloudRunnerState.buildPathFull = `/${CloudRunnerState.buildVolumeFolder}/${CloudRunnerState.buildGuid}`;
CloudRunnerState.builderPathFull = `${CloudRunnerState.buildPathFull}/builder`;
CloudRunnerState.steamPathFull = `${CloudRunnerState.buildPathFull}/steam`;
CloudRunnerState.repoPathFull = `${CloudRunnerState.buildPathFull}/${CloudRunnerState.repositoryFolder}`;
CloudRunnerState.projectPathFull = `${CloudRunnerState.repoPathFull}/${CloudRunnerState.buildParams.projectPath}`;
CloudRunnerState.libraryFolderFull = `${CloudRunnerState.projectPathFull}/Library`;
CloudRunnerState.cacheFolderFull = `/${CloudRunnerState.buildVolumeFolder}/${CloudRunnerState.cacheFolder}/${CloudRunnerState.branchName}`;
CloudRunnerState.lfsDirectory = `${CloudRunnerState.repoPathFull}/.git/lfs`;
CloudRunnerState.purgeRemoteCaching = process.env.PURGE_REMOTE_BUILDER_CACHE !== undefined;
CloudRunnerState.CloudRunnerBranch = process.env.CloudRunnerBranch
? `--branch "${process.env.CloudRunnerBranch}"`
: '';
CloudRunnerState.unityBuilderRepoUrl = `https://${CloudRunnerState.buildParams.githubToken}@github.com/game-ci/unity-builder.git`;
CloudRunnerState.targetBuildRepoUrl = `https://${CloudRunnerState.buildParams.githubToken}@github.com/${process.env.GITHUB_REPOSITORY}.git`;
}
static setupBranchName() {
var _a;
const defaultBranchName = ((_a = process.env.GITHUB_REF) === null || _a === void 0 ? void 0 : _a.split('/').filter((x) => {
x = x[0].toUpperCase() + x.slice(1);
return x;
}).join('')) || '';
CloudRunnerState.branchName =
process.env.REMOTE_BUILDER_CACHE !== undefined ? process.env.REMOTE_BUILDER_CACHE : defaultBranchName;
}
static setupDefaultSecrets() {
CloudRunnerState.defaultSecrets = [
{
ParameterKey: 'GithubToken',
EnvironmentVariable: 'GITHUB_TOKEN',
ParameterValue: CloudRunnerState.buildParams.githubToken,
},
{
ParameterKey: 'branch',
EnvironmentVariable: 'branch',
ParameterValue: CloudRunnerState.branchName,
},
{
ParameterKey: 'buildPathFull',
EnvironmentVariable: 'buildPathFull',
ParameterValue: CloudRunnerState.buildPathFull,
},
{
ParameterKey: 'projectPathFull',
EnvironmentVariable: 'projectPathFull',
ParameterValue: CloudRunnerState.projectPathFull,
},
{
ParameterKey: 'libraryFolderFull',
EnvironmentVariable: 'libraryFolderFull',
ParameterValue: CloudRunnerState.libraryFolderFull,
},
{
ParameterKey: 'builderPathFull',
EnvironmentVariable: 'builderPathFull',
ParameterValue: CloudRunnerState.builderPathFull,
},
{
ParameterKey: 'repoPathFull',
EnvironmentVariable: 'repoPathFull',
ParameterValue: CloudRunnerState.repoPathFull,
},
{
ParameterKey: 'steamPathFull',
EnvironmentVariable: 'steamPathFull',
ParameterValue: CloudRunnerState.steamPathFull,
},
];
CloudRunnerState.defaultSecrets.push(...image_environment_factory_1.default.getEnvironmentVariables(CloudRunnerState.buildParams).map((x) => {
return {
ParameterKey: x.name,
EnvironmentVariable: x.name,
ParameterValue: x.value,
};
}));
}
}
exports.CloudRunnerState = CloudRunnerState;
CloudRunnerState.defaultGitShaEnvironmentVariable = [
{
name: 'GITHUB_SHA',
value: process.env.GITHUB_SHA || '',
},
];
CloudRunnerState.repositoryFolder = 'repo';
CloudRunnerState.buildVolumeFolder = 'data';
CloudRunnerState.cacheFolder = 'cache';
/***/ }),
/***/ 81355:
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CloudRunnerStepState = void 0;
class CloudRunnerStepState {
constructor(image, environmentVariables, secrets) {
this.image = image;
this.environment = environmentVariables;
this.secrets = secrets;
}
}
exports.CloudRunnerStepState = CloudRunnerStepState;
/***/ }),
/***/ 48396:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.BuildStep = void 0;
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
const cloud_runner_state_1 = __webpack_require__(32877);
class BuildStep {
run(cloudRunnerStepState) {
return __awaiter(this, void 0, void 0, function* () {
yield BuildStep.BuildStep(cloudRunnerStepState.image, cloudRunnerStepState.environment, cloudRunnerStepState.secrets);
});
}
static BuildStep(image, environmentVariables, secrets) {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log('Starting part 2/4 (build unity project)');
yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.runBuildTask(cloud_runner_state_1.CloudRunnerState.buildGuid, image, [
`
printenv
export GITHUB_WORKSPACE="${cloud_runner_state_1.CloudRunnerState.repoPathFull}"
cp -r "${cloud_runner_state_1.CloudRunnerState.builderPathFull}/dist/default-build-script/" "/UnityBuilderAction"
cp -r "${cloud_runner_state_1.CloudRunnerState.builderPathFull}/dist/entrypoint.sh" "/entrypoint.sh"
cp -r "${cloud_runner_state_1.CloudRunnerState.builderPathFull}/dist/steps/" "/steps"
chmod -R +x "/entrypoint.sh"
chmod -R +x "/steps"
/entrypoint.sh
${process.env.DEBUG ? '' : '#'}tree -L 4 "${cloud_runner_state_1.CloudRunnerState.buildPathFull}"
${process.env.DEBUG ? '' : '#'}ls -lh "/${cloud_runner_state_1.CloudRunnerState.buildVolumeFolder}"
`,
], `/${cloud_runner_state_1.CloudRunnerState.buildVolumeFolder}`, `/${cloud_runner_state_1.CloudRunnerState.projectPathFull}`, environmentVariables, secrets);
});
}
}
exports.BuildStep = BuildStep;
/***/ }),
/***/ 30851:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CompressionStep = void 0;
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
const cloud_runner_state_1 = __webpack_require__(32877);
class CompressionStep {
run(cloudRunnerStepState) {
return __awaiter(this, void 0, void 0, function* () {
yield CompressionStep.CompressionStep(cloudRunnerStepState.environment, cloudRunnerStepState.secrets);
});
}
static CompressionStep(environmentVariables, secrets) {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log('Starting step 3/4 build compression');
// Cleanup
yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.runBuildTask(cloud_runner_state_1.CloudRunnerState.buildGuid, 'alpine', [
`
printenv
apk update -q
apk add zip tree -q
${process.env.DEBUG ? '' : '#'}tree -L 4 "$repoPathFull"
${process.env.DEBUG ? '' : '#'}ls -lh "$repoPathFull"
cd "$libraryFolderFull/.."
zip -r "lib-$BUILDID.zip" "./Library"
mv "lib-$BUILDID.zip" "/$cacheFolderFull/lib"
cd "$repoPathFull"
ls -lh "$repoPathFull"
zip -r "build-$BUILDID.zip" "./${cloud_runner_state_1.CloudRunnerState.buildParams.buildPath}"
mv "build-$BUILDID.zip" "/$cacheFolderFull/build-$BUILDID.zip"
${process.env.DEBUG ? '' : '#'}tree -L 4 "/$cacheFolderFull"
${process.env.DEBUG ? '' : '#'}tree -L 4 "/$cacheFolderFull/.."
${process.env.DEBUG ? '' : '#'}tree -L 4 "$repoPathFull"
${process.env.DEBUG ? '' : '#'}ls -lh "$repoPathFull"
`,
], `/${cloud_runner_state_1.CloudRunnerState.buildVolumeFolder}`, `/${cloud_runner_state_1.CloudRunnerState.buildVolumeFolder}`, [
...environmentVariables,
...[
{
name: 'cacheFolderFull',
value: cloud_runner_state_1.CloudRunnerState.cacheFolderFull,
},
],
], secrets);
cloud_runner_logger_1.default.log('compression step complete');
});
}
}
exports.CompressionStep = CompressionStep;
/***/ }),
/***/ 47493:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CustomStep = void 0;
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
const cloud_runner_state_1 = __webpack_require__(32877);
const yaml_1 = __importDefault(__webpack_require__(13552));
class CustomStep {
static runCustomJob(buildSteps) {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log(`Cloud Runner is running in custom job mode`);
buildSteps = yaml_1.default.parse(buildSteps);
for (const step of buildSteps) {
const stepSecrets = step.secrets.map((x) => {
const secret = {
ParameterKey: x.name,
EnvironmentVariable: x.name,
ParameterValue: x.value,
};
return secret;
});
yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.runBuildTask(cloud_runner_state_1.CloudRunnerState.buildGuid, step['image'], step['commands'], `/${cloud_runner_state_1.CloudRunnerState.buildVolumeFolder}`, `/${cloud_runner_state_1.CloudRunnerState.buildVolumeFolder}`, cloud_runner_state_1.CloudRunnerState.defaultGitShaEnvironmentVariable, [...cloud_runner_state_1.CloudRunnerState.defaultSecrets, ...stepSecrets]);
}
});
}
}
exports.CustomStep = CustomStep;
/***/ }),
/***/ 36798:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.DownloadRepositoryStep = void 0;
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
const cloud_runner_state_1 = __webpack_require__(32877);
class DownloadRepositoryStep {
run(cloudRunnerStepState) {
return __awaiter(this, void 0, void 0, function* () {
yield DownloadRepositoryStep.downloadRepositoryStep(cloudRunnerStepState.image, cloudRunnerStepState.environment, cloudRunnerStepState.secrets);
});
}
static downloadRepositoryStep(image, environmentVariables, secrets) {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log('Starting step 1/4 clone and restore cache');
yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.runBuildTask(cloud_runner_state_1.CloudRunnerState.buildGuid, image, [
` printenv
apk update -q
apk add unzip zip git-lfs jq tree -q
mkdir -p ${cloud_runner_state_1.CloudRunnerState.buildPathFull}
mkdir -p ${cloud_runner_state_1.CloudRunnerState.builderPathFull}
mkdir -p ${cloud_runner_state_1.CloudRunnerState.repoPathFull}
${cloud_runner_state_1.CloudRunnerState.getCloneBuilder()}
echo ' '
echo 'Initializing source repository for cloning with caching of LFS files'
${cloud_runner_state_1.CloudRunnerState.getCloneNoLFSCommand()}
echo 'Source repository initialized'
echo ' '
echo 'Starting checks of cache for the Unity project Library and git LFS files'
${cloud_runner_state_1.CloudRunnerState.getHandleCachingCommand()}
`,
], `/${cloud_runner_state_1.CloudRunnerState.buildVolumeFolder}`, `/${cloud_runner_state_1.CloudRunnerState.buildVolumeFolder}/`, environmentVariables, secrets);
});
}
}
exports.DownloadRepositoryStep = DownloadRepositoryStep;
/***/ }),
/***/ 15732:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.EphemeralGitHubRunnerStep = void 0;
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
const cloud_runner_state_1 = __webpack_require__(32877);
class EphemeralGitHubRunnerStep {
run(cloudRunnerStepState) {
return __awaiter(this, void 0, void 0, function* () {
yield EphemeralGitHubRunnerStep.runJobAsEphemeralGitHubRunner(cloudRunnerStepState.image, cloudRunnerStepState.environment, cloudRunnerStepState.secrets);
});
}
static runJobAsEphemeralGitHubRunner(image, environmentVariables, secrets) {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log(`Cloud Runner is running in ephemeral GitHub runner mode`);
const installAndStartRunner = 'mkdir actions-runner && cd actions-runner && curl -O -L https://github.com/actions/runner/releases/download/v2.283.1/actions-runner-linux-x64-2.283.1.tar.gz && tar xzf ./actions-runner-linux-x64-2.283.1.tar.gz';
yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.runBuildTask(cloud_runner_state_1.CloudRunnerState.buildGuid, image, [installAndStartRunner], `/${cloud_runner_state_1.CloudRunnerState.buildVolumeFolder}`, `/${cloud_runner_state_1.CloudRunnerState.buildVolumeFolder}`, environmentVariables, secrets);
});
}
}
exports.EphemeralGitHubRunnerStep = EphemeralGitHubRunnerStep;
/***/ }), /***/ }),
/***/ 79144: /***/ 79144:
@ -1005,54 +1460,45 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
const aws_build_platform_1 = __importDefault(__webpack_require__(81683)); const aws_build_platform_1 = __importDefault(__webpack_require__(81683));
const core = __importStar(__webpack_require__(42186)); const core = __importStar(__webpack_require__(42186));
const cloud_runner_namespace_1 = __importDefault(__webpack_require__(58738)); const cloud_runner_namespace_1 = __importDefault(__webpack_require__(58738));
const cloud_runner_state_1 = __webpack_require__(32877);
const kubernetes_build_platform_1 = __importDefault(__webpack_require__(10471)); const kubernetes_build_platform_1 = __importDefault(__webpack_require__(10471));
const image_environment_factory_1 = __importDefault(__webpack_require__(25145));
const yaml_1 = __importDefault(__webpack_require__(13552));
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375)); const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
const repositoryFolder = 'repo'; const build_step_1 = __webpack_require__(48396);
const buildVolumeFolder = 'data'; const compression_step_1 = __webpack_require__(30851);
const cacheFolder = 'cache'; const download_repository_step_1 = __webpack_require__(36798);
const custom_step_1 = __webpack_require__(47493);
const ephemeral_github_runner_step_1 = __webpack_require__(15732);
const cloud_runner_step_state_1 = __webpack_require__(81355);
class CloudRunner { class CloudRunner {
static setup(buildParameters) { static setup(buildParameters) {
cloud_runner_logger_1.default.setup(); cloud_runner_logger_1.default.setup();
CloudRunner.buildGuid = cloud_runner_namespace_1.default.generateBuildName(CloudRunner.readRunNumber(), buildParameters.platform); cloud_runner_state_1.CloudRunnerState.buildGuid = cloud_runner_namespace_1.default.generateBuildName(cloud_runner_state_1.CloudRunnerState.readRunNumber(), buildParameters.platform);
CloudRunner.buildParams = buildParameters; cloud_runner_state_1.CloudRunnerState.buildParams = buildParameters;
CloudRunner.setupBranchName(); cloud_runner_state_1.CloudRunnerState.setupBranchName();
CloudRunner.setupFolderVariables(); cloud_runner_state_1.CloudRunnerState.setupFolderVariables();
CloudRunner.setupDefaultSecrets(); cloud_runner_state_1.CloudRunnerState.setupDefaultSecrets();
CloudRunner.setupBuildPlatform(); CloudRunner.setupBuildPlatform();
} }
static setupFolderVariables() { static setupBuildPlatform() {
this.buildPathFull = `/${buildVolumeFolder}/${this.buildGuid}`; switch (cloud_runner_state_1.CloudRunnerState.buildParams.cloudRunnerCluster) {
this.builderPathFull = `${this.buildPathFull}/builder`; case 'aws':
this.steamPathFull = `${this.buildPathFull}/steam`; cloud_runner_logger_1.default.log('Building with AWS');
this.repoPathFull = `${this.buildPathFull}/${repositoryFolder}`; cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform = new aws_build_platform_1.default(cloud_runner_state_1.CloudRunnerState.buildParams);
this.projectPathFull = `${this.repoPathFull}/${this.buildParams.projectPath}`; break;
this.libraryFolderFull = `${this.projectPathFull}/Library`; default:
this.cacheFolderFull = `/${buildVolumeFolder}/${cacheFolder}/${this.branchName}`; case 'k8s':
this.lfsDirectory = `${this.repoPathFull}/.git/lfs`; cloud_runner_logger_1.default.log('Building with Kubernetes');
this.purgeRemoteCaching = process.env.PURGE_REMOTE_BUILDER_CACHE !== undefined; cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform = new kubernetes_build_platform_1.default(cloud_runner_state_1.CloudRunnerState.buildParams);
this.CloudRunnerBranch = process.env.CloudRunnerBranch ? `--branch "${process.env.CloudRunnerBranch}"` : ''; break;
this.unityBuilderRepoUrl = `https://${this.buildParams.githubToken}@github.com/game-ci/unity-builder.git`; }
this.targetBuildRepoUrl = `https://${this.buildParams.githubToken}@github.com/${process.env.GITHUB_REPOSITORY}.git`;
}
static getHandleCachingCommand() {
return `${this.builderPathFull}/dist/cloud-runner/handleCaching.sh "${this.cacheFolderFull}" "${this.libraryFolderFull}" "${this.lfsDirectory}" "${this.purgeRemoteCaching}"`;
}
static getCloneNoLFSCommand() {
return `${this.builderPathFull}/dist/cloud-runner/cloneNoLFS.sh "${this.repoPathFull}" "${this.targetBuildRepoUrl}"`;
}
static getCloneBuilder() {
return `git clone -q ${this.CloudRunnerBranch} ${this.unityBuilderRepoUrl} ${this.builderPathFull}`;
} }
static run(buildParameters, baseImage) { static run(buildParameters, baseImage) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
CloudRunner.setup(buildParameters); CloudRunner.setup(buildParameters);
try { try {
yield CloudRunner.setupSharedBuildResources(); yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.setupSharedBuildResources(cloud_runner_state_1.CloudRunnerState.buildGuid, cloud_runner_state_1.CloudRunnerState.buildParams, cloud_runner_state_1.CloudRunnerState.branchName, cloud_runner_state_1.CloudRunnerState.defaultSecrets);
yield CloudRunner.setupStep(); yield CloudRunner.runJob(baseImage);
yield CloudRunner.runMainJob(baseImage); yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.cleanupSharedBuildResources(cloud_runner_state_1.CloudRunnerState.buildGuid, cloud_runner_state_1.CloudRunnerState.buildParams, cloud_runner_state_1.CloudRunnerState.branchName, cloud_runner_state_1.CloudRunnerState.defaultSecrets);
yield CloudRunner.cleanupSharedBuildResources();
} }
catch (error) { catch (error) {
yield CloudRunner.handleException(error); yield CloudRunner.handleException(error);
@ -1060,293 +1506,47 @@ class CloudRunner {
} }
}); });
} }
static setupSharedBuildResources() { static runJob(baseImage) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
yield this.CloudRunnerProviderPlatform.setupSharedBuildResources(this.buildGuid, this.buildParams, this.branchName, this.defaultSecrets); core.info(`Custom build steps: ${cloud_runner_state_1.CloudRunnerState.buildParams.customBuildSteps}`);
}); if (cloud_runner_state_1.CloudRunnerState.buildParams.customBuildSteps === '') {
}
static setupBuildPlatform() {
switch (this.buildParams.cloudRunnerCluster) {
case 'aws':
cloud_runner_logger_1.default.log('Building with AWS');
this.CloudRunnerProviderPlatform = new aws_build_platform_1.default(this.buildParams);
break;
default:
case 'k8s':
cloud_runner_logger_1.default.log('Building with Kubernetes');
this.CloudRunnerProviderPlatform = new kubernetes_build_platform_1.default(this.buildParams);
break;
}
}
static readRunNumber() {
const runNumber = process.env.GITHUB_RUN_NUMBER;
if (!runNumber || runNumber === '') {
throw new Error('no run number found, exiting');
}
return runNumber;
}
static setupBranchName() {
var _a;
const defaultBranchName = ((_a = process.env.GITHUB_REF) === null || _a === void 0 ? void 0 : _a.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;
}
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,
},
{
ParameterKey: 'libraryFolderFull',
EnvironmentVariable: 'libraryFolderFull',
ParameterValue: this.libraryFolderFull,
},
{
ParameterKey: 'builderPathFull',
EnvironmentVariable: 'builderPathFull',
ParameterValue: this.builderPathFull,
},
{
ParameterKey: 'repoPathFull',
EnvironmentVariable: 'repoPathFull',
ParameterValue: this.repoPathFull,
},
{
ParameterKey: 'steamPathFull',
EnvironmentVariable: 'steamPathFull',
ParameterValue: this.steamPathFull,
},
];
this.defaultSecrets.push(...image_environment_factory_1.default.getEnvironmentVariables(this.buildParams).map((x) => {
return {
ParameterKey: x.name,
EnvironmentVariable: x.name,
ParameterValue: x.value,
};
}));
}
static readBuildEnvironmentVariables() {
return [
{
name: 'ContainerMemory',
value: this.buildParams.cloudRunnerMemory,
},
{
name: 'ContainerCpu',
value: this.buildParams.cloudRunnerCpu,
},
{
name: 'GITHUB_WORKSPACE',
value: `/${buildVolumeFolder}/${this.buildGuid}/${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,
},
];
}
static runMainJob(baseImage) {
return __awaiter(this, void 0, void 0, function* () {
if (this.buildParams.customBuildSteps === '') {
yield CloudRunner.standardBuildAutomation(baseImage); yield CloudRunner.standardBuildAutomation(baseImage);
} }
else if (this.buildParams.customBuildSteps === 'ephemeral') { else if (cloud_runner_state_1.CloudRunnerState.buildParams.customBuildSteps === 'ephemeral') {
yield CloudRunner.runJobAsEphemeralGitHubRunner(baseImage); yield new ephemeral_github_runner_step_1.EphemeralGitHubRunnerStep().run(new cloud_runner_step_state_1.CloudRunnerStepState(baseImage, cloud_runner_state_1.CloudRunnerState.readBuildEnvironmentVariables(), cloud_runner_state_1.CloudRunnerState.defaultSecrets));
}
else if (cloud_runner_state_1.CloudRunnerState.buildParams.customBuildSteps === 'download') {
yield new download_repository_step_1.DownloadRepositoryStep().run(new cloud_runner_step_state_1.CloudRunnerStepState('alpine/git', cloud_runner_state_1.CloudRunnerState.readBuildEnvironmentVariables(), cloud_runner_state_1.CloudRunnerState.defaultSecrets));
} }
else { else {
yield CloudRunner.runCustomJob(this.buildParams.customBuildSteps); yield custom_step_1.CustomStep.runCustomJob(cloud_runner_state_1.CloudRunnerState.buildParams.customBuildSteps);
} }
}); });
} }
static standardBuildAutomation(baseImage) { static standardBuildAutomation(baseImage) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log(`Cloud Runner is running in standard build automation mode`); cloud_runner_logger_1.default.log(`Cloud Runner is running standard build automation`);
cloud_runner_logger_1.default.logWithTime('Pre build steps time'); yield new download_repository_step_1.DownloadRepositoryStep().run(new cloud_runner_step_state_1.CloudRunnerStepState('alpine/git', cloud_runner_state_1.CloudRunnerState.readBuildEnvironmentVariables(), cloud_runner_state_1.CloudRunnerState.defaultSecrets));
yield this.runCustomJob(this.buildParams.preBuildSteps); cloud_runner_logger_1.default.logWithTime('Download repository step time');
cloud_runner_logger_1.default.logWithTime('Setup time'); yield custom_step_1.CustomStep.runCustomJob(cloud_runner_state_1.CloudRunnerState.buildParams.preBuildSteps);
yield CloudRunner.BuildStep(baseImage); cloud_runner_logger_1.default.logWithTime('Pre build step(s) time');
new build_step_1.BuildStep().run(new cloud_runner_step_state_1.CloudRunnerStepState(baseImage, cloud_runner_state_1.CloudRunnerState.readBuildEnvironmentVariables(), cloud_runner_state_1.CloudRunnerState.defaultSecrets));
cloud_runner_logger_1.default.logWithTime('Build time'); cloud_runner_logger_1.default.logWithTime('Build time');
yield CloudRunner.CompressionStep(); yield new compression_step_1.CompressionStep().run(new cloud_runner_step_state_1.CloudRunnerStepState('alpine', cloud_runner_state_1.CloudRunnerState.readBuildEnvironmentVariables(), cloud_runner_state_1.CloudRunnerState.defaultSecrets));
cloud_runner_logger_1.default.logWithTime('Compression time'); cloud_runner_logger_1.default.logWithTime('Compression time');
yield this.runCustomJob(this.buildParams.postBuildSteps); yield custom_step_1.CustomStep.runCustomJob(cloud_runner_state_1.CloudRunnerState.buildParams.postBuildSteps);
cloud_runner_logger_1.default.logWithTime('Post build steps time'); cloud_runner_logger_1.default.logWithTime('Post build step(s) time');
}); cloud_runner_logger_1.default.log(`Cloud Runner finished running standard build automation`);
}
static runJobAsEphemeralGitHubRunner(baseImage) {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log(`Cloud Runner is running in ephemeral GitHub runner mode`);
const installAndStartRunner = 'mkdir actions-runner && cd actions-runner && curl -O -L https://github.com/actions/runner/releases/download/v2.283.1/actions-runner-linux-x64-2.283.1.tar.gz && tar xzf ./actions-runner-linux-x64-2.283.1.tar.gz';
yield this.CloudRunnerProviderPlatform.runBuildTask(this.buildGuid, baseImage, [installAndStartRunner], `/${buildVolumeFolder}`, `/${buildVolumeFolder}`, this.defaultGitShaEnvironmentVariable, [...this.defaultSecrets]);
});
}
static runCustomJob(buildSteps) {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log(`Cloud Runner is running in custom job mode`);
buildSteps = yaml_1.default.parse(buildSteps);
for (const step of buildSteps) {
const stepSecrets = step.secrets.map((x) => {
const secret = {
ParameterKey: x.name,
EnvironmentVariable: x.name,
ParameterValue: x.value,
};
return secret;
});
yield this.CloudRunnerProviderPlatform.runBuildTask(this.buildGuid, step['image'], step['commands'], `/${buildVolumeFolder}`, `/${buildVolumeFolder}`, this.defaultGitShaEnvironmentVariable, [...this.defaultSecrets, ...stepSecrets]);
}
});
}
static setupStep() {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log('Starting step 1/4 clone and restore cache)');
yield this.CloudRunnerProviderPlatform.runBuildTask(this.buildGuid, '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}
${this.getCloneBuilder()}
echo ' '
echo 'Initializing source repository for cloning with caching of LFS files'
${this.getCloneNoLFSCommand()}
echo 'Source repository initialized'
echo ' '
echo 'Starting checks of cache for the Unity project Library and git LFS files'
${this.getHandleCachingCommand()}
`,
], `/${buildVolumeFolder}`, `/${buildVolumeFolder}/`, CloudRunner.defaultGitShaEnvironmentVariable, this.defaultSecrets);
});
}
static BuildStep(baseImage) {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log('Starting part 2/4 (build unity project)');
yield this.CloudRunnerProviderPlatform.runBuildTask(this.buildGuid, baseImage.toString(), [
`
printenv
export GITHUB_WORKSPACE="${this.repoPathFull}"
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
${process.env.DEBUG ? '' : '#'}tree -L 4 "${this.buildPathFull}"
${process.env.DEBUG ? '' : '#'}ls -lh "/${buildVolumeFolder}"
`,
], `/${buildVolumeFolder}`, `/${this.projectPathFull}`, CloudRunner.readBuildEnvironmentVariables(), this.defaultSecrets);
});
}
static CompressionStep() {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log('Starting step 3/4 build compression');
// Cleanup
yield this.CloudRunnerProviderPlatform.runBuildTask(this.buildGuid, 'alpine', [
`
printenv
apk update -q
apk add zip tree -q
${process.env.DEBUG ? '' : '#'}tree -L 4 "$repoPathFull"
${process.env.DEBUG ? '' : '#'}ls -lh "$repoPathFull"
cd "$libraryFolderFull/.."
zip -r "lib-$BUILDID.zip" "./Library"
mv "lib-$BUILDID.zip" "/$cacheFolderFull/lib"
cd "$repoPathFull"
ls -lh "$repoPathFull"
zip -r "build-$BUILDID.zip" "./${CloudRunner.buildParams.buildPath}"
mv "build-$BUILDID.zip" "/$cacheFolderFull/build-$BUILDID.zip"
${process.env.DEBUG ? '' : '#'}tree -L 4 "/$cacheFolderFull"
${process.env.DEBUG ? '' : '#'}tree -L 4 "/$cacheFolderFull/.."
${process.env.DEBUG ? '' : '#'}tree -L 4 "$repoPathFull"
${process.env.DEBUG ? '' : '#'}ls -lh "$repoPathFull"
`,
], `/${buildVolumeFolder}`, `/${buildVolumeFolder}`, [
...CloudRunner.defaultGitShaEnvironmentVariable,
...[
{
name: 'cacheFolderFull',
value: this.cacheFolderFull,
},
],
], this.defaultSecrets);
cloud_runner_logger_1.default.log('compression step complete');
});
}
static cleanupSharedBuildResources() {
return __awaiter(this, void 0, void 0, function* () {
yield this.CloudRunnerProviderPlatform.cleanupSharedBuildResources(this.buildGuid, this.buildParams, this.branchName, this.defaultSecrets);
}); });
} }
static handleException(error) { static handleException(error) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.error(JSON.stringify(error, undefined, 4)); cloud_runner_logger_1.default.error(JSON.stringify(error, undefined, 4));
core.setFailed('Remote Builder failed'); core.setFailed('Remote Builder failed');
yield this.CloudRunnerProviderPlatform.cleanupSharedBuildResources(this.buildGuid, this.buildParams, this.branchName, this.defaultSecrets); yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.cleanupSharedBuildResources(cloud_runner_state_1.CloudRunnerState.buildGuid, cloud_runner_state_1.CloudRunnerState.buildParams, cloud_runner_state_1.CloudRunnerState.branchName, cloud_runner_state_1.CloudRunnerState.defaultSecrets);
}); });
} }
} }
CloudRunner.defaultGitShaEnvironmentVariable = [
{
name: 'GITHUB_SHA',
value: process.env.GITHUB_SHA || '',
},
];
exports.default = CloudRunner; exports.default = CloudRunner;

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -242,7 +242,7 @@ class AWSBuildEnvironment implements CloudRunnerProviderInterface {
const baseResources = (await CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources; const baseResources = (await CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources;
// TODO: offer a parameter to decide if you want the guarenteed shutdown or fastest startup time possible // TODO: offer a parameter to decide if you want the guaranteed shutdown or fastest startup time possible
return { return {
taskDefStackName, taskDefStackName,

View File

@ -0,0 +1,194 @@
import { BuildParameters } from '../..';
import { CloudRunnerProviderInterface } from '../cloud-runner-services/cloud-runner-provider-interface';
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
import CloudRunnerEnvironmentVariable from '../cloud-runner-services/cloud-runner-environment-variable';
import ImageEnvironmentFactory from '../../image-environment-factory';
export class CloudRunnerState {
public static CloudRunnerProviderPlatform: CloudRunnerProviderInterface;
public static buildParams: BuildParameters;
public static defaultSecrets: CloudRunnerSecret[];
public static buildGuid: string;
public static branchName: string;
public static buildPathFull: string;
public static builderPathFull: string;
public static steamPathFull: string;
public static repoPathFull: string;
public static projectPathFull: string;
public static libraryFolderFull: string;
public static cacheFolderFull: string;
public static lfsDirectory: string;
public static purgeRemoteCaching: boolean;
public static CloudRunnerBranch: string;
public static unityBuilderRepoUrl: string;
public static targetBuildRepoUrl: string;
public static readonly defaultGitShaEnvironmentVariable = [
{
name: 'GITHUB_SHA',
value: process.env.GITHUB_SHA || '',
},
];
public static readonly repositoryFolder = 'repo';
public static readonly buildVolumeFolder = 'data';
public static readonly cacheFolder = 'cache';
public static readBuildEnvironmentVariables(): CloudRunnerEnvironmentVariable[] {
return [
{
name: 'ContainerMemory',
value: CloudRunnerState.buildParams.cloudRunnerMemory,
},
{
name: 'ContainerCpu',
value: CloudRunnerState.buildParams.cloudRunnerCpu,
},
{
name: 'GITHUB_WORKSPACE',
value: `/${CloudRunnerState.buildVolumeFolder}/${CloudRunnerState.buildGuid}/${CloudRunnerState.repositoryFolder}/`,
},
{
name: 'PROJECT_PATH',
value: CloudRunnerState.buildParams.projectPath,
},
{
name: 'BUILD_PATH',
value: CloudRunnerState.buildParams.buildPath,
},
{
name: 'BUILD_FILE',
value: CloudRunnerState.buildParams.buildFile,
},
{
name: 'BUILD_NAME',
value: CloudRunnerState.buildParams.buildName,
},
{
name: 'BUILD_METHOD',
value: CloudRunnerState.buildParams.buildMethod,
},
{
name: 'CUSTOM_PARAMETERS',
value: CloudRunnerState.buildParams.customParameters,
},
{
name: 'BUILD_TARGET',
value: CloudRunnerState.buildParams.platform,
},
{
name: 'ANDROID_VERSION_CODE',
value: CloudRunnerState.buildParams.androidVersionCode.toString(),
},
{
name: 'ANDROID_KEYSTORE_NAME',
value: CloudRunnerState.buildParams.androidKeystoreName,
},
{
name: 'ANDROID_KEYALIAS_NAME',
value: CloudRunnerState.buildParams.androidKeyaliasName,
},
];
}
public static getHandleCachingCommand() {
return `${CloudRunnerState.builderPathFull}/dist/cloud-runner/handleCaching.sh "${CloudRunnerState.cacheFolderFull}" "${CloudRunnerState.libraryFolderFull}" "${CloudRunnerState.lfsDirectory}" "${CloudRunnerState.purgeRemoteCaching}"`;
}
public static getCloneNoLFSCommand() {
return `${CloudRunnerState.builderPathFull}/dist/cloud-runner/cloneNoLFS.sh "${CloudRunnerState.repoPathFull}" "${CloudRunnerState.targetBuildRepoUrl}"`;
}
public static getCloneBuilder() {
return `git clone -q ${CloudRunnerState.CloudRunnerBranch} ${CloudRunnerState.unityBuilderRepoUrl} ${CloudRunnerState.builderPathFull}`;
}
public static readRunNumber() {
const runNumber = process.env.GITHUB_RUN_NUMBER;
if (!runNumber || runNumber === '') {
throw new Error('no run number found, exiting');
}
return runNumber;
}
public static setupFolderVariables() {
CloudRunnerState.buildPathFull = `/${CloudRunnerState.buildVolumeFolder}/${CloudRunnerState.buildGuid}`;
CloudRunnerState.builderPathFull = `${CloudRunnerState.buildPathFull}/builder`;
CloudRunnerState.steamPathFull = `${CloudRunnerState.buildPathFull}/steam`;
CloudRunnerState.repoPathFull = `${CloudRunnerState.buildPathFull}/${CloudRunnerState.repositoryFolder}`;
CloudRunnerState.projectPathFull = `${CloudRunnerState.repoPathFull}/${CloudRunnerState.buildParams.projectPath}`;
CloudRunnerState.libraryFolderFull = `${CloudRunnerState.projectPathFull}/Library`;
CloudRunnerState.cacheFolderFull = `/${CloudRunnerState.buildVolumeFolder}/${CloudRunnerState.cacheFolder}/${CloudRunnerState.branchName}`;
CloudRunnerState.lfsDirectory = `${CloudRunnerState.repoPathFull}/.git/lfs`;
CloudRunnerState.purgeRemoteCaching = process.env.PURGE_REMOTE_BUILDER_CACHE !== undefined;
CloudRunnerState.CloudRunnerBranch = process.env.CloudRunnerBranch
? `--branch "${process.env.CloudRunnerBranch}"`
: '';
CloudRunnerState.unityBuilderRepoUrl = `https://${CloudRunnerState.buildParams.githubToken}@github.com/game-ci/unity-builder.git`;
CloudRunnerState.targetBuildRepoUrl = `https://${CloudRunnerState.buildParams.githubToken}@github.com/${process.env.GITHUB_REPOSITORY}.git`;
}
public static setupBranchName() {
const defaultBranchName =
process.env.GITHUB_REF?.split('/')
.filter((x) => {
x = x[0].toUpperCase() + x.slice(1);
return x;
})
.join('') || '';
CloudRunnerState.branchName =
process.env.REMOTE_BUILDER_CACHE !== undefined ? process.env.REMOTE_BUILDER_CACHE : defaultBranchName;
}
public static setupDefaultSecrets() {
CloudRunnerState.defaultSecrets = [
{
ParameterKey: 'GithubToken',
EnvironmentVariable: 'GITHUB_TOKEN',
ParameterValue: CloudRunnerState.buildParams.githubToken,
},
{
ParameterKey: 'branch',
EnvironmentVariable: 'branch',
ParameterValue: CloudRunnerState.branchName,
},
{
ParameterKey: 'buildPathFull',
EnvironmentVariable: 'buildPathFull',
ParameterValue: CloudRunnerState.buildPathFull,
},
{
ParameterKey: 'projectPathFull',
EnvironmentVariable: 'projectPathFull',
ParameterValue: CloudRunnerState.projectPathFull,
},
{
ParameterKey: 'libraryFolderFull',
EnvironmentVariable: 'libraryFolderFull',
ParameterValue: CloudRunnerState.libraryFolderFull,
},
{
ParameterKey: 'builderPathFull',
EnvironmentVariable: 'builderPathFull',
ParameterValue: CloudRunnerState.builderPathFull,
},
{
ParameterKey: 'repoPathFull',
EnvironmentVariable: 'repoPathFull',
ParameterValue: CloudRunnerState.repoPathFull,
},
{
ParameterKey: 'steamPathFull',
EnvironmentVariable: 'steamPathFull',
ParameterValue: CloudRunnerState.steamPathFull,
},
];
CloudRunnerState.defaultSecrets.push(
...ImageEnvironmentFactory.getEnvironmentVariables(CloudRunnerState.buildParams).map((x) => {
return {
ParameterKey: x.name,
EnvironmentVariable: x.name,
ParameterValue: x.value,
};
}),
);
}
}

View File

@ -0,0 +1,13 @@
import CloudRunnerEnvironmentVariable from '../cloud-runner-services/cloud-runner-environment-variable';
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
export class CloudRunnerStepState {
public image: string;
public environment: CloudRunnerEnvironmentVariable[];
public secrets: CloudRunnerSecret[];
constructor(image: string, environmentVariables: CloudRunnerEnvironmentVariable[], secrets: CloudRunnerSecret[]) {
this.image = image;
this.environment = environmentVariables;
this.secrets = secrets;
}
}

View File

@ -0,0 +1,46 @@
import CloudRunnerEnvironmentVariable from '../cloud-runner-services/cloud-runner-environment-variable';
import CloudRunnerLogger from '../cloud-runner-services/cloud-runner-logger';
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
import { CloudRunnerState } from '../cloud-runner-state/cloud-runner-state';
import { CloudRunnerStepState } from '../cloud-runner-state/cloud-runner-step-state';
import { StandardStepInterface } from './standard-step-interface';
export class BuildStep implements StandardStepInterface {
async run(cloudRunnerStepState: CloudRunnerStepState) {
await BuildStep.BuildStep(
cloudRunnerStepState.image,
cloudRunnerStepState.environment,
cloudRunnerStepState.secrets,
);
}
private static async BuildStep(
image: string,
environmentVariables: CloudRunnerEnvironmentVariable[],
secrets: CloudRunnerSecret[],
) {
CloudRunnerLogger.log('Starting part 2/4 (build unity project)');
await CloudRunnerState.CloudRunnerProviderPlatform.runBuildTask(
CloudRunnerState.buildGuid,
image,
[
`
printenv
export GITHUB_WORKSPACE="${CloudRunnerState.repoPathFull}"
cp -r "${CloudRunnerState.builderPathFull}/dist/default-build-script/" "/UnityBuilderAction"
cp -r "${CloudRunnerState.builderPathFull}/dist/entrypoint.sh" "/entrypoint.sh"
cp -r "${CloudRunnerState.builderPathFull}/dist/steps/" "/steps"
chmod -R +x "/entrypoint.sh"
chmod -R +x "/steps"
/entrypoint.sh
${process.env.DEBUG ? '' : '#'}tree -L 4 "${CloudRunnerState.buildPathFull}"
${process.env.DEBUG ? '' : '#'}ls -lh "/${CloudRunnerState.buildVolumeFolder}"
`,
],
`/${CloudRunnerState.buildVolumeFolder}`,
`/${CloudRunnerState.projectPathFull}`,
environmentVariables,
secrets,
);
}
}

View File

@ -0,0 +1,57 @@
import CloudRunnerEnvironmentVariable from '../cloud-runner-services/cloud-runner-environment-variable';
import CloudRunnerLogger from '../cloud-runner-services/cloud-runner-logger';
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
import { CloudRunnerState } from '../cloud-runner-state/cloud-runner-state';
import { CloudRunnerStepState } from '../cloud-runner-state/cloud-runner-step-state';
import { StandardStepInterface } from './standard-step-interface';
export class CompressionStep implements StandardStepInterface {
async run(cloudRunnerStepState: CloudRunnerStepState) {
await CompressionStep.CompressionStep(cloudRunnerStepState.environment, cloudRunnerStepState.secrets);
}
private static async CompressionStep(
environmentVariables: CloudRunnerEnvironmentVariable[],
secrets: CloudRunnerSecret[],
) {
CloudRunnerLogger.log('Starting step 3/4 build compression');
// Cleanup
await CloudRunnerState.CloudRunnerProviderPlatform.runBuildTask(
CloudRunnerState.buildGuid,
'alpine',
[
`
printenv
apk update -q
apk add zip tree -q
${process.env.DEBUG ? '' : '#'}tree -L 4 "$repoPathFull"
${process.env.DEBUG ? '' : '#'}ls -lh "$repoPathFull"
cd "$libraryFolderFull/.."
zip -r "lib-$BUILDID.zip" "./Library"
mv "lib-$BUILDID.zip" "/$cacheFolderFull/lib"
cd "$repoPathFull"
ls -lh "$repoPathFull"
zip -r "build-$BUILDID.zip" "./${CloudRunnerState.buildParams.buildPath}"
mv "build-$BUILDID.zip" "/$cacheFolderFull/build-$BUILDID.zip"
${process.env.DEBUG ? '' : '#'}tree -L 4 "/$cacheFolderFull"
${process.env.DEBUG ? '' : '#'}tree -L 4 "/$cacheFolderFull/.."
${process.env.DEBUG ? '' : '#'}tree -L 4 "$repoPathFull"
${process.env.DEBUG ? '' : '#'}ls -lh "$repoPathFull"
`,
],
`/${CloudRunnerState.buildVolumeFolder}`,
`/${CloudRunnerState.buildVolumeFolder}`,
[
...environmentVariables,
...[
{
name: 'cacheFolderFull',
value: CloudRunnerState.cacheFolderFull,
},
],
],
secrets,
);
CloudRunnerLogger.log('compression step complete');
}
}

View File

@ -0,0 +1,30 @@
import CloudRunnerLogger from '../cloud-runner-services/cloud-runner-logger';
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
import { CloudRunnerState } from '../cloud-runner-state/cloud-runner-state';
import YAML from 'yaml';
export class CustomStep {
public static async runCustomJob(buildSteps) {
CloudRunnerLogger.log(`Cloud Runner is running in custom job mode`);
buildSteps = YAML.parse(buildSteps);
for (const step of buildSteps) {
const stepSecrets: CloudRunnerSecret[] = step.secrets.map((x) => {
const secret: CloudRunnerSecret = {
ParameterKey: x.name,
EnvironmentVariable: x.name,
ParameterValue: x.value,
};
return secret;
});
await CloudRunnerState.CloudRunnerProviderPlatform.runBuildTask(
CloudRunnerState.buildGuid,
step['image'],
step['commands'],
`/${CloudRunnerState.buildVolumeFolder}`,
`/${CloudRunnerState.buildVolumeFolder}`,
CloudRunnerState.defaultGitShaEnvironmentVariable,
[...CloudRunnerState.defaultSecrets, ...stepSecrets],
);
}
}
}

View File

@ -0,0 +1,49 @@
import CloudRunnerEnvironmentVariable from '../cloud-runner-services/cloud-runner-environment-variable';
import CloudRunnerLogger from '../cloud-runner-services/cloud-runner-logger';
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
import { CloudRunnerState } from '../cloud-runner-state/cloud-runner-state';
import { CloudRunnerStepState } from '../cloud-runner-state/cloud-runner-step-state';
import { StandardStepInterface } from './standard-step-interface';
export class DownloadRepositoryStep implements StandardStepInterface {
async run(cloudRunnerStepState: CloudRunnerStepState) {
await DownloadRepositoryStep.downloadRepositoryStep(
cloudRunnerStepState.image,
cloudRunnerStepState.environment,
cloudRunnerStepState.secrets,
);
}
private static async downloadRepositoryStep(
image: string,
environmentVariables: CloudRunnerEnvironmentVariable[],
secrets: CloudRunnerSecret[],
) {
CloudRunnerLogger.log('Starting step 1/4 clone and restore cache');
await CloudRunnerState.CloudRunnerProviderPlatform.runBuildTask(
CloudRunnerState.buildGuid,
image,
[
` printenv
apk update -q
apk add unzip zip git-lfs jq tree -q
mkdir -p ${CloudRunnerState.buildPathFull}
mkdir -p ${CloudRunnerState.builderPathFull}
mkdir -p ${CloudRunnerState.repoPathFull}
${CloudRunnerState.getCloneBuilder()}
echo ' '
echo 'Initializing source repository for cloning with caching of LFS files'
${CloudRunnerState.getCloneNoLFSCommand()}
echo 'Source repository initialized'
echo ' '
echo 'Starting checks of cache for the Unity project Library and git LFS files'
${CloudRunnerState.getHandleCachingCommand()}
`,
],
`/${CloudRunnerState.buildVolumeFolder}`,
`/${CloudRunnerState.buildVolumeFolder}/`,
environmentVariables,
secrets,
);
}
}

View File

@ -0,0 +1,35 @@
import CloudRunnerEnvironmentVariable from '../cloud-runner-services/cloud-runner-environment-variable';
import CloudRunnerLogger from '../cloud-runner-services/cloud-runner-logger';
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
import { CloudRunnerState } from '../cloud-runner-state/cloud-runner-state';
import { CloudRunnerStepState } from '../cloud-runner-state/cloud-runner-step-state';
import { StandardStepInterface } from './standard-step-interface';
export class EphemeralGitHubRunnerStep implements StandardStepInterface {
async run(cloudRunnerStepState: CloudRunnerStepState) {
await EphemeralGitHubRunnerStep.runJobAsEphemeralGitHubRunner(
cloudRunnerStepState.image,
cloudRunnerStepState.environment,
cloudRunnerStepState.secrets,
);
}
private static async runJobAsEphemeralGitHubRunner(
image: string,
environmentVariables: CloudRunnerEnvironmentVariable[],
secrets: CloudRunnerSecret[],
) {
CloudRunnerLogger.log(`Cloud Runner is running in ephemeral GitHub runner mode`);
const installAndStartRunner =
'mkdir actions-runner && cd actions-runner && curl -O -L https://github.com/actions/runner/releases/download/v2.283.1/actions-runner-linux-x64-2.283.1.tar.gz && tar xzf ./actions-runner-linux-x64-2.283.1.tar.gz';
await CloudRunnerState.CloudRunnerProviderPlatform.runBuildTask(
CloudRunnerState.buildGuid,
image,
[installAndStartRunner],
`/${CloudRunnerState.buildVolumeFolder}`,
`/${CloudRunnerState.buildVolumeFolder}`,
environmentVariables,
secrets,
);
}
}

View File

@ -0,0 +1,8 @@
import { CloudRunnerStepState } from '../cloud-runner-state/cloud-runner-step-state';
export interface StandardStepInterface {
run(
// eslint-disable-next-line no-unused-vars
cloudRunnerStepState: CloudRunnerStepState,
);
}

View File

@ -2,425 +2,138 @@ import AWSBuildPlatform from './aws/aws-build-platform';
import * as core from '@actions/core'; import * as core from '@actions/core';
import { BuildParameters } from '..'; import { BuildParameters } from '..';
import CloudRunnerNamespace from './cloud-runner-services/cloud-runner-namespace'; import CloudRunnerNamespace from './cloud-runner-services/cloud-runner-namespace';
import CloudRunnerSecret from './cloud-runner-services/cloud-runner-secret'; import { CloudRunnerState } from './cloud-runner-state/cloud-runner-state';
import { CloudRunnerProviderInterface } from './cloud-runner-services/cloud-runner-provider-interface';
import Kubernetes from './k8s/kubernetes-build-platform'; import Kubernetes from './k8s/kubernetes-build-platform';
import CloudRunnerEnvironmentVariable from './cloud-runner-services/cloud-runner-environment-variable';
import ImageEnvironmentFactory from '../image-environment-factory';
import YAML from 'yaml';
import CloudRunnerLogger from './cloud-runner-services/cloud-runner-logger'; import CloudRunnerLogger from './cloud-runner-services/cloud-runner-logger';
const repositoryFolder = 'repo'; import { BuildStep } from './cloud-runner-steps/build-step';
const buildVolumeFolder = 'data'; import { CompressionStep } from './cloud-runner-steps/compression-step';
const cacheFolder = 'cache'; import { DownloadRepositoryStep } from './cloud-runner-steps/download-repository-step';
import { CustomStep } from './cloud-runner-steps/custom-step';
import { EphemeralGitHubRunnerStep } from './cloud-runner-steps/ephemeral-github-runner-step';
import { CloudRunnerStepState } from './cloud-runner-state/cloud-runner-step-state';
class CloudRunner { class CloudRunner {
static CloudRunnerProviderPlatform: CloudRunnerProviderInterface;
private static buildParams: BuildParameters;
private static defaultSecrets: CloudRunnerSecret[];
private static buildGuid: 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;
private static cacheFolderFull: string;
private static lfsDirectory: string;
private static purgeRemoteCaching: boolean;
private static CloudRunnerBranch: string;
private static unityBuilderRepoUrl: string;
private static targetBuildRepoUrl: string;
private static readonly defaultGitShaEnvironmentVariable = [
{
name: 'GITHUB_SHA',
value: process.env.GITHUB_SHA || '',
},
];
private static setup(buildParameters: BuildParameters) { private static setup(buildParameters: BuildParameters) {
CloudRunnerLogger.setup(); CloudRunnerLogger.setup();
CloudRunner.buildGuid = CloudRunnerNamespace.generateBuildName( CloudRunnerState.buildGuid = CloudRunnerNamespace.generateBuildName(
CloudRunner.readRunNumber(), CloudRunnerState.readRunNumber(),
buildParameters.platform, buildParameters.platform,
); );
CloudRunner.buildParams = buildParameters; CloudRunnerState.buildParams = buildParameters;
CloudRunner.setupBranchName(); CloudRunnerState.setupBranchName();
CloudRunner.setupFolderVariables(); CloudRunnerState.setupFolderVariables();
CloudRunner.setupDefaultSecrets(); CloudRunnerState.setupDefaultSecrets();
CloudRunner.setupBuildPlatform(); CloudRunner.setupBuildPlatform();
} }
private static setupFolderVariables() { public static setupBuildPlatform() {
this.buildPathFull = `/${buildVolumeFolder}/${this.buildGuid}`; switch (CloudRunnerState.buildParams.cloudRunnerCluster) {
this.builderPathFull = `${this.buildPathFull}/builder`; case 'aws':
this.steamPathFull = `${this.buildPathFull}/steam`; CloudRunnerLogger.log('Building with AWS');
this.repoPathFull = `${this.buildPathFull}/${repositoryFolder}`; CloudRunnerState.CloudRunnerProviderPlatform = new AWSBuildPlatform(CloudRunnerState.buildParams);
this.projectPathFull = `${this.repoPathFull}/${this.buildParams.projectPath}`; break;
this.libraryFolderFull = `${this.projectPathFull}/Library`; default:
this.cacheFolderFull = `/${buildVolumeFolder}/${cacheFolder}/${this.branchName}`; case 'k8s':
this.lfsDirectory = `${this.repoPathFull}/.git/lfs`; CloudRunnerLogger.log('Building with Kubernetes');
this.purgeRemoteCaching = process.env.PURGE_REMOTE_BUILDER_CACHE !== undefined; CloudRunnerState.CloudRunnerProviderPlatform = new Kubernetes(CloudRunnerState.buildParams);
this.CloudRunnerBranch = process.env.CloudRunnerBranch ? `--branch "${process.env.CloudRunnerBranch}"` : ''; break;
this.unityBuilderRepoUrl = `https://${this.buildParams.githubToken}@github.com/game-ci/unity-builder.git`; }
this.targetBuildRepoUrl = `https://${this.buildParams.githubToken}@github.com/${process.env.GITHUB_REPOSITORY}.git`;
}
private static getHandleCachingCommand() {
return `${this.builderPathFull}/dist/cloud-runner/handleCaching.sh "${this.cacheFolderFull}" "${this.libraryFolderFull}" "${this.lfsDirectory}" "${this.purgeRemoteCaching}"`;
}
private static getCloneNoLFSCommand() {
return `${this.builderPathFull}/dist/cloud-runner/cloneNoLFS.sh "${this.repoPathFull}" "${this.targetBuildRepoUrl}"`;
}
private static getCloneBuilder() {
return `git clone -q ${this.CloudRunnerBranch} ${this.unityBuilderRepoUrl} ${this.builderPathFull}`;
} }
static async run(buildParameters: BuildParameters, baseImage) { static async run(buildParameters: BuildParameters, baseImage) {
CloudRunner.setup(buildParameters); CloudRunner.setup(buildParameters);
try { try {
await CloudRunner.setupSharedBuildResources(); await CloudRunnerState.CloudRunnerProviderPlatform.setupSharedBuildResources(
await CloudRunner.setupStep(); CloudRunnerState.buildGuid,
await CloudRunner.runMainJob(baseImage); CloudRunnerState.buildParams,
await CloudRunner.cleanupSharedBuildResources(); CloudRunnerState.branchName,
CloudRunnerState.defaultSecrets,
);
await CloudRunner.runJob(baseImage);
await CloudRunnerState.CloudRunnerProviderPlatform.cleanupSharedBuildResources(
CloudRunnerState.buildGuid,
CloudRunnerState.buildParams,
CloudRunnerState.branchName,
CloudRunnerState.defaultSecrets,
);
} catch (error) { } catch (error) {
await CloudRunner.handleException(error); await CloudRunner.handleException(error);
throw error; throw error;
} }
} }
private static async setupSharedBuildResources() { private static async runJob(baseImage: any) {
await this.CloudRunnerProviderPlatform.setupSharedBuildResources( core.info(`Custom build steps: ${CloudRunnerState.buildParams.customBuildSteps}`);
this.buildGuid, if (CloudRunnerState.buildParams.customBuildSteps === '') {
this.buildParams,
this.branchName,
this.defaultSecrets,
);
}
private static setupBuildPlatform() {
switch (this.buildParams.cloudRunnerCluster) {
case 'aws':
CloudRunnerLogger.log('Building with AWS');
this.CloudRunnerProviderPlatform = new AWSBuildPlatform(this.buildParams);
break;
default:
case 'k8s':
CloudRunnerLogger.log('Building with Kubernetes');
this.CloudRunnerProviderPlatform = 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,
},
{
ParameterKey: 'libraryFolderFull',
EnvironmentVariable: 'libraryFolderFull',
ParameterValue: this.libraryFolderFull,
},
{
ParameterKey: 'builderPathFull',
EnvironmentVariable: 'builderPathFull',
ParameterValue: this.builderPathFull,
},
{
ParameterKey: 'repoPathFull',
EnvironmentVariable: 'repoPathFull',
ParameterValue: this.repoPathFull,
},
{
ParameterKey: 'steamPathFull',
EnvironmentVariable: 'steamPathFull',
ParameterValue: this.steamPathFull,
},
];
this.defaultSecrets.push(
...ImageEnvironmentFactory.getEnvironmentVariables(this.buildParams).map((x) => {
return {
ParameterKey: x.name,
EnvironmentVariable: x.name,
ParameterValue: x.value,
};
}),
);
}
private static readBuildEnvironmentVariables(): CloudRunnerEnvironmentVariable[] {
return [
{
name: 'ContainerMemory',
value: this.buildParams.cloudRunnerMemory,
},
{
name: 'ContainerCpu',
value: this.buildParams.cloudRunnerCpu,
},
{
name: 'GITHUB_WORKSPACE',
value: `/${buildVolumeFolder}/${this.buildGuid}/${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 async runMainJob(baseImage: any) {
if (this.buildParams.customBuildSteps === '') {
await CloudRunner.standardBuildAutomation(baseImage); await CloudRunner.standardBuildAutomation(baseImage);
} else if (this.buildParams.customBuildSteps === 'ephemeral') { } else if (CloudRunnerState.buildParams.customBuildSteps === 'ephemeral') {
await CloudRunner.runJobAsEphemeralGitHubRunner(baseImage); await new EphemeralGitHubRunnerStep().run(
new CloudRunnerStepState(
baseImage,
CloudRunnerState.readBuildEnvironmentVariables(),
CloudRunnerState.defaultSecrets,
),
);
} else if (CloudRunnerState.buildParams.customBuildSteps === 'download') {
await new DownloadRepositoryStep().run(
new CloudRunnerStepState(
'alpine/git',
CloudRunnerState.readBuildEnvironmentVariables(),
CloudRunnerState.defaultSecrets,
),
);
} else { } else {
await CloudRunner.runCustomJob(this.buildParams.customBuildSteps); await CustomStep.runCustomJob(CloudRunnerState.buildParams.customBuildSteps);
} }
} }
private static async standardBuildAutomation(baseImage: any) { private static async standardBuildAutomation(baseImage: any) {
CloudRunnerLogger.log(`Cloud Runner is running in standard build automation mode`); CloudRunnerLogger.log(`Cloud Runner is running standard build automation`);
CloudRunnerLogger.logWithTime('Pre build steps time');
await this.runCustomJob(this.buildParams.preBuildSteps); await new DownloadRepositoryStep().run(
CloudRunnerLogger.logWithTime('Setup time'); new CloudRunnerStepState(
await CloudRunner.BuildStep(baseImage); 'alpine/git',
CloudRunnerState.readBuildEnvironmentVariables(),
CloudRunnerState.defaultSecrets,
),
);
CloudRunnerLogger.logWithTime('Download repository step time');
await CustomStep.runCustomJob(CloudRunnerState.buildParams.preBuildSteps);
CloudRunnerLogger.logWithTime('Pre build step(s) time');
new BuildStep().run(
new CloudRunnerStepState(
baseImage,
CloudRunnerState.readBuildEnvironmentVariables(),
CloudRunnerState.defaultSecrets,
),
);
CloudRunnerLogger.logWithTime('Build time'); CloudRunnerLogger.logWithTime('Build time');
await CloudRunner.CompressionStep();
await new CompressionStep().run(
new CloudRunnerStepState(
'alpine',
CloudRunnerState.readBuildEnvironmentVariables(),
CloudRunnerState.defaultSecrets,
),
);
CloudRunnerLogger.logWithTime('Compression time'); CloudRunnerLogger.logWithTime('Compression time');
await this.runCustomJob(this.buildParams.postBuildSteps);
CloudRunnerLogger.logWithTime('Post build steps time');
}
private static async runJobAsEphemeralGitHubRunner(baseImage: any) { await CustomStep.runCustomJob(CloudRunnerState.buildParams.postBuildSteps);
CloudRunnerLogger.log(`Cloud Runner is running in ephemeral GitHub runner mode`); CloudRunnerLogger.logWithTime('Post build step(s) time');
const installAndStartRunner =
'mkdir actions-runner && cd actions-runner && curl -O -L https://github.com/actions/runner/releases/download/v2.283.1/actions-runner-linux-x64-2.283.1.tar.gz && tar xzf ./actions-runner-linux-x64-2.283.1.tar.gz';
await this.CloudRunnerProviderPlatform.runBuildTask(
this.buildGuid,
baseImage,
[installAndStartRunner],
`/${buildVolumeFolder}`,
`/${buildVolumeFolder}`,
this.defaultGitShaEnvironmentVariable,
[...this.defaultSecrets],
);
}
private static async runCustomJob(buildSteps) { CloudRunnerLogger.log(`Cloud Runner finished running standard build automation`);
CloudRunnerLogger.log(`Cloud Runner is running in custom job mode`);
buildSteps = YAML.parse(buildSteps);
for (const step of buildSteps) {
const stepSecrets: CloudRunnerSecret[] = step.secrets.map((x) => {
const secret: CloudRunnerSecret = {
ParameterKey: x.name,
EnvironmentVariable: x.name,
ParameterValue: x.value,
};
return secret;
});
await this.CloudRunnerProviderPlatform.runBuildTask(
this.buildGuid,
step['image'],
step['commands'],
`/${buildVolumeFolder}`,
`/${buildVolumeFolder}`,
this.defaultGitShaEnvironmentVariable,
[...this.defaultSecrets, ...stepSecrets],
);
}
}
private static async setupStep() {
CloudRunnerLogger.log('Starting step 1/4 clone and restore cache)');
await this.CloudRunnerProviderPlatform.runBuildTask(
this.buildGuid,
'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}
${this.getCloneBuilder()}
echo ' '
echo 'Initializing source repository for cloning with caching of LFS files'
${this.getCloneNoLFSCommand()}
echo 'Source repository initialized'
echo ' '
echo 'Starting checks of cache for the Unity project Library and git LFS files'
${this.getHandleCachingCommand()}
`,
],
`/${buildVolumeFolder}`,
`/${buildVolumeFolder}/`,
CloudRunner.defaultGitShaEnvironmentVariable,
this.defaultSecrets,
);
}
private static async BuildStep(baseImage: any) {
CloudRunnerLogger.log('Starting part 2/4 (build unity project)');
await this.CloudRunnerProviderPlatform.runBuildTask(
this.buildGuid,
baseImage.toString(),
[
`
printenv
export GITHUB_WORKSPACE="${this.repoPathFull}"
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
${process.env.DEBUG ? '' : '#'}tree -L 4 "${this.buildPathFull}"
${process.env.DEBUG ? '' : '#'}ls -lh "/${buildVolumeFolder}"
`,
],
`/${buildVolumeFolder}`,
`/${this.projectPathFull}`,
CloudRunner.readBuildEnvironmentVariables(),
this.defaultSecrets,
);
}
private static async CompressionStep() {
CloudRunnerLogger.log('Starting step 3/4 build compression');
// Cleanup
await this.CloudRunnerProviderPlatform.runBuildTask(
this.buildGuid,
'alpine',
[
`
printenv
apk update -q
apk add zip tree -q
${process.env.DEBUG ? '' : '#'}tree -L 4 "$repoPathFull"
${process.env.DEBUG ? '' : '#'}ls -lh "$repoPathFull"
cd "$libraryFolderFull/.."
zip -r "lib-$BUILDID.zip" "./Library"
mv "lib-$BUILDID.zip" "/$cacheFolderFull/lib"
cd "$repoPathFull"
ls -lh "$repoPathFull"
zip -r "build-$BUILDID.zip" "./${CloudRunner.buildParams.buildPath}"
mv "build-$BUILDID.zip" "/$cacheFolderFull/build-$BUILDID.zip"
${process.env.DEBUG ? '' : '#'}tree -L 4 "/$cacheFolderFull"
${process.env.DEBUG ? '' : '#'}tree -L 4 "/$cacheFolderFull/.."
${process.env.DEBUG ? '' : '#'}tree -L 4 "$repoPathFull"
${process.env.DEBUG ? '' : '#'}ls -lh "$repoPathFull"
`,
],
`/${buildVolumeFolder}`,
`/${buildVolumeFolder}`,
[
...CloudRunner.defaultGitShaEnvironmentVariable,
...[
{
name: 'cacheFolderFull',
value: this.cacheFolderFull,
},
],
],
this.defaultSecrets,
);
CloudRunnerLogger.log('compression step complete');
}
private static async cleanupSharedBuildResources() {
await this.CloudRunnerProviderPlatform.cleanupSharedBuildResources(
this.buildGuid,
this.buildParams,
this.branchName,
this.defaultSecrets,
);
} }
private static async handleException(error: unknown) { private static async handleException(error: unknown) {
CloudRunnerLogger.error(JSON.stringify(error, undefined, 4)); CloudRunnerLogger.error(JSON.stringify(error, undefined, 4));
core.setFailed('Remote Builder failed'); core.setFailed('Remote Builder failed');
await this.CloudRunnerProviderPlatform.cleanupSharedBuildResources( await CloudRunnerState.CloudRunnerProviderPlatform.cleanupSharedBuildResources(
this.buildGuid, CloudRunnerState.buildGuid,
this.buildParams, CloudRunnerState.buildParams,
this.branchName, CloudRunnerState.branchName,
this.defaultSecrets, CloudRunnerState.defaultSecrets,
); );
} }
} }