@exadev/semantic-release-workspace 1.3.4 → 1.3.6
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/dist/cli.js +107 -57
- package/dist/index.cjs +105 -55
- package/dist/index.d.cts +18 -13
- package/dist/index.d.ts +18 -13
- package/dist/index.js +105 -55
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,6 @@ import { Command, InvalidArgumentError } from "commander";
|
|
|
6
6
|
import { dirname, relative, resolve, sep } from "node:path";
|
|
7
7
|
import { detachRelease, resumeRelease } from "@exadev/release-gate";
|
|
8
8
|
import { execFile } from "node:child_process";
|
|
9
|
-
import { promisify } from "node:util";
|
|
10
9
|
import validateNpmPackageName from "validate-npm-package-name";
|
|
11
10
|
import { glob } from "tinyglobby";
|
|
12
11
|
import { parse } from "yaml";
|
|
@@ -15,7 +14,7 @@ import { generateNotes } from "@semantic-release/release-notes-generator";
|
|
|
15
14
|
import semanticRelease from "semantic-release";
|
|
16
15
|
import { pathToFileURL } from "node:url";
|
|
17
16
|
//#region package.json
|
|
18
|
-
var version = "1.3.
|
|
17
|
+
var version = "1.3.6";
|
|
19
18
|
//#endregion
|
|
20
19
|
//#region src/errors.ts
|
|
21
20
|
/**
|
|
@@ -48,7 +47,7 @@ var ReleaseConfigurationError = class extends WorkspaceReleaseError {};
|
|
|
48
47
|
var GitCommandError = class extends WorkspaceReleaseError {
|
|
49
48
|
exitCode;
|
|
50
49
|
constructor(args, cwd, exitCode, detail) {
|
|
51
|
-
super(`git ${args.join(" ")} failed in ${cwd}${exitCode === void 0 ? "" : ` (exit ${exitCode})`}: ${detail}`);
|
|
50
|
+
super(`git ${args.join(" ")} failed in ${cwd}${exitCode === void 0 ? "" : ` (exit ${String(exitCode)})`}: ${detail}`);
|
|
52
51
|
this.exitCode = exitCode;
|
|
53
52
|
}
|
|
54
53
|
};
|
|
@@ -61,10 +60,30 @@ var PnpmCommandError = class extends WorkspaceReleaseError {
|
|
|
61
60
|
}
|
|
62
61
|
};
|
|
63
62
|
//#endregion
|
|
63
|
+
//#region src/exec-file.ts
|
|
64
|
+
/**
|
|
65
|
+
* A hand-written promise wrapper around `child_process.execFile`, in place of `util.promisify(execFile)`: `execFile` synchronously returns a `ChildProcess` in addition to invoking its callback, which trips `@typescript-eslint/strict-void-return` when the whole function is handed to `promisify` (a value-returning function used where a void-returning one is contextually expected there) -- exactly the void-return contravariance leniency that rule exists to catch, even though `tsc` itself accepts the pattern. Calling `execFile` directly with our own callback, whose own return type really is `void`, sidesteps the mismatch instead of suppressing it.
|
|
66
|
+
*/
|
|
67
|
+
async function execFile$1(command, args, options) {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
execFile(command, [...args], {
|
|
70
|
+
cwd: options.cwd,
|
|
71
|
+
env: options.env,
|
|
72
|
+
maxBuffer: options.maxBuffer
|
|
73
|
+
}, (error, stdout, stderr) => {
|
|
74
|
+
if (error) {
|
|
75
|
+
reject(error instanceof Error ? error : new Error(error.message));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
resolve({
|
|
79
|
+
stdout,
|
|
80
|
+
stderr
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
64
86
|
//#region src/git.ts
|
|
65
|
-
const execFileAsync$1 = promisify(execFile);
|
|
66
|
-
/** `git log --name-only` over everything since a package's last release tag can legitimately produce tens of megabytes of path output on a long-lived monorepo, well past execFile's default buffer, failing on exactly the big workspaces this tool exists for. */
|
|
67
|
-
const GIT_MAX_BUFFER_BYTES = 104857600;
|
|
68
87
|
/** Separates one commit's record in `git log --format` output. Chosen from the C0 control range so it can never appear in a hash or a file path. */
|
|
69
88
|
const COMMIT_RECORD_SEPARATOR = "";
|
|
70
89
|
/** The identity semantic-release's own core writes release commits under in CI when nothing else is configured (its COMMIT_NAME/COMMIT_EMAIL constants); dependency-bump commits use the same fallback so every commit a release run produces has a consistent author when the repository declares none. */
|
|
@@ -92,18 +111,22 @@ const GIT_REPOSITORY_DISCOVERY_ENV_KEYS = [
|
|
|
92
111
|
*/
|
|
93
112
|
function sanitizeGitEnv(env) {
|
|
94
113
|
const sanitized = { ...env };
|
|
95
|
-
for (const key of GIT_REPOSITORY_DISCOVERY_ENV_KEYS)
|
|
114
|
+
for (const key of GIT_REPOSITORY_DISCOVERY_ENV_KEYS) Reflect.deleteProperty(sanitized, key);
|
|
96
115
|
return sanitized;
|
|
97
116
|
}
|
|
98
117
|
const SANITIZED_PROCESS_GIT_ENV = sanitizeGitEnv(process.env);
|
|
118
|
+
/** `git log --name-only` over everything since a package's last release tag can legitimately produce tens of megabytes of path output on a long-lived monorepo, well past execFile's default buffer, failing on exactly the big workspaces this tool exists for -- hence the generous 100 MiB default, rather than execFile's own. */
|
|
119
|
+
async function execGit(args, cwd, maxBuffer = 104857600) {
|
|
120
|
+
const { stdout } = await execFile$1("git", args, {
|
|
121
|
+
cwd,
|
|
122
|
+
maxBuffer,
|
|
123
|
+
env: SANITIZED_PROCESS_GIT_ENV
|
|
124
|
+
});
|
|
125
|
+
return stdout;
|
|
126
|
+
}
|
|
99
127
|
async function git(args, options) {
|
|
100
128
|
try {
|
|
101
|
-
|
|
102
|
-
cwd: options.cwd,
|
|
103
|
-
maxBuffer: GIT_MAX_BUFFER_BYTES,
|
|
104
|
-
env: SANITIZED_PROCESS_GIT_ENV
|
|
105
|
-
});
|
|
106
|
-
return stdout;
|
|
129
|
+
return await execGit(args, options.cwd);
|
|
107
130
|
} catch (cause) {
|
|
108
131
|
throw toGitCommandError(args, options.cwd, cause);
|
|
109
132
|
}
|
|
@@ -217,9 +240,11 @@ async function workingTreeChanges(options) {
|
|
|
217
240
|
const paths = [];
|
|
218
241
|
for (let index = 0; index < tokens.length; index += 1) {
|
|
219
242
|
const entry = tokens[index];
|
|
220
|
-
if (entry === void 0
|
|
243
|
+
if (entry === void 0) continue;
|
|
221
244
|
const statusCode = entry.slice(0, 2);
|
|
222
|
-
|
|
245
|
+
const path = entry.slice(3);
|
|
246
|
+
if (path === "") continue;
|
|
247
|
+
paths.push(path);
|
|
223
248
|
if (statusCode.includes("R") || statusCode.includes("C")) index += 1;
|
|
224
249
|
}
|
|
225
250
|
return paths;
|
|
@@ -290,7 +315,7 @@ async function readManifest(path) {
|
|
|
290
315
|
const { name, version } = parsed;
|
|
291
316
|
if (typeof name !== "string" || name.length === 0) throw new WorkspaceDiscoveryError(`${path} has no "name". Every workspace package needs a name: releases are ordered, tagged, and matched to dependents by it.`);
|
|
292
317
|
const validity = validateNpmPackageName(name);
|
|
293
|
-
if (!validity.validForOldPackages) throw new WorkspaceDiscoveryError(`${path} has an invalid "name" ("${name}"): ${
|
|
318
|
+
if (!validity.validForOldPackages) throw new WorkspaceDiscoveryError(`${path} has an invalid "name" ("${name}"): ${validity.errors.join("; ")}`);
|
|
294
319
|
if (typeof version !== "string" || version.length === 0) throw new WorkspaceDiscoveryError(`${path} has no "version".`);
|
|
295
320
|
const dependencies = /* @__PURE__ */ new Map();
|
|
296
321
|
for (const field of DEPENDENCY_FIELDS) {
|
|
@@ -596,6 +621,10 @@ function matchTrailerLine(message, key) {
|
|
|
596
621
|
}
|
|
597
622
|
//#endregion
|
|
598
623
|
//#region src/plugins.ts
|
|
624
|
+
/** semantic-release's own `getLastRelease` returns `{}` for a package with no prior tag -- not `undefined`, and not a fully-populated `LastRelease` -- contradicting the `gitHead: string` its own type declares. Narrows structurally rather than trusting that declared type, so a first-release context's `lastRelease` (correctly, at runtime) never claims a `gitHead` it does not have. */
|
|
625
|
+
function hasGitHead(lastRelease) {
|
|
626
|
+
return typeof lastRelease === "object" && lastRelease !== null && "gitHead" in lastRelease && typeof lastRelease.gitHead === "string";
|
|
627
|
+
}
|
|
599
628
|
/** The standard publish pipeline this orchestrator coordinates when a workspace configures none of its own. Every entry reuses the corresponding official plugin -- the orchestrator scopes and sequences them per package, it does not reimplement npm publishing, GitHub release creation, or changelog writing. */
|
|
600
629
|
const DEFAULT_PUBLISH_PLUGINS = [
|
|
601
630
|
"@semantic-release/changelog",
|
|
@@ -606,7 +635,7 @@ const DEFAULT_PUBLISH_PLUGINS = [
|
|
|
606
635
|
message: "chore(release): ${nextRelease.gitTag} [skip ci]"
|
|
607
636
|
}]
|
|
608
637
|
];
|
|
609
|
-
/** The standard publish pipeline for `commitStrategy: 'single'`: the same as `DEFAULT_PUBLISH_PLUGINS` minus
|
|
638
|
+
/** The standard publish pipeline for `commitStrategy: 'single'`: the same as `DEFAULT_PUBLISH_PLUGINS` minus `@semantic-release/git`, which that mode never runs -- see `resolvePublishPlugins`'s `forbidGitPlugin` option for why it is rejected outright rather than merely unused. Single-commit mode does its own committing (one combined commit for every released package), so a `prepare`-step git plugin here would create the very per-package commits that mode exists to avoid. */
|
|
610
639
|
const SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS = [
|
|
611
640
|
"@semantic-release/changelog",
|
|
612
641
|
"@semantic-release/npm",
|
|
@@ -616,14 +645,14 @@ const STEP_PLUGINS_THE_ORCHESTRATOR_OWNS = /* @__PURE__ */ new Set(["@semantic-r
|
|
|
616
645
|
/**
|
|
617
646
|
* Builds the per-package `analyzeCommits` and `generateNotes` functions handed to semantic-release as inline plugins.
|
|
618
647
|
*
|
|
619
|
-
* Both apply the same path scoping before delegating to the real
|
|
648
|
+
* 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.
|
|
620
649
|
*
|
|
621
650
|
* The `analyzeCommits` wrapper carries one addition 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. "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.
|
|
622
651
|
*/
|
|
623
652
|
function createScopedPlugins(scope) {
|
|
624
653
|
let cached;
|
|
625
654
|
async function commitsForPackage(context) {
|
|
626
|
-
const from = context.lastRelease
|
|
655
|
+
const from = hasGitHead(context.lastRelease) ? context.lastRelease.gitHead : void 0;
|
|
627
656
|
if (cached === void 0) cached = {
|
|
628
657
|
from,
|
|
629
658
|
paths: changedPathsSince(from, { cwd: context.cwd })
|
|
@@ -643,10 +672,10 @@ function createScopedPlugins(scope) {
|
|
|
643
672
|
...context,
|
|
644
673
|
commits
|
|
645
674
|
});
|
|
646
|
-
if (type) return type;
|
|
675
|
+
if (typeof type === "string") return type;
|
|
647
676
|
const bumps = mergeDependencyBumps(scope.bumps.bumpsFor(scope.pkg.name), commits);
|
|
648
677
|
if (bumps.length === 0) return false;
|
|
649
|
-
context.logger.log(`No releasable commits under ${scope.pkg.relativeDirectory}, but ${bumps.length === 1 ? "a workspace dependency range changed" : `${bumps.length} workspace dependency ranges changed`}; forcing a patch release.`);
|
|
678
|
+
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.`);
|
|
650
679
|
return "patch";
|
|
651
680
|
},
|
|
652
681
|
async generateNotes(_pluginConfig, context) {
|
|
@@ -662,7 +691,7 @@ function createScopedPlugins(scope) {
|
|
|
662
691
|
"",
|
|
663
692
|
...bumps.map((bump) => describeDependencyBump(bump))
|
|
664
693
|
].join("\n");
|
|
665
|
-
return notes ? `${notes}\n\n${section}` : section;
|
|
694
|
+
return typeof notes === "string" ? `${notes}\n\n${section}` : section;
|
|
666
695
|
}
|
|
667
696
|
};
|
|
668
697
|
}
|
|
@@ -734,7 +763,6 @@ function parsePublishPluginSpec(spec) {
|
|
|
734
763
|
}
|
|
735
764
|
//#endregion
|
|
736
765
|
//#region src/pnpm.ts
|
|
737
|
-
const execFileAsync = promisify(execFile);
|
|
738
766
|
/**
|
|
739
767
|
* Regenerates `pnpm-lock.yaml` for the whole workspace from the manifests currently on disk, without touching `node_modules` or installing anything -- the same lockfile-refresh step a contributor runs by hand after editing a `package.json` dependency range.
|
|
740
768
|
*
|
|
@@ -742,7 +770,7 @@ const execFileAsync = promisify(execFile);
|
|
|
742
770
|
*/
|
|
743
771
|
async function regenerateLockfile(options) {
|
|
744
772
|
try {
|
|
745
|
-
await
|
|
773
|
+
await execFile$1("pnpm", ["install", "--lockfile-only"], { cwd: options.cwd });
|
|
746
774
|
} catch (cause) {
|
|
747
775
|
const stderr = cause instanceof Error && "stderr" in cause && typeof cause.stderr === "string" ? cause.stderr.trim() : "";
|
|
748
776
|
const detail = stderr !== "" ? stderr : cause instanceof Error ? cause.message : String(cause);
|
|
@@ -758,7 +786,7 @@ async function regenerateLockfile(options) {
|
|
|
758
786
|
*
|
|
759
787
|
* 1. **Analyse** (this file's `analysePackage`): for every package, in topological order, run semantic-release with `dryRun: true` forced (regardless of the caller's own `dryRun` option) using the same path-scoped `analyzeCommits`/`generateNotes` wrapper `commitStrategy: 'per-package'` uses -- computing each package's next version and notes without writing, committing, tagging, or publishing anything. Cross-package dependency bumps are tracked purely in memory during this phase (`pendingBumps`), exactly as the per-package strategy tracks them for the span of one run; nothing is committed yet for a later run to recover from, because this strategy never leaves a partial commit for a crash to recover from in the first place -- either the whole combined commit lands, or nothing does.
|
|
760
788
|
* 2. **Verify** every released package's configured publish plugins' `verifyConditions` step (npm registry auth, GitHub token/repo access), before any file is written -- the same fail-fast-before-anything-releases discipline `validateDependencyRangeShapes` already applies to dependency ranges.
|
|
761
|
-
* 3. **Prepare**: for every released package, in topological order, apply any dependency-range bump its own manifest received (writing `package.json` directly, the same `writeDependencyRange` the per-package strategy uses), then run every configured publish plugin's own `prepare` step generically (whichever it defines --
|
|
789
|
+
* 3. **Prepare**: for every released package, in topological order, apply any dependency-range bump its own manifest received (writing `package.json` directly, the same `writeDependencyRange` the per-package strategy uses), then run every configured publish plugin's own `prepare` step generically (whichever it defines -- `@semantic-release/npm` bumps `package.json`'s version, `@semantic-release/changelog` writes `CHANGELOG.md`). `@semantic-release/git` is rejected outright from this mode's plugin list (see `resolvePublishPlugins`'s `forbidGitPlugin`), since its own `prepare` step would create exactly the per-package commit this mode exists to avoid. The lockfile is regenerated once at the end, not once per bump, since `pnpm install --lockfile-only` recomputes it from whatever is on disk regardless of how many manifests changed.
|
|
762
790
|
* 4. **Commit**: discover every file phase 3 touched via `git status` (rather than predicting filenames per plugin), make one commit, tag it once per released package (`name@version`, lightweight, matching semantic-release's own tag form), and push the commit and every tag together.
|
|
763
791
|
* 5. **Publish**: for every released package, in topological order, call each configured plugin's own `publish` step directly (not through semantic-release's top-level orchestrator -- see the note below), then `success`.
|
|
764
792
|
*
|
|
@@ -775,7 +803,7 @@ async function releaseWorkspaceSingleCommit(options) {
|
|
|
775
803
|
const graph = buildDependencyGraph(workspace.packages);
|
|
776
804
|
validateDependencyRangeShapes(graph);
|
|
777
805
|
const order = topologicalOrder(graph);
|
|
778
|
-
log(`${packageName}: ${order.length} packages in release order: ${order.join(" -> ")} (commitStrategy: single)`);
|
|
806
|
+
log(`${packageName}: ${String(order.length)} packages in release order: ${order.join(" -> ")} (commitStrategy: single)`);
|
|
779
807
|
const resolvedPlugins = resolvePublishPlugins(options.plugins ?? SINGLE_COMMIT_DEFAULT_PUBLISH_PLUGINS, workspace.root, {
|
|
780
808
|
requireGitPlugin: false,
|
|
781
809
|
forbidGitPlugin: true
|
|
@@ -801,19 +829,26 @@ async function releaseWorkspaceSingleCommit(options) {
|
|
|
801
829
|
bumpsForThisPackage,
|
|
802
830
|
env,
|
|
803
831
|
branches: options.branches,
|
|
804
|
-
onCommitsResolved: (commits) =>
|
|
832
|
+
onCommitsResolved: (commits) => {
|
|
833
|
+
capturedCommits.set(name, commits);
|
|
834
|
+
},
|
|
805
835
|
onContextCaptured: (context) => {
|
|
806
836
|
captured.branch = context.branch;
|
|
807
837
|
captured.repositoryUrl = context.repositoryUrl;
|
|
808
838
|
}
|
|
809
839
|
});
|
|
810
|
-
outcomes.push({
|
|
840
|
+
outcomes.push(nextRelease === void 0 ? {
|
|
811
841
|
name,
|
|
812
842
|
directory: pkg.directory,
|
|
813
|
-
released:
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
843
|
+
released: false,
|
|
844
|
+
dependencyBumps: bumpsForThisPackage
|
|
845
|
+
} : {
|
|
846
|
+
name,
|
|
847
|
+
directory: pkg.directory,
|
|
848
|
+
released: true,
|
|
849
|
+
version: nextRelease.version,
|
|
850
|
+
gitTag: nextRelease.gitTag,
|
|
851
|
+
type: nextRelease.type,
|
|
817
852
|
dependencyBumps: bumpsForThisPackage
|
|
818
853
|
});
|
|
819
854
|
if (nextRelease === void 0) {
|
|
@@ -841,7 +876,7 @@ async function releaseWorkspaceSingleCommit(options) {
|
|
|
841
876
|
};
|
|
842
877
|
const branch = captured.branch;
|
|
843
878
|
const repositoryUrl = captured.repositoryUrl;
|
|
844
|
-
if (branch === void 0 || repositoryUrl === void 0) throw new ReleaseConfigurationError(`Internal error: ${packageName} analysed ${planned.length} package release(s) but never captured a branch/repositoryUrl from semantic-release's own context. This should be impossible when at least one package releases.`);
|
|
879
|
+
if (branch === void 0 || repositoryUrl === void 0) throw new ReleaseConfigurationError(`Internal error: ${packageName} analysed ${String(planned.length)} package release(s) but never captured a branch/repositoryUrl from semantic-release's own context. This should be impossible when at least one package releases.`);
|
|
845
880
|
const shared = {
|
|
846
881
|
env,
|
|
847
882
|
branch,
|
|
@@ -867,7 +902,7 @@ async function releaseWorkspaceSingleCommit(options) {
|
|
|
867
902
|
}
|
|
868
903
|
if (anyRangeRewritten) await regenerateLockfile({ cwd: workspace.root });
|
|
869
904
|
const touchedPaths = await workingTreeChanges({ cwd: repoRoot });
|
|
870
|
-
if (touchedPaths.length === 0) throw new ReleaseConfigurationError(`${packageName}: analysis planned ${planned.length} release(s), but no files changed while preparing them. Every configured publish plugin's own "prepare" step (bumping package.json, writing CHANGELOG.md) produced nothing to commit -- check the plugin list includes something that writes the version, e.g. @semantic-release/npm.`);
|
|
905
|
+
if (touchedPaths.length === 0) throw new ReleaseConfigurationError(`${packageName}: analysis planned ${String(planned.length)} release(s), but no files changed while preparing them. Every configured publish plugin's own "prepare" step (bumping package.json, writing CHANGELOG.md) produced nothing to commit -- check the plugin list includes something that writes the version, e.g. @semantic-release/npm.`);
|
|
871
906
|
const identity = await resolveCommitIdentity({ cwd: repoRoot });
|
|
872
907
|
await commitFiles(touchedPaths, describeCombinedCommit(planned), {
|
|
873
908
|
cwd: repoRoot,
|
|
@@ -877,7 +912,7 @@ async function releaseWorkspaceSingleCommit(options) {
|
|
|
877
912
|
const tagNames = planned.map((release) => release.gitTag);
|
|
878
913
|
for (const tagName of tagNames) await createTag(tagName, commitSha, { cwd: repoRoot });
|
|
879
914
|
await pushHeadAndTags(tagNames, { cwd: repoRoot });
|
|
880
|
-
log(`${packageName}: committed ${commitSha} and pushed ${tagNames.length} tag(s): ${tagNames.join(", ")}`);
|
|
915
|
+
log(`${packageName}: committed ${commitSha} and pushed ${String(tagNames.length)} tag(s): ${tagNames.join(", ")}`);
|
|
881
916
|
for (const release of planned) {
|
|
882
917
|
const releases = [];
|
|
883
918
|
for (const [modulePath, pluginConfig] of resolvedPlugins) {
|
|
@@ -969,10 +1004,18 @@ function describeCombinedCommit(planned) {
|
|
|
969
1004
|
}
|
|
970
1005
|
function buildPluginContext(release, shared, capturedCommits, releases) {
|
|
971
1006
|
const logger = {
|
|
972
|
-
log: (...args) =>
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1007
|
+
log: (...args) => {
|
|
1008
|
+
shared.log(`[${release.pkg.name}] ${args.map(String).join(" ")}`);
|
|
1009
|
+
},
|
|
1010
|
+
warn: (...args) => {
|
|
1011
|
+
shared.log(`[${release.pkg.name}] warn: ${args.map(String).join(" ")}`);
|
|
1012
|
+
},
|
|
1013
|
+
error: (...args) => {
|
|
1014
|
+
shared.log(`[${release.pkg.name}] error: ${args.map(String).join(" ")}`);
|
|
1015
|
+
},
|
|
1016
|
+
success: (...args) => {
|
|
1017
|
+
shared.log(`[${release.pkg.name}] ${args.map(String).join(" ")}`);
|
|
1018
|
+
}
|
|
976
1019
|
};
|
|
977
1020
|
return {
|
|
978
1021
|
cwd: release.pkg.directory,
|
|
@@ -1039,7 +1082,7 @@ async function releaseWorkspace(options = {}) {
|
|
|
1039
1082
|
const graph = buildDependencyGraph(workspace.packages);
|
|
1040
1083
|
validateDependencyRangeShapes(graph);
|
|
1041
1084
|
const order = topologicalOrder(graph);
|
|
1042
|
-
log(`${packageName}: ${order.length} packages in release order: ${order.join(" -> ")}`);
|
|
1085
|
+
log(`${packageName}: ${String(order.length)} packages in release order: ${order.join(" -> ")}`);
|
|
1043
1086
|
const publishPlugins = resolvePublishPlugins(options.plugins ?? DEFAULT_PUBLISH_PLUGINS, workspace.root, { requireGitPlugin: !dryRun });
|
|
1044
1087
|
const analyzeCommitsConfig = options.analyzeCommits ?? {};
|
|
1045
1088
|
const generateNotesConfig = options.generateNotes ?? {};
|
|
@@ -1063,13 +1106,18 @@ async function releaseWorkspace(options = {}) {
|
|
|
1063
1106
|
};
|
|
1064
1107
|
})).map((entry) => {
|
|
1065
1108
|
const nextRelease = entry.result === false ? void 0 : entry.result.nextRelease;
|
|
1066
|
-
return {
|
|
1109
|
+
return nextRelease === void 0 ? {
|
|
1067
1110
|
name: entry.name,
|
|
1068
1111
|
directory: entry.directory,
|
|
1069
|
-
released:
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1112
|
+
released: false,
|
|
1113
|
+
dependencyBumps: entry.dependencyBumps
|
|
1114
|
+
} : {
|
|
1115
|
+
name: entry.name,
|
|
1116
|
+
directory: entry.directory,
|
|
1117
|
+
released: true,
|
|
1118
|
+
version: nextRelease.version,
|
|
1119
|
+
gitTag: nextRelease.gitTag,
|
|
1120
|
+
type: nextRelease.type,
|
|
1073
1121
|
dependencyBumps: entry.dependencyBumps
|
|
1074
1122
|
};
|
|
1075
1123
|
})
|
|
@@ -1197,7 +1245,7 @@ async function detachWorkspaceRelease(options) {
|
|
|
1197
1245
|
const graph = buildDependencyGraph(workspace.packages);
|
|
1198
1246
|
validateDependencyRangeShapes(graph);
|
|
1199
1247
|
const order = topologicalOrder(graph);
|
|
1200
|
-
log(`${packageName}: ${order.length} packages in release order (gated -- tag only, publish deferred): ${order.join(" -> ")}`);
|
|
1248
|
+
log(`${packageName}: ${String(order.length)} packages in release order (gated -- tag only, publish deferred): ${order.join(" -> ")}`);
|
|
1201
1249
|
const publishPlugins = resolvePublishPlugins(options.plugins ?? DEFAULT_PUBLISH_PLUGINS, workspace.root, { requireGitPlugin: !dryRun });
|
|
1202
1250
|
const analyzeCommitsConfig = options.analyzeCommits ?? {};
|
|
1203
1251
|
const generateNotesConfig = options.generateNotes ?? {};
|
|
@@ -1219,15 +1267,20 @@ async function detachWorkspaceRelease(options) {
|
|
|
1219
1267
|
});
|
|
1220
1268
|
return {
|
|
1221
1269
|
order,
|
|
1222
|
-
packages: entries.map((entry) =>
|
|
1270
|
+
packages: entries.map((entry) => entry.result === null ? {
|
|
1223
1271
|
name: entry.name,
|
|
1224
1272
|
directory: entry.directory,
|
|
1225
|
-
released:
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1273
|
+
released: false,
|
|
1274
|
+
dependencyBumps: entry.dependencyBumps
|
|
1275
|
+
} : {
|
|
1276
|
+
name: entry.name,
|
|
1277
|
+
directory: entry.directory,
|
|
1278
|
+
released: true,
|
|
1279
|
+
version: entry.result.nextRelease.version,
|
|
1280
|
+
gitTag: entry.result.nextRelease.gitTag,
|
|
1281
|
+
type: entry.result.nextRelease.type,
|
|
1229
1282
|
dependencyBumps: entry.dependencyBumps
|
|
1230
|
-
})
|
|
1283
|
+
}),
|
|
1231
1284
|
detached: entries.map((entry) => ({
|
|
1232
1285
|
name: entry.name,
|
|
1233
1286
|
relativeDirectory: entry.relativeDirectory,
|
|
@@ -1289,9 +1342,6 @@ async function resumeWorkspaceRelease(options) {
|
|
|
1289
1342
|
name: entry.name,
|
|
1290
1343
|
directory: resolve(root, entry.relativeDirectory),
|
|
1291
1344
|
released: false,
|
|
1292
|
-
version: void 0,
|
|
1293
|
-
gitTag: void 0,
|
|
1294
|
-
type: void 0,
|
|
1295
1345
|
dependencyBumps: entry.dependencyBumps
|
|
1296
1346
|
});
|
|
1297
1347
|
continue;
|
|
@@ -1307,7 +1357,7 @@ async function resumeWorkspaceRelease(options) {
|
|
|
1307
1357
|
} catch (cause) {
|
|
1308
1358
|
throw new WorkspaceReleaseError(`Resuming ${entry.name} failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
1309
1359
|
}
|
|
1310
|
-
log(`${entry.name}: published ${entry.state.nextRelease.gitTag} (${releases.length} publish plugin${releases.length === 1 ? "" : "s"} ran)`);
|
|
1360
|
+
log(`${entry.name}: published ${entry.state.nextRelease.gitTag} (${String(releases.length)} publish plugin${releases.length === 1 ? "" : "s"} ran)`);
|
|
1311
1361
|
packages.push({
|
|
1312
1362
|
name: entry.name,
|
|
1313
1363
|
directory,
|
|
@@ -1396,7 +1446,7 @@ async function runRelease(flags) {
|
|
|
1396
1446
|
if (gatePublish) {
|
|
1397
1447
|
if (flags.gateStateFile === void 0) throw new WorkspaceReleaseError("gatePublish was true but no --gate-state-file was resolved -- this should be unreachable.");
|
|
1398
1448
|
await writeFile(flags.gateStateFile, JSON.stringify(outcome.detached ?? [], null, 2));
|
|
1399
|
-
console.log(`${packageName}: wrote gate state for ${(outcome.detached ?? []).length} package(s) to ${flags.gateStateFile}`);
|
|
1449
|
+
console.log(`${packageName}: wrote gate state for ${String((outcome.detached ?? []).length)} package(s) to ${flags.gateStateFile}`);
|
|
1400
1450
|
}
|
|
1401
1451
|
}
|
|
1402
1452
|
async function runResume(flags) {
|