@kumologica/builder
Version:
Kumologica build and deploy module
225 lines (191 loc) • 7.92 kB
JavaScript
const codegen = require('../build/codegen');
const path = require('path');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const { compareVersions } = require('compare-versions');
const fs = require('fs');
class Updater {
constructor(projectDir) {
this.projectDir = projectDir;
}
getPackageJson() {
return codegen.loadJsonFile(path.join(this.projectDir, 'package.json'));
}
async getNpmJsModuleDetails(moduleName) {
const url = `https://registry.npmjs.org/${encodeURIComponent(moduleName)}/latest`;
try {
const response = await fetch(url);
if (response.ok) {
const packageData = await response.json();
return packageData;
}
} catch (error) {
// package not in npmjs
}
}
//
// reads project node_modules directory to return details of specific moduleName
//
async getNodeModulesDetails(directory, moduleName) {
const modulePath = path.join(directory, "node_modules", moduleName, 'package.json');
try {
const module = fs.readFileSync(modulePath, 'utf-8');
return JSON.parse(module);
} catch (e) {
// ignore, dependency missing
}
}
isAWSSDKv2(pckg) {
return pckg.devDependencies["aws-sdk"] !== undefined || pckg.dependencies["aws-sdk"] !== undefined;
}
awsSDKv3UpdateRequired() {
const pckg = this.getPackageJson();
return this.isAWSSDKv2(pckg);
}
async checkNpmJs() {
const pckg = await this.getPackageJson();
if (!pckg) {
console.error(`Unable to read ${path.join(this.projectDir, "package.json")}`);
return {};
}
const dependencies = pckg.dependencies || {};
const updates = [];
for (const [dependency, usedVersion] of Object.entries(dependencies)) {
if (dependency === "@kumologica/runtime" && compareVersions(usedVersion, "3.0.0") < 0) {
continue; // only runtime 3.x are automatically upgradable, the rest go through sdk v3 upgrade
}
const latestModule = await this.getNpmJsModuleDetails(dependency);
const latestVersion = latestModule?.version;
try {
if (compareVersions(usedVersion, latestVersion) < 0) {
updates.push({"module": dependency, "used": usedVersion, "latest": latestVersion});
}
} catch (e) {
// ignore, no update required, unsupported versioning used
}
}
return updates;
}
async checkInstallRequired() {
const pckg = this.getPackageJson();
if (!pckg) {
console.error(`Unable to read ${path.join(this.projectDir, "package.json")}`);
return {};
}
const dependencies = pckg.dependencies || {};
const updates = [];
for (const [dependency, usedVersion] of Object.entries(dependencies)) {
if (dependency === "@kumologica/runtime") {
continue; // runtime doesnt need to be installed
}
const installedModule = await this.getNodeModulesDetails(this.projectDir, dependency);
const installedVersion = installedModule?.version || "";
try {
if (!installedVersion || compareVersions(usedVersion, installedVersion) < 0) {
updates.push({"module": dependency, "installed": installedVersion, "required": usedVersion});
}
} catch (e) {
// ignore error, version format issues
}
}
return updates;
}
checkFunctionAWS() {
const flowFileName = codegen.findFlowFile(this.projectDir);
if (!flowFileName) {
console.warn("Unable to find flow file in directory: " + this.projectDir);
return;
}
const flow = codegen.loadJsonFile(path.join(this.projectDir, flowFileName));
const isFunctionAWS = flow.find(n => n.type === "Function" && (n.query.includes("\"aws-sdk\"") || n.query.includes("'aws-sdk'")));
return isFunctionAWS;
}
async checkForUpdates() {
return {
"v3": this.awsSDKv3UpdateRequired(),
"updates": await this.checkNpmJs(),
"installs": await this.checkInstallRequired()
}
}
async rebuild() {
const npmCmd = `npm install`;
console.log("");
console.log("Installing updated dependencies: ");
console.log("running command: " + npmCmd + " .....");
console.log("");
try {
const response = await exec(npmCmd, { cwd: this.projectDir });
if (response) {
console.log('');
const lines =
response.stdout
.split('\n')
.filter(s => s)
.forEach(l => console.log(l));
console.log('');
}
} catch (e) {
console.error(e.stderr);
}
}
updateDependencies(updates) {
const pckg = codegen.loadJsonFile(path.join(this.projectDir, 'package.json'));
updates.forEach(u => {
pckg.dependencies[u.module] = u.latest;
});
codegen.createFile(this.projectDir, 'package.json', JSON.stringify(pckg, null, 2));
}
updateSdkV3() {
if (process.env["KUMOLOGICA_AWS_SDK_SKIP_UPDATE"] && process.env["KUMOLOGICA_AWS_SDK_SKIP_UPDATE"].toLowerCase() === "true") {
return;
}
const pckg = codegen.loadJsonFile(path.join(this.projectDir, 'package.json'));
// remove aws-sdk v2
if (pckg.dependencies["aws-sdk"]) {
delete pckg.dependencies["aws-sdk"];
}
if (pckg.devDependencies["aws-sdk"]) {
delete pckg.devDependencies["aws-sdk"];
}
// remove aws sdk v3
for (let k in Object.keys(pckg.dependencies)) {
if (pckg.dependencies[k] && pckg.dependencies[k].startsWith("@aws-sdk")) {
delete pckg.dependencies[k];
}
}
// remove aws sdk v3
for (let k in Object.keys(pckg.devDependencies)) {
if (pckg.devDependencies[k] && pckg.devDependencies[k].startsWith("@aws-sdk")) {
delete pckg.devDependencies[k];
}
}
// add aws sdk v3
pckg.devDependencies["@aws-sdk/client-cloudwatch-events"] = "^3.549.0";
pckg.devDependencies["@aws-sdk/client-dynamodb"] = "^3.540.0";
pckg.devDependencies["@aws-sdk/client-lambda"] = "^3.549.0";
pckg.devDependencies["@aws-sdk/client-rekognition"] = "^3.549.0";
pckg.devDependencies["@aws-sdk/client-s3"] = "^3.550.0";
pckg.devDependencies["@aws-sdk/client-ses"] = "^3.549.0";
pckg.devDependencies["@aws-sdk/client-sns"] = "^3.549.0";
pckg.devDependencies["@aws-sdk/client-sqs"] = "^3.549.0";
pckg.devDependencies["@aws-sdk/client-ssm"] = "^3.549.0";
pckg.devDependencies["@aws-sdk/client-sts"] = "^3.540.0";
pckg.devDependencies["@aws-sdk/lib-dynamodb"] = "^3.540.0";
// runtime to 3.5
const { version } = require('../../package.json');
pckg.dependencies["@kumologica/runtime"] = version;
codegen.createFile(this.projectDir, 'package.json', JSON.stringify(pckg, null, 2));
}
async update(updates) {
if (updates.updates) {
this.updateDependencies(updates.updates);
}
if (updates.v3) {
this.updateSdkV3();
}
if (updates.updates || updates.installs || updates.v3) {
await this.rebuild();
}
}
}
module.exports = Updater;