UNPKG

@kumologica/builder

Version:
225 lines (191 loc) 7.92 kB
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;