UNPKG

@cdwr/core

Version:

A set of core utilities for the Codeware ecosystem.

398 lines (384 loc) 12.5 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // packages/core/src/utils.ts var utils_exports = {}; __export(utils_exports, { dockerBuild: () => dockerBuild, exec: () => exec, getPackageVersion: () => getPackageVersion, isDebugEnabled: () => isDebugEnabled, killPort: () => killPort, killProcessAndPorts: () => killProcessAndPorts, killProcessTree: () => killProcessTree, logDebug: () => logDebug, logError: () => logError, logInfo: () => logInfo, logSuccess: () => logSuccess, logWarning: () => logWarning, runCommand: () => runCommand, spawn: () => spawn, spawnPty: () => spawnPty, whoami: () => whoami }); module.exports = __toCommonJS(utils_exports); // packages/core/src/lib/utils/docker-build.ts var import_fs = require("fs"); var import_path = require("path"); var import_docker_cli_js = require("docker-cli-js"); var import_tiny_invariant = __toESM(require("tiny-invariant"), 1); // packages/core/src/lib/utils/log-utils.ts var import_chalk = __toESM(require("chalk"), 1); function isDebugEnabled() { return process.env["CDWR_DEBUG_LOGGING"] === "true" || process.env["NX_VERBOSE_LOGGING"] === "true"; } function logDebug(title, body) { if (!isDebugEnabled()) { return; } const message = `${import_chalk.default.reset.inverse.bold.cyan( " DEBUG " )} ${import_chalk.default.bold.cyan(title)}`; return consoleLogger(message, body); } function logInfo(title, body) { const message = `${import_chalk.default.reset.inverse.bold.blue( " INFO " )} ${import_chalk.default.bold.blue(title)}`; return consoleLogger(message, body); } function logError(title, body) { const message = `${import_chalk.default.reset.inverse.bold.red(" ERROR ")} ${import_chalk.default.bold.red( title )}`; return consoleLogger(message, body); } function logSuccess(title, body) { const message = `${import_chalk.default.reset.inverse.bold.green( " SUCCESS " )} ${import_chalk.default.bold.green(title)}`; return consoleLogger(message, body); } function logWarning(title, body) { const message = `${import_chalk.default.reset.inverse.bold.yellow( " WARNING " )} ${import_chalk.default.bold.yellow(title)}`; return consoleLogger(message, body); } function consoleLogger(message, body) { process.stdout.write("\n"); process.stdout.write(`${getLogPrefix()} ${message} `); if (body) { process.stdout.write(`${body} `); } process.stdout.write("\n"); } function getLogPrefix() { const prefix = process.env["CDWR_LOG_PREFIX"] ?? "E2E"; return `${import_chalk.default.reset.inverse.bold.yellow(` ${prefix.trim()} `)}`; } // packages/core/src/lib/utils/docker-build.ts var dockerBuild = async (image, quiet) => { const { context, dockerfile, name, tag, args } = image; const dockerfilePath = (0, import_path.join)(context, dockerfile); (0, import_tiny_invariant.default)(!!context?.length, "Context path must be provided"); (0, import_tiny_invariant.default)(!!dockerfile?.length, "Doockerfile path must be provided"); (0, import_tiny_invariant.default)(!!name?.length, "Image name must be provided"); (0, import_tiny_invariant.default)( (0, import_fs.existsSync)((0, import_path.join)(dockerfilePath)), `Dockerfile not found: ${dockerfilePath}` ); const buildTag = `${name}:${tag}`; if (!quiet) { logInfo(`[${buildTag}] Context: '${image.context}'`); logInfo(`[${buildTag}] Dockerfile: '${image.dockerfile}'`); logInfo( `[${buildTag}] Args: ${image?.args ? JSON.stringify(image.args) : "{}"}` ); } const buildArgs = args && Object.keys(args).map((key) => `--build-arg ${key}=${args[key]}`).join(" ") || ""; const cmd = `build -f ${dockerfilePath} -t ${buildTag} ${buildArgs} ${quiet ? "-q" : ""} ${context}`; if (!quiet) { logInfo(`[${buildTag}] Building image...`); } try { const data = await (0, import_docker_cli_js.dockerCommand)(cmd, { echo: !quiet }); if (!quiet) { logInfo(`[${buildTag}] Build success: ${data.response[0]}`); } } catch (error) { logError(`[${buildTag}] Build failed`); logError(`[${buildTag}] Command: '${cmd}'`); throw error; } }; // packages/core/src/lib/utils/promisified-exec.ts var import_child_process = require("child_process"); var import_util = require("util"); var exec = (0, import_util.promisify)(import_child_process.exec); var execFile = (0, import_util.promisify)(import_child_process.execFile); // packages/core/src/lib/utils/get-package-version.ts function getNpmExecutable() { if (process.platform === "win32") { return process.env["npm_execpath"] || "npm.cmd"; } return "npm"; } async function getPackageVersion(packageName) { let version; try { const { stdout } = await execFile(getNpmExecutable(), [ "list", packageName, "--depth=0", "--json" ]); const { dependencies } = JSON.parse(stdout); const pkg = dependencies && dependencies[packageName]; version = pkg?.version; } catch (error) { logDebug( `Failed to get version for package ${packageName}`, error.message ); } return version ?? ""; } // packages/core/src/lib/utils/kill-port.ts var import_kill_port = __toESM(require("kill-port"), 1); var import_tcp_port_used = require("tcp-port-used"); var KILL_PORT_DELAY = 5e3; async function killPort(port, options) { const delay = options?.delay ?? KILL_PORT_DELAY; const verbose = options?.verbose; if (await (0, import_tcp_port_used.check)(port)) { let killPortResult; try { if (verbose) { logInfo(`Attempting to close port ${port}`); } killPortResult = await (0, import_kill_port.default)(port); await new Promise((resolve) => setTimeout(() => resolve(), delay)); if (await (0, import_tcp_port_used.check)(port)) { logError(`Port ${port} still open`, JSON.stringify(killPortResult)); } else { if (verbose) { logSuccess(`Port ${port} successfully closed`); } return true; } } catch { logError(`Port ${port} closing failed`); } return false; } else { return true; } } // packages/core/src/lib/utils/kill-process-tree.ts var import_util2 = require("util"); var import_tree_kill = __toESM(require("tree-kill"), 1); var killProcessTree = (0, import_util2.promisify)(import_tree_kill.default); // packages/core/src/lib/utils/kill-process-and-ports.ts async function killProcessAndPorts(pid, ports) { if (pid) { await killProcessTree(pid, "SIGKILL"); } for (const port of ports ?? []) { await killPort(port); } } // packages/core/src/lib/utils/promisified-spawn.ts var import_child_process2 = require("child_process"); function spawn(command, args, options) { return new Promise((resolve, reject) => { const process2 = (0, import_child_process2.spawn)(command, args, { ...options, stdio: "pipe" }); const stdoutChunks = []; const stderrChunks = []; process2.stdout.on("data", (data) => { stdoutChunks.push(Buffer.from(data)); }); process2.stderr.on("data", (data) => { stderrChunks.push(Buffer.from(data)); }); process2.on("close", (code) => { const stdout = Buffer.concat(stdoutChunks).toString(); const stderr = Buffer.concat(stderrChunks).toString(); if (code === 0) { resolve({ stdout, stderr }); } else { reject(new Error(`Process exited with code ${code} Error: ${stderr}`)); } }); process2.on("error", reject); }); } // packages/core/src/lib/utils/run-command.ts var import_child_process3 = require("child_process"); var import_testing = require("@nx/plugin/testing"); function runCommand(command, options) { const cwd = options?.cwd ?? (0, import_testing.tmpProjPath)(); const doneFn = options?.doneFn; const env = options?.env ?? process.env; const errorDetector = options?.errorDetector; const verbose = options?.verbose; if (verbose) { logDebug("Running command...", command); } const controller = new AbortController(); const { signal } = controller; const p = (0, import_child_process3.exec)(command, { cwd, encoding: "utf-8", env, signal }); return new Promise((resolve, reject) => { let output = ""; let complete = false; const checkLog = (log) => { if (verbose) { logDebug(log); } output += log; if (errorDetector && log.match(errorDetector)) { logDebug( "Error detector found a match, terminate command with failure", log ); return terminate(output, "fail"); } if (doneFn && doneFn(log) && !complete) { complete = true; logDebug("Predicate function met, terminate command successfully", log); terminate(output, "success"); } }; const terminate = (result, status) => { if (!signal.aborted) { controller.abort(); } if (status === "success") { resolve(result); } else { reject(result); } }; p.stdout?.on("data", checkLog); p.stderr?.on("data", checkLog); p.on("error", (err) => { if (signal.aborted) { return; } logError("Received error event"); terminate(err.message, "fail"); }); p.on("exit", (code) => { if (doneFn && !complete) { logError( "Command output:", output.split("\n").map((l) => ` ${l}`).join("\n") ); reject(`Exited with ${code}`); } else { terminate(output, "success"); } }); }); } // packages/core/src/lib/utils/spawn-pty.ts var import_node_pty_prebuilt_multiarch = require("@homebridge/node-pty-prebuilt-multiarch"); function spawnPty(command, args, options) { return new Promise((resolve, reject) => { const ptyData = []; const ptyProcess = (0, import_node_pty_prebuilt_multiarch.spawn)(command, args, { cwd: process.cwd(), encoding: "utf-8", env: process.env }); ptyProcess.onData((data) => { ptyData.push(data); if (options?.prompt) { const answer = options.prompt(data); if (answer) { ptyProcess.write(`${answer} `); } } }); ptyProcess.onExit(({ exitCode }) => { const output = ptyData.join(""); if (exitCode === 0) { resolve(output); } else { reject( new Error(`Process exited with code ${exitCode} Output: ${output}`) ); } }); }); } // packages/core/src/lib/utils/whoami.ts var import_npm_whoami = __toESM(require("npm-whoami"), 1); async function whoami() { return new Promise((resolve) => { (0, import_npm_whoami.default)((err, user) => { if (err || !user) { resolve(""); } else { resolve(user); } }); }); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { dockerBuild, exec, getPackageVersion, isDebugEnabled, killPort, killProcessAndPorts, killProcessTree, logDebug, logError, logInfo, logSuccess, logWarning, runCommand, spawn, spawnPty, whoami });