From ac48b3ae40dc12207d64502f852abc3cc369706d Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Fri, 8 May 2026 23:42:12 +0900 Subject: [PATCH 1/5] fix: reduce silent unit test output Co-authored-by: WillBooster (Codex CLI) --- packages/shared-lib-node/src/env.ts | 16 ++++++++-- packages/shared-lib-node/src/index.ts | 1 + packages/wb/src/commands/test.ts | 17 ++++++++++- packages/wb/src/project.ts | 8 +++-- packages/wb/src/scripts/run.ts | 39 +++++++++++++++++++++---- packages/wb/src/sharedOptionsBuilder.ts | 3 ++ 6 files changed, 71 insertions(+), 13 deletions(-) diff --git a/packages/shared-lib-node/src/env.ts b/packages/shared-lib-node/src/env.ts index d20ec398f..68f5833aa 100644 --- a/packages/shared-lib-node/src/env.ts +++ b/packages/shared-lib-node/src/env.ts @@ -34,6 +34,10 @@ export const yargsOptionsBuilderForEnv = { type: 'string', default: '.env.example', }, + 'quiet-env': { + description: 'Suppress .env file loading information.', + type: 'boolean', + }, verbose: { description: 'Whether to show verbose information', type: 'boolean', @@ -77,7 +81,8 @@ export function readEnvironmentVariables( ); } envPaths = envPaths.filter((envPath) => fs.existsSync(envPath)).map((envPath) => path.relative(cwd, envPath)); - if (argv.verbose) { + const shouldSuppressOutput = shouldSuppressEnvironmentOutput(argv); + if (argv.verbose && !shouldSuppressOutput) { console.info(`WB_ENV: ${process.env.WB_ENV}, NODE_ENV: ${process.env.NODE_ENV}`); console.info('Reading env files:', envPaths.join(', ')); } @@ -93,11 +98,11 @@ export function readEnvironmentVariables( } } envPathAndLoadedEnvVarNames.push([envPath, keys]); - if (argv.verbose && keys.length > 0) { + if (argv.verbose && !shouldSuppressOutput && keys.length > 0) { console.info(`Read ${keys.length} environment variables from ${envPath}`); } } - if (!argv.verbose) { + if (!argv.verbose && !shouldSuppressOutput) { console.info( `Read env files: ${envPathAndLoadedEnvVarNames.map(([envPath, keys]) => (keys.length > 0 ? `${envPath} (${keys.join(', ')})` : envPath)).join(', ') || 'nothing'}` ); @@ -113,6 +118,11 @@ export function readEnvironmentVariables( return [expand({ parsed: envVars, processEnv: {} }).parsed ?? envVars, envPathAndLoadedEnvVarNames]; } +export function shouldSuppressEnvironmentOutput(argv: EnvReaderOptions): boolean { + const outputOptions = argv as EnvReaderOptions & { quietEnv?: boolean; silent?: boolean }; + return outputOptions.quietEnv === true || outputOptions.silent === true; +} + /** * This function read environment variables from `.env` files and assign them in `process.env`. * */ diff --git a/packages/shared-lib-node/src/index.ts b/packages/shared-lib-node/src/index.ts index 0b388c053..74a3d1f91 100644 --- a/packages/shared-lib-node/src/index.ts +++ b/packages/shared-lib-node/src/index.ts @@ -2,6 +2,7 @@ export { readEnvironmentVariables, readAndApplyEnvironmentVariables, removeNpmAndYarnEnvironmentVariables, + shouldSuppressEnvironmentOutput, yargsOptionsBuilderForEnv, } from './env.js'; export type { EnvReaderOptions } from './env.js'; diff --git a/packages/wb/src/commands/test.ts b/packages/wb/src/commands/test.ts index 1f427622d..8384047c4 100644 --- a/packages/wb/src/commands/test.ts +++ b/packages/wb/src/commands/test.ts @@ -131,7 +131,7 @@ export async function test(argv: TestCommandArgv): Promise { const targets = unitTargets.length > 0 ? unitTargets : defaultUnitTargets.length > 0 ? defaultUnitTargets : undefined; const unitArgv = { ...argv, targets }; - await runTestCommand(scripts.testUnit(project, unitArgv), project, argv, { timeout: argv.unitTimeout }); + await runUnitTestCommand(scripts.testUnit(project, unitArgv), project, argv, { timeout: argv.unitTimeout }); } // Skip e2e tests if not needed or no e2e directory exists if (!shouldRunE2e || !fs.existsSync(path.join(project.dirPath, 'test', 'e2e'))) { @@ -283,6 +283,21 @@ function runTestCommand( }); } +function runUnitTestCommand( + script: string, + project: Project, + argv: Parameters[2], + options: Parameters[3] = {} +): Promise { + return runWithSpawn(script, project, argv, { + ...options, + omitSilentStart: true, + printSilentOutputOnFailureOnly: true, + silentProgressIntervalMs: 10_000, + silentSuccessMessage: 'Unit tests passed.', + }); +} + function dedupeNoisyTestOutput(output: string): string { const recentPrintedLines: string[] = []; const recentPrintedLineSet = new Set(); diff --git a/packages/wb/src/project.ts b/packages/wb/src/project.ts index 05d6b1cab..2bd59b4e7 100644 --- a/packages/wb/src/project.ts +++ b/packages/wb/src/project.ts @@ -2,7 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import type { EnvReaderOptions } from '@willbooster/shared-lib-node/src'; -import { readEnvironmentVariables } from '@willbooster/shared-lib-node/src'; +import { readEnvironmentVariables, shouldSuppressEnvironmentOutput } from '@willbooster/shared-lib-node/src'; import { memoizeOne } from 'at-decorators'; import { globby } from 'globby'; import type { PackageJson } from 'type-fest'; @@ -110,8 +110,10 @@ export class Project { if (!this.loadEnv) return process.env; const [envVars, envPathAndLoadedEnvVarCountPairs] = readEnvironmentVariables(this.argv, this.dirPath); - for (const [envPath, count] of envPathAndLoadedEnvVarCountPairs) { - console.info(`Loaded ${count} environment variables from ${envPath}`); + if (!shouldSuppressEnvironmentOutput(this.argv)) { + for (const [envPath, count] of envPathAndLoadedEnvVarCountPairs) { + console.info(`Loaded ${count} environment variables from ${envPath}`); + } } return { ...envVars, ...process.env }; } diff --git a/packages/wb/src/scripts/run.ts b/packages/wb/src/scripts/run.ts index 01a2dcd2b..95d4fce47 100644 --- a/packages/wb/src/scripts/run.ts +++ b/packages/wb/src/scripts/run.ts @@ -11,8 +11,12 @@ interface Options { exitIfFailed?: boolean; onSignal?: (signal: NodeJS.Signals | null) => void; forceColor?: boolean; + omitSilentStart?: boolean; processSilentOutput?: (output: string) => string; printRawOutput?: boolean; + printSilentOutputOnFailureOnly?: boolean; + silentProgressIntervalMs?: number; + silentSuccessMessage?: string; timeout?: number; } @@ -32,7 +36,9 @@ export async function runWithSpawn( opts: Options = defaultOptions ): Promise { const normalizedScript = normalizeScript(script, project); - printStart(normalizedScript.printable, project, argv.silent ? 'Command' : 'Start'); + if (!(argv.silent && opts.omitSilentStart)) { + printStart(normalizedScript.printable, project, argv.silent ? 'Command' : 'Start'); + } if (argv.verbose) { printStart(normalizedScript.runnable, project, 'Start (raw)', true); } @@ -41,7 +47,17 @@ export async function runWithSpawn( return 0; } - const shouldProcessSilentOutput = Boolean(argv.silent && opts.processSilentOutput); + const shouldProcessSilentOutput = Boolean( + argv.silent && (opts.processSilentOutput ?? opts.printSilentOutputOnFailureOnly) + ); + let wroteSilentProgress = false; + const progressTimer = + argv.silent && opts.silentProgressIntervalMs + ? setInterval(() => { + process.stdout.write('.'); + wroteSilentProgress = true; + }, opts.silentProgressIntervalMs) + : undefined; const ret = await spawnAsync(normalizedScript.runnable, undefined, { cwd: project.dirPath, env: configureEnv(project.env, opts), @@ -54,17 +70,28 @@ export async function runWithSpawn( printingStderr: argv.silent && !shouldProcessSilentOutput, omitBlankLinesWhilePrinting: argv.silent, verbose: argv.verbose, + }).finally(() => { + if (progressTimer) { + clearInterval(progressTimer); + } }); - if (shouldProcessSilentOutput) { - const output = opts.processSilentOutput?.(ret.stdout).trim(); + const exitCode = ret.status ?? 1; + if (wroteSilentProgress) { + process.stdout.write('\n'); + } + if (shouldProcessSilentOutput && (!opts.printSilentOutputOnFailureOnly || exitCode !== 0)) { + const output = (opts.processSilentOutput ? opts.processSilentOutput(ret.stdout) : ret.stdout).trim(); if (output) { process.stdout.write(output); process.stdout.write('\n'); } } + if (argv.silent && exitCode === 0 && opts.silentSuccessMessage) { + console.info(chalk.green(opts.silentSuccessMessage)); + } opts.onSignal?.(ret.signal); - printFinishedAndExitIfNeeded(normalizedScript.printable, ret.status, opts, { silentSuccess: argv.silent }); - return ret.status ?? 1; + printFinishedAndExitIfNeeded(normalizedScript.printable, exitCode, opts, { silentSuccess: argv.silent }); + return exitCode; } export function runWithSpawnInParallel( diff --git a/packages/wb/src/sharedOptionsBuilder.ts b/packages/wb/src/sharedOptionsBuilder.ts index eee66096e..a1b4d49ff 100644 --- a/packages/wb/src/sharedOptionsBuilder.ts +++ b/packages/wb/src/sharedOptionsBuilder.ts @@ -36,6 +36,9 @@ export function buildEnvReaderOptionArgs(argv: EnvReaderOptions): string[] { } } } + if ((argv as EnvReaderOptions & { silent?: boolean }).silent === true && getOptionValue(argv, 'quiet-env') !== true) { + args.push('--quiet-env'); + } return args; } From b95108d55fb1ccb697fe188cfa86c7343147e0a2 Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Fri, 8 May 2026 23:48:33 +0900 Subject: [PATCH 2/5] fix: preserve explicit quiet env setting Co-authored-by: WillBooster (Codex CLI) --- packages/wb/src/sharedOptionsBuilder.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/wb/src/sharedOptionsBuilder.ts b/packages/wb/src/sharedOptionsBuilder.ts index a1b4d49ff..082c1ce3f 100644 --- a/packages/wb/src/sharedOptionsBuilder.ts +++ b/packages/wb/src/sharedOptionsBuilder.ts @@ -36,7 +36,10 @@ export function buildEnvReaderOptionArgs(argv: EnvReaderOptions): string[] { } } } - if ((argv as EnvReaderOptions & { silent?: boolean }).silent === true && getOptionValue(argv, 'quiet-env') !== true) { + if ( + (argv as EnvReaderOptions & { silent?: boolean }).silent === true && + getOptionValue(argv, 'quiet-env') === undefined + ) { args.push('--quiet-env'); } return args; From 963cc7da0b79576a1539d8e253dc26f2c079b9b3 Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Fri, 8 May 2026 23:51:14 +0900 Subject: [PATCH 3/5] fix: respect explicit env output preference Co-authored-by: WillBooster (Codex CLI) --- packages/shared-lib-node/src/env.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared-lib-node/src/env.ts b/packages/shared-lib-node/src/env.ts index 68f5833aa..03670d079 100644 --- a/packages/shared-lib-node/src/env.ts +++ b/packages/shared-lib-node/src/env.ts @@ -120,7 +120,7 @@ export function readEnvironmentVariables( export function shouldSuppressEnvironmentOutput(argv: EnvReaderOptions): boolean { const outputOptions = argv as EnvReaderOptions & { quietEnv?: boolean; silent?: boolean }; - return outputOptions.quietEnv === true || outputOptions.silent === true; + return outputOptions.quietEnv === true || (outputOptions.quietEnv !== false && outputOptions.silent === true); } /** From f6074a8d2b1255352ddd2af6f120815582c9c5fe Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Fri, 8 May 2026 23:54:12 +0900 Subject: [PATCH 4/5] fix: show silent unit dry-run command Co-authored-by: WillBooster (Codex CLI) --- packages/wb/src/scripts/run.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wb/src/scripts/run.ts b/packages/wb/src/scripts/run.ts index 95d4fce47..fa5f1c275 100644 --- a/packages/wb/src/scripts/run.ts +++ b/packages/wb/src/scripts/run.ts @@ -36,7 +36,7 @@ export async function runWithSpawn( opts: Options = defaultOptions ): Promise { const normalizedScript = normalizeScript(script, project); - if (!(argv.silent && opts.omitSilentStart)) { + if (!(argv.silent && opts.omitSilentStart && !argv.dryRun)) { printStart(normalizedScript.printable, project, argv.silent ? 'Command' : 'Start'); } if (argv.verbose) { From c8718bf5a1c474d44b2912b13b1710db25e7cc24 Mon Sep 17 00:00:00 2001 From: "Sakamoto, Kazunori" Date: Sat, 9 May 2026 00:03:21 +0900 Subject: [PATCH 5/5] fix: preapprove types packages in yarnrc Co-authored-by: WillBooster (Codex CLI) --- packages/wbfy/src/generators/yarnrc.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/wbfy/src/generators/yarnrc.ts b/packages/wbfy/src/generators/yarnrc.ts index e22ffb708..3b57188a2 100644 --- a/packages/wbfy/src/generators/yarnrc.ts +++ b/packages/wbfy/src/generators/yarnrc.ts @@ -88,6 +88,7 @@ export async function generateYarnrcYml(config: PackageConfig): Promise { // To deal with CVE like https://nextjs.org/blog/CVE-2025-66478 'next', '@next/*', + '@types/*', '@typescript/*', '@oxfmt/*', '@oxlint/*',