fix(release): keep npm's own output in the publish log

Retry classification needs npm's failure text, so the publish call captured its
streams instead of inheriting them. That silenced npm on the success path: the
log lost the tarball contents, the notices, and the '+ name@version'
confirmation for every package.

Pipe the streams and echo them, so the log shows what npm reported and the
caller still gets the text it classifies. The registry probe behind it keeps its
streams captured, since its JSON and its E404 are internal queries rather than
progress.
This commit is contained in:
imccyu
2026-08-14 16:10:06 +08:00
parent 47399764c5
commit 70eb76eaec
2 changed files with 28 additions and 2 deletions
+26
View File
@@ -38,6 +38,32 @@ export function attempt(command: string, args: readonly string[], options: RunOp
return { status: result.status, stdout: result.stdout, stderr: result.stderr }
}
/**
* Run a command, letting its output reach the log while also returning it.
*
* A step that both shows progress and classifies its own failure needs both: the
* output has to appear in the workflow log as the command produces it, and the
* caller has to read it to decide whether a failure is worth retrying.
* @param command - executable name.
* @param args - command arguments.
* @param options - working directory and environment.
* @returns The exit status and captured streams.
*/
export function attemptStreaming(command: string, args: readonly string[], options: RunOptions = {}): CommandResult {
const result = spawnSync(command, [...args], {
cwd: options.cwd,
env: options.env,
encoding: 'utf8',
// 'inherit' would leave nothing to capture, so the streams are piped and
// echoed instead.
stdio: ['inherit', 'pipe', 'pipe'],
})
if (result.error !== undefined) throw result.error
if (result.stdout !== '') process.stdout.write(result.stdout)
if (result.stderr !== '') process.stderr.write(result.stderr)
return { status: result.status, stdout: result.stdout, stderr: result.stderr }
}
/**
* Run a command, capture its standard output, and fail on a non-zero exit.
* @param command - executable name.
+2 -2
View File
@@ -18,7 +18,7 @@ import { join, resolve } from 'node:path'
import { setTimeout as sleep } from 'node:timers/promises'
import { parseArgs } from 'node:util'
import { releaseFamily } from './families.ts'
import { attempt, isEntry } from './process.ts'
import { attempt, attemptStreaming, isEntry } from './process.ts'
import { packedIdentity, readPublishOrder } from './tarball.ts'
/**
@@ -102,7 +102,7 @@ async function publishTarball(tarball: string, name: string, version: string): P
// command-line flag could not serve both and would override the manifest
// that does. Each packed manifest decides, and
// check-workspace-constraints holds every manifest to its sequence's level.
const result = attempt('npm', ['publish', tarball, ...tagArgs])
const result = attemptStreaming('npm', ['publish', tarball, ...tagArgs])
const output = `${result.stdout}${result.stderr}`
if (result.status === 0) return