fix: prevent multi-mode from publishing monorepo workspace roots#48
Conversation
…-2056] Workspace roots (package.json with `workspaces` field) are almost never directly publishable — they lack bin/main/exports and publishing them creates broken npm packages (see LAC-2055 where lacy's root was published instead of packages/lacy/). - discover: flag workspace roots as non-npm-publishable by default - multi: after loading config, re-derive hasNpm from config's npm targets so explicit npm.cwd/targets in shipx.config.ts can override discover - UI: show "(workspace)" instead of "(private)" for workspace roots Co-Authored-By: Paperclip <noreply@paperclip.ing>
There was a problem hiding this comment.
Code Review
This pull request enhances project discovery by identifying workspace roots and adjusting npm publishability logic accordingly. It also introduces a mechanism to re-derive the hasNpm flag based on custom configuration targets. The review feedback highlights the need to ensure consistency in workspace detection during this re-derivation, correctly update the project name for registry lookups, and clean up redundant path resolutions.
| const hasCustomTarget = config.npm.targets.some( | ||
| (t) => resolve(t.cwd) !== resolve(project.path), | ||
| ); | ||
| if (hasCustomTarget) { | ||
| project.hasNpm = config.npm.targets.some((t) => { | ||
| try { | ||
| const pkg = readJson(resolve(t.cwd, "package.json")); | ||
| return pkg.private !== true; | ||
| } catch { | ||
| return false; | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
The re-derivation logic for hasNpm should also check for the workspaces field to maintain consistency with the discovery phase and prevent accidental publishing of nested workspace roots. Additionally, when a custom target is used, the project.name should be updated to match the name of the package being published (taking the name from the first publishable target found). This ensures that reconcileRegistryVersion performs the registry lookup against the correct npm package rather than the monorepo root. Finally, the resolve calls at line 364 are redundant as these paths are already absolute.
| const hasCustomTarget = config.npm.targets.some( | |
| (t) => resolve(t.cwd) !== resolve(project.path), | |
| ); | |
| if (hasCustomTarget) { | |
| project.hasNpm = config.npm.targets.some((t) => { | |
| try { | |
| const pkg = readJson(resolve(t.cwd, "package.json")); | |
| return pkg.private !== true; | |
| } catch { | |
| return false; | |
| } | |
| }); | |
| } | |
| const hasCustomTarget = config.npm.targets.some( | |
| (t) => t.cwd !== project.path, | |
| ); | |
| if (hasCustomTarget) { | |
| project.hasNpm = config.npm.targets.some((t) => { | |
| try { | |
| const pkg = readJson(resolve(t.cwd, "package.json")); | |
| const isPublishable = pkg.private !== true && !pkg.workspaces; | |
| if (isPublishable && typeof pkg.name === "string") { | |
| project.name = pkg.name; | |
| } | |
| return isPublishable; | |
| } catch { | |
| return false; | |
| } | |
| }); | |
| } |
…e override, resolve cleanup [LAC-2056] - Check `workspaces` field in re-derivation to match discover-time logic - Update `project.name` from target package.json so reconcileRegistryVersion looks up the correct npm package instead of the monorepo root - Remove redundant `resolve()` calls — paths are already absolute from config Co-Authored-By: Paperclip <noreply@paperclip.ing>
|
Addressed Gemini's review in c3d350a:
|
Summary
Fixes [LAC-2056] — multi-mode was publishing monorepo workspace roots (package.json with
workspacesfield) directly to npm, creating broken packages with no bin/main/exports. This caused the lacy v1.8.12–v1.8.15 broken releases (LAC-2055).workspacesfield and flag them as non-npm-publishable by defaulthasNpmfrom config's actual npm targets — so projects withnpm.cwdornpm.targetsset in theirshipx.config.tspointing to a sub-package will still publish correctly(workspace)label instead of(private)for workspace root projects in the multi-select UITest plan
bun run typecheckpassesbun run buildsucceedsnode dist/cli.js --helpsmoke test worksshipx --multifrom~/repo/and verify monorepo workspace roots (likelacy) show(workspace)label and are not included in npm publish phasenpm: { cwd: "packages/foo" }inshipx.config.tscorrectly publishes from the sub-package