| #! /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); |