Remove stackName from generic buildPlatform interface
parent
2b559c587f
commit
b33e19aa34
|
|
@ -848,28 +848,31 @@ const core = __importStar(__webpack_require__(42186));
|
||||||
const remote_builder_constants_1 = __importDefault(__webpack_require__(92560));
|
const remote_builder_constants_1 = __importDefault(__webpack_require__(92560));
|
||||||
const aws_build_runner_1 = __importDefault(__webpack_require__(11201));
|
const aws_build_runner_1 = __importDefault(__webpack_require__(11201));
|
||||||
class AWSBuildEnvironment {
|
class AWSBuildEnvironment {
|
||||||
runBuildTask(buildId, stackName, image, commands, mountdir, workingdir, environment, secrets) {
|
constructor(buildParameters) {
|
||||||
|
this.stackName = buildParameters.awsStackName;
|
||||||
|
}
|
||||||
|
runBuildTask(buildId, image, commands, mountdir, workingdir, environment, secrets) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const ECS = new SDK.ECS();
|
const ECS = new SDK.ECS();
|
||||||
const CF = new SDK.CloudFormation();
|
const CF = new SDK.CloudFormation();
|
||||||
const entrypoint = ['/bin/sh'];
|
const entrypoint = ['/bin/sh'];
|
||||||
const taskDef = yield AWSBuildEnvironment.setupCloudFormations(CF, buildId, stackName, image, entrypoint, commands, mountdir, workingdir, secrets);
|
const taskDef = yield this.setupCloudFormations(CF, buildId, image, entrypoint, commands, mountdir, workingdir, secrets);
|
||||||
try {
|
try {
|
||||||
yield aws_build_runner_1.default.runTask(taskDef, ECS, CF, environment, buildId);
|
yield aws_build_runner_1.default.runTask(taskDef, ECS, CF, environment, buildId);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
yield AWSBuildEnvironment.cleanupResources(CF, taskDef);
|
yield this.cleanupResources(CF, taskDef);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static getParameterTemplate(p1) {
|
getParameterTemplate(p1) {
|
||||||
return `
|
return `
|
||||||
${p1}:
|
${p1}:
|
||||||
Type: String
|
Type: String
|
||||||
Default: ''
|
Default: ''
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
static getSecretTemplate(p1) {
|
getSecretTemplate(p1) {
|
||||||
return `
|
return `
|
||||||
${p1}Secret:
|
${p1}Secret:
|
||||||
Type: AWS::SecretsManager::Secret
|
Type: AWS::SecretsManager::Secret
|
||||||
|
|
@ -878,24 +881,24 @@ class AWSBuildEnvironment {
|
||||||
SecretString: !Ref ${p1}
|
SecretString: !Ref ${p1}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
static getSecretDefinitionTemplate(p1, p2) {
|
getSecretDefinitionTemplate(p1, p2) {
|
||||||
return `
|
return `
|
||||||
- Name: '${p1}'
|
- Name: '${p1}'
|
||||||
ValueFrom: !Ref ${p2}Secret
|
ValueFrom: !Ref ${p2}Secret
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
static insertAtTemplate(template, insertionKey, insertion) {
|
insertAtTemplate(template, insertionKey, insertion) {
|
||||||
const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
|
const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
|
||||||
template = [template.slice(0, index), insertion, template.slice(index)].join('');
|
template = [template.slice(0, index), insertion, template.slice(index)].join('');
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
static setupCloudFormations(CF, buildUid, stackName, image, entrypoint, commands, mountdir, workingdir, secrets) {
|
setupCloudFormations(CF, buildUid, 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 = `${this.stackName}-${buildUid}`;
|
||||||
let taskDefCloudFormation = this.readTaskCloudFormationTemplate();
|
let taskDefCloudFormation = this.readTaskCloudFormationTemplate();
|
||||||
const cleanupTaskDefStackName = `${taskDefStackName}-cleanup`;
|
const cleanupTaskDefStackName = `${taskDefStackName}-cleanup`;
|
||||||
const cleanupCloudFormation = fs.readFileSync(`${__dirname}/cloud-formations/cloudformation-stack-ttl.yml`, 'utf8');
|
const cleanupCloudFormation = fs.readFileSync(`${__dirname}/cloud-formations/cloudformation-stack-ttl.yml`, 'utf8');
|
||||||
|
|
@ -971,13 +974,13 @@ class AWSBuildEnvironment {
|
||||||
yield CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
|
yield CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
yield AWSBuildEnvironment.handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, secrets);
|
yield this.handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, secrets);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
const taskDefResources = (yield CF.describeStackResources({
|
const taskDefResources = (yield CF.describeStackResources({
|
||||||
StackName: taskDefStackName,
|
StackName: taskDefStackName,
|
||||||
}).promise()).StackResources;
|
}).promise()).StackResources;
|
||||||
const baseResources = (yield CF.describeStackResources({ StackName: stackName }).promise()).StackResources;
|
const baseResources = (yield CF.describeStackResources({ StackName: this.stackName }).promise()).StackResources;
|
||||||
// in the future we should offer a parameter to choose if you want the guarnteed shutdown.
|
// in the future we should offer a parameter to choose if you want the guarnteed shutdown.
|
||||||
core.info('Worker cluster created successfully (skipping wait for cleanup cluster to be ready)');
|
core.info('Worker cluster created successfully (skipping wait for cleanup cluster to be ready)');
|
||||||
return {
|
return {
|
||||||
|
|
@ -991,7 +994,7 @@ class AWSBuildEnvironment {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, secrets) {
|
handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, secrets) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
core.info(JSON.stringify(secrets, undefined, 4));
|
core.info(JSON.stringify(secrets, undefined, 4));
|
||||||
core.info(taskDefCloudFormation);
|
core.info(taskDefCloudFormation);
|
||||||
|
|
@ -1002,10 +1005,10 @@ class AWSBuildEnvironment {
|
||||||
core.error(error);
|
core.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static readTaskCloudFormationTemplate() {
|
readTaskCloudFormationTemplate() {
|
||||||
return fs.readFileSync(`${__dirname}/cloud-formations/task-def-formation.yml`, 'utf8');
|
return fs.readFileSync(`${__dirname}/cloud-formations/task-def-formation.yml`, 'utf8');
|
||||||
}
|
}
|
||||||
static cleanupResources(CF, taskDef) {
|
cleanupResources(CF, taskDef) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
core.info('Cleanup starting');
|
core.info('Cleanup starting');
|
||||||
yield CF.deleteStack({
|
yield CF.deleteStack({
|
||||||
|
|
@ -1249,9 +1252,8 @@ const async_wait_until_1 = __webpack_require__(41299);
|
||||||
const kubernetes_storage_1 = __importDefault(__webpack_require__(38941));
|
const kubernetes_storage_1 = __importDefault(__webpack_require__(38941));
|
||||||
const base64 = __webpack_require__(85848);
|
const base64 = __webpack_require__(85848);
|
||||||
class Kubernetes {
|
class Kubernetes {
|
||||||
constructor(buildParameters, baseImage) {
|
constructor(buildParameters) {
|
||||||
this.buildId = '';
|
this.buildId = '';
|
||||||
this.buildCorrelationId = '';
|
|
||||||
this.pvcName = '';
|
this.pvcName = '';
|
||||||
this.secretName = '';
|
this.secretName = '';
|
||||||
this.jobName = '';
|
this.jobName = '';
|
||||||
|
|
@ -1265,45 +1267,15 @@ class Kubernetes {
|
||||||
this.kubeConfig = kc;
|
this.kubeConfig = kc;
|
||||||
this.kubeClient = k8sApi;
|
this.kubeClient = k8sApi;
|
||||||
this.kubeClientBatch = k8sBatchApi;
|
this.kubeClientBatch = k8sBatchApi;
|
||||||
this.buildCorrelationId = Kubernetes.uuidv4();
|
|
||||||
this.namespace = 'default';
|
this.namespace = 'default';
|
||||||
this.buildParameters = buildParameters;
|
this.buildParameters = buildParameters;
|
||||||
this.baseImage = baseImage;
|
|
||||||
}
|
}
|
||||||
runBuildTask(buildId, stackName, image, commands, mountdir, workingdir, environment, secrets) {
|
runBuildTask(buildId, image, commands, mountdir, workingdir, environment, secrets) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
this.setUniqueBuildId();
|
this.setUniqueBuildId(buildId);
|
||||||
const defaultSecretsArray = [
|
|
||||||
{
|
|
||||||
ParameterKey: 'GithubToken',
|
|
||||||
EnvironmentVariable: 'GITHUB_TOKEN',
|
|
||||||
ParameterValue: this.buildParameters.githubToken,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'UNITY_LICENSE',
|
|
||||||
EnvironmentVariable: 'UNITY_LICENSE',
|
|
||||||
ParameterValue: process.env.UNITY_LICENSE || '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYSTORE_BASE64',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYSTORE_BASE64',
|
|
||||||
ParameterValue: this.buildParameters.androidKeystoreBase64,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYSTORE_PASS',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYSTORE_PASS',
|
|
||||||
ParameterValue: this.buildParameters.androidKeystorePass,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYALIAS_PASS',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYALIAS_PASS',
|
|
||||||
ParameterValue: this.buildParameters.androidKeyaliasPass,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
defaultSecretsArray.push(...secrets);
|
|
||||||
// setup
|
// setup
|
||||||
yield this.createSecret(defaultSecretsArray);
|
yield this.createSecret(secrets);
|
||||||
yield kubernetes_storage_1.default.createPersistentVolumeClaim(this.buildParameters, this.pvcName, this.kubeClient, this.namespace);
|
yield kubernetes_storage_1.default.createPersistentVolumeClaim(this.buildParameters, this.pvcName, this.kubeClient, this.namespace);
|
||||||
//run
|
//run
|
||||||
const jobSpec = this.getJobSpec(commands, image, mountdir, workingdir, environment);
|
const jobSpec = this.getJobSpec(commands, image, mountdir, workingdir, environment);
|
||||||
|
|
@ -1321,14 +1293,13 @@ class Kubernetes {
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
core.info('Running job failed');
|
core.info('Running job failed');
|
||||||
|
core.error(JSON.stringify(error, undefined, 4));
|
||||||
yield this.cleanup();
|
yield this.cleanup();
|
||||||
core.error(JSON.stringify(error.response, undefined, 4));
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setUniqueBuildId() {
|
setUniqueBuildId(buildId) {
|
||||||
const buildId = Kubernetes.uuidv4();
|
|
||||||
const pvcName = `unity-builder-pvc-${buildId}`;
|
const pvcName = `unity-builder-pvc-${buildId}`;
|
||||||
const secretName = `build-credentials-${buildId}`;
|
const secretName = `build-credentials-${buildId}`;
|
||||||
const jobName = `unity-builder-job-${buildId}`;
|
const jobName = `unity-builder-job-${buildId}`;
|
||||||
|
|
@ -1486,61 +1457,6 @@ class Kubernetes {
|
||||||
};
|
};
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
runJobInKubernetesPod(command, image) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
try {
|
|
||||||
this.setUniqueBuildId();
|
|
||||||
const defaultSecretsArray = [
|
|
||||||
{
|
|
||||||
ParameterKey: 'GithubToken',
|
|
||||||
EnvironmentVariable: 'GITHUB_TOKEN',
|
|
||||||
ParameterValue: this.buildParameters.githubToken,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'UNITY_LICENSE',
|
|
||||||
EnvironmentVariable: 'UNITY_LICENSE',
|
|
||||||
ParameterValue: process.env.UNITY_LICENSE || '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYSTORE_BASE64',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYSTORE_BASE64',
|
|
||||||
ParameterValue: this.buildParameters.androidKeystoreBase64,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYSTORE_PASS',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYSTORE_PASS',
|
|
||||||
ParameterValue: this.buildParameters.androidKeystorePass,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYALIAS_PASS',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYALIAS_PASS',
|
|
||||||
ParameterValue: this.buildParameters.androidKeyaliasPass,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
// setup
|
|
||||||
yield this.createSecret(defaultSecretsArray);
|
|
||||||
yield kubernetes_storage_1.default.createPersistentVolumeClaim(this.buildParameters, this.pvcName, this.kubeClient, this.namespace);
|
|
||||||
//run
|
|
||||||
const jobSpec = this.getJobSpec(command, image, 'data', 'data', []);
|
|
||||||
core.info('Creating build job');
|
|
||||||
yield this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
|
||||||
core.info('Job created');
|
|
||||||
yield kubernetes_storage_1.default.watchUntilPVCNotPending(this.kubeClient, this.pvcName, this.namespace);
|
|
||||||
core.info('PVC Bound');
|
|
||||||
this.setPodNameAndContainerName(yield this.findPod());
|
|
||||||
core.info('Watching pod until running');
|
|
||||||
yield this.watchUntilPodRunning();
|
|
||||||
core.info('Pod running, streaming logs');
|
|
||||||
yield this.streamLogs();
|
|
||||||
yield this.cleanup();
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
core.info('Running job failed');
|
|
||||||
yield this.cleanup();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
findPod() {
|
findPod() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const pod = (yield this.kubeClient.listNamespacedPod(this.namespace)).body.items.find((x) => { var _a, _b; return ((_b = (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.labels) === null || _b === void 0 ? void 0 : _b['job-name']) === this.jobName; });
|
const pod = (yield this.kubeClient.listNamespacedPod(this.namespace)).body.items.find((x) => { var _a, _b; return ((_b = (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.labels) === null || _b === void 0 ? void 0 : _b['job-name']) === this.jobName; });
|
||||||
|
|
@ -1631,13 +1547,6 @@ class Kubernetes {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static uuidv4() {
|
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
||||||
const r = Math.trunc(Math.random() * 16);
|
|
||||||
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
||||||
return v.toString(16);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
exports.default = Kubernetes;
|
exports.default = Kubernetes;
|
||||||
|
|
||||||
|
|
@ -1822,12 +1731,12 @@ class RemoteBuilder {
|
||||||
switch (buildParameters.remoteBuildCluster) {
|
switch (buildParameters.remoteBuildCluster) {
|
||||||
case 'aws':
|
case 'aws':
|
||||||
core.info('Building with AWS');
|
core.info('Building with AWS');
|
||||||
this.RemoteBuilderProviderPlatform = new aws_build_platform_1.default();
|
this.RemoteBuilderProviderPlatform = new aws_build_platform_1.default(buildParameters);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case 'k8s':
|
case 'k8s':
|
||||||
core.info('Building with Kubernetes');
|
core.info('Building with Kubernetes');
|
||||||
this.RemoteBuilderProviderPlatform = new kubernetes_build_platform_1.default(buildParameters, baseImage);
|
this.RemoteBuilderProviderPlatform = new kubernetes_build_platform_1.default(buildParameters);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.SteamDeploy = process.env.STEAM_DEPLOY !== undefined || false;
|
this.SteamDeploy = process.env.STEAM_DEPLOY !== undefined || false;
|
||||||
|
|
@ -1862,7 +1771,7 @@ class RemoteBuilder {
|
||||||
static SetupStep(buildUid, buildParameters, branchName, defaultSecretsArray) {
|
static SetupStep(buildUid, buildParameters, branchName, defaultSecretsArray) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
core.info('Starting step 1/4 clone and restore cache)');
|
core.info('Starting step 1/4 clone and restore cache)');
|
||||||
yield this.RemoteBuilderProviderPlatform.runBuildTask(buildUid, buildParameters.awsStackName, 'alpine/git', [
|
yield this.RemoteBuilderProviderPlatform.runBuildTask(buildUid, 'alpine/git', [
|
||||||
'-c',
|
'-c',
|
||||||
`apk update;
|
`apk update;
|
||||||
apk add unzip;
|
apk add unzip;
|
||||||
|
|
@ -1974,7 +1883,7 @@ class RemoteBuilder {
|
||||||
ParameterValue: buildParameters.androidKeyaliasPass,
|
ParameterValue: buildParameters.androidKeyaliasPass,
|
||||||
});
|
});
|
||||||
core.info('Starting part 2/4 (build unity project)');
|
core.info('Starting part 2/4 (build unity project)');
|
||||||
yield this.RemoteBuilderProviderPlatform.runBuildTask(buildUid, buildParameters.awsStackName, baseImage.toString(), [
|
yield this.RemoteBuilderProviderPlatform.runBuildTask(buildUid, baseImage.toString(), [
|
||||||
'-c',
|
'-c',
|
||||||
`
|
`
|
||||||
cp -r /${buildVolumeFolder}/${buildUid}/builder/dist/default-build-script/ /UnityBuilderAction;
|
cp -r /${buildVolumeFolder}/${buildUid}/builder/dist/default-build-script/ /UnityBuilderAction;
|
||||||
|
|
@ -2044,7 +1953,7 @@ class RemoteBuilder {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
core.info('Starting step 3/4 build compression');
|
core.info('Starting step 3/4 build compression');
|
||||||
// Cleanup
|
// Cleanup
|
||||||
yield this.RemoteBuilderProviderPlatform.runBuildTask(buildUid, buildParameters.awsStackName, 'alpine', [
|
yield this.RemoteBuilderProviderPlatform.runBuildTask(buildUid, 'alpine', [
|
||||||
'-c',
|
'-c',
|
||||||
`
|
`
|
||||||
apk update
|
apk update
|
||||||
|
|
@ -2071,7 +1980,7 @@ class RemoteBuilder {
|
||||||
static UploadArtifacts(buildUid, buildParameters, branchName, defaultSecretsArray) {
|
static UploadArtifacts(buildUid, buildParameters, branchName, defaultSecretsArray) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
core.info('Starting step 4/4 upload build to s3');
|
core.info('Starting step 4/4 upload build to s3');
|
||||||
yield this.RemoteBuilderProviderPlatform.runBuildTask(buildUid, buildParameters.awsStackName, 'amazon/aws-cli', [
|
yield this.RemoteBuilderProviderPlatform.runBuildTask(buildUid, '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/
|
||||||
|
|
@ -2106,7 +2015,7 @@ class RemoteBuilder {
|
||||||
static DeployToSteam(buildUid, buildParameters, defaultSecretsArray) {
|
static DeployToSteam(buildUid, buildParameters, defaultSecretsArray) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
core.info('Starting steam deployment');
|
core.info('Starting steam deployment');
|
||||||
yield this.RemoteBuilderProviderPlatform.runBuildTask(buildUid, buildParameters.awsStackName, 'cm2network/steamcmd:root', [
|
yield this.RemoteBuilderProviderPlatform.runBuildTask(buildUid, 'cm2network/steamcmd:root', [
|
||||||
'-c',
|
'-c',
|
||||||
`
|
`
|
||||||
ls
|
ls
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -8,11 +8,17 @@ import RemoteBuilderTaskDef from './remote-builder-task-def';
|
||||||
import RemoteBuilderConstants from './remote-builder-constants';
|
import RemoteBuilderConstants from './remote-builder-constants';
|
||||||
import AWSBuildRunner from './aws-build-runner';
|
import AWSBuildRunner from './aws-build-runner';
|
||||||
import { RemoteBuilderProviderInterface } from './remote-builder-provider-interface';
|
import { RemoteBuilderProviderInterface } from './remote-builder-provider-interface';
|
||||||
|
import BuildParameters from '../build-parameters';
|
||||||
|
|
||||||
class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
||||||
|
private stackName: string;
|
||||||
|
|
||||||
|
constructor(buildParameters: BuildParameters) {
|
||||||
|
this.stackName = buildParameters.awsStackName;
|
||||||
|
}
|
||||||
|
|
||||||
async runBuildTask(
|
async runBuildTask(
|
||||||
buildId: string,
|
buildId: string,
|
||||||
stackName: string,
|
|
||||||
image: string,
|
image: string,
|
||||||
commands: string[],
|
commands: string[],
|
||||||
mountdir: string,
|
mountdir: string,
|
||||||
|
|
@ -24,10 +30,9 @@ class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
||||||
const CF = new SDK.CloudFormation();
|
const CF = new SDK.CloudFormation();
|
||||||
const entrypoint = ['/bin/sh'];
|
const entrypoint = ['/bin/sh'];
|
||||||
|
|
||||||
const taskDef = await AWSBuildEnvironment.setupCloudFormations(
|
const taskDef = await this.setupCloudFormations(
|
||||||
CF,
|
CF,
|
||||||
buildId,
|
buildId,
|
||||||
stackName,
|
|
||||||
image,
|
image,
|
||||||
entrypoint,
|
entrypoint,
|
||||||
commands,
|
commands,
|
||||||
|
|
@ -38,11 +43,11 @@ class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
||||||
try {
|
try {
|
||||||
await AWSBuildRunner.runTask(taskDef, ECS, CF, environment, buildId);
|
await AWSBuildRunner.runTask(taskDef, ECS, CF, environment, buildId);
|
||||||
} finally {
|
} finally {
|
||||||
await AWSBuildEnvironment.cleanupResources(CF, taskDef);
|
await this.cleanupResources(CF, taskDef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getParameterTemplate(p1) {
|
getParameterTemplate(p1) {
|
||||||
return `
|
return `
|
||||||
${p1}:
|
${p1}:
|
||||||
Type: String
|
Type: String
|
||||||
|
|
@ -50,7 +55,7 @@ class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSecretTemplate(p1) {
|
getSecretTemplate(p1) {
|
||||||
return `
|
return `
|
||||||
${p1}Secret:
|
${p1}Secret:
|
||||||
Type: AWS::SecretsManager::Secret
|
Type: AWS::SecretsManager::Secret
|
||||||
|
|
@ -60,23 +65,22 @@ class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSecretDefinitionTemplate(p1, p2) {
|
getSecretDefinitionTemplate(p1, p2) {
|
||||||
return `
|
return `
|
||||||
- Name: '${p1}'
|
- Name: '${p1}'
|
||||||
ValueFrom: !Ref ${p2}Secret
|
ValueFrom: !Ref ${p2}Secret
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static insertAtTemplate(template, insertionKey, insertion) {
|
insertAtTemplate(template, insertionKey, insertion) {
|
||||||
const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
|
const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
|
||||||
template = [template.slice(0, index), insertion, template.slice(index)].join('');
|
template = [template.slice(0, index), insertion, template.slice(index)].join('');
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async setupCloudFormations(
|
async setupCloudFormations(
|
||||||
CF: SDK.CloudFormation,
|
CF: SDK.CloudFormation,
|
||||||
buildUid: string,
|
buildUid: string,
|
||||||
stackName: string,
|
|
||||||
image: string,
|
image: string,
|
||||||
entrypoint: string[],
|
entrypoint: string[],
|
||||||
commands: string[],
|
commands: string[],
|
||||||
|
|
@ -88,7 +92,7 @@ class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
||||||
commands[1] += `
|
commands[1] += `
|
||||||
echo "${logid}"
|
echo "${logid}"
|
||||||
`;
|
`;
|
||||||
const taskDefStackName = `${stackName}-${buildUid}`;
|
const taskDefStackName = `${this.stackName}-${buildUid}`;
|
||||||
let taskDefCloudFormation = this.readTaskCloudFormationTemplate();
|
let taskDefCloudFormation = this.readTaskCloudFormationTemplate();
|
||||||
const cleanupTaskDefStackName = `${taskDefStackName}-cleanup`;
|
const cleanupTaskDefStackName = `${taskDefStackName}-cleanup`;
|
||||||
const cleanupCloudFormation = fs.readFileSync(`${__dirname}/cloud-formations/cloudformation-stack-ttl.yml`, 'utf8');
|
const cleanupCloudFormation = fs.readFileSync(`${__dirname}/cloud-formations/cloudformation-stack-ttl.yml`, 'utf8');
|
||||||
|
|
@ -178,7 +182,7 @@ class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
||||||
|
|
||||||
await CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
|
await CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await AWSBuildEnvironment.handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, secrets);
|
await this.handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, secrets);
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
@ -189,7 +193,7 @@ class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
||||||
}).promise()
|
}).promise()
|
||||||
).StackResources;
|
).StackResources;
|
||||||
|
|
||||||
const baseResources = (await CF.describeStackResources({ StackName: stackName }).promise()).StackResources;
|
const baseResources = (await CF.describeStackResources({ StackName: this.stackName }).promise()).StackResources;
|
||||||
|
|
||||||
// in the future we should offer a parameter to choose if you want the guarnteed shutdown.
|
// in the future we should offer a parameter to choose if you want the guarnteed shutdown.
|
||||||
core.info('Worker cluster created successfully (skipping wait for cleanup cluster to be ready)');
|
core.info('Worker cluster created successfully (skipping wait for cleanup cluster to be ready)');
|
||||||
|
|
@ -205,7 +209,7 @@ class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async handleStackCreationFailure(
|
async handleStackCreationFailure(
|
||||||
error: any,
|
error: any,
|
||||||
CF: SDK.CloudFormation,
|
CF: SDK.CloudFormation,
|
||||||
taskDefStackName: string,
|
taskDefStackName: string,
|
||||||
|
|
@ -221,11 +225,11 @@ class AWSBuildEnvironment implements RemoteBuilderProviderInterface {
|
||||||
core.error(error);
|
core.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static readTaskCloudFormationTemplate(): string {
|
readTaskCloudFormationTemplate(): string {
|
||||||
return fs.readFileSync(`${__dirname}/cloud-formations/task-def-formation.yml`, 'utf8');
|
return fs.readFileSync(`${__dirname}/cloud-formations/task-def-formation.yml`, 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
static async cleanupResources(CF: SDK.CloudFormation, taskDef: RemoteBuilderTaskDef) {
|
async cleanupResources(CF: SDK.CloudFormation, taskDef: RemoteBuilderTaskDef) {
|
||||||
core.info('Cleanup starting');
|
core.info('Cleanup starting');
|
||||||
await CF.deleteStack({
|
await CF.deleteStack({
|
||||||
StackName: taskDef.taskDefStackName,
|
StackName: taskDef.taskDefStackName,
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
private kubeClient: k8s.CoreV1Api;
|
private kubeClient: k8s.CoreV1Api;
|
||||||
private kubeClientBatch: k8s.BatchV1Api;
|
private kubeClientBatch: k8s.BatchV1Api;
|
||||||
private buildId: string = '';
|
private buildId: string = '';
|
||||||
private buildCorrelationId: string = '';
|
|
||||||
private buildParameters: BuildParameters;
|
private buildParameters: BuildParameters;
|
||||||
private baseImage: any;
|
|
||||||
private pvcName: string = '';
|
private pvcName: string = '';
|
||||||
private secretName: string = '';
|
private secretName: string = '';
|
||||||
private jobName: string = '';
|
private jobName: string = '';
|
||||||
|
|
@ -25,7 +23,7 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
private podName: string = '';
|
private podName: string = '';
|
||||||
private containerName: string = '';
|
private containerName: string = '';
|
||||||
|
|
||||||
constructor(buildParameters: BuildParameters, baseImage) {
|
constructor(buildParameters: BuildParameters) {
|
||||||
const kc = new k8s.KubeConfig();
|
const kc = new k8s.KubeConfig();
|
||||||
kc.loadFromDefault();
|
kc.loadFromDefault();
|
||||||
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
|
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
|
||||||
|
|
@ -36,15 +34,11 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
this.kubeClient = k8sApi;
|
this.kubeClient = k8sApi;
|
||||||
this.kubeClientBatch = k8sBatchApi;
|
this.kubeClientBatch = k8sBatchApi;
|
||||||
|
|
||||||
this.buildCorrelationId = Kubernetes.uuidv4();
|
|
||||||
|
|
||||||
this.namespace = 'default';
|
this.namespace = 'default';
|
||||||
this.buildParameters = buildParameters;
|
this.buildParameters = buildParameters;
|
||||||
this.baseImage = baseImage;
|
|
||||||
}
|
}
|
||||||
async runBuildTask(
|
async runBuildTask(
|
||||||
buildId: string,
|
buildId: string,
|
||||||
stackName: string,
|
|
||||||
image: string,
|
image: string,
|
||||||
commands: string[],
|
commands: string[],
|
||||||
mountdir: string,
|
mountdir: string,
|
||||||
|
|
@ -53,37 +47,9 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
secrets: RemoteBuilderSecret[],
|
secrets: RemoteBuilderSecret[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
this.setUniqueBuildId();
|
this.setUniqueBuildId(buildId);
|
||||||
const defaultSecretsArray: RemoteBuilderSecret[] = [
|
|
||||||
{
|
|
||||||
ParameterKey: 'GithubToken',
|
|
||||||
EnvironmentVariable: 'GITHUB_TOKEN',
|
|
||||||
ParameterValue: this.buildParameters.githubToken,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'UNITY_LICENSE',
|
|
||||||
EnvironmentVariable: 'UNITY_LICENSE',
|
|
||||||
ParameterValue: process.env.UNITY_LICENSE || '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYSTORE_BASE64',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYSTORE_BASE64',
|
|
||||||
ParameterValue: this.buildParameters.androidKeystoreBase64,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYSTORE_PASS',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYSTORE_PASS',
|
|
||||||
ParameterValue: this.buildParameters.androidKeystorePass,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYALIAS_PASS',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYALIAS_PASS',
|
|
||||||
ParameterValue: this.buildParameters.androidKeyaliasPass,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
defaultSecretsArray.push(...secrets);
|
|
||||||
// setup
|
// setup
|
||||||
await this.createSecret(defaultSecretsArray);
|
await this.createSecret(secrets);
|
||||||
await KubernetesStorage.createPersistentVolumeClaim(
|
await KubernetesStorage.createPersistentVolumeClaim(
|
||||||
this.buildParameters,
|
this.buildParameters,
|
||||||
this.pvcName,
|
this.pvcName,
|
||||||
|
|
@ -106,14 +72,13 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
await this.cleanup();
|
await this.cleanup();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.info('Running job failed');
|
core.info('Running job failed');
|
||||||
|
core.error(JSON.stringify(error, undefined, 4));
|
||||||
await this.cleanup();
|
await this.cleanup();
|
||||||
core.error(JSON.stringify(error.response, undefined, 4));
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setUniqueBuildId() {
|
setUniqueBuildId(buildId: string) {
|
||||||
const buildId = Kubernetes.uuidv4();
|
|
||||||
const pvcName = `unity-builder-pvc-${buildId}`;
|
const pvcName = `unity-builder-pvc-${buildId}`;
|
||||||
const secretName = `build-credentials-${buildId}`;
|
const secretName = `build-credentials-${buildId}`;
|
||||||
const jobName = `unity-builder-job-${buildId}`;
|
const jobName = `unity-builder-job-${buildId}`;
|
||||||
|
|
@ -280,66 +245,6 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
async runJobInKubernetesPod(command: string[], image: string) {
|
|
||||||
try {
|
|
||||||
this.setUniqueBuildId();
|
|
||||||
const defaultSecretsArray: RemoteBuilderSecret[] = [
|
|
||||||
{
|
|
||||||
ParameterKey: 'GithubToken',
|
|
||||||
EnvironmentVariable: 'GITHUB_TOKEN',
|
|
||||||
ParameterValue: this.buildParameters.githubToken,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'UNITY_LICENSE',
|
|
||||||
EnvironmentVariable: 'UNITY_LICENSE',
|
|
||||||
ParameterValue: process.env.UNITY_LICENSE || '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYSTORE_BASE64',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYSTORE_BASE64',
|
|
||||||
ParameterValue: this.buildParameters.androidKeystoreBase64,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYSTORE_PASS',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYSTORE_PASS',
|
|
||||||
ParameterValue: this.buildParameters.androidKeystorePass,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYALIAS_PASS',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYALIAS_PASS',
|
|
||||||
ParameterValue: this.buildParameters.androidKeyaliasPass,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// setup
|
|
||||||
await this.createSecret(defaultSecretsArray);
|
|
||||||
await KubernetesStorage.createPersistentVolumeClaim(
|
|
||||||
this.buildParameters,
|
|
||||||
this.pvcName,
|
|
||||||
this.kubeClient,
|
|
||||||
this.namespace,
|
|
||||||
);
|
|
||||||
|
|
||||||
//run
|
|
||||||
const jobSpec = this.getJobSpec(command, image, 'data', 'data', []);
|
|
||||||
core.info('Creating build job');
|
|
||||||
await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
|
||||||
core.info('Job created');
|
|
||||||
await KubernetesStorage.watchUntilPVCNotPending(this.kubeClient, this.pvcName, this.namespace);
|
|
||||||
core.info('PVC Bound');
|
|
||||||
this.setPodNameAndContainerName(await this.findPod());
|
|
||||||
core.info('Watching pod until running');
|
|
||||||
await this.watchUntilPodRunning();
|
|
||||||
core.info('Pod running, streaming logs');
|
|
||||||
await this.streamLogs();
|
|
||||||
await this.cleanup();
|
|
||||||
} catch (error) {
|
|
||||||
core.info('Running job failed');
|
|
||||||
await this.cleanup();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async findPod() {
|
async findPod() {
|
||||||
const pod = (await this.kubeClient.listNamespacedPod(this.namespace)).body.items.find(
|
const pod = (await this.kubeClient.listNamespacedPod(this.namespace)).body.items.find(
|
||||||
(x) => x.metadata?.labels?.['job-name'] === this.jobName,
|
(x) => x.metadata?.labels?.['job-name'] === this.jobName,
|
||||||
|
|
@ -434,13 +339,5 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
core.info('Abandoning cleanup, build error:');
|
core.info('Abandoning cleanup, build error:');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uuidv4() {
|
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
||||||
const r = Math.trunc(Math.random() * 16);
|
|
||||||
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
||||||
return v.toString(16);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export default Kubernetes;
|
export default Kubernetes;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ export interface RemoteBuilderProviderInterface {
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
buildId: string,
|
buildId: string,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
stackName: string,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
image: string,
|
image: string,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
commands: string[],
|
commands: string[],
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,12 @@ class RemoteBuilder {
|
||||||
switch (buildParameters.remoteBuildCluster) {
|
switch (buildParameters.remoteBuildCluster) {
|
||||||
case 'aws':
|
case 'aws':
|
||||||
core.info('Building with AWS');
|
core.info('Building with AWS');
|
||||||
this.RemoteBuilderProviderPlatform = new AWSBuildPlatform();
|
this.RemoteBuilderProviderPlatform = new AWSBuildPlatform(buildParameters);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case 'k8s':
|
case 'k8s':
|
||||||
core.info('Building with Kubernetes');
|
core.info('Building with Kubernetes');
|
||||||
this.RemoteBuilderProviderPlatform = new Kubernetes(buildParameters, baseImage);
|
this.RemoteBuilderProviderPlatform = new Kubernetes(buildParameters);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.SteamDeploy = process.env.STEAM_DEPLOY !== undefined || false;
|
this.SteamDeploy = process.env.STEAM_DEPLOY !== undefined || false;
|
||||||
|
|
@ -69,7 +69,6 @@ class RemoteBuilder {
|
||||||
core.info('Starting step 1/4 clone and restore cache)');
|
core.info('Starting step 1/4 clone and restore cache)');
|
||||||
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
||||||
buildUid,
|
buildUid,
|
||||||
buildParameters.awsStackName,
|
|
||||||
'alpine/git',
|
'alpine/git',
|
||||||
[
|
[
|
||||||
'-c',
|
'-c',
|
||||||
|
|
@ -204,7 +203,6 @@ class RemoteBuilder {
|
||||||
core.info('Starting part 2/4 (build unity project)');
|
core.info('Starting part 2/4 (build unity project)');
|
||||||
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
||||||
buildUid,
|
buildUid,
|
||||||
buildParameters.awsStackName,
|
|
||||||
baseImage.toString(),
|
baseImage.toString(),
|
||||||
[
|
[
|
||||||
'-c',
|
'-c',
|
||||||
|
|
@ -287,7 +285,6 @@ class RemoteBuilder {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
||||||
buildUid,
|
buildUid,
|
||||||
buildParameters.awsStackName,
|
|
||||||
'alpine',
|
'alpine',
|
||||||
[
|
[
|
||||||
'-c',
|
'-c',
|
||||||
|
|
@ -327,7 +324,6 @@ class RemoteBuilder {
|
||||||
core.info('Starting step 4/4 upload build to s3');
|
core.info('Starting step 4/4 upload build to s3');
|
||||||
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
||||||
buildUid,
|
buildUid,
|
||||||
buildParameters.awsStackName,
|
|
||||||
'amazon/aws-cli',
|
'amazon/aws-cli',
|
||||||
[
|
[
|
||||||
'-c',
|
'-c',
|
||||||
|
|
@ -374,7 +370,6 @@ class RemoteBuilder {
|
||||||
core.info('Starting steam deployment');
|
core.info('Starting steam deployment');
|
||||||
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
await this.RemoteBuilderProviderPlatform.runBuildTask(
|
||||||
buildUid,
|
buildUid,
|
||||||
buildParameters.awsStackName,
|
|
||||||
'cm2network/steamcmd:root',
|
'cm2network/steamcmd:root',
|
||||||
[
|
[
|
||||||
'-c',
|
'-c',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue