Skip to content

feat(workbench): rebase onto main#1110

Closed
gu-stav wants to merge 41 commits into
mainfrom
feat/workbench-rebase-2026-05-20
Closed

feat(workbench): rebase onto main#1110
gu-stav wants to merge 41 commits into
mainfrom
feat/workbench-rebase-2026-05-20

Conversation

@gu-stav
Copy link
Copy Markdown
Member

@gu-stav gu-stav commented May 20, 2026

Rebase of #907 (feat/workbench) onto current main. Opened as a separate branch so the result is reviewable before force-pushing back to feat/workbench.

Conflicts resolved

  • buildApp.ts / buildStudio.ts — main split buildApp/buildStudio into thin entry + internalBuild*. Plumbed federation through InternalBuildOptions so the federation guard and the prop passed to buildStaticFiles still work.
  • buildStaticFiles.ts / getViteConfig.ts — dropped workbench's local writeFavicons/readPackageUp imports; main moved writeFavicons/copyDir to @sanity/cli-build/_internal and replaced readPackageUp with getDefaultFaviconsPath.
  • cli-core/_exports/index.ts — kept both getLocalPackageVersion (moved into cli-core on main, refactor: Move getLocalPackageVersion to cli-core #1053) and getSanityConfigDir (added by workbench).
  • getViteConfig.test.ts — rewrote the "cannot resolve sanity package" test to mock getDefaultFaviconsPath rejecting, since the underlying readPackageUp call moved to @sanity/cli-build.
  • buildStaticFiles.test.ts / startStudioDevServer.test.ts — updated mocks to the new paths (@sanity/cli-build/_internal, @sanity/cli-core).
  • pnpm-lock.yaml — regenerated via pnpm install.

Validation

  • pnpm build
  • pnpm check:types
  • pnpm check:lint ✅ (0 errors)
  • pnpm test — only failures are the pre-existing TypeError: stream.getWindowSize is not a function flake in checkForUpdates.test.ts / users/list.test.ts, also reproducible on origin/main.

Once approved, the result can be force-pushed back to feat/workbench.


Note

Medium Risk
Touches CLI build and sanity dev orchestration paths, including Vite config generation and process/lockfile handling, which can affect local dev startup and build output across platforms.

Overview
Adds experimental federation support to the CLI config (federation.enabled) and plumbs it through build/dev flows.

When federation is enabled, buildStaticFiles switches to building the federation environment via Vite’s createBuilder (skipping runtime generation, static copies, favicons, and auto-updates import maps) while still applying user Vite config/plugins.

sanity dev is reworked to optionally start a singleton workbench dev server, bump the app/studio port when needed, wire reactRefreshHost, register running servers into a per-user registry/lock under ~/.sanity{-staging}/, and watch config/manifests for updates (with Windows PID-reuse detection). Also updates snapshot-release workflow tagging, bumps @sanity/federation, and adds a new federated-studio fixture plus extensive new tests.

Reviewed by Cursor Bugbot for commit fcf69a8. Bugbot is set up for automated code reviews on this repo. Configure here.

gu-stav and others added 30 commits May 20, 2026 09:18
Co-authored-by: Josh <joshua.ellis18@gmail.com>
fix(workbench): allow for a dynamic port (#830)
)

* feat(dev): forward CLI config organization id to workbench runtime

* chore: update auto-generated changeset for PR #905

---------

Co-authored-by: ecospark[bot] <ecospark[bot]@users.noreply.github.com>
Co-authored-by: Gustav Hansen <gustav.hansen@sanity.io>
…913)

Pass `reactRefreshHost` to `@vitejs/plugin-react` so federated Studio
modules connect their react-refresh preamble to the workbench host,
enabling component-level HMR across the module federation boundary.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(dev): disable strict ports for applications

* chore: update auto-generated changeset for PR #930

* fix: format

* fix: format

* chore: update tests

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(workbench): propagate staging env to workbench dev server

The workbench dev server was missing the `__SANITY_STAGING__` Vite
define that the app/studio dev servers receive via `getViteConfig`.
This meant `SANITY_INTERNAL_ENV=staging` had no effect on the
workbench client bundle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update auto-generated changeset for PR #964

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* fix(workbench): externalize sanity and @sanity/workbench

* chore: update auto-generated changeset for PR #971

* chore: exclude .github from oxfmt format check

Co-authored-by: Gustav Hansen <gu-stav@users.noreply.github.com>

* fix: revert update changeset

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Gustav Hansen <gu-stav@users.noreply.github.com>
Co-authored-by: Gustav Hansen <gu-stav@users.noreply.github.com>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
* feat(init): add promt for federation

