Skip to content

fix(wbfy): stop calling Bun's spawnSync from wbfy and shared-lib-node's getGlobIgnore #1433

Closed

Description

@exKAZUu

Background and goal

Bun 1.4.2's synchronous spawns (spawnSync, execSync, execFileSync, Bun.spawnSync) can miss a child's exit and spin until something kills the child (oven-sh/bun#34069; fix pending in oven-sh/bun#40078). The cause is not specific to bun test. If the GC finalizes any object that owns a poll on the main event loop (a FileSink, a Subprocess, a socket) while a spawnSync call is waiting, the private loop of every later spawnSync call in that process loses count. #1432 moved the unit tests off synchronous spawns. That covers the wb CLI, which runs under Node, but not the code below, which still runs under Bun:

  • wbfy runs under Bun (bin/wbfy.js is #!/usr/bin/env bun) and makes all of its child-process calls synchronously (src/utils/spawnUtil.ts, fnoxToml.ts, gitattributes.ts, globalReleaseAgeGate.ts, githubUtil.ts, version.ts), next to async fetch calls and generators running concurrently. In wbfy's own unit suite these calls run in-process, inside the test workers: 125 calls taking 23 s per run, most of them npm show (34 s across 40 calls in an earlier measurement) and mise latest lookups.
  • getGlobIgnore of @willbooster/shared-lib-node (fix(wbfy,wb): ignore gitignored files when detecting repository contents #1431) runs git rev-parse and git ls-files through spawnSync/execFileSync. wbfy calls it from packageConfig.ts, packageJson.ts, typos.ts, and chakraToaster.ts (about 120 calls per wbfy unit run). wb slidev-check calls it as well.

A hang has not been observed in a real wbfy run so far. In a test worker, it would show up as a test killed at its timeout with killed 1 dangling process. In a wbfy run, the process would spin at 100% CPU with no timeout to end it.

Behavior

wbfy and getGlobIgnore launch child processes asynchronously (e.g., spawnAsync of @willbooster/shared-lib-node), so no synchronous spawn runs in a Bun process.

Acceptance criteria

  • A preload that counts the synchronous spawn APIs called inside test workers counts 0 calls from wbfy's and getGlobIgnore's code during bun wb test in packages/wbfy.
  • wbfy produces the same files as before on a sample repository, with and without workspaces.

Scope and non-goals

In scope: packages/wbfy/src and packages/shared-lib-node/src/glob.ts with their callers. Not in scope: readEnvironmentVariables and treeKill of @willbooster/shared-lib-node, whose callers depend on them being synchronous.

Design constraints

The conversion lets concurrently running generators interleave at every spawn. In src/index.ts, generators pushed to promises (e.g. generateTsconfig, generateOxlintConfig) run while the next package's generatePackageJson runs bun add and sort-package-json, which rewrite package.json. Check every such pair for reads of files another generator rewrites, or run those steps sequentially. Before this change, a synchronous spawn kept them from interleaving.

Verification

The probe count above, the existing wbfy unit tests, and a wbfy run on a WillBooster repository before and after with an identical diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions