unity-builder/src/model/aws.js

312 lines
8.6 KiB
JavaScript
Raw Normal View History

2021-01-31 19:22:28 +00:00
/* eslint-disable no-plusplus */
/* eslint-disable no-await-in-loop */
import * as SDK from 'aws-sdk';
2021-02-07 00:45:33 +00:00
import { nanoid } from 'nanoid';
2021-01-31 20:03:11 +00:00
2021-01-31 19:22:28 +00:00
const fs = require('fs');
const core = require('@actions/core');
2021-02-07 22:44:08 +00:00
const zlib = require('zlib');
2021-01-31 19:22:28 +00:00
class AWS {
static async runBuildJob(buildParameters, baseImage) {
2021-02-14 17:01:48 +00:00
let buildId = nanoid();
2021-01-31 22:11:13 +00:00
await this.run(
buildParameters.awsStackName,
2021-02-14 16:52:50 +00:00
'alpine/git',
2021-02-12 23:35:27 +00:00
['/bin/sh'],
2021-02-12 23:33:34 +00:00
[
'-c',
2021-02-14 16:52:50 +00:00
`apk update;
apk add git-lfs;
2021-02-14 16:40:06 +00:00
ls;
2021-02-14 16:56:07 +00:00
git clone https://github.com/${process.env.GITHUB_REPOSITORY}.git $BUILD_ID/repo;
git clone https://github.com/webbertakken/unity-builder.git $BUILD_ID/builder;
cd $BUILD_ID/repo;
2021-02-14 16:47:13 +00:00
git checkout $GITHUB_SHA;
2021-02-12 23:33:34 +00:00
`],
2021-02-14 16:52:50 +00:00
'/data',
'/data/',
2021-01-31 22:11:13 +00:00
[
{
name: 'GITHUB_SHA',
value: process.env.GITHUB_SHA,
},
2021-02-14 16:56:07 +00:00
{
name: 'BUILD_ID',
2021-02-14 17:01:48 +00:00
value: buildId,
2021-02-14 16:56:07 +00:00
},
2021-01-31 22:11:13 +00:00
],
2021-02-14 18:24:09 +00:00
[],
2021-01-31 22:11:13 +00:00
);
await this.run(
buildParameters.awsStackName,
baseImage.toString(),
2021-02-12 23:41:20 +00:00
['/bin/sh'],
2021-02-14 16:47:13 +00:00
['-c', `
2021-02-14 17:52:23 +00:00
cp -r /data/$BUILD_ID/builder/action/default-build-script /UnityBuilderAction;
cp -r /data/$BUILD_ID/builder/action/entrypoint.sh /entrypoint.sh;
cp -r /data/$BUILD_ID/builder/action/steps /steps;
2021-02-14 16:47:13 +00:00
ls;
2021-02-14 17:52:23 +00:00
chmod -R +x /entrypoint.sh;
chmod -R +x /steps;
/entrypoint.sh;
2021-02-14 16:47:13 +00:00
`],
2021-02-14 16:52:50 +00:00
'/data',
2021-02-14 17:13:21 +00:00
`/data/${buildId}/repo/`,
2021-02-14 17:01:48 +00:00
[
{
name: 'BUILD_ID',
value: buildId,
},
2021-02-14 17:32:08 +00:00
{
name: 'GITHUB_WORKSPACE',
2021-02-14 17:52:23 +00:00
value: `/data/${buildId}/repo/`,
2021-02-14 17:32:08 +00:00
},
{
name: 'PROJECT_PATH',
value: buildParameters.projectPath,
},
{
name: 'BUILD_PATH',
value: buildParameters.buildPath,
},
{
name: 'BUILD_FILE',
value: buildParameters.buildFile,
},
{
name: 'BUILD_NAME',
value: buildParameters.buildName,
},
{
name: 'BUILD_METHOD',
value: buildParameters.buildMethod,
},
{
name: 'CUSTOM_PARAMETERS',
value: buildParameters.customParameters,
},
{
name: 'BUILD_TARGET',
value: buildParameters.platform,
},
{
name: 'ANDROID_VERSION_CODE',
value: buildParameters.androidVersionCode.toString(),
},
{
name: 'ANDROID_KEYSTORE_NAME',
value: buildParameters.androidKeystoreName,
},
{
name: 'ANDROID_KEYALIAS_NAME',
value: buildParameters.androidKeyaliasName,
},
2021-02-14 17:01:48 +00:00
],
2021-02-14 18:24:09 +00:00
[
{name: 'GITHUB_TOKEN',
2021-02-14 18:29:30 +00:00
value: buildParameters.githubToken,},
2021-02-14 18:24:09 +00:00
{name: 'UNITY_LICENSE',
2021-02-14 18:29:30 +00:00
value: process.env.UNITY_LICENSE,},
2021-02-14 18:24:09 +00:00
{name: 'ANDROID_KEYSTORE_BASE64',
2021-02-14 18:29:30 +00:00
value: buildParameters.androidKeystoreBase64,},
2021-02-14 18:24:09 +00:00
{name: 'ANDROID_KEYSTORE_PASS',
2021-02-14 18:29:30 +00:00
value: buildParameters.androidKeystorePass,},
2021-02-14 18:24:09 +00:00
{name: 'ANDROID_KEYALIAS_PASS',
2021-02-14 18:29:30 +00:00
value: buildParameters.androidKeyaliasPass,},
2021-02-14 18:24:09 +00:00
]
2021-01-31 22:11:13 +00:00
);
2021-01-31 19:22:28 +00:00
}
2021-02-14 18:24:09 +00:00
static async run(stackName, image, entrypoint, commands, mountdir, workingdir, environment, secrets) {
2021-01-31 19:22:28 +00:00
const ECS = new SDK.ECS();
const CF = new SDK.CloudFormation();
2021-02-07 02:14:58 +00:00
const taskDefStackName = `${stackName}-taskDef-${image}-${nanoid()}`
.toString()
.replace(/[^\da-z]/gi, '');
core.info('Creating build job resources');
2021-02-07 02:08:17 +00:00
const taskDefCloudFormation = fs.readFileSync(`${__dirname}/task-def-formation.yml`, 'utf8');
await CF.createStack({
StackName: taskDefStackName,
TemplateBody: taskDefCloudFormation,
Parameters: [
{
ParameterKey: 'ImageUrl',
ParameterValue: image,
},
2021-02-07 14:33:42 +00:00
{
ParameterKey: 'ServiceName',
ParameterValue: taskDefStackName,
},
{
ParameterKey: 'Command',
ParameterValue: commands.join(','),
},
{
ParameterKey: 'EntryPoint',
ParameterValue: entrypoint.join(','),
},
{
ParameterKey: 'WorkingDirectory',
ParameterValue: workingdir,
},
{
ParameterKey: 'EFSMountDirectory',
ParameterValue: mountdir,
},
2021-02-14 18:24:09 +00:00
{
ParameterKey: 'SecretName',
ParameterValue: 'Secrets'+nanoid(),
},
{
ParameterKey: 'SecretValue',
ParameterValue: Buffer.from(JSON.stringify(secrets)).toString('base64'),
}
2021-02-07 02:08:17 +00:00
],
}).promise();
await CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
2021-01-31 19:22:28 +00:00
const taskDefResources = await CF.describeStackResources({
StackName: taskDefStackName,
}).promise();
const baseResources = await CF.describeStackResources({ StackName: stackName }).promise();
const clusterName = baseResources.StackResources.find(
(x) => x.LogicalResourceId === 'ECSCluster',
).PhysicalResourceId;
const task = await ECS.runTask({
cluster: clusterName,
taskDefinition: taskDefResources.StackResources.find(
(x) => x.LogicalResourceId === 'TaskDefinition',
).PhysicalResourceId,
platformVersion: '1.4.0',
overrides: {
containerOverrides: [
{
2021-02-07 14:37:59 +00:00
name: taskDefStackName,
2021-01-31 22:11:13 +00:00
environment,
2021-01-31 19:22:28 +00:00
},
],
},
launchType: 'FARGATE',
2021-01-31 19:22:28 +00:00
networkConfiguration: {
awsvpcConfiguration: {
subnets: [
baseResources.StackResources.find((x) => x.LogicalResourceId === 'PublicSubnetOne')
.PhysicalResourceId,
baseResources.StackResources.find((x) => x.LogicalResourceId === 'PublicSubnetTwo')
.PhysicalResourceId,
],
assignPublicIp: 'ENABLED',
securityGroups: [
baseResources.StackResources.find(
(x) => x.LogicalResourceId === 'ContainerSecurityGroup',
).PhysicalResourceId,
],
},
},
}).promise();
2021-02-07 18:10:30 +00:00
2021-02-07 18:00:57 +00:00
core.info('Build job is starting');
2021-02-07 18:10:30 +00:00
2021-02-12 22:00:28 +00:00
try {
await ECS.waitFor('tasksRunning', {
cluster: clusterName,
tasks: [task.tasks[0].taskArn],
}).promise();
} catch (error) {
core.error(error);
}
2021-02-07 18:10:30 +00:00
2021-02-07 18:00:57 +00:00
core.info(`Build job is running`);
2021-01-31 19:22:28 +00:00
// watching logs
2021-02-07 15:42:15 +00:00
const kinesis = new SDK.Kinesis();
2021-02-07 15:27:24 +00:00
const getTaskStatus = async () => {
const tasks = await ECS.describeTasks({
cluster: clusterName,
tasks: [task.tasks[0].taskArn],
}).promise();
return tasks.tasks[0].lastStatus;
};
const stream = await kinesis
.describeStream({
StreamName: taskDefResources.StackResources.find(
(x) => x.LogicalResourceId === 'KinesisStream',
).PhysicalResourceId,
})
.promise();
let iterator = (
await kinesis
.getShardIterator({
ShardIteratorType: 'TRIM_HORIZON',
StreamName: stream.StreamDescription.StreamName,
ShardId: stream.StreamDescription.Shards[0].ShardId,
})
.promise()
).ShardIterator;
2021-02-07 16:36:53 +00:00
core.info(`Task status is ${await getTaskStatus()}`);
2021-02-12 22:00:28 +00:00
let readingLogs = true;
while (readingLogs) {
await new Promise((resolve) => setTimeout(resolve, 3000));
2021-02-12 22:12:15 +00:00
if ((await getTaskStatus()) !== 'RUNNING') {
2021-02-12 22:00:28 +00:00
readingLogs = false;
2021-02-12 22:51:33 +00:00
await new Promise((resolve) => setTimeout(resolve, 35000));
2021-02-12 22:00:28 +00:00
}
const records = await kinesis
.getRecords({
ShardIterator: iterator,
})
.promise();
iterator = records.NextShardIterator;
2021-02-07 16:56:12 +00:00
if (records.Records.length > 0) {
for (let index = 0; index < records.Records.length; index++) {
2021-02-07 22:50:13 +00:00
const json = JSON.parse(
2021-02-07 22:44:08 +00:00
zlib.gunzipSync(Buffer.from(records.Records[index].Data, 'base64')).toString('utf8'),
);
2021-02-07 22:50:13 +00:00
if (json.messageType === 'DATA_MESSAGE') {
2021-02-07 22:55:13 +00:00
for (let logEventsIndex = 0; logEventsIndex < json.logEvents.length; logEventsIndex++) {
core.info(json.logEvents[logEventsIndex].message);
}
2021-02-07 22:50:13 +00:00
}
}
2021-02-07 16:24:06 +00:00
}
}
2021-01-31 19:22:28 +00:00
await ECS.waitFor('tasksStopped', {
cluster: clusterName,
2021-01-31 20:43:20 +00:00
tasks: [task.tasks[0].taskArn],
2021-01-31 19:22:28 +00:00
}).promise();
2021-02-12 22:02:51 +00:00
core.info(
`Build job has ended ${
2021-02-12 22:09:35 +00:00
(
await ECS.describeTasks({
tasks: [task.tasks[0].taskArn],
cluster: clusterName,
}).promise()
).tasks[0].containers[0].exitCode
2021-02-12 22:02:51 +00:00
}`,
);
2021-02-07 17:53:37 +00:00
2021-02-07 17:47:51 +00:00
await CF.deleteStack({
StackName: taskDefStackName,
}).promise();
2021-02-07 17:53:37 +00:00
core.info('Cleanup complete');
2021-01-31 19:22:28 +00:00
}
2021-02-05 23:14:45 +00:00
2021-02-05 23:24:01 +00:00
static onlog(batch) {
batch.forEach((log) => {
core.info(`log: ${log}`);
});
2021-02-05 23:14:45 +00:00
}
2021-01-31 19:22:28 +00:00
}
export default AWS;