fix: strip clack UI from postinstall, keep silent Playwright download (#783)

npm ≥7 suppresses lifecycle script output by default, so the clack
banner/spinner was invisible during `npm install -g`. The user-facing
onboarding experience already lives at first `gsd` launch (onboarding.ts),
making the postinstall UI redundant dead code.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
TÂCHES 2026-03-16 21:35:04 -06:00 committed by GitHub
parent 69d37d3196
commit 193b2b32a5
2 changed files with 7 additions and 114 deletions

View file

@ -1,125 +1,23 @@
#!/usr/bin/env node
import { exec as execCb } from 'child_process'
import { createRequire } from 'module'
import os from 'os'
import { dirname, resolve } from 'path'
import { fileURLToPath } from 'url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const require = createRequire(import.meta.url)
const pkg = require(resolve(__dirname, '..', 'package.json'))
const cwd = resolve(__dirname, '..')
const shouldSkipBrowserDownload =
const shouldSkip =
process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === '1' ||
process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === 'true'
// ---------------------------------------------------------------------------
// Async exec helper — captures stdout+stderr, never inherits to terminal
// ---------------------------------------------------------------------------
function run(cmd, options = {}) {
function run(cmd) {
return new Promise((resolve) => {
execCb(cmd, { cwd, ...options }, (error, stdout, stderr) => {
resolve({ ok: !error, stdout, stderr, error })
execCb(cmd, { cwd }, (error, stdout, stderr) => {
resolve({ ok: !error, stdout, stderr })
})
})
}
// ---------------------------------------------------------------------------
// Redirect stdout → stderr so npm always shows postinstall output.
// npm ≥7 suppresses stdout from lifecycle scripts by default; stderr is
// always forwarded. Clack writes to process.stdout, so we reroute it.
// ---------------------------------------------------------------------------
process.stdout.write = process.stderr.write.bind(process.stderr)
// ---------------------------------------------------------------------------
// ASCII banner — printed before clack UI for brand recognition
// ---------------------------------------------------------------------------
const cyan = '\x1b[36m'
const dim = '\x1b[2m'
const reset = '\x1b[0m'
const banner =
'\n' +
cyan +
' ██████╗ ███████╗██████╗ \n' +
' ██╔════╝ ██╔════╝██╔══██╗\n' +
' ██║ ███╗███████╗██║ ██║\n' +
' ██║ ██║╚════██║██║ ██║\n' +
' ╚██████╔╝███████║██████╔╝\n' +
' ╚═════╝ ╚══════╝╚═════╝ ' +
reset + '\n' +
'\n' +
` Get Shit Done ${dim}v${pkg.version}${reset}\n`
// ---------------------------------------------------------------------------
// Main — wrapped in async IIFE, with graceful fallback if clack fails
// ---------------------------------------------------------------------------
;(async () => {
process.stderr.write(banner)
let p, pc
try {
p = await import('@clack/prompts')
pc = (await import('picocolors')).default
} catch {
// Clack or picocolors unavailable — fall back to minimal output
process.stderr.write(` Run gsd to get started.\n\n`)
if (!shouldSkipBrowserDownload) {
await run('npx playwright install chromium')
}
return
}
// --- Branded intro -------------------------------------------------------
p.intro('Setup')
const results = []
const s = p.spinner()
// --- Playwright browser --------------------------------------------------
// Avoid --with-deps: install scripts should not block on interactive sudo
// prompts. If Linux libs are missing, suggest the explicit follow-up.
s.start('Setting up browser tools…')
if (shouldSkipBrowserDownload) {
s.stop(pc.yellow('Browser tools skipped via PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD'))
results.push({
label: 'Browser tools skipped — run ' + pc.cyan('npx playwright install chromium') + ' later if needed',
ok: false,
})
} else {
const pwResult = await run('npx playwright install chromium')
if (pwResult.ok) {
s.stop('Browser tools ready')
results.push({ label: 'Browser tools ready', ok: true })
} else {
const output = `${pwResult.stdout ?? ''}${pwResult.stderr ?? ''}`
if (os.platform() === 'linux' && output.includes('Host system is missing dependencies to run browsers.')) {
s.stop(pc.yellow('Browser downloaded, missing Linux deps'))
results.push({
label: 'Run ' + pc.cyan('sudo npx playwright install-deps chromium') + ' to finish setup',
ok: false,
})
} else {
s.stop(pc.yellow('Browser tools — skipped (non-fatal)'))
results.push({
label: 'Browser tools unavailable — run ' + pc.cyan('npx playwright install chromium'),
ok: false,
})
}
}
}
// --- Summary note --------------------------------------------------------
const lines = results.map(
(r) => (r.ok ? pc.green('✓') : pc.yellow('⚠')) + ' ' + r.label
)
lines.push('')
lines.push('Run ' + pc.cyan('gsd') + ' to get started.')
p.note(lines.join('\n'), 'Installed')
// --- Outro ---------------------------------------------------------------
p.outro(pc.green('Done!'))
})()
if (!shouldSkip) {
await run('npx playwright install chromium')
}

View file

@ -14,9 +14,4 @@ test("postinstall respects PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD", () => {
});
assert.equal(result.status, 0, `postinstall exits cleanly: ${result.stderr}`);
assert.match(
result.stderr,
/PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD|Browser tools skipped/,
"postinstall reports that browser download was skipped",
);
});