fix(release): make npm publish noninteractive

This commit is contained in:
Andrey Avtomonov 2026-05-17 00:43:37 +02:00
parent 78ca891c0c
commit 45374fa3ab
2 changed files with 42 additions and 12 deletions

View file

@ -1,14 +1,13 @@
#!/usr/bin/env node #!/usr/bin/env node
import { execFile } from 'node:child_process'; import { spawn } from 'node:child_process';
import { access } from 'node:fs/promises'; import { access } from 'node:fs/promises';
import { pathToFileURL } from 'node:url'; import { pathToFileURL } from 'node:url';
import { promisify } from 'node:util';
import { packageArtifactLayout } from './package-artifacts.mjs'; import { packageArtifactLayout } from './package-artifacts.mjs';
import { releaseReadinessReport } from './release-readiness.mjs'; import { releaseReadinessReport } from './release-readiness.mjs';
const execFileAsync = promisify(execFile); export const NPM_PUBLISH_TIMEOUT_MS = 180_000;
export function resolvePublishMode(args = process.argv.slice(2)) { export function resolvePublishMode(args = process.argv.slice(2)) {
return { live: args.includes('--publish') }; return { live: args.includes('--publish') };
@ -23,7 +22,7 @@ export function requireNpmPublicReleaseReady(report) {
export function buildNpmPublishCommand(tarballPath, publish, mode) { export function buildNpmPublishCommand(tarballPath, publish, mode) {
return { return {
command: 'pnpm', command: 'npm',
args: [ args: [
'publish', 'publish',
tarballPath, tarballPath,
@ -31,7 +30,7 @@ export function buildNpmPublishCommand(tarballPath, publish, mode) {
publish.access, publish.access,
'--tag', '--tag',
publish.tag, publish.tag,
...(mode.live ? [] : ['--dry-run', '--no-git-checks']), ...(mode.live ? [] : ['--dry-run']),
], ],
env: publish.registry ? { npm_config_registry: publish.registry } : {}, env: publish.registry ? { npm_config_registry: publish.registry } : {},
}; };
@ -47,10 +46,42 @@ async function assertFileExists(path) {
async function runPublishCommand(command) { async function runPublishCommand(command) {
process.stdout.write(`$ ${command.command} ${command.args.join(' ')}\n`); process.stdout.write(`$ ${command.command} ${command.args.join(' ')}\n`);
await execFileAsync(command.command, command.args, {
env: { ...process.env, ...command.env }, await new Promise((resolvePromise, reject) => {
encoding: 'utf8', let settled = false;
maxBuffer: 1024 * 1024 * 20, const child = spawn(command.command, command.args, {
env: { ...process.env, ...command.env },
stdio: ['ignore', 'pipe', 'pipe'],
});
const settle = (callback, value) => {
if (settled) {
return;
}
settled = true;
clearTimeout(timeout);
callback(value);
};
const timeout = setTimeout(() => {
child.kill('SIGTERM');
settle(reject, new Error(`Timed out after ${NPM_PUBLISH_TIMEOUT_MS}ms while publishing npm package`));
}, NPM_PUBLISH_TIMEOUT_MS);
child.stdout.on('data', (chunk) => {
process.stdout.write(chunk);
});
child.stderr.on('data', (chunk) => {
process.stderr.write(chunk);
});
child.on('error', (error) => {
settle(reject, error);
});
child.on('close', (code, signal) => {
if (code === 0) {
settle(resolvePromise);
return;
}
settle(reject, new Error(`npm publish failed with ${signal ? `signal ${signal}` : `exit code ${code}`}`));
});
}); });
} }

View file

@ -49,13 +49,13 @@ describe('requireNpmPublicReleaseReady', () => {
}); });
describe('buildNpmPublishCommand', () => { describe('buildNpmPublishCommand', () => {
it('builds a dry-run pnpm publish command by default', () => { it('builds a dry-run npm publish command by default', () => {
assert.deepEqual( assert.deepEqual(
buildNpmPublishCommand('/repo/ktx/dist/artifacts/npm/kaelio-ktx-0.1.0-rc.1.tgz', readyReport.npmPublish, { buildNpmPublishCommand('/repo/ktx/dist/artifacts/npm/kaelio-ktx-0.1.0-rc.1.tgz', readyReport.npmPublish, {
live: false, live: false,
}), }),
{ {
command: 'pnpm', command: 'npm',
args: [ args: [
'publish', 'publish',
'/repo/ktx/dist/artifacts/npm/kaelio-ktx-0.1.0-rc.1.tgz', '/repo/ktx/dist/artifacts/npm/kaelio-ktx-0.1.0-rc.1.tgz',
@ -64,7 +64,6 @@ describe('buildNpmPublishCommand', () => {
'--tag', '--tag',
'next', 'next',
'--dry-run', '--dry-run',
'--no-git-checks',
], ],
env: {}, env: {},
}, },