From d09a117054c2a270dc60fe77d9c1fbf8be06c600 Mon Sep 17 00:00:00 2001 From: Anton Golub Date: Sat, 4 Jun 2022 11:19:21 +0300 Subject: [PATCH] fix getCtx() (#425) * fix: bind fetch output to log() * fix: use root ctx as getCtx fallback closes #424 * refactor: simplify * refactor: rm deep-proxy * chore: revert shell tweak * chore: protect promise.ctx from accidental removal * test: test promise.ctx guard * fix: use root as fallback for `runInCtx` --- package.json | 2 +- src/context.ts | 13 ++++++++++--- src/core.ts | 6 ++++-- src/goods.ts | 15 ++++++++------- test/index.test.js | 11 +++++++++++ 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 967350cf64..cb0554955e 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "globby": "^13.1.1", "ignore": "^5.2.0", "minimist": "^1.2.6", - "node-fetch": "^3.2.4", + "node-fetch": "^3.2.5", "ps-tree": "^1.2.0", "which": "^2.0.2", "yaml": "^2.1.1" diff --git a/src/context.ts b/src/context.ts index 75fcb6f30d..f456e7cb4e 100644 --- a/src/context.ts +++ b/src/context.ts @@ -20,7 +20,7 @@ export type Options = { cwd: string env: NodeJS.ProcessEnv prefix: string - shell: string + shell: string | boolean maxBuffer: number quote: (v: string) => string spawn: typeof spawn @@ -43,13 +43,20 @@ let root: Options const storage = new AsyncLocalStorage() export function getCtx() { - return storage.getStore() as Context + return (storage.getStore() as Context) || getRootCtx() } + export function setRootCtx(ctx: Options) { storage.enterWith(ctx) root = ctx } + export function getRootCtx() { return root } -export const runInCtx = storage.run.bind(storage) + +export const runInCtx = ( + ctx: Options = root, + cb: (...args: TArgs) => R, + ...args: TArgs +): R => storage.run(ctx, cb, ...args) diff --git a/src/core.ts b/src/core.ts index 9ce7770e49..57cf06d54e 100644 --- a/src/core.ts +++ b/src/core.ts @@ -23,7 +23,7 @@ import { inspect, promisify } from 'node:util' import { spawn } from 'node:child_process' import { chalk, which } from './goods.js' -import { runInCtx, getCtx, setRootCtx, Context, Options } from './context.js' +import { runInCtx, getCtx, Context, Options, setRootCtx } from './context.js' import { printCmd, log } from './print.js' import { quote, substitute } from './guards.js' @@ -52,13 +52,14 @@ export const $: Zx = function (pieces: TemplateStringsArray, ...args: any[]) { cmd += s + pieces[++i] } - promise.ctx = { + const ctx = { ...getCtx(), cmd, __from: new Error().stack!.split(/^\s*at\s/m)[2].trim(), resolve, reject, } + Object.defineProperty(promise, 'ctx', { value: ctx }) setImmediate(() => promise._run()) // Make sure all subprocesses are started, if not explicitly by await or then(). @@ -72,6 +73,7 @@ $.spawn = spawn $.verbose = 2 $.maxBuffer = 200 * 1024 * 1024 /* 200 MiB*/ $.prefix = '' // Bash not found, no prefix. +$.shell = true try { $.shell = which.sync('bash') $.prefix = 'set -euo pipefail;' diff --git a/src/goods.ts b/src/goods.ts index e800410f92..b5c5d0dc40 100644 --- a/src/goods.ts +++ b/src/goods.ts @@ -18,6 +18,7 @@ import { setTimeout as sleep } from 'node:timers/promises' import nodeFetch, { RequestInfo, RequestInit } from 'node-fetch' import { getCtx, getRootCtx } from './context.js' import { colorize } from './print.js' +import { log } from './print.js' export { default as chalk } from 'chalk' export { default as fs } from 'fs-extra' @@ -40,13 +41,13 @@ globbyModule) export const glob = globby export async function fetch(url: RequestInfo, init?: RequestInit) { - if (getCtx().verbose) { - if (typeof init !== 'undefined') { - console.log('$', colorize(`fetch ${url}`), init) - } else { - console.log('$', colorize(`fetch ${url}`)) - } - } + log( + { scope: 'fetch' }, + '$', + colorize(`fetch ${url}`), + init && JSON.stringify(init, null, 2) + ) + return nodeFetch(url, init) } diff --git a/test/index.test.js b/test/index.test.js index 4db9220e22..5377030a58 100755 --- a/test/index.test.js +++ b/test/index.test.js @@ -169,6 +169,17 @@ test('ProcessPromise: inherits native Promise', async () => { assert.ok(p5 !== p1) }) +test('ProcessPromise: ctx is protected from removal', async () => { + const p = $`echo 1` + + try { + delete p.ctx + assert.unreachable() + } catch (e) { + assert.match(e.message, /Cannot delete property/) + } +}) + test('ProcessOutput thrown as error', async () => { let err try {