process.ts 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. /**
  2. * Process helpers shared by the release scripts: the release steps drive `git`,
  3. * `pnpm`, `npm`, and `tar`, and each needs one of three failure behaviours.
  4. */
  5. import { spawnSync } from 'node:child_process'
  6. /** Where and with what environment a release step runs a command. */
  7. export interface RunOptions {
  8. /** Working directory; defaults to the current one. */
  9. readonly cwd?: string
  10. /** Child environment; defaults to this process's. */
  11. readonly env?: NodeJS.ProcessEnv
  12. }
  13. /** What a command produced, for a caller that decides what a failure means. */
  14. export interface CommandResult {
  15. /** Exit status, or null when a signal ended the process. */
  16. readonly status: number | null
  17. /** Captured standard output. */
  18. readonly stdout: string
  19. /** Captured standard error. */
  20. readonly stderr: string
  21. }
  22. /**
  23. * Run a command and capture its output without judging the exit status.
  24. * @param command - executable name.
  25. * @param args - command arguments.
  26. * @param options - working directory and environment.
  27. * @returns The exit status and captured streams.
  28. */
  29. export function attempt(command: string, args: readonly string[], options: RunOptions = {}): CommandResult {
  30. const result = spawnSync(command, [...args], { cwd: options.cwd, env: options.env, encoding: 'utf8' })
  31. if (result.error !== undefined) throw result.error
  32. return { status: result.status, stdout: result.stdout, stderr: result.stderr }
  33. }
  34. /**
  35. * Run a command, capture its standard output, and fail on a non-zero exit.
  36. * @param command - executable name.
  37. * @param args - command arguments.
  38. * @param options - working directory and environment.
  39. * @returns The trimmed standard output.
  40. */
  41. export function capture(command: string, args: readonly string[], options: RunOptions = {}): string {
  42. const result = attempt(command, args, options)
  43. if (result.status !== 0) {
  44. throw new Error(`${command} ${args.join(' ')} exited with ${String(result.status)}:\n${result.stdout}\n${result.stderr}`)
  45. }
  46. return result.stdout.trim()
  47. }
  48. /**
  49. * Run a command with inherited streams, so its progress reaches the log, and
  50. * fail on a non-zero exit.
  51. * @param command - executable name.
  52. * @param args - command arguments.
  53. * @param options - working directory and environment.
  54. */
  55. export function run(command: string, args: readonly string[], options: RunOptions = {}): void {
  56. const result = spawnSync(command, [...args], { cwd: options.cwd, env: options.env, stdio: 'inherit' })
  57. if (result.error !== undefined) throw result.error
  58. if (result.status !== 0) throw new Error(`${command} ${args.join(' ')} exited with ${String(result.status)}`)
  59. }