UNPKG

workspace-tools

Version:

A collection of utilities that are useful in a git-controlled monorepo managed by one of these tools:

144 lines 5.37 kB
"use strict"; // // Basic git wrappers // Object.defineProperty(exports, "__esModule", { value: true }); exports.processGitOutput = exports.gitFailFast = exports.git = exports.clearGitObservers = exports.addGitObserver = exports.GitError = void 0; const child_process_1 = require("child_process"); class GitError extends Error { constructor(message, originalError, gitOutput) { if (originalError instanceof Error) { super(`${message}: ${originalError.message}`); } else if (gitOutput?.stderr) { super(`${message} -- stderr:\n${gitOutput.stderr}`); } else { super(message); } this.originalError = originalError; this.gitOutput = gitOutput; } } exports.GitError = GitError; /** * A global maxBuffer override for all git operations. * Bumps up the default to 500MB instead of 1MB. * Override this value with the `GIT_MAX_BUFFER` environment variable. */ const defaultMaxBuffer = process.env.GIT_MAX_BUFFER ? parseInt(process.env.GIT_MAX_BUFFER) : 500 * 1024 * 1024; const isDebug = !!process.env.GIT_DEBUG; const observers = []; let observing; /** * Adds an observer for the git operations, e.g. for testing * @returns a function to remove the observer */ function addGitObserver(observer) { observers.push(observer); return () => removeGitObserver(observer); } exports.addGitObserver = addGitObserver; /** Clear all git observers */ function clearGitObservers() { observers.splice(0, observers.length); } exports.clearGitObservers = clearGitObservers; /** Remove a git observer */ function removeGitObserver(observer) { const index = observers.indexOf(observer); if (index > -1) { observers.splice(index, 1); } } /** * Runs git command - use this for read-only commands, or if you'd like to explicitly check the * result and implement custom error handling. * * The caller is responsible for validating the input. * `shell` will always be set to false. */ function git(args, options) { if (args.some((arg) => arg.startsWith("--upload-pack"))) { // This is a security issue and not needed for any expected usage of this library. throw new GitError("git command contains --upload-pack, which is not allowed: " + args.join(" ")); } const gitDescription = `git ${args.join(" ")}`; const { throwOnError, description = gitDescription, debug = isDebug, ...spawnOptions } = options || {}; debug && console.log(gitDescription); let results; try { // this only throws if git isn't found or other rare cases results = (0, child_process_1.spawnSync)("git", args, { maxBuffer: defaultMaxBuffer, ...spawnOptions }); } catch (e) { throw new GitError(`${description} failed (while spawning process)`, e); } const output = { ...results, // these may be undefined if stdio: inherit is set stderr: (results.stderr || "").toString().trimEnd(), stdout: (results.stdout || "").toString().trimEnd(), success: results.status === 0, }; if (debug) { console.log("exited with code " + results.status); output.stdout && console.log("git stdout:\n", output.stdout); output.stderr && console.warn("git stderr:\n", output.stderr); } // notify observers, flipping the observing bit to prevent infinite loops if (!observing) { observing = true; for (const observer of observers) { observer(args, output); } observing = false; } if (!output.success && throwOnError) { throw new GitError(`${description} failed${output.stderr ? `\n${output.stderr}` : ""}`, undefined, output); } return output; } exports.git = git; /** * Run a git command. Use this for commands that make critical changes to the filesystem. * If it fails, throw an error and set `process.exitCode = 1` (unless `options.noExitCode` is set). * * The caller is responsible for validating the input. * `shell` will always be set to false. */ function gitFailFast(args, options) { const gitResult = git(args, options); if (!gitResult.success) { if (!options?.noExitCode) { process.exitCode = 1; } throw new GitError(`CRITICAL ERROR: running git command: git ${args.join(" ")}! ${gitResult.stdout?.toString().trimEnd()} ${gitResult.stderr?.toString().trimEnd()}`); } } exports.gitFailFast = gitFailFast; /** * Processes git command output by splitting it into lines and filtering out empty lines. * Also filters out `node_modules` lines if specified in options. * * If the command failed with stderr output, an error is thrown. * * @param output - The git command output to process * @returns An array of lines (presumably file paths), or an empty array if the command failed * without stderr output. * @internal */ function processGitOutput(output, options) { if (!output.success) { // If the intent was to throw on failure, `throwOnError` should have been set for the git command. return []; } return output.stdout .split(/\n/) .map((line) => line.trim()) .filter((line) => !!line && (!options?.excludeNodeModules || !line.includes("node_modules"))); } exports.processGitOutput = processGitOutput; //# sourceMappingURL=git.js.map