@exadev/semantic-release-workspace 1.2.4 → 1.3.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 +83 -1
- package/dist/cli.js +428 -232
- package/dist/index.cjs +179 -23
- package/dist/index.d.cts +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +179 -24
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -35,6 +35,7 @@ let _semantic_release_release_notes_generator = require("@semantic-release/relea
|
|
|
35
35
|
let semantic_release = require("semantic-release");
|
|
36
36
|
semantic_release = __toESM(semantic_release, 1);
|
|
37
37
|
let node_url = require("node:url");
|
|
38
|
+
let _exadev_release_gate = require("@exadev/release-gate");
|
|
38
39
|
//#region src/package-name.ts
|
|
39
40
|
const packageName = "@exadev/semantic-release-workspace";
|
|
40
41
|
//#endregion
|
|
@@ -1031,6 +1032,134 @@ async function loadReleasePlugin(absolutePath, cache) {
|
|
|
1031
1032
|
return loaded;
|
|
1032
1033
|
}
|
|
1033
1034
|
//#endregion
|
|
1035
|
+
//#region src/gate-publish.ts
|
|
1036
|
+
/**
|
|
1037
|
+
* The `gatePublish: true` half of `releaseWorkspace`: tags and pushes every due package via `@exadev/release-gate`'s `detachRelease`, but never publishes. Built on the same `runReleaseLoop` the normal per-package path uses, so dependency-range bumps between packages happen identically either way -- only the release call itself (`detachRelease` instead of `semanticRelease`) and how "did it release" gets read differ.
|
|
1038
|
+
*/
|
|
1039
|
+
async function detachWorkspaceRelease(options) {
|
|
1040
|
+
const root = (0, node_path.resolve)(options.root ?? process.cwd());
|
|
1041
|
+
const log = options.log ?? console.log;
|
|
1042
|
+
const dryRun = options.dryRun === true;
|
|
1043
|
+
const env = sanitizeGitEnv(options.env ?? process.env);
|
|
1044
|
+
const workspace = await discoverWorkspace(root);
|
|
1045
|
+
const graph = buildDependencyGraph(workspace.packages);
|
|
1046
|
+
validateDependencyRangeShapes(graph);
|
|
1047
|
+
const order = topologicalOrder(graph);
|
|
1048
|
+
log(`${packageName}: ${order.length} packages in release order (gated -- tag only, publish deferred): ${order.join(" -> ")}`);
|
|
1049
|
+
const publishPlugins = resolvePublishPlugins(options.plugins ?? DEFAULT_PUBLISH_PLUGINS, workspace.root, { requireGitPlugin: !dryRun });
|
|
1050
|
+
const analyzeCommitsConfig = options.analyzeCommits ?? {};
|
|
1051
|
+
const generateNotesConfig = options.generateNotes ?? {};
|
|
1052
|
+
const entries = await runReleaseLoop(graph, order, workspace, dryRun, log, async (pkg, bumpsForThisPackage) => {
|
|
1053
|
+
const state = await runPackageDetach(pkg, {
|
|
1054
|
+
publishPlugins,
|
|
1055
|
+
analyzeCommitsConfig,
|
|
1056
|
+
generateNotesConfig,
|
|
1057
|
+
bumpsForThisPackage,
|
|
1058
|
+
dryRun,
|
|
1059
|
+
env,
|
|
1060
|
+
branches: options.branches
|
|
1061
|
+
});
|
|
1062
|
+
return {
|
|
1063
|
+
released: state !== null,
|
|
1064
|
+
version: state?.nextRelease.version,
|
|
1065
|
+
result: state
|
|
1066
|
+
};
|
|
1067
|
+
});
|
|
1068
|
+
return {
|
|
1069
|
+
order,
|
|
1070
|
+
packages: entries.map((entry) => ({
|
|
1071
|
+
name: entry.name,
|
|
1072
|
+
directory: entry.directory,
|
|
1073
|
+
released: entry.result !== null,
|
|
1074
|
+
version: entry.result?.nextRelease.version,
|
|
1075
|
+
gitTag: entry.result?.nextRelease.gitTag,
|
|
1076
|
+
type: entry.result?.nextRelease.type,
|
|
1077
|
+
dependencyBumps: entry.dependencyBumps
|
|
1078
|
+
})),
|
|
1079
|
+
detached: entries.map((entry) => ({
|
|
1080
|
+
name: entry.name,
|
|
1081
|
+
relativeDirectory: entry.relativeDirectory,
|
|
1082
|
+
state: entry.result,
|
|
1083
|
+
dependencyBumps: entry.dependencyBumps
|
|
1084
|
+
}))
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
async function runPackageDetach(pkg, options) {
|
|
1088
|
+
const scoped = createScopedPlugins({
|
|
1089
|
+
pkg,
|
|
1090
|
+
analyzeCommitsConfig: options.analyzeCommitsConfig,
|
|
1091
|
+
generateNotesConfig: options.generateNotesConfig,
|
|
1092
|
+
bumps: { bumpsFor: () => options.bumpsForThisPackage }
|
|
1093
|
+
});
|
|
1094
|
+
const cliOptions = {
|
|
1095
|
+
tagFormat: `${pkg.name}@\${version}`,
|
|
1096
|
+
plugins: options.publishPlugins,
|
|
1097
|
+
analyzeCommits: scoped.analyzeCommits,
|
|
1098
|
+
generateNotes: scoped.generateNotes
|
|
1099
|
+
};
|
|
1100
|
+
if (options.dryRun) cliOptions.dryRun = true;
|
|
1101
|
+
if (options.branches !== void 0) cliOptions.branches = options.branches;
|
|
1102
|
+
try {
|
|
1103
|
+
return await (0, _exadev_release_gate.detachRelease)(cliOptions, {
|
|
1104
|
+
cwd: pkg.directory,
|
|
1105
|
+
env: { ...options.env }
|
|
1106
|
+
});
|
|
1107
|
+
} catch (cause) {
|
|
1108
|
+
throw new WorkspaceReleaseError(`Detaching ${pkg.name} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Finishes every package a `gatePublish: true` `releaseWorkspace` run tagged and pushed but did not publish. Resumes each `detached` entry in the array's own order -- not re-derived from the workspace's dependency graph, since a resume pass may run in a separate process or checkout, where re-deriving order should only ever matter for directory lookup, not sequencing.
|
|
1113
|
+
*/
|
|
1114
|
+
async function resumeWorkspaceRelease(options) {
|
|
1115
|
+
const root = (0, node_path.resolve)(options.root ?? process.cwd());
|
|
1116
|
+
const log = options.log ?? console.log;
|
|
1117
|
+
const env = sanitizeGitEnv(options.env ?? process.env);
|
|
1118
|
+
const order = [];
|
|
1119
|
+
const packages = [];
|
|
1120
|
+
for (const entry of options.detached) {
|
|
1121
|
+
order.push(entry.name);
|
|
1122
|
+
if (entry.state === null) {
|
|
1123
|
+
log(`${entry.name}: no release to resume`);
|
|
1124
|
+
packages.push({
|
|
1125
|
+
name: entry.name,
|
|
1126
|
+
directory: (0, node_path.resolve)(root, entry.relativeDirectory),
|
|
1127
|
+
released: false,
|
|
1128
|
+
version: void 0,
|
|
1129
|
+
gitTag: void 0,
|
|
1130
|
+
type: void 0,
|
|
1131
|
+
dependencyBumps: entry.dependencyBumps
|
|
1132
|
+
});
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
const directory = (0, node_path.resolve)(root, entry.relativeDirectory);
|
|
1136
|
+
log(`Resuming ${entry.name} from ${entry.relativeDirectory}`);
|
|
1137
|
+
let releases;
|
|
1138
|
+
try {
|
|
1139
|
+
releases = await (0, _exadev_release_gate.resumeRelease)(entry.state, {
|
|
1140
|
+
cwd: directory,
|
|
1141
|
+
env: { ...env }
|
|
1142
|
+
});
|
|
1143
|
+
} catch (cause) {
|
|
1144
|
+
throw new WorkspaceReleaseError(`Resuming ${entry.name} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
1145
|
+
}
|
|
1146
|
+
log(`${entry.name}: published ${entry.state.nextRelease.gitTag} (${releases.length} publish plugin${releases.length === 1 ? "" : "s"} ran)`);
|
|
1147
|
+
packages.push({
|
|
1148
|
+
name: entry.name,
|
|
1149
|
+
directory,
|
|
1150
|
+
released: true,
|
|
1151
|
+
version: entry.state.nextRelease.version,
|
|
1152
|
+
gitTag: entry.state.nextRelease.gitTag,
|
|
1153
|
+
type: entry.state.nextRelease.type,
|
|
1154
|
+
dependencyBumps: entry.dependencyBumps
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
return {
|
|
1158
|
+
order,
|
|
1159
|
+
packages
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
//#endregion
|
|
1034
1163
|
//#region src/release.ts
|
|
1035
1164
|
/**
|
|
1036
1165
|
* Releases every package in a pnpm workspace with independent versions, in dependency order.
|
|
@@ -1038,6 +1167,10 @@ async function loadReleasePlugin(absolutePath, cache) {
|
|
|
1038
1167
|
* For each package, in topological order: run semantic-release's programmatic API with `cwd` scoped to the package directory, a `name@version` tag format to keep each package's tags distinct in the one shared tag namespace, and inline `analyzeCommits`/`generateNotes` plugins that filter the release range's commits down to the package's own directory before delegating to the standard plugins. When a package releases, every workspace package that depends on it and has not run yet gets its dependency range rewritten in its manifest and committed immediately -- before its own turn, so its commit analysis and its published manifest both see the new range.
|
|
1039
1168
|
*/
|
|
1040
1169
|
async function releaseWorkspace(options = {}) {
|
|
1170
|
+
if (options.gatePublish === true) {
|
|
1171
|
+
if ((options.commitStrategy ?? "per-package") === "single") throw new ReleaseConfigurationError("gatePublish is not supported together with commitStrategy: \"single\" -- single-commit-release.ts's tag/publish machinery is entirely bespoke and never goes through semantic-release's own run(), so it has no insertion point for release-gate's detach/resume primitives.");
|
|
1172
|
+
return detachWorkspaceRelease(options);
|
|
1173
|
+
}
|
|
1041
1174
|
if ((options.commitStrategy ?? "per-package") === "single") return releaseWorkspaceSingleCommit(options);
|
|
1042
1175
|
const root = (0, node_path.resolve)(options.root ?? process.cwd());
|
|
1043
1176
|
const log = options.log ?? console.log;
|
|
@@ -1051,40 +1184,65 @@ async function releaseWorkspace(options = {}) {
|
|
|
1051
1184
|
const publishPlugins = resolvePublishPlugins(options.plugins ?? DEFAULT_PUBLISH_PLUGINS, workspace.root, { requireGitPlugin: !dryRun });
|
|
1052
1185
|
const analyzeCommitsConfig = options.analyzeCommits ?? {};
|
|
1053
1186
|
const generateNotesConfig = options.generateNotes ?? {};
|
|
1187
|
+
return {
|
|
1188
|
+
order,
|
|
1189
|
+
packages: (await runReleaseLoop(graph, order, workspace, dryRun, log, async (pkg, bumpsForThisPackage) => {
|
|
1190
|
+
const result = await runPackageRelease(pkg, {
|
|
1191
|
+
publishPlugins,
|
|
1192
|
+
analyzeCommitsConfig,
|
|
1193
|
+
generateNotesConfig,
|
|
1194
|
+
bumpsForThisPackage,
|
|
1195
|
+
dryRun,
|
|
1196
|
+
env,
|
|
1197
|
+
branches: options.branches
|
|
1198
|
+
});
|
|
1199
|
+
const nextRelease = result === false ? void 0 : result.nextRelease;
|
|
1200
|
+
return {
|
|
1201
|
+
released: nextRelease !== void 0,
|
|
1202
|
+
version: nextRelease?.version,
|
|
1203
|
+
result
|
|
1204
|
+
};
|
|
1205
|
+
})).map((entry) => {
|
|
1206
|
+
const nextRelease = entry.result === false ? void 0 : entry.result.nextRelease;
|
|
1207
|
+
return {
|
|
1208
|
+
name: entry.name,
|
|
1209
|
+
directory: entry.directory,
|
|
1210
|
+
released: nextRelease !== void 0,
|
|
1211
|
+
version: nextRelease?.version,
|
|
1212
|
+
gitTag: nextRelease?.gitTag,
|
|
1213
|
+
type: nextRelease?.type,
|
|
1214
|
+
dependencyBumps: entry.dependencyBumps
|
|
1215
|
+
};
|
|
1216
|
+
})
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* 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, and what version" off the result, both captured by `releaseOne`.
|
|
1221
|
+
*/
|
|
1222
|
+
async function runReleaseLoop(graph, order, workspace, dryRun, log, releaseOne) {
|
|
1054
1223
|
const pendingBumps = /* @__PURE__ */ new Map();
|
|
1055
1224
|
let identity;
|
|
1056
|
-
const
|
|
1225
|
+
const entries = [];
|
|
1057
1226
|
for (const name of order) {
|
|
1058
1227
|
const pkg = mustGet(graph.packages, name, "package");
|
|
1059
1228
|
const bumpsForThisPackage = pendingBumps.get(name) ?? [];
|
|
1060
1229
|
log(`Releasing ${name} from ${pkg.relativeDirectory}${bumpsForThisPackage.length > 0 ? ` (dependency ranges already bumped: ${bumpsForThisPackage.map((bump) => bump.dependency).join(", ")})` : ""}`);
|
|
1061
|
-
const result = await
|
|
1062
|
-
|
|
1063
|
-
analyzeCommitsConfig,
|
|
1064
|
-
generateNotesConfig,
|
|
1065
|
-
bumpsForThisPackage,
|
|
1066
|
-
dryRun,
|
|
1067
|
-
env,
|
|
1068
|
-
branches: options.branches
|
|
1069
|
-
});
|
|
1070
|
-
const nextRelease = result === false ? void 0 : result.nextRelease;
|
|
1071
|
-
outcomes.push({
|
|
1230
|
+
const { released, version, result } = await releaseOne(pkg, bumpsForThisPackage);
|
|
1231
|
+
entries.push({
|
|
1072
1232
|
name,
|
|
1073
1233
|
directory: pkg.directory,
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
gitTag: nextRelease?.gitTag,
|
|
1077
|
-
type: nextRelease?.type,
|
|
1234
|
+
relativeDirectory: pkg.relativeDirectory,
|
|
1235
|
+
result,
|
|
1078
1236
|
dependencyBumps: bumpsForThisPackage
|
|
1079
1237
|
});
|
|
1080
1238
|
pendingBumps.delete(name);
|
|
1081
|
-
if (
|
|
1239
|
+
if (!released || version === void 0) {
|
|
1082
1240
|
log(`${name}: no release`);
|
|
1083
1241
|
continue;
|
|
1084
1242
|
}
|
|
1085
|
-
log(`${name}: released ${
|
|
1243
|
+
log(`${name}: released ${version}`);
|
|
1086
1244
|
identity ??= await resolveCommitIdentity({ cwd: workspace.root });
|
|
1087
|
-
const bumps = await bumpDependents(pkg,
|
|
1245
|
+
const bumps = await bumpDependents(pkg, version, graph, {
|
|
1088
1246
|
workspace,
|
|
1089
1247
|
dryRun,
|
|
1090
1248
|
identity,
|
|
@@ -1096,10 +1254,7 @@ async function releaseWorkspace(options = {}) {
|
|
|
1096
1254
|
pendingBumps.set(bump.dependent, forDependent);
|
|
1097
1255
|
}
|
|
1098
1256
|
}
|
|
1099
|
-
return
|
|
1100
|
-
order,
|
|
1101
|
-
packages: outcomes
|
|
1102
|
-
};
|
|
1257
|
+
return entries;
|
|
1103
1258
|
}
|
|
1104
1259
|
async function runPackageRelease(pkg, options) {
|
|
1105
1260
|
const scoped = createScopedPlugins({
|
|
@@ -1188,6 +1343,7 @@ exports.packageName = packageName;
|
|
|
1188
1343
|
exports.readManifest = readManifest;
|
|
1189
1344
|
exports.releaseWorkspace = releaseWorkspace;
|
|
1190
1345
|
exports.resolvePublishPlugins = resolvePublishPlugins;
|
|
1346
|
+
exports.resumeWorkspaceRelease = resumeWorkspaceRelease;
|
|
1191
1347
|
exports.topologicalOrder = topologicalOrder;
|
|
1192
1348
|
exports.updateDependencyRange = updateDependencyRange;
|
|
1193
1349
|
exports.writeDependencyRange = writeDependencyRange;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AnalyzeCommitsContext, BranchSpec, Commit, GenerateNotesContext } from "semantic-release";
|
|
2
|
+
import { ReleaseGateState } from "@exadev/release-gate";
|
|
2
3
|
//#region src/package-name.d.ts
|
|
3
4
|
declare const packageName = "@exadev/semantic-release-workspace";
|
|
4
5
|
//#endregion
|
|
@@ -184,6 +185,30 @@ declare function resolvePublishPlugins(specs: readonly PublishPluginSpec[], work
|
|
|
184
185
|
readonly forbidGitPlugin?: boolean;
|
|
185
186
|
}): readonly ResolvedPublishPlugin[];
|
|
186
187
|
//#endregion
|
|
188
|
+
//#region src/gate-publish.d.ts
|
|
189
|
+
/**
|
|
190
|
+
* One package's outcome from a gated (`gatePublish: true`) detach pass -- what `resumeWorkspaceRelease` needs later to finish publishing it. `relativeDirectory`, not the absolute `directory`, is what a resume pass actually uses to resolve `cwd`: a resume can run against a different checkout of the same repository (a separate CI job, a separate clone) at a different absolute path, and `relativeDirectory` is the portable part.
|
|
191
|
+
*/
|
|
192
|
+
interface DetachedPackageRelease {
|
|
193
|
+
readonly name: string;
|
|
194
|
+
readonly relativeDirectory: string;
|
|
195
|
+
/** The state to hand `resumeRelease`, or `null` if this package had nothing to release. */
|
|
196
|
+
readonly state: ReleaseGateState | null;
|
|
197
|
+
readonly dependencyBumps: readonly AppliedDependencyBump[];
|
|
198
|
+
}
|
|
199
|
+
interface ResumeWorkspaceReleaseOptions {
|
|
200
|
+
/** Directory holding the workspace's `pnpm-workspace.yaml` in *this* checkout -- not necessarily the same absolute path the detach pass ran from. Defaults to the process working directory. */
|
|
201
|
+
readonly root?: string;
|
|
202
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
203
|
+
/** `WorkspaceReleaseOutcome.detached` from the earlier `releaseWorkspace({gatePublish: true})` call this resumes. */
|
|
204
|
+
readonly detached: readonly DetachedPackageRelease[];
|
|
205
|
+
readonly log?: (message: string) => void;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Finishes every package a `gatePublish: true` `releaseWorkspace` run tagged and pushed but did not publish. Resumes each `detached` entry in the array's own order -- not re-derived from the workspace's dependency graph, since a resume pass may run in a separate process or checkout, where re-deriving order should only ever matter for directory lookup, not sequencing.
|
|
209
|
+
*/
|
|
210
|
+
declare function resumeWorkspaceRelease(options: ResumeWorkspaceReleaseOptions): Promise<WorkspaceReleaseOutcome>;
|
|
211
|
+
//#endregion
|
|
187
212
|
//#region src/release.d.ts
|
|
188
213
|
/**
|
|
189
214
|
* How a run commits its released changes:
|
|
@@ -211,6 +236,10 @@ interface ReleaseWorkspaceOptions {
|
|
|
211
236
|
readonly log?: (message: string) => void;
|
|
212
237
|
/** How the run commits its released changes. Defaults to `'per-package'`, today's exact existing behaviour. See `CommitStrategy` for what each mode does. */
|
|
213
238
|
readonly commitStrategy?: CommitStrategy;
|
|
239
|
+
/**
|
|
240
|
+
* Opt-in, orthogonal to `commitStrategy` -- it governs *when* publish/success run relative to tag+push, not *how many commits* the release makes. When `true`, each package's release is tagged and pushed via `@exadev/release-gate`'s `detachRelease` but never published: `WorkspaceReleaseOutcome.detached` carries the state `resumeWorkspaceRelease` needs to finish publishing later, from the same process or a different one, once an external gate confirms the release should actually go out. Rejected outright in combination with `commitStrategy: 'single'` -- `single-commit-release.ts`'s tag/publish machinery is entirely bespoke and never goes through semantic-release's own `run()`, so it has no insertion point for `release-gate`'s primitives. Defaults to `false`, today's exact existing behaviour.
|
|
241
|
+
*/
|
|
242
|
+
readonly gatePublish?: boolean;
|
|
214
243
|
}
|
|
215
244
|
/** One dependency-range change applied to a dependent package's manifest during the run, attached to the dependent's own outcome. */
|
|
216
245
|
interface AppliedDependencyBump extends DependencyBump {
|
|
@@ -233,6 +262,8 @@ interface WorkspaceReleaseOutcome {
|
|
|
233
262
|
/** The topological order the packages were released in. */
|
|
234
263
|
readonly order: readonly string[];
|
|
235
264
|
readonly packages: readonly PackageReleaseOutcome[];
|
|
265
|
+
/** Present only when `gatePublish: true`: one entry per package in `order`, carrying the `@exadev/release-gate` state `resumeWorkspaceRelease` needs to finish publishing it later, or `null` for a package that had nothing to release. */
|
|
266
|
+
readonly detached?: readonly DetachedPackageRelease[];
|
|
236
267
|
}
|
|
237
268
|
/**
|
|
238
269
|
* Releases every package in a pnpm workspace with independent versions, in dependency order.
|
|
@@ -270,4 +301,4 @@ declare class GitCommandError extends WorkspaceReleaseError {
|
|
|
270
301
|
/** The workspace's git state does not support the release operation -- for example a detached HEAD, which names no branch that dependency-bump commits could be pushed to. */
|
|
271
302
|
declare class WorkspaceStateError extends WorkspaceReleaseError {}
|
|
272
303
|
//#endregion
|
|
273
|
-
export { type AppliedDependencyBump, type CommitStrategy, DEFAULT_PUBLISH_PLUGINS, type DependencyBump, type DependencyBumpSource, DependencyCycleError, type DependencyField, type DependencyGraph, type DependencyRangeShape, type DependencyRangeUpdate, GitCommandError, type PackageManifest, type PackageReleaseOutcome, type PublishPluginSpec, ReleaseConfigurationError, type ReleaseWorkspaceOptions, type ResolvedPublishPlugin, SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS, type ScopedPlugins, UnsupportedDependencyRangeError, type Workspace, type WorkspaceDependency, WorkspaceDiscoveryError, type WorkspacePackage, WorkspaceReleaseError, type WorkspaceReleaseOutcome, WorkspaceStateError, buildDependencyGraph, classifyDependencyRange, createScopedPlugins, discoverWorkspace, filterCommitsToDirectory, packageName, readManifest, releaseWorkspace, resolvePublishPlugins, topologicalOrder, updateDependencyRange, writeDependencyRange };
|
|
304
|
+
export { type AppliedDependencyBump, type CommitStrategy, DEFAULT_PUBLISH_PLUGINS, type DependencyBump, type DependencyBumpSource, DependencyCycleError, type DependencyField, type DependencyGraph, type DependencyRangeShape, type DependencyRangeUpdate, type DetachedPackageRelease, GitCommandError, type PackageManifest, type PackageReleaseOutcome, type PublishPluginSpec, ReleaseConfigurationError, type ReleaseWorkspaceOptions, type ResolvedPublishPlugin, type ResumeWorkspaceReleaseOptions, SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS, type ScopedPlugins, UnsupportedDependencyRangeError, type Workspace, type WorkspaceDependency, WorkspaceDiscoveryError, type WorkspacePackage, WorkspaceReleaseError, type WorkspaceReleaseOutcome, WorkspaceStateError, buildDependencyGraph, classifyDependencyRange, createScopedPlugins, discoverWorkspace, filterCommitsToDirectory, packageName, readManifest, releaseWorkspace, resolvePublishPlugins, resumeWorkspaceRelease, topologicalOrder, updateDependencyRange, writeDependencyRange };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AnalyzeCommitsContext, BranchSpec, Commit, GenerateNotesContext } from "semantic-release";
|
|
2
|
+
import { ReleaseGateState } from "@exadev/release-gate";
|
|
2
3
|
//#region src/package-name.d.ts
|
|
3
4
|
declare const packageName = "@exadev/semantic-release-workspace";
|
|
4
5
|
//#endregion
|
|
@@ -184,6 +185,30 @@ declare function resolvePublishPlugins(specs: readonly PublishPluginSpec[], work
|
|
|
184
185
|
readonly forbidGitPlugin?: boolean;
|
|
185
186
|
}): readonly ResolvedPublishPlugin[];
|
|
186
187
|
//#endregion
|
|
188
|
+
//#region src/gate-publish.d.ts
|
|
189
|
+
/**
|
|
190
|
+
* One package's outcome from a gated (`gatePublish: true`) detach pass -- what `resumeWorkspaceRelease` needs later to finish publishing it. `relativeDirectory`, not the absolute `directory`, is what a resume pass actually uses to resolve `cwd`: a resume can run against a different checkout of the same repository (a separate CI job, a separate clone) at a different absolute path, and `relativeDirectory` is the portable part.
|
|
191
|
+
*/
|
|
192
|
+
interface DetachedPackageRelease {
|
|
193
|
+
readonly name: string;
|
|
194
|
+
readonly relativeDirectory: string;
|
|
195
|
+
/** The state to hand `resumeRelease`, or `null` if this package had nothing to release. */
|
|
196
|
+
readonly state: ReleaseGateState | null;
|
|
197
|
+
readonly dependencyBumps: readonly AppliedDependencyBump[];
|
|
198
|
+
}
|
|
199
|
+
interface ResumeWorkspaceReleaseOptions {
|
|
200
|
+
/** Directory holding the workspace's `pnpm-workspace.yaml` in *this* checkout -- not necessarily the same absolute path the detach pass ran from. Defaults to the process working directory. */
|
|
201
|
+
readonly root?: string;
|
|
202
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
203
|
+
/** `WorkspaceReleaseOutcome.detached` from the earlier `releaseWorkspace({gatePublish: true})` call this resumes. */
|
|
204
|
+
readonly detached: readonly DetachedPackageRelease[];
|
|
205
|
+
readonly log?: (message: string) => void;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Finishes every package a `gatePublish: true` `releaseWorkspace` run tagged and pushed but did not publish. Resumes each `detached` entry in the array's own order -- not re-derived from the workspace's dependency graph, since a resume pass may run in a separate process or checkout, where re-deriving order should only ever matter for directory lookup, not sequencing.
|
|
209
|
+
*/
|
|
210
|
+
declare function resumeWorkspaceRelease(options: ResumeWorkspaceReleaseOptions): Promise<WorkspaceReleaseOutcome>;
|
|
211
|
+
//#endregion
|
|
187
212
|
//#region src/release.d.ts
|
|
188
213
|
/**
|
|
189
214
|
* How a run commits its released changes:
|
|
@@ -211,6 +236,10 @@ interface ReleaseWorkspaceOptions {
|
|
|
211
236
|
readonly log?: (message: string) => void;
|
|
212
237
|
/** How the run commits its released changes. Defaults to `'per-package'`, today's exact existing behaviour. See `CommitStrategy` for what each mode does. */
|
|
213
238
|
readonly commitStrategy?: CommitStrategy;
|
|
239
|
+
/**
|
|
240
|
+
* Opt-in, orthogonal to `commitStrategy` -- it governs *when* publish/success run relative to tag+push, not *how many commits* the release makes. When `true`, each package's release is tagged and pushed via `@exadev/release-gate`'s `detachRelease` but never published: `WorkspaceReleaseOutcome.detached` carries the state `resumeWorkspaceRelease` needs to finish publishing later, from the same process or a different one, once an external gate confirms the release should actually go out. Rejected outright in combination with `commitStrategy: 'single'` -- `single-commit-release.ts`'s tag/publish machinery is entirely bespoke and never goes through semantic-release's own `run()`, so it has no insertion point for `release-gate`'s primitives. Defaults to `false`, today's exact existing behaviour.
|
|
241
|
+
*/
|
|
242
|
+
readonly gatePublish?: boolean;
|
|
214
243
|
}
|
|
215
244
|
/** One dependency-range change applied to a dependent package's manifest during the run, attached to the dependent's own outcome. */
|
|
216
245
|
interface AppliedDependencyBump extends DependencyBump {
|
|
@@ -233,6 +262,8 @@ interface WorkspaceReleaseOutcome {
|
|
|
233
262
|
/** The topological order the packages were released in. */
|
|
234
263
|
readonly order: readonly string[];
|
|
235
264
|
readonly packages: readonly PackageReleaseOutcome[];
|
|
265
|
+
/** Present only when `gatePublish: true`: one entry per package in `order`, carrying the `@exadev/release-gate` state `resumeWorkspaceRelease` needs to finish publishing it later, or `null` for a package that had nothing to release. */
|
|
266
|
+
readonly detached?: readonly DetachedPackageRelease[];
|
|
236
267
|
}
|
|
237
268
|
/**
|
|
238
269
|
* Releases every package in a pnpm workspace with independent versions, in dependency order.
|
|
@@ -270,4 +301,4 @@ declare class GitCommandError extends WorkspaceReleaseError {
|
|
|
270
301
|
/** The workspace's git state does not support the release operation -- for example a detached HEAD, which names no branch that dependency-bump commits could be pushed to. */
|
|
271
302
|
declare class WorkspaceStateError extends WorkspaceReleaseError {}
|
|
272
303
|
//#endregion
|
|
273
|
-
export { type AppliedDependencyBump, type CommitStrategy, DEFAULT_PUBLISH_PLUGINS, type DependencyBump, type DependencyBumpSource, DependencyCycleError, type DependencyField, type DependencyGraph, type DependencyRangeShape, type DependencyRangeUpdate, GitCommandError, type PackageManifest, type PackageReleaseOutcome, type PublishPluginSpec, ReleaseConfigurationError, type ReleaseWorkspaceOptions, type ResolvedPublishPlugin, SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS, type ScopedPlugins, UnsupportedDependencyRangeError, type Workspace, type WorkspaceDependency, WorkspaceDiscoveryError, type WorkspacePackage, WorkspaceReleaseError, type WorkspaceReleaseOutcome, WorkspaceStateError, buildDependencyGraph, classifyDependencyRange, createScopedPlugins, discoverWorkspace, filterCommitsToDirectory, packageName, readManifest, releaseWorkspace, resolvePublishPlugins, topologicalOrder, updateDependencyRange, writeDependencyRange };
|
|
304
|
+
export { type AppliedDependencyBump, type CommitStrategy, DEFAULT_PUBLISH_PLUGINS, type DependencyBump, type DependencyBumpSource, DependencyCycleError, type DependencyField, type DependencyGraph, type DependencyRangeShape, type DependencyRangeUpdate, type DetachedPackageRelease, GitCommandError, type PackageManifest, type PackageReleaseOutcome, type PublishPluginSpec, ReleaseConfigurationError, type ReleaseWorkspaceOptions, type ResolvedPublishPlugin, type ResumeWorkspaceReleaseOptions, SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS, type ScopedPlugins, UnsupportedDependencyRangeError, type Workspace, type WorkspaceDependency, WorkspaceDiscoveryError, type WorkspacePackage, WorkspaceReleaseError, type WorkspaceReleaseOutcome, WorkspaceStateError, buildDependencyGraph, classifyDependencyRange, createScopedPlugins, discoverWorkspace, filterCommitsToDirectory, packageName, readManifest, releaseWorkspace, resolvePublishPlugins, resumeWorkspaceRelease, topologicalOrder, updateDependencyRange, writeDependencyRange };
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { analyzeCommits } from "@semantic-release/commit-analyzer";
|
|
|
10
10
|
import { generateNotes } from "@semantic-release/release-notes-generator";
|
|
11
11
|
import semanticRelease from "semantic-release";
|
|
12
12
|
import { pathToFileURL } from "node:url";
|
|
13
|
+
import { detachRelease, resumeRelease } from "@exadev/release-gate";
|
|
13
14
|
//#region src/package-name.ts
|
|
14
15
|
const packageName = "@exadev/semantic-release-workspace";
|
|
15
16
|
//#endregion
|
|
@@ -1006,6 +1007,134 @@ async function loadReleasePlugin(absolutePath, cache) {
|
|
|
1006
1007
|
return loaded;
|
|
1007
1008
|
}
|
|
1008
1009
|
//#endregion
|
|
1010
|
+
//#region src/gate-publish.ts
|
|
1011
|
+
/**
|
|
1012
|
+
* The `gatePublish: true` half of `releaseWorkspace`: tags and pushes every due package via `@exadev/release-gate`'s `detachRelease`, but never publishes. Built on the same `runReleaseLoop` the normal per-package path uses, so dependency-range bumps between packages happen identically either way -- only the release call itself (`detachRelease` instead of `semanticRelease`) and how "did it release" gets read differ.
|
|
1013
|
+
*/
|
|
1014
|
+
async function detachWorkspaceRelease(options) {
|
|
1015
|
+
const root = resolve(options.root ?? process.cwd());
|
|
1016
|
+
const log = options.log ?? console.log;
|
|
1017
|
+
const dryRun = options.dryRun === true;
|
|
1018
|
+
const env = sanitizeGitEnv(options.env ?? process.env);
|
|
1019
|
+
const workspace = await discoverWorkspace(root);
|
|
1020
|
+
const graph = buildDependencyGraph(workspace.packages);
|
|
1021
|
+
validateDependencyRangeShapes(graph);
|
|
1022
|
+
const order = topologicalOrder(graph);
|
|
1023
|
+
log(`${packageName}: ${order.length} packages in release order (gated -- tag only, publish deferred): ${order.join(" -> ")}`);
|
|
1024
|
+
const publishPlugins = resolvePublishPlugins(options.plugins ?? DEFAULT_PUBLISH_PLUGINS, workspace.root, { requireGitPlugin: !dryRun });
|
|
1025
|
+
const analyzeCommitsConfig = options.analyzeCommits ?? {};
|
|
1026
|
+
const generateNotesConfig = options.generateNotes ?? {};
|
|
1027
|
+
const entries = await runReleaseLoop(graph, order, workspace, dryRun, log, async (pkg, bumpsForThisPackage) => {
|
|
1028
|
+
const state = await runPackageDetach(pkg, {
|
|
1029
|
+
publishPlugins,
|
|
1030
|
+
analyzeCommitsConfig,
|
|
1031
|
+
generateNotesConfig,
|
|
1032
|
+
bumpsForThisPackage,
|
|
1033
|
+
dryRun,
|
|
1034
|
+
env,
|
|
1035
|
+
branches: options.branches
|
|
1036
|
+
});
|
|
1037
|
+
return {
|
|
1038
|
+
released: state !== null,
|
|
1039
|
+
version: state?.nextRelease.version,
|
|
1040
|
+
result: state
|
|
1041
|
+
};
|
|
1042
|
+
});
|
|
1043
|
+
return {
|
|
1044
|
+
order,
|
|
1045
|
+
packages: entries.map((entry) => ({
|
|
1046
|
+
name: entry.name,
|
|
1047
|
+
directory: entry.directory,
|
|
1048
|
+
released: entry.result !== null,
|
|
1049
|
+
version: entry.result?.nextRelease.version,
|
|
1050
|
+
gitTag: entry.result?.nextRelease.gitTag,
|
|
1051
|
+
type: entry.result?.nextRelease.type,
|
|
1052
|
+
dependencyBumps: entry.dependencyBumps
|
|
1053
|
+
})),
|
|
1054
|
+
detached: entries.map((entry) => ({
|
|
1055
|
+
name: entry.name,
|
|
1056
|
+
relativeDirectory: entry.relativeDirectory,
|
|
1057
|
+
state: entry.result,
|
|
1058
|
+
dependencyBumps: entry.dependencyBumps
|
|
1059
|
+
}))
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
async function runPackageDetach(pkg, options) {
|
|
1063
|
+
const scoped = createScopedPlugins({
|
|
1064
|
+
pkg,
|
|
1065
|
+
analyzeCommitsConfig: options.analyzeCommitsConfig,
|
|
1066
|
+
generateNotesConfig: options.generateNotesConfig,
|
|
1067
|
+
bumps: { bumpsFor: () => options.bumpsForThisPackage }
|
|
1068
|
+
});
|
|
1069
|
+
const cliOptions = {
|
|
1070
|
+
tagFormat: `${pkg.name}@\${version}`,
|
|
1071
|
+
plugins: options.publishPlugins,
|
|
1072
|
+
analyzeCommits: scoped.analyzeCommits,
|
|
1073
|
+
generateNotes: scoped.generateNotes
|
|
1074
|
+
};
|
|
1075
|
+
if (options.dryRun) cliOptions.dryRun = true;
|
|
1076
|
+
if (options.branches !== void 0) cliOptions.branches = options.branches;
|
|
1077
|
+
try {
|
|
1078
|
+
return await detachRelease(cliOptions, {
|
|
1079
|
+
cwd: pkg.directory,
|
|
1080
|
+
env: { ...options.env }
|
|
1081
|
+
});
|
|
1082
|
+
} catch (cause) {
|
|
1083
|
+
throw new WorkspaceReleaseError(`Detaching ${pkg.name} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Finishes every package a `gatePublish: true` `releaseWorkspace` run tagged and pushed but did not publish. Resumes each `detached` entry in the array's own order -- not re-derived from the workspace's dependency graph, since a resume pass may run in a separate process or checkout, where re-deriving order should only ever matter for directory lookup, not sequencing.
|
|
1088
|
+
*/
|
|
1089
|
+
async function resumeWorkspaceRelease(options) {
|
|
1090
|
+
const root = resolve(options.root ?? process.cwd());
|
|
1091
|
+
const log = options.log ?? console.log;
|
|
1092
|
+
const env = sanitizeGitEnv(options.env ?? process.env);
|
|
1093
|
+
const order = [];
|
|
1094
|
+
const packages = [];
|
|
1095
|
+
for (const entry of options.detached) {
|
|
1096
|
+
order.push(entry.name);
|
|
1097
|
+
if (entry.state === null) {
|
|
1098
|
+
log(`${entry.name}: no release to resume`);
|
|
1099
|
+
packages.push({
|
|
1100
|
+
name: entry.name,
|
|
1101
|
+
directory: resolve(root, entry.relativeDirectory),
|
|
1102
|
+
released: false,
|
|
1103
|
+
version: void 0,
|
|
1104
|
+
gitTag: void 0,
|
|
1105
|
+
type: void 0,
|
|
1106
|
+
dependencyBumps: entry.dependencyBumps
|
|
1107
|
+
});
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
const directory = resolve(root, entry.relativeDirectory);
|
|
1111
|
+
log(`Resuming ${entry.name} from ${entry.relativeDirectory}`);
|
|
1112
|
+
let releases;
|
|
1113
|
+
try {
|
|
1114
|
+
releases = await resumeRelease(entry.state, {
|
|
1115
|
+
cwd: directory,
|
|
1116
|
+
env: { ...env }
|
|
1117
|
+
});
|
|
1118
|
+
} catch (cause) {
|
|
1119
|
+
throw new WorkspaceReleaseError(`Resuming ${entry.name} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
1120
|
+
}
|
|
1121
|
+
log(`${entry.name}: published ${entry.state.nextRelease.gitTag} (${releases.length} publish plugin${releases.length === 1 ? "" : "s"} ran)`);
|
|
1122
|
+
packages.push({
|
|
1123
|
+
name: entry.name,
|
|
1124
|
+
directory,
|
|
1125
|
+
released: true,
|
|
1126
|
+
version: entry.state.nextRelease.version,
|
|
1127
|
+
gitTag: entry.state.nextRelease.gitTag,
|
|
1128
|
+
type: entry.state.nextRelease.type,
|
|
1129
|
+
dependencyBumps: entry.dependencyBumps
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
return {
|
|
1133
|
+
order,
|
|
1134
|
+
packages
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
//#endregion
|
|
1009
1138
|
//#region src/release.ts
|
|
1010
1139
|
/**
|
|
1011
1140
|
* Releases every package in a pnpm workspace with independent versions, in dependency order.
|
|
@@ -1013,6 +1142,10 @@ async function loadReleasePlugin(absolutePath, cache) {
|
|
|
1013
1142
|
* For each package, in topological order: run semantic-release's programmatic API with `cwd` scoped to the package directory, a `name@version` tag format to keep each package's tags distinct in the one shared tag namespace, and inline `analyzeCommits`/`generateNotes` plugins that filter the release range's commits down to the package's own directory before delegating to the standard plugins. When a package releases, every workspace package that depends on it and has not run yet gets its dependency range rewritten in its manifest and committed immediately -- before its own turn, so its commit analysis and its published manifest both see the new range.
|
|
1014
1143
|
*/
|
|
1015
1144
|
async function releaseWorkspace(options = {}) {
|
|
1145
|
+
if (options.gatePublish === true) {
|
|
1146
|
+
if ((options.commitStrategy ?? "per-package") === "single") throw new ReleaseConfigurationError("gatePublish is not supported together with commitStrategy: \"single\" -- single-commit-release.ts's tag/publish machinery is entirely bespoke and never goes through semantic-release's own run(), so it has no insertion point for release-gate's detach/resume primitives.");
|
|
1147
|
+
return detachWorkspaceRelease(options);
|
|
1148
|
+
}
|
|
1016
1149
|
if ((options.commitStrategy ?? "per-package") === "single") return releaseWorkspaceSingleCommit(options);
|
|
1017
1150
|
const root = resolve(options.root ?? process.cwd());
|
|
1018
1151
|
const log = options.log ?? console.log;
|
|
@@ -1026,40 +1159,65 @@ async function releaseWorkspace(options = {}) {
|
|
|
1026
1159
|
const publishPlugins = resolvePublishPlugins(options.plugins ?? DEFAULT_PUBLISH_PLUGINS, workspace.root, { requireGitPlugin: !dryRun });
|
|
1027
1160
|
const analyzeCommitsConfig = options.analyzeCommits ?? {};
|
|
1028
1161
|
const generateNotesConfig = options.generateNotes ?? {};
|
|
1162
|
+
return {
|
|
1163
|
+
order,
|
|
1164
|
+
packages: (await runReleaseLoop(graph, order, workspace, dryRun, log, async (pkg, bumpsForThisPackage) => {
|
|
1165
|
+
const result = await runPackageRelease(pkg, {
|
|
1166
|
+
publishPlugins,
|
|
1167
|
+
analyzeCommitsConfig,
|
|
1168
|
+
generateNotesConfig,
|
|
1169
|
+
bumpsForThisPackage,
|
|
1170
|
+
dryRun,
|
|
1171
|
+
env,
|
|
1172
|
+
branches: options.branches
|
|
1173
|
+
});
|
|
1174
|
+
const nextRelease = result === false ? void 0 : result.nextRelease;
|
|
1175
|
+
return {
|
|
1176
|
+
released: nextRelease !== void 0,
|
|
1177
|
+
version: nextRelease?.version,
|
|
1178
|
+
result
|
|
1179
|
+
};
|
|
1180
|
+
})).map((entry) => {
|
|
1181
|
+
const nextRelease = entry.result === false ? void 0 : entry.result.nextRelease;
|
|
1182
|
+
return {
|
|
1183
|
+
name: entry.name,
|
|
1184
|
+
directory: entry.directory,
|
|
1185
|
+
released: nextRelease !== void 0,
|
|
1186
|
+
version: nextRelease?.version,
|
|
1187
|
+
gitTag: nextRelease?.gitTag,
|
|
1188
|
+
type: nextRelease?.type,
|
|
1189
|
+
dependencyBumps: entry.dependencyBumps
|
|
1190
|
+
};
|
|
1191
|
+
})
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* 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, and what version" off the result, both captured by `releaseOne`.
|
|
1196
|
+
*/
|
|
1197
|
+
async function runReleaseLoop(graph, order, workspace, dryRun, log, releaseOne) {
|
|
1029
1198
|
const pendingBumps = /* @__PURE__ */ new Map();
|
|
1030
1199
|
let identity;
|
|
1031
|
-
const
|
|
1200
|
+
const entries = [];
|
|
1032
1201
|
for (const name of order) {
|
|
1033
1202
|
const pkg = mustGet(graph.packages, name, "package");
|
|
1034
1203
|
const bumpsForThisPackage = pendingBumps.get(name) ?? [];
|
|
1035
1204
|
log(`Releasing ${name} from ${pkg.relativeDirectory}${bumpsForThisPackage.length > 0 ? ` (dependency ranges already bumped: ${bumpsForThisPackage.map((bump) => bump.dependency).join(", ")})` : ""}`);
|
|
1036
|
-
const result = await
|
|
1037
|
-
|
|
1038
|
-
analyzeCommitsConfig,
|
|
1039
|
-
generateNotesConfig,
|
|
1040
|
-
bumpsForThisPackage,
|
|
1041
|
-
dryRun,
|
|
1042
|
-
env,
|
|
1043
|
-
branches: options.branches
|
|
1044
|
-
});
|
|
1045
|
-
const nextRelease = result === false ? void 0 : result.nextRelease;
|
|
1046
|
-
outcomes.push({
|
|
1205
|
+
const { released, version, result } = await releaseOne(pkg, bumpsForThisPackage);
|
|
1206
|
+
entries.push({
|
|
1047
1207
|
name,
|
|
1048
1208
|
directory: pkg.directory,
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
gitTag: nextRelease?.gitTag,
|
|
1052
|
-
type: nextRelease?.type,
|
|
1209
|
+
relativeDirectory: pkg.relativeDirectory,
|
|
1210
|
+
result,
|
|
1053
1211
|
dependencyBumps: bumpsForThisPackage
|
|
1054
1212
|
});
|
|
1055
1213
|
pendingBumps.delete(name);
|
|
1056
|
-
if (
|
|
1214
|
+
if (!released || version === void 0) {
|
|
1057
1215
|
log(`${name}: no release`);
|
|
1058
1216
|
continue;
|
|
1059
1217
|
}
|
|
1060
|
-
log(`${name}: released ${
|
|
1218
|
+
log(`${name}: released ${version}`);
|
|
1061
1219
|
identity ??= await resolveCommitIdentity({ cwd: workspace.root });
|
|
1062
|
-
const bumps = await bumpDependents(pkg,
|
|
1220
|
+
const bumps = await bumpDependents(pkg, version, graph, {
|
|
1063
1221
|
workspace,
|
|
1064
1222
|
dryRun,
|
|
1065
1223
|
identity,
|
|
@@ -1071,10 +1229,7 @@ async function releaseWorkspace(options = {}) {
|
|
|
1071
1229
|
pendingBumps.set(bump.dependent, forDependent);
|
|
1072
1230
|
}
|
|
1073
1231
|
}
|
|
1074
|
-
return
|
|
1075
|
-
order,
|
|
1076
|
-
packages: outcomes
|
|
1077
|
-
};
|
|
1232
|
+
return entries;
|
|
1078
1233
|
}
|
|
1079
1234
|
async function runPackageRelease(pkg, options) {
|
|
1080
1235
|
const scoped = createScopedPlugins({
|
|
@@ -1145,4 +1300,4 @@ async function bumpDependents(released, version, graph, options) {
|
|
|
1145
1300
|
return applied;
|
|
1146
1301
|
}
|
|
1147
1302
|
//#endregion
|
|
1148
|
-
export { DEFAULT_PUBLISH_PLUGINS, DependencyCycleError, GitCommandError, ReleaseConfigurationError, SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS, UnsupportedDependencyRangeError, WorkspaceDiscoveryError, WorkspaceReleaseError, WorkspaceStateError, buildDependencyGraph, classifyDependencyRange, createScopedPlugins, discoverWorkspace, filterCommitsToDirectory, packageName, readManifest, releaseWorkspace, resolvePublishPlugins, topologicalOrder, updateDependencyRange, writeDependencyRange };
|
|
1303
|
+
export { DEFAULT_PUBLISH_PLUGINS, DependencyCycleError, GitCommandError, ReleaseConfigurationError, SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS, UnsupportedDependencyRangeError, WorkspaceDiscoveryError, WorkspaceReleaseError, WorkspaceStateError, buildDependencyGraph, classifyDependencyRange, createScopedPlugins, discoverWorkspace, filterCommitsToDirectory, packageName, readManifest, releaseWorkspace, resolvePublishPlugins, resumeWorkspaceRelease, topologicalOrder, updateDependencyRange, writeDependencyRange };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exadev/semantic-release-workspace",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Independent per-package semantic-release orchestration for pnpm workspaces, without lockstep versioning.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
"license": "MIT",
|
|
78
78
|
"packageManager": "pnpm@11.6.0",
|
|
79
79
|
"dependencies": {
|
|
80
|
+
"@exadev/release-gate": "^1.0.0",
|
|
80
81
|
"commander": "^15.0.0",
|
|
81
82
|
"cosmiconfig": "^9.0.2",
|
|
82
83
|
"tinyglobby": "^0.2.17",
|