Skip to content

Commit

Permalink
feat: Implemenation (#3)
Browse files Browse the repository at this point in the history
initial commit
  • Loading branch information
seebees authored Jan 16, 2020
1 parent a5e23d9 commit 5863308
Show file tree
Hide file tree
Showing 4 changed files with 435 additions and 0 deletions.
18 changes: 18 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: '"AWS CodeBuild run project" Action For GitHub Actions'
description: 'Execute CodeBuild::startBuild for the current repo.'
branding:
icon: 'cloud'
color: 'orange'
inputs:
project-name:
description: 'AWS CodeBuild Project Name'
required: true
buildspec-override:
description: 'Buildspec Override'
required: false
env-passthrough:
description: 'Comma separated list of environment variables to send to CodeBuild'
required: false
runs:
using: 'node12'
main: 'dist/index.js'
139 changes: 139 additions & 0 deletions code-build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

const core = require("@actions/core");
const github = require("@actions/github");
const aws = require("aws-sdk");
const assert = require("assert");

module.exports = {
buildProject,
_buildProject,
waitForBuildEndTime,
inputs2Parameters,
buildSdk,
logName
};

function buildProject() {
// get a codeBuild instance from the SDK
const sdk = buildSdk();

// Get input options for startBuild
const params = inputs2Parameters();

return _buildProject(sdk, params);
}

async function _buildProject(sdk, params) {
// Start the build
const start = await sdk.codeBuild.startBuild(params).promise();

// Wait for the build to "complete"
const build = await waitForBuildEndTime(sdk, start.build);

// Signal the outcome
assert(
build.buildStatus === "SUCCEEDED",
`Build status: ${build.buildStatus}`
);
}

async function waitForBuildEndTime(sdk, { id, logs }, nextToken) {
const { codeBuild, cloudWatchLogs, wait = 1000 * 5 } = sdk;

// Get the CloudWatchLog info
const startFromHead = true;
const { cloudWatchLogsArn } = logs;
const { logGroupName, logStreamName } = logName(cloudWatchLogsArn);

// Check the state
const [batch, cloudWatch = {}] = await Promise.all([
codeBuild.batchGetBuilds({ ids: [id] }).promise(),
// The CloudWatchLog _may_ not be set up, only make the call if we have a logGroupName
logGroupName &&
cloudWatchLogs
.getLogEvents({ logGroupName, logStreamName, startFromHead, nextToken })
.promise()
]);
// Pluck off the relevant state
const [current] = batch.builds;
const { nextForwardToken, events = [] } = cloudWatch;

// stdout the CloudWatchLog (everyone likes progress...)
events.forEach(({ message }) => console.log(message));

// We did it! We can stop looking!
if (current.endTime && !events.length) return current;

// More to do: Sleep for 5 seconds :)
await new Promise(resolve => setTimeout(resolve, wait));

// Try again
return waitForBuildEndTime(sdk, current, nextForwardToken);
}

function inputs2Parameters() {
const projectName = core.getInput("project-name", { required: true });

// The github.context.sha is evaluated on import.
// This makes it hard to test.
// So I use the raw ENV
const sourceVersion = process.env[`GITHUB_SHA`];
const sourceTypeOverride = "GITHUB";
const { owner, repo } = github.context.repo;
const sourceLocationOverride = `https://github.com/${owner}/${repo}.git`;

const buildspecOverride =
core.getInput("buildspec-override", { required: false }) || undefined;

const envVars = core
.getInput("env-passthrough", { required: false })
.split(",")

.map(i => i.trim());

const environmentVariablesOverride = Object.entries(process.env)
.filter(([key]) => key.startsWith("GITHUB_") || envVars.includes(key))
.map(([name, value]) => ({ name, value, type: "PLAINTEXT" }));

// The idempotencyToken is intentionally not set.
// This way the GitHub events can manage the builds.
return {
projectName,
sourceVersion,
sourceTypeOverride,
sourceLocationOverride,
buildspecOverride,
environmentVariablesOverride
};
}

function buildSdk() {
const codeBuild = new aws.CodeBuild({
customUserAgent: "aws-codbuild-run-project"
});

const cloudWatchLogs = new aws.CloudWatchLogs({
customUserAgent: "aws-codbuild-run-project"
});

assert(
codeBuild.config.credentials && cloudWatchLogs.config.credentials,
"No credentials. Try adding @aws-actions/configure-aws-credentials earlier in your job to set up AWS credentials."
);

return { codeBuild, cloudWatchLogs };
}

function logName(Arn) {
const [logGroupName, logStreamName] = Arn.split(":log-group:")
.pop()
.split(":log-stream:");
if (logGroupName === "null" || logStreamName === "null")
return {
logGroupName: undefined,
logStreamName: undefined
};
return { logGroupName, logStreamName };
}
20 changes: 20 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

const core = require("@actions/core");
const { buildProject } = require("./code-build");

/* istanbul ignore next */
if (require.main === module) {
run();
}

module.exports = run;

async function run() {
try {
await buildProject();
} catch (error) {
core.setFailed(error.message);
}
}
Loading

0 comments on commit 5863308

Please sign in to comment.