* chore: update auto-generated changeset for PR #988

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* fix: types

* chore: update auto-generated changeset for PR #989

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(workbench): extend local application payloads

* fix: types

* fix: pr feedback

* chore: improve tests

* fix: concise

* fix: pr feedback
* feat(init): use `workbench` dist-tag for `sanity` package

* chore: update auto-generated changeset for PR #992

* test: add unit tests

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* fix(init): do not resolve dist tags

* chore: update auto-generated changeset for PR #1000

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* feat(dev): extract studio manifest and pass it for local applications

* chore: update auto-generated changeset for PR #997

* fix: rework to use manifests for both

* chore: update auto-generated changeset for PR #997

* fix: cleanup

* chore: share cache dir constant

* feat: extract manifest in background

* fix: path resolution on windows

* fix: pr feedback

* fix: pr feedback

---------

Co-Authored-By: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
joshuaellis and others added 10 commits May 20, 2026 09:25
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* fix(workbench): remove warmup for dependencies

* chore: update auto-generated changeset for PR #1047

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Adds `--federation` / `--no-federation` flag to `sanity init` and wires
it through `initAction` so the value is passed to `initApp` / `initStudio`
alongside the federation prompt added in #988.

The flag plumbing was previously part of the rebase merge that resolved
feat/workbench against main, and was dropped in the latest rebase.
* fix(workbench): prune stale lock files

* fix: add back

