@lumenflow/cli 5.1.0 → 5.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/dist/capacity-snapshot-emitter.js +10 -12
- package/dist/capacity-snapshot-emitter.js.map +1 -1
- package/dist/docs-generate-pack-reference.js +54 -0
- package/dist/docs-generate-pack-reference.js.map +1 -1
- package/dist/flow-bottlenecks.js +55 -0
- package/dist/flow-bottlenecks.js.map +1 -1
- package/dist/kernel-event-sync/lifecycle-emitters.js +13 -14
- package/dist/kernel-event-sync/lifecycle-emitters.js.map +1 -1
- package/dist/kernel-event-sync/narrow-emissions.js +7 -4
- package/dist/kernel-event-sync/narrow-emissions.js.map +1 -1
- package/dist/kernel-event-sync/software-delivery-emitters.js +91 -0
- package/dist/kernel-event-sync/software-delivery-emitters.js.map +1 -1
- package/dist/lumenflow-upgrade.js +39 -10
- package/dist/lumenflow-upgrade.js.map +1 -1
- package/dist/metrics-snapshot.js +51 -0
- package/dist/metrics-snapshot.js.map +1 -1
- package/dist/orchestrate-init-status.js +24 -11
- package/dist/orchestrate-init-status.js.map +1 -1
- package/dist/pack-install.js +27 -20
- package/dist/pack-install.js.map +1 -1
- package/dist/pack-publish.js +34 -26
- package/dist/pack-publish.js.map +1 -1
- package/dist/release.js +308 -149
- package/dist/release.js.map +1 -1
- package/dist/strict-progress.js +49 -0
- package/dist/strict-progress.js.map +1 -1
- package/dist/temp-dir-cleanup.js +112 -0
- package/dist/temp-dir-cleanup.js.map +1 -0
- package/dist/validate-agent-sync.js +6 -5
- package/dist/validate-agent-sync.js.map +1 -1
- package/dist/wu-escalate.js +33 -0
- package/dist/wu-escalate.js.map +1 -1
- package/dist/wu-preflight.js +28 -0
- package/dist/wu-preflight.js.map +1 -1
- package/dist/wu-recover.js +35 -0
- package/dist/wu-recover.js.map +1 -1
- package/dist/wu-spawn-strategy-resolver.js +4 -0
- package/dist/wu-spawn-strategy-resolver.js.map +1 -1
- package/dist/wu-validate.js +63 -0
- package/dist/wu-validate.js.map +1 -1
- package/package.json +11 -11
- package/packs/agent-runtime/manifest.ts +55 -4
- package/packs/agent-runtime/manifest.yaml +16 -6
- package/packs/agent-runtime/orchestration.ts +26 -1
- package/packs/agent-runtime/package.json +1 -1
- package/packs/agent-runtime/remote-controls/operations.ts +6 -0
- package/packs/agent-runtime/tool-impl/remote-controls.mock.ts +4 -0
- package/packs/agent-runtime/turn-lifecycle-events.ts +90 -1
- package/packs/sidekick/manifest.yaml +6 -0
- package/packs/sidekick/package.json +2 -1
- package/packs/sidekick/sidekick-events.ts +195 -18
- package/packs/sidekick/src/adapters/control-plane-bridge.adapter.ts +18 -10
- package/packs/sidekick/src/adapters/filesystem-bridge.adapter.ts +4 -0
- package/packs/sidekick/src/domain/channel.types.ts +34 -54
- package/packs/sidekick/src/ports/channel-bridge.port.ts +29 -12
- package/packs/sidekick/tool-impl/channel-tools.ts +47 -16
- package/packs/sidekick/tool-impl/system-tools.ts +4 -6
- package/packs/software-delivery/manifest.ts +94 -7
- package/packs/software-delivery/manifest.yaml +42 -5
- package/packs/software-delivery/package.json +1 -1
- package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +30 -0
package/dist/release.js
CHANGED
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
* WU-1074: Add release command for npm publishing
|
|
27
27
|
*/
|
|
28
28
|
import { Command } from 'commander';
|
|
29
|
-
import { existsSync, lstatSync, mkdirSync,
|
|
29
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
30
30
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
31
|
-
import { dirname, join } from 'node:path';
|
|
31
|
+
import { dirname, isAbsolute, join } from 'node:path';
|
|
32
32
|
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
33
|
-
import { homedir
|
|
33
|
+
import { homedir } from 'node:os';
|
|
34
34
|
import { execSync } from 'node:child_process';
|
|
35
35
|
import { getGitForCwd } from '@lumenflow/core/git-adapter';
|
|
36
36
|
import { die, createError, ErrorCodes } from '@lumenflow/core/error-handler';
|
|
@@ -38,6 +38,7 @@ import { ensureOnMain } from '@lumenflow/core/wu-helpers';
|
|
|
38
38
|
import { REMOTES, FILE_SYSTEM, PKG_MANAGER } from '@lumenflow/core/wu-constants';
|
|
39
39
|
import { withMicroWorktree } from '@lumenflow/core/micro-worktree';
|
|
40
40
|
import { runCLI } from './cli-entry-point.js';
|
|
41
|
+
import { cleanupTempDir, createTrackedTempDir } from './temp-dir-cleanup.js';
|
|
41
42
|
/** Log prefix for console output */
|
|
42
43
|
const LOG_PREFIX = '[release]';
|
|
43
44
|
/** Directory containing @lumenflow packages */
|
|
@@ -150,6 +151,104 @@ export const RELEASE_WU_TOOL = 'release';
|
|
|
150
151
|
* Used as the operation identifier when creating the micro-worktree.
|
|
151
152
|
*/
|
|
152
153
|
export const RELEASE_OPERATION_NAME = 'release';
|
|
154
|
+
/**
|
|
155
|
+
* WU-2854: Env var that overrides the release micro-worktree base directory.
|
|
156
|
+
*
|
|
157
|
+
* Mirrors the WU-2826/WU-2850 pattern exported by `lumenflow-upgrade.ts`.
|
|
158
|
+
* Operators can point the release worktree at a specific location (for
|
|
159
|
+
* example a tmpfs-backed scratch disk or a fast NVMe mount) when the
|
|
160
|
+
* default XDG cache target is inappropriate for their environment.
|
|
161
|
+
*/
|
|
162
|
+
export const RELEASE_WORKTREE_DIR_ENV = 'LUMENFLOW_RELEASE_WORKTREE_DIR';
|
|
163
|
+
/**
|
|
164
|
+
* WU-2854: XDG Base Directory Specification env var. When set by the user
|
|
165
|
+
* we respect it as the cache root; otherwise we fall back to
|
|
166
|
+
* `${HOME}/.cache`. Kept lowercase in our own code for lint parity with
|
|
167
|
+
* `lumenflow-upgrade.ts`.
|
|
168
|
+
*/
|
|
169
|
+
const XDG_CACHE_HOME_ENV = 'XDG_CACHE_HOME';
|
|
170
|
+
/**
|
|
171
|
+
* WU-2854: Sub-path under the cache root where per-version release
|
|
172
|
+
* worktrees live. The `${version}` leaf keeps concurrent releases — or a
|
|
173
|
+
* release retried after a crash — from clashing on the same directory
|
|
174
|
+
* while still being trivially inspectable on disk.
|
|
175
|
+
*/
|
|
176
|
+
const RELEASE_WORKTREE_CACHE_SUBDIR = 'lumenflow/release-worktree';
|
|
177
|
+
/**
|
|
178
|
+
* WU-2854: Resolve the user cache root (XDG-compliant). When
|
|
179
|
+
* `XDG_CACHE_HOME` is set to an absolute non-empty path we use it
|
|
180
|
+
* verbatim; otherwise we default to `${HOME}/.cache`.
|
|
181
|
+
*
|
|
182
|
+
* Duplicated locally instead of imported from `lumenflow-upgrade.ts` to
|
|
183
|
+
* keep the release.ts blast radius narrow for WU-2854. A future refactor
|
|
184
|
+
* can extract a shared `xdg-cache-paths` module once a third consumer
|
|
185
|
+
* appears (tracking note: if `withMicroWorktree` itself moves off `/tmp`
|
|
186
|
+
* for *all* operations, consolidate there).
|
|
187
|
+
*/
|
|
188
|
+
export function resolveReleaseCacheHome() {
|
|
189
|
+
const xdg = process.env[XDG_CACHE_HOME_ENV];
|
|
190
|
+
const trimmed = typeof xdg === 'string' ? xdg.trim() : '';
|
|
191
|
+
if (trimmed.length > 0 && isAbsolute(trimmed)) {
|
|
192
|
+
return trimmed;
|
|
193
|
+
}
|
|
194
|
+
return join(homedir(), '.cache');
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* WU-2854: Resolve the micro-worktree base directory for `pnpm release`.
|
|
198
|
+
*
|
|
199
|
+
* Priority:
|
|
200
|
+
* 1. `LUMENFLOW_RELEASE_WORKTREE_DIR` env var (when set and non-empty)
|
|
201
|
+
* 2. `${XDG_CACHE_HOME:-${HOME}/.cache}/lumenflow/release-worktree/${version}/`
|
|
202
|
+
*
|
|
203
|
+
* The v5.1.0 cut hit `ERR_PNPM_ENOSPC` during `pnpm install
|
|
204
|
+
* --frozen-lockfile` inside `/tmp/release-*` on a host with tmpfs `/tmp`.
|
|
205
|
+
* `withMicroWorktree` creates its directory via `mkdtempSync(join(tmpdir(),
|
|
206
|
+
* ...))`, so routing `TMPDIR` at this path before the call is the
|
|
207
|
+
* least-invasive way to land the ~1000 dep install footprint on the
|
|
208
|
+
* user's home partition without touching `@lumenflow/core`.
|
|
209
|
+
*
|
|
210
|
+
* Ensures the directory exists so the downstream `mkdtempSync` call inside
|
|
211
|
+
* `withMicroWorktree` does not ENOENT on first run.
|
|
212
|
+
*/
|
|
213
|
+
export function resolveReleaseWorktreeDir(version) {
|
|
214
|
+
const override = process.env[RELEASE_WORKTREE_DIR_ENV];
|
|
215
|
+
const trimmed = typeof override === 'string' ? override.trim() : '';
|
|
216
|
+
const dir = trimmed.length > 0
|
|
217
|
+
? isAbsolute(trimmed)
|
|
218
|
+
? trimmed
|
|
219
|
+
: join(process.cwd(), trimmed)
|
|
220
|
+
: join(resolveReleaseCacheHome(), RELEASE_WORKTREE_CACHE_SUBDIR, version);
|
|
221
|
+
try {
|
|
222
|
+
mkdirSync(dir, { recursive: true });
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// Best-effort — if we cannot create it, downstream will surface a
|
|
226
|
+
// clearer error. Returning the path keeps the helper deterministic.
|
|
227
|
+
}
|
|
228
|
+
return dir;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* WU-2854: Remove the release worktree directory after a successful run.
|
|
232
|
+
*
|
|
233
|
+
* Release worktrees are strictly disposable (version bump + build + pack +
|
|
234
|
+
* publish — no state worth preserving across runs) so we always clean up
|
|
235
|
+
* on success. Failure branches intentionally retain the directory so the
|
|
236
|
+
* operator can inspect residue (partial `pnpm install`, failed publish
|
|
237
|
+
* logs, etc.) before the next attempt reclaims the space.
|
|
238
|
+
*
|
|
239
|
+
* Always best-effort: residual removal errors must never fail an
|
|
240
|
+
* otherwise successful release. Mirrors `cleanupUpgradeWorktreeDir` from
|
|
241
|
+
* `lumenflow-upgrade.ts` (WU-2850).
|
|
242
|
+
*/
|
|
243
|
+
export function cleanupReleaseWorktreeDir(dir) {
|
|
244
|
+
try {
|
|
245
|
+
rmSync(dir, { recursive: true, force: true });
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// Intentional swallow — cleanup is an optimisation, not a correctness
|
|
249
|
+
// requirement. The next run will reuse/recreate the path.
|
|
250
|
+
}
|
|
251
|
+
}
|
|
153
252
|
/**
|
|
154
253
|
* Build a micro-worktree ID from a release version (WU-2219)
|
|
155
254
|
*
|
|
@@ -764,9 +863,17 @@ export function parsePackDryRunMetadata(rawOutput) {
|
|
|
764
863
|
}
|
|
765
864
|
}
|
|
766
865
|
const entryCount = normalized.entryCount;
|
|
866
|
+
// WU-2860: pnpm 10.x `pnpm pack --json` (non-dry-run) emits the absolute
|
|
867
|
+
// tarball path at the top level as `filename`. Older/alternate shapes have
|
|
868
|
+
// used `tarballPath`. Surface both when present so downstream callers
|
|
869
|
+
// (verifyConsumerInstall) can locate the on-disk artifact without re-parsing.
|
|
870
|
+
const filename = normalized.filename;
|
|
871
|
+
const tarballPath = normalized.tarballPath;
|
|
767
872
|
return {
|
|
768
873
|
files: files,
|
|
769
874
|
entryCount: typeof entryCount === 'number' ? entryCount : undefined,
|
|
875
|
+
filename: typeof filename === 'string' ? filename : undefined,
|
|
876
|
+
tarballPath: typeof tarballPath === 'string' ? tarballPath : undefined,
|
|
770
877
|
};
|
|
771
878
|
}
|
|
772
879
|
/**
|
|
@@ -998,46 +1105,62 @@ const NPM_404_TOKEN = '404';
|
|
|
998
1105
|
* Throws via `die` on failure. Opt-in via `--verify-consumer-install`.
|
|
999
1106
|
*/
|
|
1000
1107
|
export function verifyConsumerInstall(packageJsonPaths, options = {}) {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
const
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
continue;
|
|
1010
|
-
}
|
|
1011
|
-
const tarballPath = packWorkspacePackage(packageName, tarballDir, options.cwd);
|
|
1012
|
-
tarballs.push({ packageName, tarballPath });
|
|
1013
|
-
}
|
|
1014
|
-
// Seed the consumer directory with a minimal package.json so `npm install`
|
|
1015
|
-
// has somewhere to write node_modules.
|
|
1016
|
-
writeFileSync(join(consumerDir, PACKAGE_JSON_FILENAME), JSON.stringify({ name: 'lumenflow-release-consumer-probe', private: true }));
|
|
1017
|
-
const installArgs = tarballs.map((t) => t.tarballPath).join(' ');
|
|
1018
|
-
const installCommand = `npm install --no-save --no-audit --no-fund ${installArgs}`;
|
|
1019
|
-
let output = '';
|
|
1108
|
+
// WU-2859: /tmp/lumenflow-* scratch dirs must be cleaned up on success
|
|
1109
|
+
// AND failure. Previously these leaked on every release preflight and on
|
|
1110
|
+
// every npm-install failure, filling tmpfs during parallel wu:prep waves.
|
|
1111
|
+
const callerProvidedTarballDir = options.tarballDir !== undefined;
|
|
1112
|
+
const tarballDir = callerProvidedTarballDir
|
|
1113
|
+
? options.tarballDir
|
|
1114
|
+
: createTrackedTempDir('lumenflow-release-tarballs-');
|
|
1115
|
+
const consumerDir = createTrackedTempDir('lumenflow-release-consumer-');
|
|
1020
1116
|
try {
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1117
|
+
console.log(`${LOG_PREFIX} [${CONSUMER_INSTALL_LABEL}] Packing ${packageJsonPaths.length} packages into ${tarballDir}`);
|
|
1118
|
+
const tarballs = [];
|
|
1119
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
1120
|
+
const manifest = readPackageManifest(packageJsonPath);
|
|
1121
|
+
const packageName = manifest.name;
|
|
1122
|
+
if (!packageName) {
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
const tarballPath = packWorkspacePackage(packageName, tarballDir, options.cwd);
|
|
1126
|
+
tarballs.push({ packageName, tarballPath });
|
|
1127
|
+
}
|
|
1128
|
+
// Seed the consumer directory with a minimal package.json so `npm install`
|
|
1129
|
+
// has somewhere to write node_modules.
|
|
1130
|
+
writeFileSync(join(consumerDir, PACKAGE_JSON_FILENAME), JSON.stringify({ name: 'lumenflow-release-consumer-probe', private: true }));
|
|
1131
|
+
const installArgs = tarballs.map((t) => t.tarballPath).join(' ');
|
|
1132
|
+
const installCommand = `npm install --no-save --no-audit --no-fund ${installArgs}`;
|
|
1133
|
+
let output = '';
|
|
1134
|
+
try {
|
|
1135
|
+
output = execSync(installCommand, {
|
|
1136
|
+
cwd: consumerDir,
|
|
1137
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1138
|
+
encoding: FILE_SYSTEM.ENCODING,
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
catch (error) {
|
|
1142
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1143
|
+
die(`${CONSUMER_INSTALL_FAILURE_HEADER}\n\n` +
|
|
1144
|
+
`Scratch consumer dir: ${consumerDir}\n` +
|
|
1145
|
+
`Tarball dir: ${tarballDir}\n\n` +
|
|
1146
|
+
`${message}`);
|
|
1147
|
+
}
|
|
1148
|
+
if (output.includes(NPM_404_TOKEN)) {
|
|
1149
|
+
die(`${CONSUMER_INSTALL_FAILURE_HEADER}\n\n` +
|
|
1150
|
+
`Scratch consumer dir: ${consumerDir}\n` +
|
|
1151
|
+
`Tarball dir: ${tarballDir}\n\n` +
|
|
1152
|
+
`npm install printed a 404:\n${output}`);
|
|
1153
|
+
}
|
|
1154
|
+
console.log(`${LOG_PREFIX} ✅ [${CONSUMER_INSTALL_LABEL}] Consumer install succeeded for ${tarballs.length} packages`);
|
|
1155
|
+
}
|
|
1156
|
+
finally {
|
|
1157
|
+
// Caller-provided tarball dirs are cleaned up by the caller; our contract
|
|
1158
|
+
// is that any dir we created here is removed before returning.
|
|
1159
|
+
if (!callerProvidedTarballDir) {
|
|
1160
|
+
cleanupTempDir(tarballDir);
|
|
1161
|
+
}
|
|
1162
|
+
cleanupTempDir(consumerDir);
|
|
1026
1163
|
}
|
|
1027
|
-
catch (error) {
|
|
1028
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1029
|
-
die(`${CONSUMER_INSTALL_FAILURE_HEADER}\n\n` +
|
|
1030
|
-
`Scratch consumer dir: ${consumerDir}\n` +
|
|
1031
|
-
`Tarball dir: ${tarballDir}\n\n` +
|
|
1032
|
-
`${message}`);
|
|
1033
|
-
}
|
|
1034
|
-
if (output.includes(NPM_404_TOKEN)) {
|
|
1035
|
-
die(`${CONSUMER_INSTALL_FAILURE_HEADER}\n\n` +
|
|
1036
|
-
`Scratch consumer dir: ${consumerDir}\n` +
|
|
1037
|
-
`Tarball dir: ${tarballDir}\n\n` +
|
|
1038
|
-
`npm install printed a 404:\n${output}`);
|
|
1039
|
-
}
|
|
1040
|
-
console.log(`${LOG_PREFIX} ✅ [${CONSUMER_INSTALL_LABEL}] Consumer install succeeded for ${tarballs.length} packages`);
|
|
1041
1164
|
}
|
|
1042
1165
|
/**
|
|
1043
1166
|
* Pack a workspace package into a tarball on disk and return the absolute path.
|
|
@@ -1046,10 +1169,14 @@ function packWorkspacePackage(packageName, destDir, cwd) {
|
|
|
1046
1169
|
const cmd = `${PKG_MANAGER} --filter "${packageName}" pack --pack-destination "${destDir}" --json`;
|
|
1047
1170
|
const raw = runCommandCapture(cmd, { label: CONSUMER_INSTALL_LABEL, cwd });
|
|
1048
1171
|
const metadata = parsePackDryRunMetadata(raw);
|
|
1049
|
-
// pnpm 10 emits
|
|
1050
|
-
|
|
1172
|
+
// WU-2860: pnpm 10.x emits the absolute tarball path at the top level as
|
|
1173
|
+
// `filename` when --json is set. Older/alternate shapes used `tarballPath`.
|
|
1174
|
+
// Prefer `filename` (current pnpm), fall back to `tarballPath` (historical).
|
|
1175
|
+
const tarballPath = metadata.filename ?? metadata.tarballPath;
|
|
1051
1176
|
if (!tarballPath) {
|
|
1052
|
-
die(`Could not determine packed tarball path for ${packageName}.
|
|
1177
|
+
die(`Could not determine packed tarball path for ${packageName}. ` +
|
|
1178
|
+
`Expected top-level 'filename' or 'tarballPath' field in pnpm pack JSON. ` +
|
|
1179
|
+
`pnpm pack output:\n${raw}`);
|
|
1053
1180
|
}
|
|
1054
1181
|
return tarballPath;
|
|
1055
1182
|
}
|
|
@@ -1241,115 +1368,147 @@ export async function executeReleaseInMicroWorktree(opts) {
|
|
|
1241
1368
|
}
|
|
1242
1369
|
// Exit changeset pre mode if active (read-only check, write to micro-worktree)
|
|
1243
1370
|
const changesetPreActive = isInChangesetPreMode(mainCwd);
|
|
1371
|
+
// WU-2854: Retarget the micro-worktree base dir off tmpfs /tmp (default on
|
|
1372
|
+
// many Debian/Ubuntu cloud hosts) onto the user's home partition.
|
|
1373
|
+
// `withMicroWorktree` resolves its worktree path via
|
|
1374
|
+
// `mkdtempSync(join(tmpdir(), …))`, so setting TMPDIR for the duration of
|
|
1375
|
+
// the call is the least-invasive way to steer the worktree location
|
|
1376
|
+
// without modifying @lumenflow/core. The default target is the user's
|
|
1377
|
+
// XDG cache (${XDG_CACHE_HOME:-$HOME/.cache}/lumenflow/release-worktree/
|
|
1378
|
+
// <version>/). Consumers can override via LUMENFLOW_RELEASE_WORKTREE_DIR.
|
|
1379
|
+
// Mirrors WU-2850 pattern used by lumenflow-upgrade.ts.
|
|
1380
|
+
const releaseWorktreeDir = resolveReleaseWorktreeDir(version);
|
|
1381
|
+
/* eslint-disable sonarjs/publicly-writable-directories -- WU-2854: target is user-owned XDG cache dir, not /tmp. */
|
|
1382
|
+
const originalTmpdir = process.env.TMPDIR;
|
|
1383
|
+
process.env.TMPDIR = releaseWorktreeDir;
|
|
1384
|
+
let releaseTransactionSucceeded = false;
|
|
1244
1385
|
// ── Micro-worktree: all writes happen here ──────────────────────────
|
|
1245
|
-
|
|
1246
|
-
await
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
});
|
|
1273
|
-
console.log(`${LOG_PREFIX} ✅ Dependencies installed in micro-worktree`);
|
|
1274
|
-
if (!skipBuild) {
|
|
1275
|
-
runCommand(`${PKG_MANAGER} build`, { label: 'build', cwd: worktreePath });
|
|
1276
|
-
console.log(`${LOG_PREFIX} ✅ Build complete`);
|
|
1277
|
-
}
|
|
1278
|
-
else {
|
|
1279
|
-
console.log(`${LOG_PREFIX} Skipping build (--skip-build)`);
|
|
1280
|
-
}
|
|
1281
|
-
// Phase 2: Version bump (inside micro-worktree)
|
|
1282
|
-
console.log(`${LOG_PREFIX} Bumping versions to ${version}...`);
|
|
1283
|
-
await updatePackageVersions(worktreePackagePaths, version);
|
|
1284
|
-
const versionPolicyPath = await updateVersionPolicy(version, worktreePath);
|
|
1285
|
-
if (versionPolicyPath) {
|
|
1286
|
-
console.log(`${LOG_PREFIX} ✅ Updated ${VERSION_POLICY_RELATIVE_PATH}`);
|
|
1287
|
-
}
|
|
1288
|
-
console.log(`${LOG_PREFIX} ✅ Versions updated to ${version}`);
|
|
1289
|
-
// Phase 2b: Regenerate Starlight changelog manifest (WU-2570).
|
|
1290
|
-
// The generator reads currentVersion from the freshly bumped
|
|
1291
|
-
// version-policy.yaml, so it must run AFTER updateVersionPolicy
|
|
1292
|
-
// and BEFORE the release commit is built, otherwise the tagged
|
|
1293
|
-
// commit carries a stale currentVersion and the live docs site
|
|
1294
|
-
// displays the previous release.
|
|
1295
|
-
const changelogAbsPath = join(worktreePath, CHANGELOG_RELATIVE_PATH);
|
|
1296
|
-
const changelogExisted = existsSync(changelogAbsPath);
|
|
1297
|
-
if (changelogExisted) {
|
|
1298
|
-
regenerateChangelog(worktreePath);
|
|
1299
|
-
console.log(`${LOG_PREFIX} ✅ Regenerated ${CHANGELOG_RELATIVE_PATH}`);
|
|
1300
|
-
}
|
|
1301
|
-
// Phase 3: Validate packed artifacts after the version bump and before
|
|
1302
|
-
// any commit, tag, or optional npm publish step.
|
|
1303
|
-
validateReleaseArtifactsForPublish(worktreePackagePaths, false, worktreePath);
|
|
1304
|
-
// Phase 3b (WU-2825): Static preflight — every publishable package's
|
|
1305
|
-
// @lumenflow/* deps must either be in the current publish set or
|
|
1306
|
-
// already available on the npm registry. Catches private:true leaks
|
|
1307
|
-
// (the v5.0.1 @lumenflow/host regression) before publish.
|
|
1308
|
-
assertPublishSetResolvable(worktreePackagePaths);
|
|
1309
|
-
// Phase 3c (WU-2825): Optional dynamic check — pack all tarballs
|
|
1310
|
-
// and run `npm install` in a scratch consumer dir to catch ERESOLVE,
|
|
1311
|
-
// exports-map drift, and peer conflicts that the static check cannot
|
|
1312
|
-
// see.
|
|
1313
|
-
if (opts.verifyConsumerInstall) {
|
|
1314
|
-
verifyConsumerInstall(worktreePackagePaths, { cwd: worktreePath });
|
|
1315
|
-
}
|
|
1316
|
-
// Phase 4: Publish to npm (from micro-worktree)
|
|
1317
|
-
if (shouldPublishPackages) {
|
|
1318
|
-
console.log(`${LOG_PREFIX} Publishing packages to npm...`);
|
|
1319
|
-
runCommand(`${PKG_MANAGER} -r publish --access public --no-git-checks`, {
|
|
1320
|
-
label: 'publish',
|
|
1386
|
+
try {
|
|
1387
|
+
await withReleaseEnv(async () => {
|
|
1388
|
+
await withMicroWorktree({
|
|
1389
|
+
operation: RELEASE_OPERATION_NAME,
|
|
1390
|
+
id: buildReleaseWorktreeId(version),
|
|
1391
|
+
logPrefix: LOG_PREFIX,
|
|
1392
|
+
execute: async ({ worktreePath }) => {
|
|
1393
|
+
// Resolve package paths relative to micro-worktree
|
|
1394
|
+
const worktreePackagePaths = findPackageJsonPaths(worktreePath);
|
|
1395
|
+
const worktreePackageDirs = worktreePackagePaths.map((path) => dirname(path));
|
|
1396
|
+
// Exit changeset pre mode in micro-worktree if it was active on main
|
|
1397
|
+
if (changesetPreActive) {
|
|
1398
|
+
console.log(`${LOG_PREFIX} Detected changeset pre-release mode, exiting...`);
|
|
1399
|
+
exitChangesetPreMode(worktreePath);
|
|
1400
|
+
console.log(`${LOG_PREFIX} ✅ Exited changeset pre mode`);
|
|
1401
|
+
}
|
|
1402
|
+
// Phase 1: Materialize dist symlinks and build
|
|
1403
|
+
const distPreparation = ensureDistPathsMaterialized(worktreePackageDirs, {
|
|
1404
|
+
skipBuild,
|
|
1405
|
+
dryRun: false,
|
|
1406
|
+
});
|
|
1407
|
+
if (distPreparation.materializedCount > 0) {
|
|
1408
|
+
console.log(`${LOG_PREFIX} [${PRE_FLIGHT_LABEL}] Materialized ${distPreparation.materializedCount} symlinked dist directories`);
|
|
1409
|
+
}
|
|
1410
|
+
// WU-2221: Install deps in micro-worktree (no node_modules by default)
|
|
1411
|
+
runCommand(`${PKG_MANAGER} install --frozen-lockfile`, {
|
|
1412
|
+
label: 'install',
|
|
1321
1413
|
cwd: worktreePath,
|
|
1322
1414
|
});
|
|
1323
|
-
console.log(`${LOG_PREFIX} ✅
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1415
|
+
console.log(`${LOG_PREFIX} ✅ Dependencies installed in micro-worktree`);
|
|
1416
|
+
if (!skipBuild) {
|
|
1417
|
+
runCommand(`${PKG_MANAGER} build`, { label: 'build', cwd: worktreePath });
|
|
1418
|
+
console.log(`${LOG_PREFIX} ✅ Build complete`);
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
console.log(`${LOG_PREFIX} Skipping build (--skip-build)`);
|
|
1422
|
+
}
|
|
1423
|
+
// Phase 2: Version bump (inside micro-worktree)
|
|
1424
|
+
console.log(`${LOG_PREFIX} Bumping versions to ${version}...`);
|
|
1425
|
+
await updatePackageVersions(worktreePackagePaths, version);
|
|
1426
|
+
const versionPolicyPath = await updateVersionPolicy(version, worktreePath);
|
|
1427
|
+
if (versionPolicyPath) {
|
|
1428
|
+
console.log(`${LOG_PREFIX} ✅ Updated ${VERSION_POLICY_RELATIVE_PATH}`);
|
|
1429
|
+
}
|
|
1430
|
+
console.log(`${LOG_PREFIX} ✅ Versions updated to ${version}`);
|
|
1431
|
+
// Phase 2b: Regenerate Starlight changelog manifest (WU-2570).
|
|
1432
|
+
// The generator reads currentVersion from the freshly bumped
|
|
1433
|
+
// version-policy.yaml, so it must run AFTER updateVersionPolicy
|
|
1434
|
+
// and BEFORE the release commit is built, otherwise the tagged
|
|
1435
|
+
// commit carries a stale currentVersion and the live docs site
|
|
1436
|
+
// displays the previous release.
|
|
1437
|
+
const changelogAbsPath = join(worktreePath, CHANGELOG_RELATIVE_PATH);
|
|
1438
|
+
const changelogExisted = existsSync(changelogAbsPath);
|
|
1439
|
+
if (changelogExisted) {
|
|
1440
|
+
regenerateChangelog(worktreePath);
|
|
1441
|
+
console.log(`${LOG_PREFIX} ✅ Regenerated ${CHANGELOG_RELATIVE_PATH}`);
|
|
1442
|
+
}
|
|
1443
|
+
// Phase 3: Validate packed artifacts after the version bump and before
|
|
1444
|
+
// any commit, tag, or optional npm publish step.
|
|
1445
|
+
validateReleaseArtifactsForPublish(worktreePackagePaths, false, worktreePath);
|
|
1446
|
+
// Phase 3b (WU-2825): Static preflight — every publishable package's
|
|
1447
|
+
// @lumenflow/* deps must either be in the current publish set or
|
|
1448
|
+
// already available on the npm registry. Catches private:true leaks
|
|
1449
|
+
// (the v5.0.1 @lumenflow/host regression) before publish.
|
|
1450
|
+
assertPublishSetResolvable(worktreePackagePaths);
|
|
1451
|
+
// Phase 3c (WU-2825): Optional dynamic check — pack all tarballs
|
|
1452
|
+
// and run `npm install` in a scratch consumer dir to catch ERESOLVE,
|
|
1453
|
+
// exports-map drift, and peer conflicts that the static check cannot
|
|
1454
|
+
// see.
|
|
1455
|
+
if (opts.verifyConsumerInstall) {
|
|
1456
|
+
verifyConsumerInstall(worktreePackagePaths, { cwd: worktreePath });
|
|
1457
|
+
}
|
|
1458
|
+
// Phase 4: Publish to npm (from micro-worktree)
|
|
1459
|
+
if (shouldPublishPackages) {
|
|
1460
|
+
console.log(`${LOG_PREFIX} Publishing packages to npm...`);
|
|
1461
|
+
runCommand(`${PKG_MANAGER} -r publish --access public --no-git-checks`, {
|
|
1462
|
+
label: 'publish',
|
|
1463
|
+
cwd: worktreePath,
|
|
1464
|
+
});
|
|
1465
|
+
console.log(`${LOG_PREFIX} ✅ Packages published to npm`);
|
|
1466
|
+
}
|
|
1467
|
+
else {
|
|
1468
|
+
console.log(`${LOG_PREFIX} Skipping npm publish (--skip-publish)`);
|
|
1469
|
+
}
|
|
1470
|
+
// Build list of modified files for commit
|
|
1471
|
+
const relativePaths = worktreePackagePaths.map((p) => p.replace(worktreePath + '/', ''));
|
|
1472
|
+
// Include version-policy.yaml if it exists
|
|
1473
|
+
if (existsSync(join(worktreePath, VERSION_POLICY_RELATIVE_PATH))) {
|
|
1474
|
+
relativePaths.push(VERSION_POLICY_RELATIVE_PATH);
|
|
1475
|
+
}
|
|
1476
|
+
// Include changelog.json if it was regenerated (WU-2570).
|
|
1477
|
+
// The file is always regenerated when it existed before the bump,
|
|
1478
|
+
// so only check the pre-bump existence flag — not whether the file
|
|
1479
|
+
// is dirty in git — to keep the commit deterministic.
|
|
1480
|
+
if (changelogExisted) {
|
|
1481
|
+
relativePaths.push(CHANGELOG_RELATIVE_PATH);
|
|
1482
|
+
}
|
|
1483
|
+
// Include changeset pre.json removal if applicable
|
|
1484
|
+
if (changesetPreActive) {
|
|
1485
|
+
const changesetPrePath = join(CHANGESET_DIR, CHANGESET_PRE_JSON);
|
|
1486
|
+
relativePaths.push(changesetPrePath);
|
|
1487
|
+
}
|
|
1488
|
+
return {
|
|
1489
|
+
commitMessage: buildCommitMessage(version),
|
|
1490
|
+
files: relativePaths,
|
|
1491
|
+
};
|
|
1492
|
+
},
|
|
1493
|
+
});
|
|
1351
1494
|
});
|
|
1352
|
-
|
|
1495
|
+
releaseTransactionSucceeded = true;
|
|
1496
|
+
}
|
|
1497
|
+
finally {
|
|
1498
|
+
if (originalTmpdir === undefined) {
|
|
1499
|
+
delete process.env.TMPDIR;
|
|
1500
|
+
}
|
|
1501
|
+
else {
|
|
1502
|
+
process.env.TMPDIR = originalTmpdir;
|
|
1503
|
+
}
|
|
1504
|
+
// WU-2854: Only clean up on success. Preserving the directory on
|
|
1505
|
+
// failure lets operators inspect residue (partial install, failed
|
|
1506
|
+
// publish logs) before the next attempt reclaims the space.
|
|
1507
|
+
if (releaseTransactionSucceeded) {
|
|
1508
|
+
cleanupReleaseWorktreeDir(releaseWorktreeDir);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
/* eslint-enable sonarjs/publicly-writable-directories */
|
|
1353
1512
|
// ── Post micro-worktree: tag and push tag ─────────────────────────
|
|
1354
1513
|
// The micro-worktree has already merged the version bump commit to main
|
|
1355
1514
|
// and pushed to origin. Now create and push the git tag.
|