blob: cec5940295d8d6596659499f53409e2cc375c574 [file] [log] [blame] [edit]
#! /usr/bin/env node
import commandLineArgs from "command-line-args";
import fs from "fs";
import { fileURLToPath } from "url";
import path from "path";
import { logError, logCommand, printHelp, runTest, sh, logInfo } from "./helper.mjs";
const FILE_PATH = fileURLToPath(import.meta.url);
const SRC_DIR = path.dirname(path.dirname(FILE_PATH));
const optionDefinitions = [
{ name: "help", alias: "h", description: "Print this help text." },
{ name: "diff", type: String, description: "A git commit range to determine which builds to run (e.g. main...HEAD)." },
];
const options = commandLineArgs(optionDefinitions);
if ("help" in options)
printHelp(optionDefinitions);
if (options.diff) {
const { stdoutString } = await sh("git", "diff", "--name-only", options.diff);
const changedDirs = new Set();
const changedFiles = stdoutString.trim().split("\n");
for (const file of changedFiles) {
let currentDir = path.dirname(file)
while (currentDir !== ".") {
changedDirs.add(path.join(SRC_DIR, currentDir));
currentDir = path.dirname(currentDir);
}
}
options.changedDirs = changedDirs;
} else {
options.changedDirs = undefined;
}
async function findPackageJsonFiles(dir, accumulator=[]) {
const dirEntries = fs.readdirSync(dir, { withFileTypes: true });
for (const dirent of dirEntries) {
if (dirent.name === "node_modules" || dirent.name === ".git")
continue;
const fullPath = path.join(dir, dirent.name);
if (dirent.isDirectory()) {
// Ignore third-party git dirs.
if (fs.existsSync(path.join(fullPath, ".git"))) {
continue;
}
findPackageJsonFiles(fullPath, accumulator);
} else if (dirent.name === "package.json") {
accumulator.push(fullPath)
}
}
return accumulator;
}
async function runBuilds() {
const packageJsonFiles = await findPackageJsonFiles(SRC_DIR);
let success = true;
logInfo(`Found ${packageJsonFiles.length} package.json files`);
console.log(packageJsonFiles)
let filteredPackageJsonFiles = packageJsonFiles;
if (options.changedDirs?.size === 0) {
logInfo("No file changes detected, skipping all");
}
if (options.changedDirs?.size > 0) {
filteredPackageJsonFiles = packageJsonFiles.filter(file => {
const dir = path.dirname(file);
return options.changedDirs.has(dir);
});
logInfo(`Found ${filteredPackageJsonFiles.length} modified package.json files to build`);
}
for (const file of filteredPackageJsonFiles) {
const content = fs.readFileSync(file, "utf-8");
const packageJson = JSON.parse(content);
if (!packageJson.scripts?.build) {
continue;
}
const dir = path.dirname(file);
const relativeDir = path.relative(SRC_DIR, dir);
const testName = `Building ./${relativeDir}:`;
const buildTask = async () => {
const oldCWD = process.cwd();
try {
logCommand("cd", dir);
process.chdir(dir);
await sh("npm", "ci");
await sh("npm", "run", "build");
} finally {
process.chdir(oldCWD);
await sh("git", "reset", "--hard");
}
};
success &&= await runTest(testName, buildTask);
}
if (!success) {
logError("One or more builds failed.");
process.exit(1);
}
}
setImmediate(runBuilds);