* chore: update auto-generated changeset for PR #1057

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…OTE_URL` (#1066)

* fix(workbench): throw error in invalid `SANITY_INTERNAL_WORKBENCH_REMOTE_URL`

* chore: update auto-generated changeset for PR #1066

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* fix(workbench): detect PID reuse on Windows via PowerShell

* chore: update auto-generated changeset for PR #1067

* chore(changeset): rewrite changeset summary as user-facing description

Co-authored-by: Rune Botten <runeb@users.noreply.github.com>

* chore: update auto-generated changeset for PR #1067

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Rune Botten <runeb@users.noreply.github.com>
After rebasing onto main:
- buildStudio.ts: plumb `federation` through `InternalBuildOptions` so the
  `!options.federation?.enabled` guard and the `federation` prop passed to
  `buildStaticFiles` resolve. Mirrors the same plumbing already done in
  buildApp.ts during the rebase.
- buildStaticFiles.test.ts: `copyDir`/`writeFavicons` moved to `@sanity/cli-build/_internal`
  in main's #1062 refactor — point the mock at the new path.
- startStudioDevServer.test.ts: `checkStudioDependencyVersions` moved to
  `@sanity/cli-build/_internal`; `getLocalPackageVersion` moved to
  `@sanity/cli-core` (main's #1062 and #1053). Update mocks accordingly.
@gu-stav gu-stav requested a review from a team as a code owner May 20, 2026 07:52
@claude
Copy link
Copy Markdown

claude Bot commented May 20, 2026

Claude finished @gu-stav's task in 5m 59s —— View job


Review

Both Bugbot findings are real. A couple of additional items below.

1. buildStaticFiles.ts federation path drops schemaExtraction — confirmed

packages/@sanity/cli/src/actions/build/buildStaticFiles.ts:76-87 calls getViteConfig without forwarding schemaExtraction, while the non-federation branch at lines 121–135 does. In getViteConfig.ts:138-149, schemaExtraction plugins live in sharedPlugins — which the federation branch does still spread (line 197) — so users who enable both federation.enabled and schemaExtraction.enabled will silently get no schema extraction during federated builds.

Note: Bugbot also flagged typegen, but typegen is never threaded through from buildStudio.ts/buildApp.ts to buildStaticFiles in either path — that's a pre-existing gap, not caused by this PR. If you fix schemaExtraction, consider plumbing typegen through at the same time so getViteConfig's typegen?.enabled branch can actually fire.

let viteConfig = await getViteConfig({
  basePath,
  cwd,
  entries,
  federation,
  isApp,
  minify,
  mode,
  outputDir,
  reactCompiler,
+ schemaExtraction,
  sourceMap,
})

2. .github/workflows/snapshot-release.yml — confirmed (but partly intentional)

  • Line 75: pnpm changeset publish --tag "workbench" hardcodes the tag and ignores the DIST_TAG env var set on line 77. The tag workflow_dispatch input is silently dead. The leading # TODO: revert to "snapshot" before the feature branch gets merged to main comment on line 13 suggests the hardcode is intentional during the feature-branch period, but the inert inputs.tag and DIST_TAG env var are misleading — either wire them up or drop them.
  • Lines 16–25: the forceBump input is also dead — no step references it.

I can't modify files under .github/workflows/ (App permissions), so flagging for manual fix.

3. initAction.ts:254 — federation defaults to true

let federation = flagOrDefault(options.federation, true)

Every new project opts into federation unless the user passes --no-federation. That's fine for the feature branch, but easy to miss when this is force-pushed back to feat/workbench / merged to main — please double-check the desired default before the eventual merge, alongside the snapshot-release workbench tag.

4. Changeset summary issues

Per the product-copy conventions, changeset summaries are user-facing copy. Several issues to fix:

  • .changeset/pr-988.md — typo: add promt for federationPrompt for federation during init
  • .changeset/pr-989.md — typo + describes internals: types for federation promt → drop or fold into pr-988
  • .changeset/bump-federation-alpha-6.md — describes an internal dep bump (bump @sanity/federation to 0.1.0-alpha.6). Either drop it or rephrase to a user-facing effect, since end users won't see this directly.
  • .changeset/pr-1028.md, pr-1042.md, pr-1047.md, pr-1057.md, pr-1066.md, pr-1067.md — bodies still contain the conventional-commit prefix (fix(workbench): / perf(workbench):). Strip the prefix; the summary should read as plain user-facing copy.
  • Several summaries start lowercase (apply user vite config…, extract manifests…, do not resolve dist tags, forward CLI project id…, propagate staging env…, use the same clock…, externalize sanity and @sanity/workbench). Capitalize the first letter for consistency with the others.
  • .changeset/pr-997.mdextract manifests for local applications is vague; consider what the user-visible effect is (e.g. "Workbench now sees inlined manifests for local studios/apps during dev").

5. Minor — acquireWorkbenchLock.updatePort ownership

devServerRegistry.ts:374-376: updatePort rewrites workbench.lock unconditionally. If the lock has been pruned and re-acquired by another process between acquireWorkbenchLock and updatePort (very narrow window — only if our process froze long enough for PID-reuse detection to evict us), we'd silently clobber the other process's lock contents. Probably not worth fixing given how narrow the window is, but worth a comment noting that updatePort assumes the lock is still ours.

6. Test coverage

Test coverage for the new dev/registry surface is comprehensive: devServerRegistry.test.ts, startWorkbenchDevServer.test.ts, startFederationRegistration.test.ts, startDevManifestWatcher.test.ts, plus the Windows integration file. Good.


Todos

  • Read PR diff and identify changes
  • Review key files (build, dev, federation)
  • Validate Cursor Bugbot findings (1 fully valid, 1 partially valid)
  • Check changesets
  • Post review
    · feat/workbench-rebase-2026-05-20

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

📦 Bundle Stats — @sanity/cli

Compared against main (5b4d3c08)

@sanity/cli

Metric Value vs main (5b4d3c0)
Internal (raw) 2.1 KB -
Internal (gzip) 799 B -
Bundled (raw) 10.97 MB -28 B, -0.0%
Bundled (gzip) 2.06 MB -8 B, -0.0%
Import time 798ms +16ms, +2.0%

bin:sanity

Metric Value vs main (5b4d3c0)
Internal (raw) 1023 B -
Internal (gzip) 486 B -
Bundled (raw) 9.84 MB -
Bundled (gzip) 1.77 MB -
Import time 2.26s +400ms, +21.5% ⚠️

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-core

Compared against main (5b4d3c08)

Metric Value vs main (5b4d3c0)
Internal (raw) 97.9 KB +1.8 KB, +1.8%
Internal (gzip) 23.1 KB +634 B, +2.7%
Bundled (raw) 21.63 MB +1.5 KB, +0.0%
Bundled (gzip) 3.43 MB +594 B, +0.0%
Import time 752ms -1ms, -0.2%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — create-sanity

Compared against main (5b4d3c08)

Metric Value vs main (5b4d3c0)
Internal (raw) 908 B -
Internal (gzip) 483 B -
Bundled (raw) 931 B -
Bundled (gzip) 491 B -
Import time ❌ ChildProcess denied: node -
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 007a278. Configure here.

- name: Publish snapshot packages
if: ${{ inputs.forceBump == 'false' }}
run: pnpm changeset publish --tag "$DIST_TAG" 2>&1 | tee publish-output.txt
run: pnpm changeset publish --tag "workbench" 2>&1 | tee publish-output.txt
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snapshot publish ignores user-provided dist-tag input

Medium Severity

The pnpm changeset publish command hardcodes --tag "workbench" instead of using the $DIST_TAG environment variable (which is still set from inputs.tag on the next line). Any workflow_dispatch invocation with a custom tag value will be silently ignored, and packages will always be published under the workbench dist-tag. The forceBump input definition also remains but all steps that referenced it were removed, making it dead configuration.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 007a278. Configure here.

outputDir,
reactCompiler,
sourceMap,
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Federation build path omits typegen config from vite

Low Severity

When federation?.enabled is true, the call to getViteConfig omits schemaExtraction and typegen options. Inside getViteConfig, these are part of sharedPlugins and are conditionally included based on ?.enabled. Since they default to undefined, the plugins are silently excluded from federation builds even when the user has enabled them in their CLI config. The non-federation path correctly forwards both options.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 007a278. Configure here.

@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 20, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​sanity/​federation@​0.1.0-alpha.7761008094100
Updatednpm/​turbo@​2.9.14 ⏵ 2.9.6100 +198 -28597100

View full report

@github-actions
Copy link
Copy Markdown
Contributor

Coverage Delta

File Statements
packages/@sanity/cli-core/src/config/cli/getCliConfig.ts 100.0% (±0%)
packages/@sanity/cli-core/src/config/cli/schemas.ts 100.0% (±0%)
packages/@sanity/cli-core/src/services/cliUserConfig.ts 100.0% (±0%)
packages/@sanity/cli-core/src/util/getSanityConfigDir.ts 66.7% (new)
packages/@sanity/cli/src/actions/build/buildApp.ts 95.3% (±0%)
packages/@sanity/cli/src/actions/build/buildStaticFiles.ts 97.6% (+ 1.0%)
packages/@sanity/cli/src/actions/build/buildStudio.ts 96.7% (±0%)
packages/@sanity/cli/src/actions/build/getViteConfig.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/build/writeSanityRuntime.ts 96.2% (+ 0.7%)
packages/@sanity/cli/src/actions/deploy/deployApp.ts 80.5% (±0%)
packages/@sanity/cli/src/actions/dev/devAction.ts 96.8% (- 3.2%)
packages/@sanity/cli/src/actions/dev/devServerRegistry.ts 93.8% (new)
packages/@sanity/cli/src/actions/dev/extractDevServerManifest.ts 20.0% (new)
packages/@sanity/cli/src/actions/dev/getDevServerConfig.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/dev/startAppDevServer.ts 100.0% (+ 13.0%)
packages/@sanity/cli/src/actions/dev/startDevManifestWatcher.ts 90.9% (new)
packages/@sanity/cli/src/actions/dev/startFederationRegistration.ts 100.0% (new)
packages/@sanity/cli/src/actions/dev/startStudioDevServer.ts 100.0% (+ 5.0%)
packages/@sanity/cli/src/actions/dev/startWorkbenchDevServer.ts 98.7% (new)
packages/@sanity/cli/src/actions/dev/writeWorkbenchRuntime.ts 100.0% (new)
packages/@sanity/cli/src/actions/init/bootstrapLocalTemplate.ts 89.7% (±0%)
packages/@sanity/cli/src/actions/init/bootstrapTemplate.ts 0.0% (±0%)
packages/@sanity/cli/src/actions/init/createAppCliConfig.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/init/createCliConfig.ts 100.0% (+ 50.0%)
packages/@sanity/cli/src/actions/init/createStudioConfig.ts 83.3% (±0%)
packages/@sanity/cli/src/actions/init/initAction.ts 97.2% (+ 0.1%)
packages/@sanity/cli/src/actions/init/initApp.ts 96.0% (±0%)
packages/@sanity/cli/src/actions/init/initStudio.ts 87.0% (±0%)
packages/@sanity/cli/src/actions/init/scaffoldTemplate.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/init/types.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/manifest/extractCoreAppManifest.ts 93.1% (new)
packages/@sanity/cli/src/actions/manifest/extractManifest.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/manifest/types.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/manifest/writeManifestFile.ts 100.0% (±0%)
packages/@sanity/cli/src/commands/dev.ts 100.0% (±0%)
packages/@sanity/cli/src/commands/init.ts 100.0% (±0%)
packages/@sanity/cli/src/commands/manifest/extract.ts 100.0% (±0%)
packages/@sanity/cli/src/prompts/init/federation.ts 100.0% (new)
packages/@sanity/cli/src/server/devServer.ts 94.1% (±0%)
packages/@sanity/cli/src/util/resolveReactStrictMode.ts 100.0% (new)

Comparing 40 changed files against main @ 5b4d3c089fce9266552982808bb5f2457fd50cf8

Overall Coverage

Metric Coverage
Statements 84.6% (+ 0.3%)
Branches 74.7% (+ 0.4%)
Functions 84.5% (+ 0.3%)
Lines 85.1% (+ 0.4%)

@gu-stav gu-stav closed this May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants