@exadev/semantic-release-workspace 3.1.29 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -2
- package/dist/cli.js +334 -210
- package/dist/index.cjs +140 -21
- package/dist/index.d.cts +46 -3
- package/dist/index.d.ts +46 -3
- package/dist/index.js +140 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -677,6 +677,65 @@ function mustGet(map, key, what) {
|
|
|
677
677
|
return value;
|
|
678
678
|
}
|
|
679
679
|
//#endregion
|
|
680
|
+
//#region src/bundling.ts
|
|
681
|
+
/**
|
|
682
|
+
* Validates `bundlingPackages` against the workspace and works out, for every package, which workspace dependencies it bundles.
|
|
683
|
+
*
|
|
684
|
+
* A package named in `bundlingPackages` declares that its build inlines every workspace sibling it lists in `devDependencies`, so consumers never install those siblings from the registry and their code reaches consumers only through this package. When such a sibling releases, this package's release type is at least the sibling's shipped release type (a `feat` in the sibling releases this package as a minor), instead of the patch an ordinary dependency bump forces. Edges in `dependencies`, `peerDependencies` and `optionalDependencies` are installed by consumers, so they keep the forced patch.
|
|
685
|
+
*
|
|
686
|
+
* A private package's own release type never changes: it keeps the patch a dependency bump forces, so its versions and tags are exactly what they would be without `bundlingPackages`. What it gains is a carried release type (see `BundledDependencies.carried`), which lets a bundling package that lists only the private package still see the release type of a private sibling the private package depends on.
|
|
687
|
+
*
|
|
688
|
+
* `bundlingPackages` names public workspace packages whose builds bundle their workspace `devDependencies`; `undefined` or an empty list leaves every package's release type as it was. Returns one entry per package in the graph. Throws `ReleaseConfigurationError` when a name is not a package in the workspace, or names a private package, which never publishes and so ships nothing to consumers.
|
|
689
|
+
*/
|
|
690
|
+
function resolveBundledDependencies(graph, bundlingPackages) {
|
|
691
|
+
const bundling = new Set(bundlingPackages);
|
|
692
|
+
const unknown = [...bundling].filter((name) => !graph.packages.has(name));
|
|
693
|
+
if (unknown.length > 0) throw new ReleaseConfigurationError(`bundlingPackages names ${unknown.map((name) => `"${name}"`).join(", ")}, which ${unknown.length === 1 ? "is" : "are"} not a package in this workspace. Packages: ${[...graph.packages.keys()].join(", ")}.`);
|
|
694
|
+
const privateNames = [...bundling].filter((name) => mustGet(graph.packages, name, "package").private);
|
|
695
|
+
if (privateNames.length > 0) throw new ReleaseConfigurationError(`bundlingPackages names ${privateNames.map((name) => `"${name}"`).join(", ")}, which ${privateNames.length === 1 ? "is" : "are"} private. A private package is never published, so nothing it bundles reaches a consumer through it; list the public package whose build inlines it instead.`);
|
|
696
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
697
|
+
for (const [name, pkg] of graph.packages) {
|
|
698
|
+
const edges = mustGet(graph.dependencies, name, "dependency edges");
|
|
699
|
+
const releaseTypeFrom = new Set(edges.filter((edge) => bundling.has(name) && edge.field === "devDependencies").map((edge) => edge.dependency));
|
|
700
|
+
const carried = /* @__PURE__ */ new Set([...releaseTypeFrom, ...edges.filter((edge) => pkg.private && isPrivateRuntimeEdge(edge, graph)).map((edge) => edge.dependency)]);
|
|
701
|
+
resolved.set(name, {
|
|
702
|
+
releaseTypeFrom,
|
|
703
|
+
carried
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
return resolved;
|
|
707
|
+
}
|
|
708
|
+
function isPrivateRuntimeEdge(edge, graph) {
|
|
709
|
+
return edge.field !== "devDependencies" && mustGet(graph.packages, edge.dependency, "package").private;
|
|
710
|
+
}
|
|
711
|
+
//#endregion
|
|
712
|
+
//#region src/release-type.ts
|
|
713
|
+
/** Every release type semantic-release knows, highest first: the order `@semantic-release/commit-analyzer` itself ranks them in (its `lib/default-release-types.js`), so comparing two types here agrees with how the analyzer picks the highest type among a package's own commits. */
|
|
714
|
+
const RELEASE_TYPES_HIGHEST_FIRST = [
|
|
715
|
+
"major",
|
|
716
|
+
"premajor",
|
|
717
|
+
"minor",
|
|
718
|
+
"preminor",
|
|
719
|
+
"patch",
|
|
720
|
+
"prepatch",
|
|
721
|
+
"prerelease"
|
|
722
|
+
];
|
|
723
|
+
const KNOWN_RELEASE_TYPES = new Set(RELEASE_TYPES_HIGHEST_FIRST);
|
|
724
|
+
/** Whether `value` is one of semantic-release's release types. */
|
|
725
|
+
function isReleaseType(value) {
|
|
726
|
+
return typeof value === "string" && KNOWN_RELEASE_TYPES.has(value);
|
|
727
|
+
}
|
|
728
|
+
/** The highest of the given release types, ranked as `@semantic-release/commit-analyzer` ranks them, or `undefined` when `types` is empty. */
|
|
729
|
+
function highestReleaseType(types) {
|
|
730
|
+
let highest;
|
|
731
|
+
for (const type of types) if (highest === void 0 || RELEASE_TYPES_HIGHEST_FIRST.indexOf(type) < RELEASE_TYPES_HIGHEST_FIRST.indexOf(highest)) highest = type;
|
|
732
|
+
return highest;
|
|
733
|
+
}
|
|
734
|
+
/** `base`, raised to `other` when `other` is the higher release type; an `undefined` `other` leaves `base` as it is. */
|
|
735
|
+
function raiseReleaseType(base, other) {
|
|
736
|
+
return other === void 0 || RELEASE_TYPES_HIGHEST_FIRST.indexOf(base) <= RELEASE_TYPES_HIGHEST_FIRST.indexOf(other) ? base : other;
|
|
737
|
+
}
|
|
738
|
+
//#endregion
|
|
680
739
|
//#region src/dependency-bump-commit.ts
|
|
681
740
|
/**
|
|
682
741
|
* The commit message format for a cross-package dependency-range bump, shared between the code that writes it (`bumpDependents` in `release.ts`) and the code that reads it back (`createScopedPlugins` in `plugins.ts`).
|
|
@@ -686,26 +745,30 @@ function mustGet(map, key, what) {
|
|
|
686
745
|
const TRAILER_DEPENDENCY = "Bumped-Workspace-Dependency";
|
|
687
746
|
const TRAILER_VERSION = "Bumped-Workspace-Dependency-Version";
|
|
688
747
|
const TRAILER_RANGE = "Bumped-Workspace-Dependency-Range";
|
|
748
|
+
const TRAILER_RELEASE_TYPE = "Bumped-Workspace-Dependency-Release-Type";
|
|
689
749
|
/** Builds the full commit message (subject and trailer) for one dependency-range bump. */
|
|
690
750
|
function formatDependencyBumpMessage(info) {
|
|
691
751
|
return `${`chore(deps): bump ${info.dependency} to ${info.range} in ${info.dependent} [skip ci]`}\n\n${[
|
|
692
752
|
`${TRAILER_DEPENDENCY}: ${info.dependency}`,
|
|
693
753
|
`${TRAILER_VERSION}: ${info.version}`,
|
|
694
|
-
`${TRAILER_RANGE}: ${info.range}
|
|
754
|
+
`${TRAILER_RANGE}: ${info.range}`,
|
|
755
|
+
`${TRAILER_RELEASE_TYPE}: ${info.releaseType}`
|
|
695
756
|
].join("\n")}`;
|
|
696
757
|
}
|
|
697
758
|
/**
|
|
698
|
-
* Recovers the dependency bump a `formatDependencyBumpMessage` commit recorded, from its full git message (subject and body), or `undefined` if the message carries no such trailer.
|
|
759
|
+
* Recovers the dependency bump a `formatDependencyBumpMessage` commit recorded, from its full git message (subject and body), or `undefined` if the message carries no such trailer. The dependency, version and range lines must all be present for the commit to be treated as a bump commit at all -- a message missing even one is left alone rather than partially trusted. The release type line is optional, since bump commits written before it existed lack it; when it is absent or names no release type, `releaseType` is `undefined`.
|
|
699
760
|
*/
|
|
700
761
|
function parseDependencyBumpTrailer(message) {
|
|
701
762
|
const dependency = matchTrailerLine(message, TRAILER_DEPENDENCY);
|
|
702
763
|
const version = matchTrailerLine(message, TRAILER_VERSION);
|
|
703
764
|
const range = matchTrailerLine(message, TRAILER_RANGE);
|
|
704
765
|
if (dependency === void 0 || version === void 0 || range === void 0) return;
|
|
766
|
+
const releaseType = matchTrailerLine(message, TRAILER_RELEASE_TYPE);
|
|
705
767
|
return {
|
|
706
768
|
dependency,
|
|
707
769
|
version,
|
|
708
|
-
range
|
|
770
|
+
range,
|
|
771
|
+
releaseType: isReleaseType(releaseType) ? releaseType : void 0
|
|
709
772
|
};
|
|
710
773
|
}
|
|
711
774
|
function matchTrailerLine(message, key) {
|
|
@@ -743,10 +806,11 @@ const STEP_PLUGINS_THE_ORCHESTRATOR_OWNS = /* @__PURE__ */ new Set(["@semantic-r
|
|
|
743
806
|
*
|
|
744
807
|
* Both apply the same path scoping before delegating to the real `@semantic-release/commit-analyzer` and `@semantic-release/release-notes-generator`: the commit list semantic-release already fetched for the release range is filtered down to commits whose `git log --name-only` file list intersects the package's own directory, and only the filtered list reaches the standard plugin. Conventional-commit parsing and changelog formatting stay entirely inside the standard plugins.
|
|
745
808
|
*
|
|
746
|
-
* The `analyzeCommits` wrapper carries
|
|
809
|
+
* The `analyzeCommits` wrapper carries two additions beyond filtering. When the standard analyzer finds no releasable commits but a workspace dependency range of the package's has changed, it returns 'patch' anyway: a dependent whose only change is a dependency bump still needs a release for that range to reach the registry. And when a bumped dependency is one the package bundles (`scope.bundled.releaseTypeFrom`), the package's release type is raised to at least that dependency's shipped release type, whether or not the package has releasable commits of its own, because the dependency's change reaches consumers only inside this package. "Has changed" is read from two sources, merged: bumps recorded in memory earlier in the current run (`scope.bumps`), and bumps recorded in the package's own filtered commit history via the trailer `dependency-bump-commit.ts` writes and reads -- the latter is what lets a run that starts after a previous run already committed and pushed the bump (a crash recovery, or simply a later run) reach the same decision, rather than depending on state that existed only inside the process that made the commit.
|
|
747
810
|
*/
|
|
748
811
|
function createScopedPlugins(scope) {
|
|
749
812
|
let cached;
|
|
813
|
+
let carriedReleaseType;
|
|
750
814
|
async function commitsForPackage(context) {
|
|
751
815
|
const from = hasGitHead(context.lastRelease) ? context.lastRelease.gitHead : void 0;
|
|
752
816
|
if (cached === void 0) cached = {
|
|
@@ -764,13 +828,26 @@ function createScopedPlugins(scope) {
|
|
|
764
828
|
return {
|
|
765
829
|
async analyzeCommits(_pluginConfig, context) {
|
|
766
830
|
const commits = await commitsForPackage(context);
|
|
767
|
-
const
|
|
831
|
+
const analysed = await analyzeCommits(scope.analyzeCommitsConfig, {
|
|
768
832
|
...context,
|
|
769
833
|
commits
|
|
770
834
|
});
|
|
771
|
-
if (typeof
|
|
835
|
+
if (typeof analysed === "string" && !isReleaseType(analysed)) throw new ReleaseConfigurationError(`@semantic-release/commit-analyzer returned "${analysed}" for ${scope.pkg.name}, which is not a semantic-release release type.`);
|
|
836
|
+
const own = typeof analysed === "string" ? analysed : void 0;
|
|
772
837
|
const bumps = mergeDependencyBumps(scope.bumps.bumpsFor(scope.pkg.name), commits);
|
|
838
|
+
carriedReleaseType = releaseTypeOfBumps(bumps.filter((bump) => scope.bundled?.carried.has(bump.dependency) === true));
|
|
839
|
+
const bundledBumps = bumps.filter((bump) => scope.bundled?.releaseTypeFrom.has(bump.dependency) === true);
|
|
840
|
+
const fromBundled = releaseTypeOfBumps(bundledBumps);
|
|
841
|
+
if (own !== void 0) {
|
|
842
|
+
const type = raiseReleaseType(own, fromBundled);
|
|
843
|
+
if (type !== own) context.logger.log(`${scope.pkg.name} bundles ${describeBundledBumps(bundledBumps)}; raising its ${own} release to ${type}.`);
|
|
844
|
+
return type;
|
|
845
|
+
}
|
|
773
846
|
if (bumps.length === 0) return false;
|
|
847
|
+
if (fromBundled !== void 0) {
|
|
848
|
+
context.logger.log(`No releasable commits under ${scope.pkg.relativeDirectory}, but it bundles ${describeBundledBumps(bundledBumps)}; releasing it as ${fromBundled}.`);
|
|
849
|
+
return fromBundled;
|
|
850
|
+
}
|
|
774
851
|
context.logger.log(`No releasable commits under ${scope.pkg.relativeDirectory}, but ${bumps.length === 1 ? "a workspace dependency range changed" : `${String(bumps.length)} workspace dependency ranges changed`}; forcing a patch release.`);
|
|
775
852
|
return "patch";
|
|
776
853
|
},
|
|
@@ -788,9 +865,19 @@ function createScopedPlugins(scope) {
|
|
|
788
865
|
...bumps.map((bump) => describeDependencyBump(bump))
|
|
789
866
|
].join("\n");
|
|
790
867
|
return typeof notes === "string" ? `${notes}\n\n${section}` : section;
|
|
868
|
+
},
|
|
869
|
+
shippedReleaseType(releaseType) {
|
|
870
|
+
return raiseReleaseType(releaseType, carriedReleaseType);
|
|
791
871
|
}
|
|
792
872
|
};
|
|
793
873
|
}
|
|
874
|
+
/** The highest release type among `bumps`, or `undefined` when there are none. A bump with no recorded type counts as the patch it always forced. */
|
|
875
|
+
function releaseTypeOfBumps(bumps) {
|
|
876
|
+
return highestReleaseType(bumps.map((bump) => bump.releaseType ?? "patch"));
|
|
877
|
+
}
|
|
878
|
+
function describeBundledBumps(bumps) {
|
|
879
|
+
return bumps.map((bump) => `${bump.dependency} (released ${bump.version}, ${bump.releaseType ?? "patch"})`).join(", ");
|
|
880
|
+
}
|
|
794
881
|
/**
|
|
795
882
|
* Combines the bumps recorded in memory earlier in the current run with bumps recovered from the package's own filtered commit history (a bump commit from this run, already visible because it touches the package's own directory, or one left over from a previous run), de-duplicated by dependency name. The in-memory entry wins on overlap: it carries the manifest field and dependent name a `resolved-at-publish` bump has no commit to recover from at all.
|
|
796
883
|
*/
|
|
@@ -989,6 +1076,7 @@ async function attemptSingleCommitRelease(options) {
|
|
|
989
1076
|
requireGitPlugin: false,
|
|
990
1077
|
forbidGitPlugin: true
|
|
991
1078
|
});
|
|
1079
|
+
const bundled = resolveBundledDependencies(graph, options.bundlingPackages);
|
|
992
1080
|
const analyzeCommitsConfig = options.analyzeCommits ?? {};
|
|
993
1081
|
const generateNotesConfig = options.generateNotes ?? {};
|
|
994
1082
|
const tagFormat = validateTagFormat(options.tagFormat ?? "${name}@${version}");
|
|
@@ -1010,6 +1098,7 @@ async function attemptSingleCommitRelease(options) {
|
|
|
1010
1098
|
analyzeCommitsConfig,
|
|
1011
1099
|
generateNotesConfig,
|
|
1012
1100
|
bumpsForThisPackage,
|
|
1101
|
+
bundled: mustGet(bundled, name, "bundled dependencies"),
|
|
1013
1102
|
env,
|
|
1014
1103
|
branches: options.branches,
|
|
1015
1104
|
onCommitsResolved: (commits) => {
|
|
@@ -1047,7 +1136,10 @@ async function attemptSingleCommitRelease(options) {
|
|
|
1047
1136
|
notes: nextRelease.notes,
|
|
1048
1137
|
bumps: bumpsForThisPackage
|
|
1049
1138
|
});
|
|
1050
|
-
for (const bump of planDependentBumps(pkg,
|
|
1139
|
+
for (const bump of planDependentBumps(pkg, {
|
|
1140
|
+
version: nextRelease.version,
|
|
1141
|
+
releaseType: nextRelease.shippedReleaseType
|
|
1142
|
+
}, graph)) {
|
|
1051
1143
|
const forDependent = pendingBumps.get(bump.dependent) ?? [];
|
|
1052
1144
|
forDependent.push(bump);
|
|
1053
1145
|
pendingBumps.set(bump.dependent, forDependent);
|
|
@@ -1153,6 +1245,7 @@ async function analysePackage(pkg, options) {
|
|
|
1153
1245
|
analyzeCommitsConfig: options.analyzeCommitsConfig,
|
|
1154
1246
|
generateNotesConfig: options.generateNotesConfig,
|
|
1155
1247
|
bumps: { bumpsFor: () => options.bumpsForThisPackage },
|
|
1248
|
+
bundled: options.bundled,
|
|
1156
1249
|
onCommitsResolved: options.onCommitsResolved
|
|
1157
1250
|
});
|
|
1158
1251
|
const semanticReleaseOptions = {
|
|
@@ -1178,13 +1271,15 @@ async function analysePackage(pkg, options) {
|
|
|
1178
1271
|
if (result.nextRelease.notes === void 0) throw new ReleaseConfigurationError(`Internal error: ${pkg.name} released with no notes computed.`);
|
|
1179
1272
|
return {
|
|
1180
1273
|
type: result.nextRelease.type,
|
|
1274
|
+
shippedReleaseType: scoped.shippedReleaseType(result.nextRelease.type),
|
|
1181
1275
|
version: result.nextRelease.version,
|
|
1182
1276
|
gitTag: result.nextRelease.gitTag,
|
|
1183
1277
|
notes: result.nextRelease.notes
|
|
1184
1278
|
};
|
|
1185
1279
|
}
|
|
1186
1280
|
/** Pure classification of what a released package's version does to each dependent's declared range -- the same logic `bumpDependents` in `release.ts` applies, minus the file write/commit/push: `commitStrategy: 'single'` defers every write to phase 3, applying the same classification once analysis has finished for the whole run. */
|
|
1187
|
-
function planDependentBumps(released,
|
|
1281
|
+
function planDependentBumps(released, release, graph) {
|
|
1282
|
+
const { version, releaseType } = release;
|
|
1188
1283
|
const applied = [];
|
|
1189
1284
|
const dependents = graph.dependents.get(released.name);
|
|
1190
1285
|
if (dependents === void 0) return applied;
|
|
@@ -1197,7 +1292,8 @@ function planDependentBumps(released, version, graph) {
|
|
|
1197
1292
|
field: edge.field,
|
|
1198
1293
|
version,
|
|
1199
1294
|
range: update.kind === "rewritten" ? update.range : edge.range,
|
|
1200
|
-
kind: update.kind
|
|
1295
|
+
kind: update.kind,
|
|
1296
|
+
releaseType
|
|
1201
1297
|
});
|
|
1202
1298
|
}
|
|
1203
1299
|
return applied;
|
|
@@ -1291,14 +1387,16 @@ async function detachWorkspaceRelease(options) {
|
|
|
1291
1387
|
plugins: options.plugins ?? DEFAULT_PUBLISH_PLUGINS,
|
|
1292
1388
|
packagePlugins: options.packagePlugins
|
|
1293
1389
|
}, workspace.root, { requireGitPlugin: !dryRun });
|
|
1390
|
+
const bundled = resolveBundledDependencies(graph, options.bundlingPackages);
|
|
1294
1391
|
const analyzeCommitsConfig = options.analyzeCommits ?? {};
|
|
1295
1392
|
const generateNotesConfig = options.generateNotes ?? {};
|
|
1296
1393
|
const entries = await runReleaseLoop(graph, order, workspace, dryRun, log, async (pkg, bumpsForThisPackage) => {
|
|
1297
|
-
const state = await runPackageDetach(pkg, {
|
|
1394
|
+
const { state, releaseType } = await runPackageDetach(pkg, {
|
|
1298
1395
|
publishPlugins: mustGet(publishPlugins, pkg.name, "publish plugins"),
|
|
1299
1396
|
analyzeCommitsConfig,
|
|
1300
1397
|
generateNotesConfig,
|
|
1301
1398
|
bumpsForThisPackage,
|
|
1399
|
+
bundled: mustGet(bundled, pkg.name, "bundled dependencies"),
|
|
1302
1400
|
tagFormat: validateTagFormat(options.tagFormat ?? "${name}@${version}"),
|
|
1303
1401
|
dryRun,
|
|
1304
1402
|
env,
|
|
@@ -1307,6 +1405,7 @@ async function detachWorkspaceRelease(options) {
|
|
|
1307
1405
|
return {
|
|
1308
1406
|
released: state !== null,
|
|
1309
1407
|
version: state?.nextRelease.version,
|
|
1408
|
+
releaseType,
|
|
1310
1409
|
result: state
|
|
1311
1410
|
};
|
|
1312
1411
|
});
|
|
@@ -1339,7 +1438,8 @@ async function runPackageDetach(pkg, options) {
|
|
|
1339
1438
|
pkg,
|
|
1340
1439
|
analyzeCommitsConfig: options.analyzeCommitsConfig,
|
|
1341
1440
|
generateNotesConfig: options.generateNotesConfig,
|
|
1342
|
-
bumps: { bumpsFor: () => options.bumpsForThisPackage }
|
|
1441
|
+
bumps: { bumpsFor: () => options.bumpsForThisPackage },
|
|
1442
|
+
bundled: options.bundled
|
|
1343
1443
|
});
|
|
1344
1444
|
const cliOptions = {
|
|
1345
1445
|
tagFormat: formatTagForPackage(options.tagFormat, pkg.name),
|
|
@@ -1350,10 +1450,14 @@ async function runPackageDetach(pkg, options) {
|
|
|
1350
1450
|
if (options.dryRun) cliOptions.dryRun = true;
|
|
1351
1451
|
if (options.branches !== void 0) cliOptions.branches = options.branches;
|
|
1352
1452
|
try {
|
|
1353
|
-
|
|
1453
|
+
const state = await detachRelease(cliOptions, {
|
|
1354
1454
|
cwd: pkg.directory,
|
|
1355
1455
|
env: { ...options.env }
|
|
1356
1456
|
});
|
|
1457
|
+
return {
|
|
1458
|
+
state,
|
|
1459
|
+
releaseType: state === null ? void 0 : scoped.shippedReleaseType(state.nextRelease.type)
|
|
1460
|
+
};
|
|
1357
1461
|
} catch (cause) {
|
|
1358
1462
|
throw new WorkspaceReleaseError(`Detaching ${pkg.name} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
1359
1463
|
}
|
|
@@ -1433,17 +1537,19 @@ async function releaseWorkspace(options = {}) {
|
|
|
1433
1537
|
plugins: options.plugins ?? DEFAULT_PUBLISH_PLUGINS,
|
|
1434
1538
|
packagePlugins: options.packagePlugins
|
|
1435
1539
|
}, workspace.root, { requireGitPlugin: !dryRun });
|
|
1540
|
+
const bundled = resolveBundledDependencies(graph, options.bundlingPackages);
|
|
1436
1541
|
const analyzeCommitsConfig = options.analyzeCommits ?? {};
|
|
1437
1542
|
const generateNotesConfig = options.generateNotes ?? {};
|
|
1438
1543
|
return {
|
|
1439
1544
|
order,
|
|
1440
1545
|
packages: (await runReleaseLoop(graph, order, workspace, dryRun, log, async (pkg, bumpsForThisPackage) => {
|
|
1441
|
-
const result = await runPackageRelease(pkg, {
|
|
1546
|
+
const { result, releaseType } = await runPackageRelease(pkg, {
|
|
1442
1547
|
tagFormat: validateTagFormat(options.tagFormat ?? "${name}@${version}"),
|
|
1443
1548
|
publishPlugins: mustGet(publishPlugins, pkg.name, "publish plugins"),
|
|
1444
1549
|
analyzeCommitsConfig,
|
|
1445
1550
|
generateNotesConfig,
|
|
1446
1551
|
bumpsForThisPackage,
|
|
1552
|
+
bundled: mustGet(bundled, pkg.name, "bundled dependencies"),
|
|
1447
1553
|
dryRun,
|
|
1448
1554
|
env,
|
|
1449
1555
|
branches: options.branches
|
|
@@ -1452,6 +1558,7 @@ async function releaseWorkspace(options = {}) {
|
|
|
1452
1558
|
return {
|
|
1453
1559
|
released: nextRelease !== void 0,
|
|
1454
1560
|
version: nextRelease?.version,
|
|
1561
|
+
releaseType,
|
|
1455
1562
|
result
|
|
1456
1563
|
};
|
|
1457
1564
|
})).map((entry) => {
|
|
@@ -1474,7 +1581,7 @@ async function releaseWorkspace(options = {}) {
|
|
|
1474
1581
|
};
|
|
1475
1582
|
}
|
|
1476
1583
|
/**
|
|
1477
|
-
* The per-package release loop shared by `releaseWorkspace` (normal per-package releases) and `detachWorkspaceRelease` (gated tag-only releases): iterate the topological `order`, call the caller's own `releaseOne` for each package, record which dependency-range bumps were already applied to it, and -- when it released -- bump every workspace dependent's manifest before that dependent's own turn (see `bumpDependents`'s doc comment for why this happens immediately rather than being staged). The two call sites differ only in how they call semantic-release (`semanticRelease(...)` vs `detachRelease(...)`) and how they read "did it release,
|
|
1584
|
+
* The per-package release loop shared by `releaseWorkspace` (normal per-package releases) and `detachWorkspaceRelease` (gated tag-only releases): iterate the topological `order`, call the caller's own `releaseOne` for each package, record which dependency-range bumps were already applied to it, and -- when it released -- bump every workspace dependent's manifest before that dependent's own turn (see `bumpDependents`'s doc comment for why this happens immediately rather than being staged). The two call sites differ only in how they call semantic-release (`semanticRelease(...)` vs `detachRelease(...)`) and how they read "did it release, at what version and with which shipped release type" off the result, all captured by `releaseOne`.
|
|
1478
1585
|
*/
|
|
1479
1586
|
async function runReleaseLoop(graph, order, workspace, dryRun, log, releaseOne) {
|
|
1480
1587
|
const pendingBumps = /* @__PURE__ */ new Map();
|
|
@@ -1484,7 +1591,7 @@ async function runReleaseLoop(graph, order, workspace, dryRun, log, releaseOne)
|
|
|
1484
1591
|
const pkg = mustGet(graph.packages, name, "package");
|
|
1485
1592
|
const bumpsForThisPackage = pendingBumps.get(name) ?? [];
|
|
1486
1593
|
log(`Releasing ${name} from ${pkg.relativeDirectory}${bumpsForThisPackage.length > 0 ? ` (dependency ranges already bumped: ${bumpsForThisPackage.map((bump) => bump.dependency).join(", ")})` : ""}`);
|
|
1487
|
-
const { released, version, result } = dryRun ? await releaseOne(pkg, bumpsForThisPackage) : await releasePackageAcrossBranchMoves(name, branchTracker(workspace, log), async () => await releaseOne(pkg, bumpsForThisPackage), log);
|
|
1594
|
+
const { released, version, releaseType, result } = dryRun ? await releaseOne(pkg, bumpsForThisPackage) : await releasePackageAcrossBranchMoves(name, branchTracker(workspace, log), async () => await releaseOne(pkg, bumpsForThisPackage), log);
|
|
1488
1595
|
entries.push({
|
|
1489
1596
|
name,
|
|
1490
1597
|
directory: pkg.directory,
|
|
@@ -1493,13 +1600,16 @@ async function runReleaseLoop(graph, order, workspace, dryRun, log, releaseOne)
|
|
|
1493
1600
|
dependencyBumps: bumpsForThisPackage
|
|
1494
1601
|
});
|
|
1495
1602
|
pendingBumps.delete(name);
|
|
1496
|
-
if (!released || version === void 0) {
|
|
1603
|
+
if (!released || version === void 0 || releaseType === void 0) {
|
|
1497
1604
|
log(`${name}: no release`);
|
|
1498
1605
|
continue;
|
|
1499
1606
|
}
|
|
1500
1607
|
log(`${name}: released ${version}`);
|
|
1501
1608
|
identity ??= await resolveCommitIdentity({ cwd: workspace.root });
|
|
1502
|
-
const bumps = await bumpDependents(pkg,
|
|
1609
|
+
const bumps = await bumpDependents(pkg, {
|
|
1610
|
+
version,
|
|
1611
|
+
releaseType
|
|
1612
|
+
}, graph, {
|
|
1503
1613
|
workspace,
|
|
1504
1614
|
dryRun,
|
|
1505
1615
|
identity,
|
|
@@ -1518,7 +1628,8 @@ async function runPackageRelease(pkg, options) {
|
|
|
1518
1628
|
pkg,
|
|
1519
1629
|
analyzeCommitsConfig: options.analyzeCommitsConfig,
|
|
1520
1630
|
generateNotesConfig: options.generateNotesConfig,
|
|
1521
|
-
bumps: { bumpsFor: () => options.bumpsForThisPackage }
|
|
1631
|
+
bumps: { bumpsFor: () => options.bumpsForThisPackage },
|
|
1632
|
+
bundled: options.bundled
|
|
1522
1633
|
});
|
|
1523
1634
|
const semanticReleaseOptions = {
|
|
1524
1635
|
tagFormat: formatTagForPackage(options.tagFormat, pkg.name),
|
|
@@ -1529,10 +1640,14 @@ async function runPackageRelease(pkg, options) {
|
|
|
1529
1640
|
if (options.dryRun) semanticReleaseOptions.dryRun = true;
|
|
1530
1641
|
if (options.branches !== void 0) semanticReleaseOptions.branches = options.branches;
|
|
1531
1642
|
try {
|
|
1532
|
-
|
|
1643
|
+
const result = await semanticRelease(semanticReleaseOptions, {
|
|
1533
1644
|
cwd: pkg.directory,
|
|
1534
1645
|
env: { ...options.env }
|
|
1535
1646
|
});
|
|
1647
|
+
return {
|
|
1648
|
+
result,
|
|
1649
|
+
releaseType: result === false ? void 0 : scoped.shippedReleaseType(result.nextRelease.type)
|
|
1650
|
+
};
|
|
1536
1651
|
} catch (cause) {
|
|
1537
1652
|
throw new WorkspaceReleaseError(`Release of ${pkg.name} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
1538
1653
|
}
|
|
@@ -1544,10 +1659,11 @@ const LOCKFILE_FILENAME = "pnpm-lock.yaml";
|
|
|
1544
1659
|
*
|
|
1545
1660
|
* The bump is committed (and pushed) right away rather than staged, because a dependent's semantic-release run analyses git history, not the working tree: an uncommitted bump would be invisible to its commit analysis, and would be stranded uncommitted if the dependent then released nothing -- a manifest that names versions the registry has never seen. Committing immediately means the dependent's own run sees the bump commit (it touches only the dependent's directory, so it passes that dependent's path filter), the forced-patch logic in the scoped analyzer covers the case where that bump is the dependent's only change, and a run interrupted partway leaves the remote describing exactly what was published. Pushing immediately mirrors what semantic-release itself does with release commits. The lockfile is regenerated (`pnpm install --lockfile-only`) and committed alongside the manifest for the same reason: a manifest bump committed without it leaves `pnpm-lock.yaml` naming the old range, which `pnpm install --frozen-lockfile` (what CI runs) then rejects.
|
|
1546
1661
|
*/
|
|
1547
|
-
async function bumpDependents(released,
|
|
1662
|
+
async function bumpDependents(released, release, graph, options) {
|
|
1548
1663
|
const applied = [];
|
|
1549
1664
|
const dependents = graph.dependents.get(released.name);
|
|
1550
1665
|
if (dependents === void 0) return applied;
|
|
1666
|
+
const { version, releaseType } = release;
|
|
1551
1667
|
for (const edge of dependents) {
|
|
1552
1668
|
const update = updateDependencyRange(edge.range, version);
|
|
1553
1669
|
if (update.kind === "wildcard") continue;
|
|
@@ -1560,6 +1676,7 @@ async function bumpDependents(released, version, graph, options) {
|
|
|
1560
1676
|
dependency: released.name,
|
|
1561
1677
|
version,
|
|
1562
1678
|
range: update.range,
|
|
1679
|
+
releaseType,
|
|
1563
1680
|
dependent: edge.dependent
|
|
1564
1681
|
});
|
|
1565
1682
|
await commitFiles([`${dependent.relativeDirectory}/package.json`, LOCKFILE_FILENAME], message, {
|
|
@@ -1576,7 +1693,8 @@ async function bumpDependents(released, version, graph, options) {
|
|
|
1576
1693
|
field: edge.field,
|
|
1577
1694
|
version,
|
|
1578
1695
|
range: update.kind === "rewritten" ? update.range : edge.range,
|
|
1579
|
-
kind: update.kind
|
|
1696
|
+
kind: update.kind,
|
|
1697
|
+
releaseType
|
|
1580
1698
|
});
|
|
1581
1699
|
}
|
|
1582
1700
|
return applied;
|
|
@@ -1604,4 +1722,4 @@ function branchTracker(workspace, log) {
|
|
|
1604
1722
|
};
|
|
1605
1723
|
}
|
|
1606
1724
|
//#endregion
|
|
1607
|
-
export { DEFAULT_PUBLISH_PLUGINS, DependencyCycleError, GitCommandError, ReleaseConfigurationError, SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS, UnsupportedDependencyRangeError, WorkspaceDiscoveryError, WorkspaceReleaseError, WorkspaceStateError, buildDependencyGraph, classifyDependencyRange, createScopedPlugins, discoverWorkspace, filterCommitsToDirectory, orderedPackages, packageName, readManifest, releaseWorkspace, resolvePublishPlugins, resolveWorkspacePublishPlugins, resumeWorkspaceRelease, topologicalOrder, updateDependencyRange, writeDependencyRange };
|
|
1725
|
+
export { DEFAULT_PUBLISH_PLUGINS, DependencyCycleError, GitCommandError, ReleaseConfigurationError, SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS, UnsupportedDependencyRangeError, WorkspaceDiscoveryError, WorkspaceReleaseError, WorkspaceStateError, buildDependencyGraph, classifyDependencyRange, createScopedPlugins, discoverWorkspace, filterCommitsToDirectory, orderedPackages, packageName, readManifest, releaseWorkspace, resolveBundledDependencies, resolvePublishPlugins, resolveWorkspacePublishPlugins, resumeWorkspaceRelease, topologicalOrder, updateDependencyRange, writeDependencyRange };
|
package/package.json
CHANGED