Repository navigation
feat(config)!: unify per-ecosystem enablement — detection enables, config opts out - #563
Conversation
|
Standing release PR: #555 · v0.40.1 queued (4 packages) · open 3h 29m · ✅ ready to merge Release Preview — v0.40.0 → v0.41.0
These changes will be added to the release PR (#555) when merged: Changelogmonorepo 0.40.0 → 0.41.0
After merge — predicted release
Updated automatically by ReleaseKit |
61f1373 to
037e4db
Compare
|
Both P1s addressed in the latest push:
Verified the single/async (packageProcessor) and group strategies track members independently of which manifest was written, so they don't have the same class of bug. Added a sync-strategy regression test for the npm-disabled hybrid case. |
037e4db to
65e6237
Compare
65e6237 to
046a069
Compare
|
Addressed the follow-up P1 ( |
…nfig opts out Every detected ecosystem is versioned and, where publishing is configured and authenticated, published by default; explicit config is only ever an opt-out. - publish.cargo.enabled / publish.pub.enabled now default true (was false) - add version.npm.enabled (default true) so package.json handling is opt-out-able like Cargo.toml/pubspec.yaml; wired into every version-stage package.json write - keep Cargo.toml (root and hybrid packages) versioned when npm handling is disabled — the npm gate must not skip or abandon cargo updates - reframe the enabled describe() text as opt-outs; regenerate schema + config docs Default-on is safe: an enabled registry that finds zero targets no-ops, and publishing without credentials fails/skips at the auth check. Refs #554 BREAKING CHANGE: crates.io and pub.dev publishing now default on for detected packages. Set publish.cargo.enabled / publish.pub.enabled to false to opt out (version and tag only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011i1KvoyxATU3bz5V5Sf1Xu
046a069 to
56166dd
Compare
|
Addressed the single-package abandonment P1: the root block now records |
What
Make ecosystem enablement symmetric: detection enables, config opts out. Every ecosystem ReleaseKit detects is versioned and — where publishing is configured and authenticated — published by default. Explicit config is only ever an opt-out.
version.*.enabledtrue(new toggle)truetruepublish.*.enabledtruetrue(wasfalse)true(wasfalse)Changes
publish.cargo.enabled/publish.pub.enableddefaultfalse→true.version.npm.enabled(defaulttrue) so package.json handling is opt-out-able like Cargo.toml/pubspec.yaml, wired into every version-stage package.json write (packageProcessor, sync/single strategies, group + ungrouped-member strategies, and the root lockstep bump).enabled.describe()text as opt-outs; regeneratereleasekit.schema.json+docs/configuration.md(schema:checkpasses).docs/migration.md.Two independent opt-outs per ecosystem are kept deliberately:
version.<eco>.enabled: false→ don't touch this ecosystem's manifests at all.publish.<eco>.enabled: false→ version and tag, but don't publish.Why it's safe
Default-on relies on the pipeline's existing structural fail-safes (
publish/src/registry/dispatcher.ts): an enabled registry that discovers zero targets no-ops, and publishing without credentials fails/skips at the auth check. This repo (npm-only) is unaffected — the cargo/pub flip no-ops with no Rust/Dart packages.Tests
version.npm.enableddefaults true and accepts an opt-out.version.npm.enabled: falseleaves package.json untouched while Cargo.toml is still versioned.Breaking change
crates.io and pub.dev publishing now default on for detected packages. Set
publish.cargo.enabled/publish.pub.enabledtofalseto opt out (version and tag only).Closes #554
Greptile Summary
This PR makes ecosystem enablement symmetric across npm, Cargo, and pub.dev: every detected ecosystem is versioned and published by default, with explicit config only ever serving as an opt-out. It adds
version.npm.enabled(defaulttrue) to make package.json handling opt-out-able like Cargo.toml/pubspec.yaml, flipspublish.cargo.enabledandpublish.pub.enabledfromfalsetotrue, and wires the new npm gate into every version-stage code path.version.npm.enabledgate added to sync strategy (root + per-package loop), single strategy, async/packageProcessor, group strategy (grouped members + ungrouped packages) — all paths consistently checkconfig.npm?.enabled !== falsebefore writing package.json.processedPaths.add(rootPkgPath)is now unconditional (single-package dedup), root Cargo.toml update runs outside the npm guard (no silent skip), and hybrid-package Cargo.toml changes are tracked inupdatedPackageswhen npm is disabled (no abandoned writes).Confidence Score: 5/5
Safe to merge — the three logic bugs called out in prior review rounds are all correctly addressed, and the new npm-gate is applied consistently across every version-stage code path.
The sync-strategy restructuring (unconditional processedPaths.add, root Cargo outside the npm gate, hybrid-package Cargo tracking) is well-reasoned and covered by two targeted new tests. Schema, docs, and type changes are internally consistent. The only gap is a documentation omission rather than a logic defect.
No files require special attention; the documentation gap (missing migration note) is the only remaining item.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Version stage triggered] --> B{packages.root defined?} B -->|yes| C{npm enabled?\nconfig.npm?.enabled !== false} B -->|no| G C -->|yes + package.json exists| D[updatePackageVersion root package.json\nupdatedPackages.push root] C -->|no| E[skip package.json\nlog debug] D --> F[processedPaths.add rootPkgPath\nunconditional] E --> F F --> G[updateCargoFiles root dir\nindependent of npm gate] G --> H{rootCargoFiles.length > 0\nand root not yet tracked?} H -->|yes| I[updatedPackages.push root] H -->|no| J I --> J[for each pkg in packages] J --> K{processedPaths.has\npkg package.json path?} K -->|yes - single-pkg dedup| L[continue - skip] K -->|no| M{npm enabled?} M -->|yes + file exists| N[updatePackageVersion pkg package.json\nupdatedPackages.push pkg.name\nprocessedPaths.add] M -->|no| O[skip package.json\nlog debug] N --> P[updateCargoFiles pkg dir] O --> P P --> Q{no package.json OR npm disabled\nAND pkgCargoFiles.length > 0?} Q -->|yes| R[updatedPackages.push pkg.name] Q -->|no| S R --> S{more packages?} S -->|yes| J S -->|no| T{updatedPackages empty?} T -->|yes| U[early return - no packages updated] T -->|no| V[proceed to tag + commit]%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD A[Version stage triggered] --> B{packages.root defined?} B -->|yes| C{npm enabled?\nconfig.npm?.enabled !== false} B -->|no| G C -->|yes + package.json exists| D[updatePackageVersion root package.json\nupdatedPackages.push root] C -->|no| E[skip package.json\nlog debug] D --> F[processedPaths.add rootPkgPath\nunconditional] E --> F F --> G[updateCargoFiles root dir\nindependent of npm gate] G --> H{rootCargoFiles.length > 0\nand root not yet tracked?} H -->|yes| I[updatedPackages.push root] H -->|no| J I --> J[for each pkg in packages] J --> K{processedPaths.has\npkg package.json path?} K -->|yes - single-pkg dedup| L[continue - skip] K -->|no| M{npm enabled?} M -->|yes + file exists| N[updatePackageVersion pkg package.json\nupdatedPackages.push pkg.name\nprocessedPaths.add] M -->|no| O[skip package.json\nlog debug] N --> P[updateCargoFiles pkg dir] O --> P P --> Q{no package.json OR npm disabled\nAND pkgCargoFiles.length > 0?} Q -->|yes| R[updatedPackages.push pkg.name] Q -->|no| S R --> S{more packages?} S -->|yes| J S -->|no| T{updatedPackages empty?} T -->|yes| U[early return - no packages updated] T -->|no| V[proceed to tag + commit]Comments Outside Diff (3)
packages/version/src/core/versionStrategies.ts, line 226-240 (link)The
updateCargoFilescall for the workspace root is nested inside the npm-enabled guard, so settingversion.npm.enabled: falseon a hybrid monorepo (npm + Rust, sync mode) silently skips the rootCargo.tomlupdate entirely. Compare the per-package loop directly below: there,updateCargoFilesruns after the npm block unconditionally. The fix is to move the rootupdateCargoFilescall (and itsfiles.push) outside the npm gate, matching the pattern used for every workspace package in the same function.packages/version/src/core/versionStrategies.ts, line 277-293 (link)When
version.npm.enabled: falseon a sync-mode hybrid monorepo (packages have bothpackage.jsonandCargo.toml),pkgCargoFilesare written to disk and pushed intofiles, butupdatedPackagesnever receives the package name — the npm path skips it, and the cargo fallback at line 282 only fires when!fs.existsSync(packageJsonPath)(i.e. pure-Rust packages without apackage.json). WithupdatedPackagesempty, thereturnat line 292 fires and the already-written Cargo.toml changes are never committed, leaving the working tree dirty.Fix: extend the cargo tracking to also cover hybrid packages when npm is disabled:
packages/version/src/core/versionStrategies.ts, line 258-261 (link)processedPathsdedup broken for single-package repos when npm is disabledprocessedPathsis populated only when npm is enabled and apackage.jsonis written (line 275). Whenversion.npm.enabled: false, the set stays empty, soprocessedPaths.has(packageJsonPath)is alwaysfalseand thecontinueguard never fires.For a single-package repo configured with
sync: true— wherepackages.root === packages.packages[0].dir—updateCargoFilesis called twice: once unconditionally for the root (line 240) and again in the loop without being short-circuited. The result is the rootCargo.tomlpath appearing twice infilesandupdatedPackagesbeing inflated by one. The comment on line 221 (// Track processed paths to avoid duplicates in single-package repos) acknowledges this invariant, but it no longer holds when npm versioning is opted out.The simplest fix is to populate
processedPathswithpackageJsonPathin the npm-disabled branch (line 265) so the loop still skips the root-as-package path, even though nopackage.jsonwas written.Reviews (8): Last reviewed commit: "feat(config)!: unify per-ecosystem enabl..." | Re-trigger Greptile