@kumologica/builder
Version:
Kumologica build and deploy module
244 lines (210 loc) • 6.43 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const AdmZip = require('adm-zip');
const codegen = require('./codegen');
/**
* sample events:
* https://docs.aws.amazon.com/lambda/latest/dg/lambda-services.html
*/
class AzureBuilder {
constructor(log) {
if (log) {
this.logger = log;
}
}
log(text, calog = true) {
if (this.logger) {
this.logger(text, calog);
} else {
console.log(text);
}
}
async buildFunction(buildDir, zipFileName) {
this.log(`Building Azure function... ${buildDir}/${zipFileName}`);
const npmCmd = `npm install --production --prefix "${buildDir}"`;
const response = await exec(npmCmd, { cwd: buildDir });
if (response) {
this.log('', false);
const lines = response.stdout.split('\n');
for (var i = 0; i < lines.length; i++) {
if (lines[i]) {
this.log(lines[i], false);
}
}
this.log('', false);
}
// for npm 8 on windows is creating symlink that causes recursions and zip to fail
const splitdir = path.dirname(buildDir).split(path.sep);
const linkname = splitdir[splitdir.length-1];
try {
fs.unlinkSync(path.join(buildDir, 'node_modules', linkname));
} catch (e) {
// ignore ENOENT
if (e.code !== "ENOENT") {
throw e;
}
}
this.log('Zipping Function ...');
const zip = new AdmZip();
zip.addLocalFolder(buildDir);
zip.writeZip(path.join(buildDir, zipFileName));
this.log('Function has been succesfully built:');
this.log(` zip file: ${path.join(buildDir, zipFileName)}`);
}
prepare(projectDir, flowFileName) {
const buildDir = path.join(projectDir, 'build');
const flowName = flowFileName.replace('.json', '');
const webappDir = path.join(buildDir, flowName);
// setup deployment directory
fs.ensureDirSync(buildDir);
fs.emptyDirSync(buildDir);
fs.ensureDirSync(webappDir);
fs.ensureDirSync(path.join(buildDir, 'node_modules'));
// copy project files
codegen.generateAzureProjectCode(
buildDir,
flowFileName,
flowName
);
this.generateBindings(webappDir, flowName);
this.generateHost(buildDir);
fs.copySync(
path.join(projectDir, flowFileName),
path.join(buildDir, flowFileName)
);
this.processPackageJson(projectDir, buildDir);
}
processPackageJson(projectDir, buildDir) {
const pckg = codegen.loadJsonFile(path.join(projectDir, 'package.json'));
if (!pckg.dependencies["@kumologica/runtime"]) {
pckg.dependencies["@kumologica/runtime"] = `^${pckg.version.substr(0, 1)}`;
}
if (!pckg.dependencies["@aws-sdk/client-cloudwatch-events"]) {
pckg.dependencies["@aws-sdk/client-cloudwatch-events"] = `^3.549.0`;
}
if (!pckg.dependencies["aws-sdk/client-dynamodb"]) {
pckg.dependencies["aws-sdk/client-dynamodb"] = `^3.549.0`;
}
if (!pckg.dependencies["@aws-sdk/client-lambda"]) {
pckg.dependencies["@aws-sdk/client-lambda"] = `^3.549.0`;
}
if (!pckg.dependencies["@aws-sdk/client-rekognition"]) {
pckg.dependencies["@aws-sdk/client-rekognition"] = `^3.549.0`;
}
if (!pckg.dependencies["@aws-sdk/client-s3"]) {
pckg.dependencies["@aws-sdk/client-s3"] = `^3.549.0`;
}
if (!pckg.dependencies["@aws-sdk/client-ses"]) {
pckg.dependencies["@aws-sdk/client-ses"] = `^3.549.0`;
}
if (!pckg.dependencies["@aws-sdk/client-sns"]) {
pckg.dependencies["@aws-sdk/client-sns"] = `^3.549.0`;
}
if (!pckg.dependencies["@aws-sdk/client-sqs"]) {
pckg.dependencies["@aws-sdk/client-sqs"] = `^3.549.0`;
}
if (!pckg.dependencies["@aws-sdk/client-ssm"]) {
pckg.dependencies["@aws-sdk/client-ssm"] = `^3.549.0`;
}
if (!pckg.dependencies["@aws-sdk/client-sts"]) {
pckg.dependencies["@aws-sdk/client-sts"] = `^3.549.0`;
}
if (!pckg.dependencies["@aws-sdk/lib-dynamodb"]) {
pckg.dependencies["@aws-sdk/lib-dynamodb"] = `^3.549.0`;
}
// ensure dependencies contain runtime
if (!pckg.dependencies["@kumologica/runtime"]) {
pckg.dependencies["@kumologica/runtime"] = `^${pckg.version.substr(0, 1)}`;
}
codegen.createFile(buildDir, 'package.json', JSON.stringify(pckg));
if (pckg.files) {
pckg.files.forEach(f => {
if (!["node_modules/**/*", "lambda.js", "src/**/*"].includes(f)) {
fs.copySync(
path.join(projectDir, f),
path.join(buildDir, f)
);
}
});
}
}
generateBindings(directory, functionName) {
let bindings = {
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post",
"delete",
"put",
"patch"
],
"route": `${functionName}/{*restOfPath}`
},
{
"type": "http",
"direction": "out",
"name": "res"
}
],
"entryPoint": "handler",
"scriptFile": "../index.js"
}
codegen.createFile(directory, 'function.json', JSON.stringify(bindings));
}
generateHost(directory) {
let host = {
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
},
"extensions": {
"http": {
"routePrefix": ""
}
}
}
codegen.createFile(directory, 'host.json', JSON.stringify(host));
}
validate(flow, provider) {
const FlowValidator = require('../validator');
const validator = new FlowValidator(this.logger);
validator.validate(flow, provider);
}
async build(argv) {
let projectDir = argv["project-directory"] || process.cwd();
let buildDir = path.join(projectDir, 'build');
let projectFlowName = argv["flow-file-name"] || codegen.findFlowFile(projectDir);
if (!projectFlowName) {
throw new Error(`Unable to find flow file in project directory: ${projectDir}`);
}
const flow = codegen.loadJsonFile(path.join(projectDir, projectFlowName));
this.validate(flow, "azure");
console.log(`project directory: ${projectDir}`);
console.log(`flow file name: ${projectFlowName}`);
console.log(`zip file name: ${argv["zip-file-name"] || 'azure.zip'}`);
this.prepare(
projectDir,
projectFlowName
);
const zipFileName = argv["zip-file-name"] || "azure.zip";
await this.buildFunction(buildDir, zipFileName);
return zipFileName;
}
}
module.exports = AzureBuilder;