Skip to content

Build: Migrate sandbox generation to Yarn 4 with 7d npmMinimalAgeGate#34850

Closed
valentinpalkovic wants to merge 2 commits into
storybookjs:nextfrom
valentinpalkovic:valentin/sandbox-yarn4-migration
Closed

Build: Migrate sandbox generation to Yarn 4 with 7d npmMinimalAgeGate#34850
valentinpalkovic wants to merge 2 commits into
storybookjs:nextfrom
valentinpalkovic:valentin/sandbox-yarn4-migration

Conversation

@valentinpalkovic
Copy link
Copy Markdown
Contributor

Stacked on top of #34849 (Phase 1 — publish-time .yarnrc.yml filter).
Until that PR merges, the diff here includes both Phase 1 and Phase 2 commits. Phase 2-specific commits are everything under `valentin/sandbox-yarn4-migration` branch since `valentin/sandbox-publish-yarnrc-filter`.

Summary

Make the before-storybook published sandbox tree:

  1. Use Yarn 4 (flip setupYarn default classicberry).
  2. Carry an authoritative yarn.lock that consumers install from.
  3. Resolve against npmMinimalAgeGate: 7d so freshly-published (potentially malicious) versions are skipped at publish time AND on any later yarn install a consumer runs.

This delivers the architectural fix the team agreed on: sandboxes become reproductions of a known good state at a single point in time, rather than "latest at consumption time."

What this PR does

  • scripts/sandbox/utils/yarn.ts:
    • setupYarn default version flips 'classic''berry'.
    • New refreshBeforeStorybookLockfile({ cwd, debug }) helper.
  • scripts/sandbox/generate.ts:
    • After localizeYarnConfigFiles and before moveDir, run refreshBeforeStorybookLockfile for non-pnp templates.
    • Wrapped in try/catch — on failure, the template's original state is shipped with a CI warning.

The refresh helper:

  1. Drops any non-Yarn-4 lockfile (legacy yarn.lock, package-lock.json, pnpm-lock.yaml).
  2. Re-runs yarn set version berry to restore the packageManager field that the template removed.
  3. yarn config set nodeLinker node-modules + yarn config set npmMinimalAgeGate 10080.
  4. yarn install --mode=update-lockfile + yarn up '*' --mode=update-lockfile to produce the lockfile.
  5. Uses YARN_ENABLE_IMMUTABLE_INSTALLS=false via env (not .yarnrc.yml) to keep consumer config clean.

What's intentionally NOT in this PR

  • sbInit --package-manager=YARN2 flag flip — deferred. The after-storybook flow still uses the existing YARN1 path. This avoids coupling the before-storybook reproducibility fix with the after-storybook install-path migration.
  • installYarn2 destructive-rm rewrite — deferred. Still uses Build: Disable yarn npmMinimalAgeGate inside sandboxes #34846's gate=0 for the local CI sandbox build path.
  • AGENTS.md updates on contributor minimum yarn version — follow-up if needed.

Test plan

  • yarn nx run scripts:check passes
  • Phase 1 sanitizer unit tests still green
  • Sandbox generation CI passes on a sampled subset
  • Manually clone a generated sandbox and verify yarn install produces the lockfile-pinned tree

🤖 Generated with Claude Code

Strip Verdaccio / host-local Yarn settings (npmRegistryServer pointing at
localhost:6001, unsafeHttpWhitelist, enableImmutableInstalls, logFilters,
npmMinimalAgeGate, etc.) from `*\/after-storybook\/.yarnrc.yml` before the
sandbox tree is pushed to `storybookjs/sandboxes`. Also drop install
artifacts (`.yarn/cache`, `node_modules`, `.pnp.cjs`, `storybook-static`)
from the published copy.

`before-storybook/.yarnrc.yml` is intentionally untouched — its contents
are the user-facing Yarn setup we want consumers to reproduce.

Includes a regression test asserting the exact STRIP_KEYS / EXCLUDE_GLOBS
contracts so mutations require a deliberate review.
Flip the default sandbox-generation Yarn version from classic to berry so
the `before-storybook` directory ships with a Yarn 4 lockfile. After the
template's CLI finishes bootstrapping, drop any non-Yarn-4 lockfile,
re-pin `packageManager`, set `npmMinimalAgeGate` to 7 days, and run
`yarn install` + `yarn up '*'` in `--mode=update-lockfile` to produce a
deterministic dependency tree pinned to the newest non-quarantined
versions matching the template's package.json ranges.

`YARN_ENABLE_IMMUTABLE_INSTALLS=false` is passed via env, not written to
`.yarnrc.yml`, so the consumer-facing config stays clean.

Consumers who clone the published sandbox now reproduce the exact
dependency tree at publish time, and freshly-published (potentially
malicious) dependency versions are blocked by yarn's age gate on any
subsequent local install.

Failure of the refresh step is non-fatal — the template's default state
is shipped instead, with a CI warning surfaced.
@valentinpalkovic
Copy link
Copy Markdown
Contributor Author

Reopening from upstream branch (correctly stacked); see follow-up PR.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant