Skip to content

Fix CI: stop running pnpm install next to builds - #800

Merged
NullVoxPopuli merged 1 commit into
universal-ember:mainfrom
NullVoxPopuli-ai-agent:nvp/fix-ci-bin-race
Aug 16, 2026
Merged

Fix CI: stop running pnpm install next to builds#800
NullVoxPopuli merged 1 commit into
universal-ember:mainfrom
NullVoxPopuli-ai-agent:nvp/fix-ci-bin-race

Conversation

@NullVoxPopuli-ai-agent

Copy link
Copy Markdown
Contributor

Every PR run has been red since at least Aug 9 — including renovate lockfile-only PRs (example) — with Setup dying at:

ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL  Command failed with EACCES: ember-tsc --declaration --declarationDir declarations
spawn ember-tsc EACCES
Failed: @universal-ember/docs-support#build

What it actually was

_syncPnpm was pnpm install --frozen-lockfile, declared as a turbo task with dependsOn: ["^build"]. Both test-app#_syncPnpm and @universal-ember/docs-support#build are unblocked by ember-primitives#build, so turbo ran an install concurrently with a build.

An install rewrites every package's node_modules/.bin. Rollup's declarations plugin spawns ember-tsc out of exactly that directory. If the spawn lands mid-rewrite, the shim is either gone or written-but-not-yet-chmod'd — and spawning a non-executable file is EACCES.

Nothing about the code was involved, which is why lockfile-only PRs failed identically.

Measured, not guessed

Polling packages/docs-support/node_modules/.bin/ember-tsc during a full pnpm build --force, while separately recording the window in which docs-support's own ember-tsc process was alive:

shim unusable, inside that spawn window
main 27 samples
this branch 0 samples

Isolated, a single pnpm install --frozen-lockfile in test-app leaves the docs-support shim missing or -rw-rw-r-- for a good fraction of its run — 178 unusable samples in one install.

Note the race is probabilistic: pnpm build often passes locally on main. The CI runner loses it reliably.

The install was redundant

.npmrc already has sync-injected-deps-after-scripts[]=build, so pnpm re-syncs injected workspace deps itself after each package's build script. Verified directly: added a marker to ember-primitives/src, ran pnpm --filter ember-primitives build, and found the marker in test-app's injected copy — with no install of any kind. test-app's own sync script already says as much in its echo.

That sync runs inside the owning package's pnpm run build, which turbo waits on, so it cannot overlap a sibling's build. The remaining rewrites all land outside the spawn window — that is what the 0 above measures.

_syncPnpm was also how build, build:dev, test, test:ember, and start inherited ^build. That is now declared directly on each, which is what they meant.

Checked locally

  • pnpm build green, five consecutive --force runs
  • pnpm lint — 24/24 tasks
  • pnpm turbo test:ember --filter test-app — green apart from four <InViewport /> failures that are pre-existing on main here (IntersectionObserver under headless Chrome); they are unrelated to this change, and CI has never gotten far enough to say whether they fail on a runner too

The workflow's own pnpm i -f steps are untouched: they run sequentially, before the build, and race nothing.

🤖 Generated with Claude Code

@bolt-new-by-stackblitz

Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor
Project Preview URL
Docs https://c8b8006b.ember-primitives.pages.dev

Logs

`@universal-ember/docs-support#build` has been failing in CI with

    Command failed with EACCES: ember-tsc --declaration --declarationDir declarations
    spawn ember-tsc EACCES

`_syncPnpm` was `pnpm install --frozen-lockfile`, as a turbo task that
depended on `^build`. So `test-app#_syncPnpm` and
`@universal-ember/docs-support#build` were both unblocked by
`ember-primitives#build`, and turbo ran them at the same time. An
install rewrites every package's `node_modules/.bin`, and rollup's
declarations plugin spawns `ember-tsc` out of exactly that directory --
so the spawn could land on a shim that was momentarily absent, or
present but not yet chmod'd. That is the EACCES.

Measured, by polling the shim while building and recording when
docs-support's own `ember-tsc` was running:

              shim unusable, during that spawn window
    before                                         27
    after                                           0

The install was redundant anyway. `.npmrc` already carries
`sync-injected-deps-after-scripts[]=build`, so pnpm re-syncs injected
workspace deps itself after each package's `build` script -- verified by
adding a marker to ember-primitives' source, building only that package,
and finding the marker in test-app's injected copy with no install of
any kind. That sync runs inside the owning package's `pnpm run build`,
which turbo waits on, so it cannot overlap a sibling.

`_syncPnpm` was also how these tasks inherited `^build`. That is now
declared directly, which is what they meant.

Local, on this branch: `pnpm build` green five times over, `pnpm lint`
24/24. `turbo test:ember --filter test-app` is green apart from four
pre-existing `<InViewport />` failures that predate this branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@NullVoxPopuli
NullVoxPopuli merged commit ebcbe45 into universal-ember:main Aug 16, 2026
17 checks passed
@NullVoxPopuli
NullVoxPopuli deleted the nvp/fix-ci-bin-race branch August 16, 2026 19:14
@github-actions github-actions Bot mentioned this pull request Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants