kodebuild
Version:
Code-Build-like module to build your project from your buildspec.yml
168 lines (136 loc) • 4.33 kB
JavaScript
//
// File: kodebuild.builder.js
// Author: Ivan Kluzak
// Date: 05/01/2018
// Notes: An attempt at loading your buildspec.yml and building your project locally
// use with Docker... I don't see why I should create one to build there and
// then not be able to use it locally. This attempts to bridge that gap or
// at least kludge that gap.
// Note:
// It's a little bit of a mess, but this is first draft..
//
const YAML = require('yamljs');
const { exec } = require('child_process');
const fs = require('fs');
const chalk = require('chalk');
//
// The Buildspec file we will try to load and process
const BUILDSPEC = 'buildspec.yml';
function lets_build_it() {
// Look for the BUILDSPEC file
if (!fs.existsSync(BUILDSPEC)) {
console.error(chalk.red(`${BUILDSPEC} does not exist`));
process.exit();
}
//
// Load the buildspec file:
const bs = YAML.load(BUILDSPEC);
// Simplistic sanity check
if (typeof bs.version !== 'undefined') {
if (bs.version !== 0.2) {
console.log(chalk.red("unsupported buildspec version. Looking for 0.2"));
}
}
//
// Define the environment variables for scripts
var ENV = bs.env.variables;
// TO-DO: add-in a bunch of default CodeBuild environmental variables
//
console.log(`kodebuild[ENV][${process.pid}]: ${JSON.stringify(bs.env.variables)}`);
// Don't display these...but include them
ENV['AWS_ACCESS_KEY_ID'] = process.env.AWS_ACCESS_KEY_ID;
ENV['AWS_SECRET_ACCESS_KEY'] = process.env.AWS_SECRET_ACCESS_KEY;
//
// Execute a single command:
function spawn(a, callback) {
var o = exec(a, {
env: ENV
}, (error, stdout, stderr) => {
if (error) {
//console.error(chalk.red(`exec error: ${error}`));
console.log(chalk.red(`\n\n${a} has finished - WITH ERRORS\n\n`));
callback(error);
} else {
console.log(chalk.red(`\n\n${a} has finished\n\n`));
callback(null, stdout, stderr);
}
});
o.stdout.on('data', function(data) {
//console.log(data);
process.stdout.write(data);
});
o.stderr.on('data', function(data) {
process.stderr.write(chalk.red(data));
});
}
//
// Execute the commands:
function runCommands(node, section, idx, callback) {
if (Array.isArray(node.commands)) {
if (idx < node.commands.length) {
console.log(`kodebuild[${section}][${process.pid}]: ${node.commands[idx]}`);
// Spawn the actual comand finally
spawn(node.commands[idx], function (err, stdout, stderr) {
if (err) {
return callback(err);
} //else {
//process.stdout.write(stdout);
//process.stderr.write(stderr);
//}
runCommands(node, section, idx+1, callback);
});
} else {
// end of the array
// TO-DO: return a status code here or something
callback(null);
}
} else {
// Empty section, just return
callback(null);
}
}
//
// Deal with artifacts:
function artifactUpload(a, idx, callback) {
/*
* for example:
"artifacts": {
"files": [
"artifacts-dir/package.tar.gz"
]
*/
if (idx < a.files.length) {
if (fs.existsSync(a.files[idx])) {
// TO-DO: copy to S3...
console.log(`TO-DO: upload ${a.files[idx]}`);
artifactUpload(a, idx+1, callback);
} else {
callback(new Error(`BUILD_FAILED: missing artifact '${a.files[idx]}'`));
}
} else {
console.log("kodebuild: upload complete");
callback(null);
}
}
//
// Process each of the phases if they exist:
// TO-DO: make sure all the phases are here
// TO-DO: maybe fail the build early if any one section fails
//
runCommands(bs.phases.install, "INSTALL", 0, function (err, res) {
runCommands(bs.phases.pre_build, "PRE_BUILD", 0, function (pre_err, pre_res) {
runCommands(bs.phases.build, "BUILD", 0, function (b_err, b_res) {
runCommands(bs.phases.post_build, "POST_BUILD", 0, function (pb_err, pb_res) {
artifactUpload(bs.artifacts, 0, function (a_err, a_res) {
if (a_err) {
console.log(`kodebuild: ${a_err}`);
}
console.log("kodebuild: done!");
});
});
});
});
});
}
module.exports = lets_build_it;