payments-dashboard/scripts/package_lambdas.mjs

93 lines
2.9 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* Build one Node zip per Lambda key. Used by deploy.yaml.
* Each zip is functions/<name>/<sha>.zip on S3. GIT_SHA is written to
* src/buildInfo.js inside the zip so a deploy is identifiable without a
* Terraform-owned env var.
*/
import { spawn, spawnSync } from "node:child_process";
import { cpSync, existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const ROOT = fileURLToPath(new URL("..", import.meta.url));
const FUNCTIONS = [
"process_csv",
"slack_app_home",
"fetch_boa",
"expense_receiver",
"expense_processor",
];
function parseArgs(argv) {
const out = { gitSha: "", outDir: join(ROOT, "build", "packages"), only: [] };
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
if (arg === "--git-sha") {
out.gitSha = argv[++i];
} else if (arg === "--out-dir") {
out.outDir = resolve(argv[++i]);
} else if (arg === "--only") {
out.only.push(argv[++i]);
} else {
throw new Error(`unknown argument: ${arg}`);
}
}
if (!out.gitSha) {
throw new Error("--git-sha is required");
}
return out;
}
function zipDir(srcDir, zipPath) {
return new Promise((resolve, reject) => {
const child = spawn("zip", ["-qr", zipPath, "."], { cwd: srcDir, stdio: "inherit" });
child.on("exit", (code) => {
if (code === 0) resolve();
else reject(new Error(`zip exited ${code}`));
});
});
}
async function build(name, gitSha, outDir) {
const dest = mkdtempSync(join(tmpdir(), `payments-${name}-`));
try {
cpSync(join(ROOT, "src"), join(dest, "src"), { recursive: true });
cpSync(join(ROOT, "package.json"), join(dest, "package.json"));
if (existsSync(join(ROOT, "package-lock.json"))) {
cpSync(join(ROOT, "package-lock.json"), join(dest, "package-lock.json"));
}
writeFileSync(
join(dest, "src", "buildInfo.js"),
`export const GIT_SHA = ${JSON.stringify(gitSha)};\n`,
"utf8",
);
const npm = spawnSync("npm", ["ci", "--omit=dev"], { cwd: dest, stdio: "inherit" });
if (npm.status !== 0) {
throw new Error("npm ci --omit=dev failed");
}
mkdirSync(outDir, { recursive: true });
const zipPath = resolve(outDir, `${name}.zip`);
rmSync(zipPath, { force: true });
await zipDir(dest, zipPath);
return zipPath;
} finally {
rmSync(dest, { recursive: true, force: true });
}
}
const args = parseArgs(process.argv.slice(2));
const selected = args.only.length ? args.only : FUNCTIONS;
const unknown = selected.filter((name) => !FUNCTIONS.includes(name));
if (unknown.length) {
console.error(`unknown function keys: ${unknown.join(", ")}`);
process.exit(2);
}
for (const name of selected) {
const path = await build(name, args.gitSha, args.outDir);
console.log(path);
}