verelease 0.1.2-alpha.2 → 0.1.2-alpha.3
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/bin.js +816 -550
- package/dist/bin.js.map +1 -1
- package/dist/index.js +818 -637
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/bin.js
CHANGED
|
@@ -1,12 +1,201 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/bin.ts
|
|
4
|
+
import { defineCommand, runMain } from "citty";
|
|
5
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { join as join15, dirname } from "path";
|
|
8
|
+
|
|
3
9
|
// src/cli.ts
|
|
4
|
-
import { resolve as resolve2, join as
|
|
5
|
-
import { writeFile as
|
|
10
|
+
import { resolve as resolve2, join as join14 } from "path";
|
|
11
|
+
import { writeFile as writeFile6, mkdir as mkdir6, readFile as readFile8, rm as rm4 } from "fs/promises";
|
|
6
12
|
import { mkdtemp as mkdtemp5 } from "fs/promises";
|
|
7
13
|
import { tmpdir as tmpdir5 } from "os";
|
|
8
14
|
import { existsSync } from "fs";
|
|
9
|
-
import
|
|
15
|
+
import * as prompts2 from "@clack/prompts";
|
|
16
|
+
import colors2 from "picocolors";
|
|
17
|
+
import * as Effect3 from "effect/Effect";
|
|
18
|
+
import * as Exit2 from "effect/Exit";
|
|
19
|
+
|
|
20
|
+
// ../core/dist/errors.js
|
|
21
|
+
import * as Data from "effect/Data";
|
|
22
|
+
var NpmAuthError = class extends Data.TaggedError("NpmAuthError") {
|
|
23
|
+
};
|
|
24
|
+
var BaselineFetchError = class extends Data.TaggedError("BaselineFetchError") {
|
|
25
|
+
};
|
|
26
|
+
var TestExecutionError = class extends Data.TaggedError("TestExecutionError") {
|
|
27
|
+
};
|
|
28
|
+
var ManifestMutationError = class extends Data.TaggedError("ManifestMutationError") {
|
|
29
|
+
};
|
|
30
|
+
var LockfileSyncError = class extends Data.TaggedError("LockfileSyncError") {
|
|
31
|
+
};
|
|
32
|
+
var PublishError = class extends Data.TaggedError("PublishError") {
|
|
33
|
+
};
|
|
34
|
+
var RegistryCollisionError = class extends Data.TaggedError("RegistryCollisionError") {
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// ../core/dist/transaction.js
|
|
38
|
+
import * as Effect from "effect/Effect";
|
|
39
|
+
import * as Exit from "effect/Exit";
|
|
40
|
+
import { readFile, writeFile, unlink, stat } from "fs/promises";
|
|
41
|
+
import { join } from "path";
|
|
42
|
+
async function fileExists(path) {
|
|
43
|
+
try {
|
|
44
|
+
const s = await stat(path);
|
|
45
|
+
return s.isFile();
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function executeReleaseTransaction(options, use) {
|
|
51
|
+
const { plan, cwd, lockfileAdapter, onRollback, onStep } = options;
|
|
52
|
+
const acquire = Effect.tryPromise({
|
|
53
|
+
try: async () => {
|
|
54
|
+
onStep?.("Creating manifest backups...");
|
|
55
|
+
const filesToTouch = /* @__PURE__ */ new Set();
|
|
56
|
+
for (const pkgPlan of plan.packages) {
|
|
57
|
+
if (pkgPlan.bump !== "none") {
|
|
58
|
+
const pkgJson = join(pkgPlan.directory, "package.json");
|
|
59
|
+
const jsrJson = join(pkgPlan.directory, "jsr.json");
|
|
60
|
+
const denoJson = join(pkgPlan.directory, "deno.json");
|
|
61
|
+
if (await fileExists(pkgJson))
|
|
62
|
+
filesToTouch.add(pkgJson);
|
|
63
|
+
if (await fileExists(jsrJson))
|
|
64
|
+
filesToTouch.add(jsrJson);
|
|
65
|
+
if (await fileExists(denoJson))
|
|
66
|
+
filesToTouch.add(denoJson);
|
|
67
|
+
}
|
|
68
|
+
for (const update of pkgPlan.dependentUpdates) {
|
|
69
|
+
const depPkgPlan = plan.packages.find((p) => p.name === update.dependentName);
|
|
70
|
+
if (depPkgPlan) {
|
|
71
|
+
const depPkgJson = join(depPkgPlan.directory, "package.json");
|
|
72
|
+
if (await fileExists(depPkgJson))
|
|
73
|
+
filesToTouch.add(depPkgJson);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const backups = [];
|
|
78
|
+
for (const filePath of filesToTouch) {
|
|
79
|
+
const originalContent = await readFile(filePath, "utf8");
|
|
80
|
+
backups.push({ filePath, originalContent });
|
|
81
|
+
}
|
|
82
|
+
onStep?.("Applying version bumps to manifests...");
|
|
83
|
+
const versionMap = /* @__PURE__ */ new Map();
|
|
84
|
+
for (const pkgPlan of plan.packages) {
|
|
85
|
+
if (pkgPlan.bump !== "none") {
|
|
86
|
+
versionMap.set(pkgPlan.name, pkgPlan.proposedVersion);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const depUpdateMap = /* @__PURE__ */ new Map();
|
|
90
|
+
for (const pkgPlan of plan.packages) {
|
|
91
|
+
for (const depUp of pkgPlan.dependentUpdates) {
|
|
92
|
+
if (!depUpdateMap.has(depUp.dependentName)) {
|
|
93
|
+
depUpdateMap.set(depUp.dependentName, []);
|
|
94
|
+
}
|
|
95
|
+
depUpdateMap.get(depUp.dependentName).push({
|
|
96
|
+
dependencyName: depUp.dependencyName,
|
|
97
|
+
newRange: depUp.newRange,
|
|
98
|
+
type: depUp.dependencyType
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const modifiedManifests = [];
|
|
103
|
+
for (const pkgPlan of plan.packages) {
|
|
104
|
+
const pkgJsonPath = join(pkgPlan.directory, "package.json");
|
|
105
|
+
if (filesToTouch.has(pkgJsonPath)) {
|
|
106
|
+
const content = await readFile(pkgJsonPath, "utf8");
|
|
107
|
+
const parsed = JSON.parse(content);
|
|
108
|
+
if (versionMap.has(pkgPlan.name)) {
|
|
109
|
+
parsed.version = versionMap.get(pkgPlan.name);
|
|
110
|
+
}
|
|
111
|
+
const depUpdates = depUpdateMap.get(pkgPlan.name);
|
|
112
|
+
if (depUpdates) {
|
|
113
|
+
for (const up of depUpdates) {
|
|
114
|
+
const sec = parsed[up.type];
|
|
115
|
+
if (sec && sec[up.dependencyName]) {
|
|
116
|
+
sec[up.dependencyName] = up.newRange;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
await writeFile(pkgJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
|
|
121
|
+
modifiedManifests.push(pkgJsonPath);
|
|
122
|
+
}
|
|
123
|
+
const jsrJsonPath = join(pkgPlan.directory, "jsr.json");
|
|
124
|
+
if (filesToTouch.has(jsrJsonPath)) {
|
|
125
|
+
const content = await readFile(jsrJsonPath, "utf8");
|
|
126
|
+
const parsed = JSON.parse(content);
|
|
127
|
+
if (versionMap.has(pkgPlan.name)) {
|
|
128
|
+
parsed.version = versionMap.get(pkgPlan.name);
|
|
129
|
+
}
|
|
130
|
+
await writeFile(jsrJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
|
|
131
|
+
modifiedManifests.push(jsrJsonPath);
|
|
132
|
+
}
|
|
133
|
+
const denoJsonPath = join(pkgPlan.directory, "deno.json");
|
|
134
|
+
if (filesToTouch.has(denoJsonPath)) {
|
|
135
|
+
const content = await readFile(denoJsonPath, "utf8");
|
|
136
|
+
const parsed = JSON.parse(content);
|
|
137
|
+
if (versionMap.has(pkgPlan.name)) {
|
|
138
|
+
parsed.version = versionMap.get(pkgPlan.name);
|
|
139
|
+
}
|
|
140
|
+
await writeFile(denoJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
|
|
141
|
+
modifiedManifests.push(denoJsonPath);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
onStep?.(`Regenerating lockfile with ${lockfileAdapter.packageManager}...`);
|
|
145
|
+
const lockRes = await lockfileAdapter.regenerateLockfile(cwd);
|
|
146
|
+
if (!lockRes.success) {
|
|
147
|
+
for (const b of backups) {
|
|
148
|
+
await writeFile(b.filePath, b.originalContent, "utf8");
|
|
149
|
+
}
|
|
150
|
+
throw new Error(`Lockfile generation failed: ${lockRes.output}`);
|
|
151
|
+
}
|
|
152
|
+
const changelogBackups = /* @__PURE__ */ new Map();
|
|
153
|
+
const scope = {
|
|
154
|
+
plan,
|
|
155
|
+
modifiedManifests,
|
|
156
|
+
changelogBackups,
|
|
157
|
+
backupChangelog(path, original) {
|
|
158
|
+
changelogBackups.set(path, original);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
return { scope, backups };
|
|
162
|
+
},
|
|
163
|
+
catch: (err) => new ManifestMutationError({
|
|
164
|
+
message: err instanceof Error ? err.message : String(err)
|
|
165
|
+
})
|
|
166
|
+
});
|
|
167
|
+
const release = (resource, exit) => Effect.promise(async () => {
|
|
168
|
+
if (Exit.isFailure(exit)) {
|
|
169
|
+
onRollback?.("Rolling back manifests to previous versions...");
|
|
170
|
+
for (const b of resource.backups) {
|
|
171
|
+
try {
|
|
172
|
+
await writeFile(b.filePath, b.originalContent, "utf8");
|
|
173
|
+
} catch {
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
onRollback?.("Restored manifests.");
|
|
177
|
+
onRollback?.("Restoring changelog files...");
|
|
178
|
+
for (const [chPath, original] of resource.scope.changelogBackups) {
|
|
179
|
+
try {
|
|
180
|
+
if (original === null) {
|
|
181
|
+
await unlink(chPath);
|
|
182
|
+
} else {
|
|
183
|
+
await writeFile(chPath, original, "utf8");
|
|
184
|
+
}
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
onRollback?.("Restored changelog files.");
|
|
189
|
+
onRollback?.("Synchronizing workspace lockfile...");
|
|
190
|
+
try {
|
|
191
|
+
await lockfileAdapter.regenerateLockfile(cwd);
|
|
192
|
+
onRollback?.("Synchronized lockfile.");
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
return Effect.acquireUseRelease(acquire, ({ scope }) => use(scope), release);
|
|
198
|
+
}
|
|
10
199
|
|
|
11
200
|
// ../workspace/dist/graph.js
|
|
12
201
|
var PackageGraphImpl = class {
|
|
@@ -160,12 +349,12 @@ var PackageGraphImpl = class {
|
|
|
160
349
|
};
|
|
161
350
|
|
|
162
351
|
// ../workspace/dist/resolver.js
|
|
163
|
-
import { readFile, stat, readdir } from "fs/promises";
|
|
164
|
-
import { join, relative, resolve } from "path";
|
|
352
|
+
import { readFile as readFile2, stat as stat2, readdir } from "fs/promises";
|
|
353
|
+
import { join as join2, relative, resolve } from "path";
|
|
165
354
|
import { parse as parseYaml } from "yaml";
|
|
166
|
-
async function
|
|
355
|
+
async function fileExists2(path) {
|
|
167
356
|
try {
|
|
168
|
-
const s = await
|
|
357
|
+
const s = await stat2(path);
|
|
169
358
|
return s.isFile();
|
|
170
359
|
} catch {
|
|
171
360
|
return false;
|
|
@@ -173,7 +362,7 @@ async function fileExists(path) {
|
|
|
173
362
|
}
|
|
174
363
|
async function dirExists(path) {
|
|
175
364
|
try {
|
|
176
|
-
const s = await
|
|
365
|
+
const s = await stat2(path);
|
|
177
366
|
return s.isDirectory();
|
|
178
367
|
} catch {
|
|
179
368
|
return false;
|
|
@@ -182,25 +371,25 @@ async function dirExists(path) {
|
|
|
182
371
|
var DefaultWorkspaceResolver = class {
|
|
183
372
|
name = "default-workspace-resolver";
|
|
184
373
|
async supports(rootDir) {
|
|
185
|
-
const pnpmWorkspace =
|
|
186
|
-
const packageJson =
|
|
187
|
-
return await
|
|
374
|
+
const pnpmWorkspace = join2(rootDir, "pnpm-workspace.yaml");
|
|
375
|
+
const packageJson = join2(rootDir, "package.json");
|
|
376
|
+
return await fileExists2(pnpmWorkspace) || await fileExists2(packageJson);
|
|
188
377
|
}
|
|
189
378
|
async resolve(rootDir) {
|
|
190
379
|
const absRootDir = resolve(rootDir);
|
|
191
|
-
const pnpmWorkspacePath =
|
|
192
|
-
const rootPkgJsonPath =
|
|
380
|
+
const pnpmWorkspacePath = join2(absRootDir, "pnpm-workspace.yaml");
|
|
381
|
+
const rootPkgJsonPath = join2(absRootDir, "package.json");
|
|
193
382
|
let packageManager = "unknown";
|
|
194
383
|
let patterns = [];
|
|
195
|
-
if (await
|
|
384
|
+
if (await fileExists2(pnpmWorkspacePath)) {
|
|
196
385
|
packageManager = "pnpm";
|
|
197
|
-
const content = await
|
|
386
|
+
const content = await readFile2(pnpmWorkspacePath, "utf8");
|
|
198
387
|
const parsed = parseYaml(content);
|
|
199
388
|
if (parsed && Array.isArray(parsed.packages)) {
|
|
200
389
|
patterns = parsed.packages;
|
|
201
390
|
}
|
|
202
|
-
} else if (await
|
|
203
|
-
const content = await
|
|
391
|
+
} else if (await fileExists2(rootPkgJsonPath)) {
|
|
392
|
+
const content = await readFile2(rootPkgJsonPath, "utf8");
|
|
204
393
|
const parsed = JSON.parse(content);
|
|
205
394
|
if (Array.isArray(parsed.workspaces)) {
|
|
206
395
|
patterns = parsed.workspaces;
|
|
@@ -233,17 +422,17 @@ var DefaultWorkspaceResolver = class {
|
|
|
233
422
|
const matchedDirs = /* @__PURE__ */ new Set();
|
|
234
423
|
for (const pattern of patterns) {
|
|
235
424
|
if (pattern.endsWith("/*")) {
|
|
236
|
-
const baseDir =
|
|
425
|
+
const baseDir = join2(absRootDir, pattern.slice(0, -2));
|
|
237
426
|
if (await dirExists(baseDir)) {
|
|
238
427
|
const entries = await readdir(baseDir, { withFileTypes: true });
|
|
239
428
|
for (const entry of entries) {
|
|
240
429
|
if (entry.isDirectory()) {
|
|
241
|
-
matchedDirs.add(
|
|
430
|
+
matchedDirs.add(join2(baseDir, entry.name));
|
|
242
431
|
}
|
|
243
432
|
}
|
|
244
433
|
}
|
|
245
434
|
} else {
|
|
246
|
-
const exactDir =
|
|
435
|
+
const exactDir = join2(absRootDir, pattern);
|
|
247
436
|
if (await dirExists(exactDir)) {
|
|
248
437
|
matchedDirs.add(exactDir);
|
|
249
438
|
}
|
|
@@ -251,10 +440,10 @@ var DefaultWorkspaceResolver = class {
|
|
|
251
440
|
}
|
|
252
441
|
const packages = [];
|
|
253
442
|
for (const dir of matchedDirs) {
|
|
254
|
-
const manifestPath =
|
|
255
|
-
if (await
|
|
443
|
+
const manifestPath = join2(dir, "package.json");
|
|
444
|
+
if (await fileExists2(manifestPath)) {
|
|
256
445
|
try {
|
|
257
|
-
const content = await
|
|
446
|
+
const content = await readFile2(manifestPath, "utf8");
|
|
258
447
|
const manifest = JSON.parse(content);
|
|
259
448
|
if (manifest.name && manifest.version) {
|
|
260
449
|
packages.push({
|
|
@@ -450,8 +639,8 @@ var DefaultSemVerDecisionEngine = class {
|
|
|
450
639
|
};
|
|
451
640
|
|
|
452
641
|
// ../contracts/dist/store.js
|
|
453
|
-
import { readFile as
|
|
454
|
-
import { join as
|
|
642
|
+
import { readFile as readFile3, writeFile as writeFile2, mkdir, readdir as readdir2 } from "fs/promises";
|
|
643
|
+
import { join as join3 } from "path";
|
|
455
644
|
import semver3 from "semver";
|
|
456
645
|
var FileContractStore = class {
|
|
457
646
|
directory;
|
|
@@ -471,9 +660,9 @@ var FileContractStore = class {
|
|
|
471
660
|
const files = await readdir2(this.directory, { recursive: true });
|
|
472
661
|
for (const file of files) {
|
|
473
662
|
if (typeof file === "string" && file.endsWith(".json")) {
|
|
474
|
-
const fullPath =
|
|
663
|
+
const fullPath = join3(this.directory, file);
|
|
475
664
|
try {
|
|
476
|
-
const content = await
|
|
665
|
+
const content = await readFile3(fullPath, "utf8");
|
|
477
666
|
const parsed = JSON.parse(content);
|
|
478
667
|
if (!packageName || parsed.package === packageName) {
|
|
479
668
|
contracts.push(parsed);
|
|
@@ -494,8 +683,8 @@ var FileContractStore = class {
|
|
|
494
683
|
await this.ensureDir();
|
|
495
684
|
const sanitizedPackage = contract.package.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
496
685
|
const sanitizedId = contract.id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
497
|
-
const filePath =
|
|
498
|
-
await
|
|
686
|
+
const filePath = join3(this.directory, `${sanitizedPackage}__${sanitizedId}.json`);
|
|
687
|
+
await writeFile2(filePath, JSON.stringify(contract, null, 2), "utf8");
|
|
499
688
|
}
|
|
500
689
|
async getActiveContracts(packageName, targetVersion) {
|
|
501
690
|
const all = await this.listContracts(packageName);
|
|
@@ -519,20 +708,20 @@ var FileContractStore = class {
|
|
|
519
708
|
import { execFile } from "child_process";
|
|
520
709
|
import { promisify } from "util";
|
|
521
710
|
import { mkdtemp, rm, mkdir as mkdir2 } from "fs/promises";
|
|
522
|
-
import { join as
|
|
711
|
+
import { join as join4 } from "path";
|
|
523
712
|
import { tmpdir } from "os";
|
|
524
713
|
import * as tar from "tar";
|
|
525
714
|
var execFileAsync = promisify(execFile);
|
|
526
715
|
var CandidatePacker = class {
|
|
527
|
-
async pack(packageDir, packageName,
|
|
528
|
-
const tempDir = await mkdtemp(
|
|
716
|
+
async pack(packageDir, packageName, version2) {
|
|
717
|
+
const tempDir = await mkdtemp(join4(tmpdir(), "verelease-candidate-"));
|
|
529
718
|
const tarballDest = tempDir;
|
|
530
719
|
const { stdout } = await execFileAsync("npm", ["pack", "--pack-destination", tarballDest], {
|
|
531
720
|
cwd: packageDir
|
|
532
721
|
});
|
|
533
722
|
const tarballFilename = stdout.trim().split("\n").pop().trim();
|
|
534
|
-
const tarballPath =
|
|
535
|
-
const unpackedDir =
|
|
723
|
+
const tarballPath = join4(tarballDest, tarballFilename);
|
|
724
|
+
const unpackedDir = join4(tempDir, "unpacked");
|
|
536
725
|
await mkdir2(unpackedDir, { recursive: true });
|
|
537
726
|
await tar.x({
|
|
538
727
|
file: tarballPath,
|
|
@@ -542,14 +731,14 @@ var CandidatePacker = class {
|
|
|
542
731
|
});
|
|
543
732
|
return {
|
|
544
733
|
packageName,
|
|
545
|
-
version,
|
|
734
|
+
version: version2,
|
|
546
735
|
tarballPath,
|
|
547
736
|
unpackedDir
|
|
548
737
|
};
|
|
549
738
|
}
|
|
550
739
|
async cleanup(packResult) {
|
|
551
740
|
try {
|
|
552
|
-
const parentDir =
|
|
741
|
+
const parentDir = join4(packResult.unpackedDir, "..");
|
|
553
742
|
await rm(parentDir, { recursive: true, force: true });
|
|
554
743
|
} catch {
|
|
555
744
|
}
|
|
@@ -557,15 +746,15 @@ var CandidatePacker = class {
|
|
|
557
746
|
};
|
|
558
747
|
|
|
559
748
|
// ../contracts/dist/runner.js
|
|
560
|
-
import { mkdir as mkdir3, symlink, rm as rm2, writeFile as
|
|
561
|
-
import { join as
|
|
749
|
+
import { mkdir as mkdir3, symlink, rm as rm2, writeFile as writeFile3, copyFile } from "fs/promises";
|
|
750
|
+
import { join as join6, basename } from "path";
|
|
562
751
|
import { tmpdir as tmpdir2 } from "os";
|
|
563
752
|
import { mkdtemp as mkdtemp2 } from "fs/promises";
|
|
564
753
|
|
|
565
754
|
// ../test-adapter-node/dist/node-test.js
|
|
566
755
|
import { spawn } from "child_process";
|
|
567
|
-
import { readFile as
|
|
568
|
-
import { join as
|
|
756
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
757
|
+
import { join as join5 } from "path";
|
|
569
758
|
var NodeTestAdapter = class {
|
|
570
759
|
options;
|
|
571
760
|
name = "node:test";
|
|
@@ -592,7 +781,7 @@ var NodeTestAdapter = class {
|
|
|
592
781
|
let testFiles = ctx.testFiles && ctx.testFiles.length > 0 ? ctx.testFiles : [];
|
|
593
782
|
if (loaderArgs.length === 0 || testFiles.length === 0) {
|
|
594
783
|
try {
|
|
595
|
-
const pkgRaw = await
|
|
784
|
+
const pkgRaw = await readFile4(join5(ctx.packagePath, "package.json"), "utf8");
|
|
596
785
|
const pkg = JSON.parse(pkgRaw);
|
|
597
786
|
const testScript = pkg.scripts?.test || "";
|
|
598
787
|
if (loaderArgs.length === 0 && (testScript.includes("--import tsx") || testScript.includes("tsx"))) {
|
|
@@ -766,22 +955,22 @@ var ContractRunner = class {
|
|
|
766
955
|
}
|
|
767
956
|
async runContract(contract, candidate) {
|
|
768
957
|
const startTime = Date.now();
|
|
769
|
-
const consumerEnvDir = await mkdtemp2(
|
|
770
|
-
const nodeModulesDir =
|
|
958
|
+
const consumerEnvDir = await mkdtemp2(join6(tmpdir2(), "verelease-consumer-"));
|
|
959
|
+
const nodeModulesDir = join6(consumerEnvDir, "node_modules");
|
|
771
960
|
try {
|
|
772
|
-
await
|
|
773
|
-
let targetPkgLink =
|
|
961
|
+
await writeFile3(join6(consumerEnvDir, "package.json"), JSON.stringify({ name: "consumer-fixture", type: "module" }, null, 2), "utf8");
|
|
962
|
+
let targetPkgLink = join6(nodeModulesDir, candidate.packageName);
|
|
774
963
|
if (candidate.packageName.startsWith("@")) {
|
|
775
964
|
const [scope, pkgName] = candidate.packageName.split("/");
|
|
776
|
-
const scopeDir =
|
|
965
|
+
const scopeDir = join6(nodeModulesDir, scope);
|
|
777
966
|
await mkdir3(scopeDir, { recursive: true });
|
|
778
|
-
targetPkgLink =
|
|
967
|
+
targetPkgLink = join6(scopeDir, pkgName);
|
|
779
968
|
} else {
|
|
780
969
|
await mkdir3(nodeModulesDir, { recursive: true });
|
|
781
970
|
}
|
|
782
971
|
await symlink(candidate.unpackedDir, targetPkgLink, "junction");
|
|
783
972
|
const isolatedTestName = basename(contract.testFile);
|
|
784
|
-
const isolatedTestPath =
|
|
973
|
+
const isolatedTestPath = join6(consumerEnvDir, isolatedTestName);
|
|
785
974
|
await copyFile(contract.testFile, isolatedTestPath);
|
|
786
975
|
const testRun = await this.testAdapter.run({
|
|
787
976
|
packagePath: consumerEnvDir,
|
|
@@ -1118,11 +1307,11 @@ var ApiDiffEngine = class {
|
|
|
1118
1307
|
};
|
|
1119
1308
|
|
|
1120
1309
|
// ../api-diff/dist/analyzer.js
|
|
1121
|
-
import { readFile as
|
|
1122
|
-
import { join as
|
|
1123
|
-
async function
|
|
1310
|
+
import { readFile as readFile5, stat as stat3 } from "fs/promises";
|
|
1311
|
+
import { join as join7 } from "path";
|
|
1312
|
+
async function fileExists3(path) {
|
|
1124
1313
|
try {
|
|
1125
|
-
const s = await
|
|
1314
|
+
const s = await stat3(path);
|
|
1126
1315
|
return s.isFile();
|
|
1127
1316
|
} catch {
|
|
1128
1317
|
return false;
|
|
@@ -1141,11 +1330,11 @@ var ApiDiffAnalyzer = class {
|
|
|
1141
1330
|
if (!candidateEntrypoint) {
|
|
1142
1331
|
return [];
|
|
1143
1332
|
}
|
|
1144
|
-
const candidateSource = await
|
|
1333
|
+
const candidateSource = await readFile5(candidateEntrypoint, "utf8");
|
|
1145
1334
|
const candidateSnapshot = this.extractor.extractFromSource(candidateSource, candidateEntrypoint);
|
|
1146
1335
|
let baselineSnapshot;
|
|
1147
|
-
if (context.baselinePath && await
|
|
1148
|
-
const baseSource = await
|
|
1336
|
+
if (context.baselinePath && await fileExists3(context.baselinePath)) {
|
|
1337
|
+
const baseSource = await readFile5(context.baselinePath, "utf8");
|
|
1149
1338
|
if (context.baselinePath.endsWith(".json")) {
|
|
1150
1339
|
baselineSnapshot = JSON.parse(baseSource);
|
|
1151
1340
|
} else {
|
|
@@ -1159,14 +1348,14 @@ var ApiDiffAnalyzer = class {
|
|
|
1159
1348
|
}
|
|
1160
1349
|
async findEntrypoint(pkgDir) {
|
|
1161
1350
|
const candidates = [
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1351
|
+
join7(pkgDir, "src", "index.ts"),
|
|
1352
|
+
join7(pkgDir, "src", "index.js"),
|
|
1353
|
+
join7(pkgDir, "index.ts"),
|
|
1354
|
+
join7(pkgDir, "index.js"),
|
|
1355
|
+
join7(pkgDir, "dist", "index.d.ts")
|
|
1167
1356
|
];
|
|
1168
1357
|
for (const c of candidates) {
|
|
1169
|
-
if (await
|
|
1358
|
+
if (await fileExists3(c)) {
|
|
1170
1359
|
return c;
|
|
1171
1360
|
}
|
|
1172
1361
|
}
|
|
@@ -1177,14 +1366,14 @@ var ApiDiffAnalyzer = class {
|
|
|
1177
1366
|
// ../api-diff/dist/baseline.js
|
|
1178
1367
|
import { execFile as execFile2 } from "child_process";
|
|
1179
1368
|
import { promisify as promisify2 } from "util";
|
|
1180
|
-
import { mkdtemp as mkdtemp3, mkdir as mkdir4, stat as
|
|
1181
|
-
import { join as
|
|
1369
|
+
import { mkdtemp as mkdtemp3, mkdir as mkdir4, stat as stat4 } from "fs/promises";
|
|
1370
|
+
import { join as join8 } from "path";
|
|
1182
1371
|
import { tmpdir as tmpdir3 } from "os";
|
|
1183
1372
|
import * as tar2 from "tar";
|
|
1184
1373
|
var execFileAsync2 = promisify2(execFile2);
|
|
1185
|
-
async function
|
|
1374
|
+
async function fileExists4(path) {
|
|
1186
1375
|
try {
|
|
1187
|
-
const s = await
|
|
1376
|
+
const s = await stat4(path);
|
|
1188
1377
|
return s.isFile();
|
|
1189
1378
|
} catch {
|
|
1190
1379
|
return false;
|
|
@@ -1199,20 +1388,20 @@ var BaselineFetcher = class {
|
|
|
1199
1388
|
* or the extracted package has no recognizable entry point.
|
|
1200
1389
|
*/
|
|
1201
1390
|
async fetch(packageName, parentTmpDir) {
|
|
1202
|
-
const
|
|
1203
|
-
if (!
|
|
1391
|
+
const version2 = await this.getLatestVersion(packageName);
|
|
1392
|
+
if (!version2)
|
|
1204
1393
|
return null;
|
|
1205
|
-
const tempDir = parentTmpDir ?
|
|
1394
|
+
const tempDir = parentTmpDir ? join8(parentTmpDir, packageName.replace(/\//g, "__")) : await mkdtemp3(join8(tmpdir3(), "verelease-baseline-"));
|
|
1206
1395
|
await mkdir4(tempDir, { recursive: true });
|
|
1207
1396
|
let tarballFilename;
|
|
1208
1397
|
try {
|
|
1209
|
-
const { stdout } = await execFileAsync2("npm", ["pack", `${packageName}@${
|
|
1398
|
+
const { stdout } = await execFileAsync2("npm", ["pack", `${packageName}@${version2}`, "--pack-destination", tempDir], { timeout: 3e4 });
|
|
1210
1399
|
tarballFilename = stdout.trim().split("\n").pop().trim();
|
|
1211
1400
|
} catch {
|
|
1212
1401
|
return null;
|
|
1213
1402
|
}
|
|
1214
|
-
const tarballPath =
|
|
1215
|
-
const extractedDir =
|
|
1403
|
+
const tarballPath = join8(tempDir, tarballFilename);
|
|
1404
|
+
const extractedDir = join8(tempDir, "extracted");
|
|
1216
1405
|
await mkdir4(extractedDir, { recursive: true });
|
|
1217
1406
|
try {
|
|
1218
1407
|
await tar2.x({
|
|
@@ -1227,7 +1416,7 @@ var BaselineFetcher = class {
|
|
|
1227
1416
|
const entryPointPath = await this.findEntrypoint(extractedDir);
|
|
1228
1417
|
if (!entryPointPath)
|
|
1229
1418
|
return null;
|
|
1230
|
-
return { version, entryPointPath, extractedDir };
|
|
1419
|
+
return { version: version2, entryPointPath, extractedDir };
|
|
1231
1420
|
}
|
|
1232
1421
|
async getLatestVersion(packageName) {
|
|
1233
1422
|
try {
|
|
@@ -1242,16 +1431,16 @@ var BaselineFetcher = class {
|
|
|
1242
1431
|
}
|
|
1243
1432
|
async findEntrypoint(pkgDir) {
|
|
1244
1433
|
const candidates = [
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1434
|
+
join8(pkgDir, "dist", "index.d.ts"),
|
|
1435
|
+
join8(pkgDir, "dist", "index.d.mts"),
|
|
1436
|
+
join8(pkgDir, "src", "index.ts"),
|
|
1437
|
+
join8(pkgDir, "src", "index.js"),
|
|
1438
|
+
join8(pkgDir, "index.d.ts"),
|
|
1439
|
+
join8(pkgDir, "index.ts"),
|
|
1440
|
+
join8(pkgDir, "index.js")
|
|
1252
1441
|
];
|
|
1253
1442
|
for (const candidate of candidates) {
|
|
1254
|
-
if (await
|
|
1443
|
+
if (await fileExists4(candidate)) {
|
|
1255
1444
|
return candidate;
|
|
1256
1445
|
}
|
|
1257
1446
|
}
|
|
@@ -1260,140 +1449,18 @@ var BaselineFetcher = class {
|
|
|
1260
1449
|
};
|
|
1261
1450
|
|
|
1262
1451
|
// ../manifest/dist/mutator.js
|
|
1263
|
-
import { readFile as
|
|
1264
|
-
import { join as
|
|
1265
|
-
async function fileExists4(path) {
|
|
1266
|
-
try {
|
|
1267
|
-
const s = await stat4(path);
|
|
1268
|
-
return s.isFile();
|
|
1269
|
-
} catch {
|
|
1270
|
-
return false;
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
var TransactionalManifestMutator = class {
|
|
1274
|
-
async applyPlan(plan) {
|
|
1275
|
-
const backups = [];
|
|
1276
|
-
const modifiedFiles = [];
|
|
1277
|
-
const filesToTouch = /* @__PURE__ */ new Set();
|
|
1278
|
-
for (const pkgPlan of plan.packages) {
|
|
1279
|
-
if (pkgPlan.bump !== "none") {
|
|
1280
|
-
const pkgJson = join8(pkgPlan.directory, "package.json");
|
|
1281
|
-
const jsrJson = join8(pkgPlan.directory, "jsr.json");
|
|
1282
|
-
const denoJson = join8(pkgPlan.directory, "deno.json");
|
|
1283
|
-
if (await fileExists4(pkgJson))
|
|
1284
|
-
filesToTouch.add(pkgJson);
|
|
1285
|
-
if (await fileExists4(jsrJson))
|
|
1286
|
-
filesToTouch.add(jsrJson);
|
|
1287
|
-
if (await fileExists4(denoJson))
|
|
1288
|
-
filesToTouch.add(denoJson);
|
|
1289
|
-
}
|
|
1290
|
-
for (const update of pkgPlan.dependentUpdates) {
|
|
1291
|
-
const depPkgPlan = plan.packages.find((p) => p.name === update.dependentName);
|
|
1292
|
-
if (depPkgPlan) {
|
|
1293
|
-
const depPkgJson = join8(depPkgPlan.directory, "package.json");
|
|
1294
|
-
if (await fileExists4(depPkgJson))
|
|
1295
|
-
filesToTouch.add(depPkgJson);
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
for (const filePath of filesToTouch) {
|
|
1300
|
-
const originalContent = await readFile5(filePath, "utf8");
|
|
1301
|
-
backups.push({ filePath, originalContent });
|
|
1302
|
-
}
|
|
1303
|
-
try {
|
|
1304
|
-
const versionMap = /* @__PURE__ */ new Map();
|
|
1305
|
-
for (const pkgPlan of plan.packages) {
|
|
1306
|
-
if (pkgPlan.bump !== "none") {
|
|
1307
|
-
versionMap.set(pkgPlan.name, pkgPlan.proposedVersion);
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
const depUpdateMap = /* @__PURE__ */ new Map();
|
|
1311
|
-
for (const pkgPlan of plan.packages) {
|
|
1312
|
-
for (const depUp of pkgPlan.dependentUpdates) {
|
|
1313
|
-
if (!depUpdateMap.has(depUp.dependentName)) {
|
|
1314
|
-
depUpdateMap.set(depUp.dependentName, []);
|
|
1315
|
-
}
|
|
1316
|
-
depUpdateMap.get(depUp.dependentName).push({
|
|
1317
|
-
dependencyName: depUp.dependencyName,
|
|
1318
|
-
newRange: depUp.newRange,
|
|
1319
|
-
type: depUp.dependencyType
|
|
1320
|
-
});
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
for (const pkgPlan of plan.packages) {
|
|
1324
|
-
const pkgJsonPath = join8(pkgPlan.directory, "package.json");
|
|
1325
|
-
if (filesToTouch.has(pkgJsonPath)) {
|
|
1326
|
-
const content = await readFile5(pkgJsonPath, "utf8");
|
|
1327
|
-
const parsed = JSON.parse(content);
|
|
1328
|
-
if (versionMap.has(pkgPlan.name)) {
|
|
1329
|
-
parsed.version = versionMap.get(pkgPlan.name);
|
|
1330
|
-
}
|
|
1331
|
-
const depUpdates = depUpdateMap.get(pkgPlan.name);
|
|
1332
|
-
if (depUpdates) {
|
|
1333
|
-
for (const up of depUpdates) {
|
|
1334
|
-
const sec = parsed[up.type];
|
|
1335
|
-
if (sec && sec[up.dependencyName]) {
|
|
1336
|
-
sec[up.dependencyName] = up.newRange;
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
await writeFile3(pkgJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
|
|
1341
|
-
modifiedFiles.push(pkgJsonPath);
|
|
1342
|
-
}
|
|
1343
|
-
const jsrJsonPath = join8(pkgPlan.directory, "jsr.json");
|
|
1344
|
-
if (filesToTouch.has(jsrJsonPath)) {
|
|
1345
|
-
const content = await readFile5(jsrJsonPath, "utf8");
|
|
1346
|
-
const parsed = JSON.parse(content);
|
|
1347
|
-
if (versionMap.has(pkgPlan.name)) {
|
|
1348
|
-
parsed.version = versionMap.get(pkgPlan.name);
|
|
1349
|
-
}
|
|
1350
|
-
await writeFile3(jsrJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
|
|
1351
|
-
modifiedFiles.push(jsrJsonPath);
|
|
1352
|
-
}
|
|
1353
|
-
const denoJsonPath = join8(pkgPlan.directory, "deno.json");
|
|
1354
|
-
if (filesToTouch.has(denoJsonPath)) {
|
|
1355
|
-
const content = await readFile5(denoJsonPath, "utf8");
|
|
1356
|
-
const parsed = JSON.parse(content);
|
|
1357
|
-
if (versionMap.has(pkgPlan.name)) {
|
|
1358
|
-
parsed.version = versionMap.get(pkgPlan.name);
|
|
1359
|
-
}
|
|
1360
|
-
await writeFile3(denoJsonPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
|
|
1361
|
-
modifiedFiles.push(denoJsonPath);
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
} catch (err) {
|
|
1365
|
-
for (const b of backups) {
|
|
1366
|
-
await writeFile3(b.filePath, b.originalContent, "utf8");
|
|
1367
|
-
}
|
|
1368
|
-
throw err;
|
|
1369
|
-
}
|
|
1370
|
-
const transaction = {
|
|
1371
|
-
id: `tx-${Date.now()}`,
|
|
1372
|
-
plan,
|
|
1373
|
-
backups,
|
|
1374
|
-
modifiedFiles,
|
|
1375
|
-
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1376
|
-
async rollback() {
|
|
1377
|
-
for (const b of backups) {
|
|
1378
|
-
await writeFile3(b.filePath, b.originalContent, "utf8");
|
|
1379
|
-
}
|
|
1380
|
-
},
|
|
1381
|
-
async commit() {
|
|
1382
|
-
}
|
|
1383
|
-
};
|
|
1384
|
-
return transaction;
|
|
1385
|
-
}
|
|
1386
|
-
};
|
|
1452
|
+
import { readFile as readFile6, writeFile as writeFile4, stat as stat5 } from "fs/promises";
|
|
1453
|
+
import { join as join9 } from "path";
|
|
1387
1454
|
|
|
1388
1455
|
// ../manifest/dist/lockfile.js
|
|
1389
1456
|
import { execFile as execFile3 } from "child_process";
|
|
1390
1457
|
import { promisify as promisify3 } from "util";
|
|
1391
|
-
import { stat as
|
|
1392
|
-
import { join as
|
|
1458
|
+
import { stat as stat6 } from "fs/promises";
|
|
1459
|
+
import { join as join10 } from "path";
|
|
1393
1460
|
var execFileAsync3 = promisify3(execFile3);
|
|
1394
1461
|
async function fileExists5(path) {
|
|
1395
1462
|
try {
|
|
1396
|
-
const s = await
|
|
1463
|
+
const s = await stat6(path);
|
|
1397
1464
|
return s.isFile();
|
|
1398
1465
|
} catch {
|
|
1399
1466
|
return false;
|
|
@@ -1446,11 +1513,11 @@ var NpmLockfileAdapter = class {
|
|
|
1446
1513
|
var AutoLockfileAdapter = class {
|
|
1447
1514
|
packageManager = "npm";
|
|
1448
1515
|
async regenerateLockfile(rootDirectory) {
|
|
1449
|
-
if (await fileExists5(
|
|
1516
|
+
if (await fileExists5(join10(rootDirectory, "pnpm-lock.yaml"))) {
|
|
1450
1517
|
this.packageManager = "pnpm";
|
|
1451
1518
|
return new PnpmLockfileAdapter().regenerateLockfile(rootDirectory);
|
|
1452
1519
|
}
|
|
1453
|
-
if (await fileExists5(
|
|
1520
|
+
if (await fileExists5(join10(rootDirectory, "package-lock.json"))) {
|
|
1454
1521
|
this.packageManager = "npm";
|
|
1455
1522
|
return new NpmLockfileAdapter().regenerateLockfile(rootDirectory);
|
|
1456
1523
|
}
|
|
@@ -1461,6 +1528,10 @@ var AutoLockfileAdapter = class {
|
|
|
1461
1528
|
// ../publisher-npm/dist/publisher.js
|
|
1462
1529
|
import { execFile as execFile4, spawn as spawn2 } from "child_process";
|
|
1463
1530
|
import { promisify as promisify4 } from "util";
|
|
1531
|
+
import * as Effect2 from "effect/Effect";
|
|
1532
|
+
import * as Schedule from "effect/Schedule";
|
|
1533
|
+
import * as prompts from "@clack/prompts";
|
|
1534
|
+
import colors from "picocolors";
|
|
1464
1535
|
var execFileAsync4 = promisify4(execFile4);
|
|
1465
1536
|
function openBrowser(url) {
|
|
1466
1537
|
const platform = process.platform;
|
|
@@ -1507,20 +1578,90 @@ var NpmPublishProvider = class {
|
|
|
1507
1578
|
}
|
|
1508
1579
|
return channel;
|
|
1509
1580
|
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Check npm authentication status without throwing.
|
|
1583
|
+
*/
|
|
1584
|
+
async checkAuth() {
|
|
1585
|
+
try {
|
|
1586
|
+
const { stdout } = await execFileAsync4("npm", ["whoami"]);
|
|
1587
|
+
const username = stdout.trim();
|
|
1588
|
+
return { authenticated: true, username };
|
|
1589
|
+
} catch (err) {
|
|
1590
|
+
return {
|
|
1591
|
+
authenticated: false,
|
|
1592
|
+
error: err.stderr || err.message || "401 Unauthorized"
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
/**
|
|
1597
|
+
* Proactively verifies npm authentication.
|
|
1598
|
+
* If unauthenticated and interactive, prompts the user to log in now,
|
|
1599
|
+
* runs `npm login` interactively, verifies the login, and continues.
|
|
1600
|
+
*/
|
|
1601
|
+
async ensureAuth(options = {}) {
|
|
1602
|
+
const isInteractive = options.interactive ?? (this.options.interactive || !process.env.CI && process.stdin.isTTY);
|
|
1603
|
+
const status = await this.checkAuth();
|
|
1604
|
+
if (status.authenticated && status.username) {
|
|
1605
|
+
return status.username;
|
|
1606
|
+
}
|
|
1607
|
+
if (!isInteractive) {
|
|
1608
|
+
throw new NpmAuthError({
|
|
1609
|
+
message: "You are not logged in to npm (npm whoami failed with 401). Run 'npm login' or configure NPM_TOKEN / NODE_AUTH_TOKEN in CI."
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
prompts.log.warn(colors.yellow("You are not currently logged in to npm."));
|
|
1613
|
+
const shouldLogin = await prompts.confirm({
|
|
1614
|
+
message: "Would you like to log in to npm now?",
|
|
1615
|
+
initialValue: true
|
|
1616
|
+
});
|
|
1617
|
+
if (prompts.isCancel(shouldLogin) || !shouldLogin) {
|
|
1618
|
+
throw new NpmAuthError({
|
|
1619
|
+
message: "npm authentication cancelled. Run 'npm login' before releasing."
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
prompts.log.info(colors.cyan("Launching npm login in your terminal..."));
|
|
1623
|
+
await new Promise((resolve3, reject) => {
|
|
1624
|
+
const child = spawn2("npm", ["login"], { stdio: "inherit" });
|
|
1625
|
+
child.on("close", (code) => {
|
|
1626
|
+
if (code === 0) {
|
|
1627
|
+
resolve3();
|
|
1628
|
+
} else {
|
|
1629
|
+
reject(new NpmAuthError({
|
|
1630
|
+
message: `npm login exited with code ${code}`,
|
|
1631
|
+
code: code ?? void 0
|
|
1632
|
+
}));
|
|
1633
|
+
}
|
|
1634
|
+
});
|
|
1635
|
+
child.on("error", (err) => {
|
|
1636
|
+
reject(new NpmAuthError({
|
|
1637
|
+
message: `Failed to launch npm login: ${err.message}`
|
|
1638
|
+
}));
|
|
1639
|
+
});
|
|
1640
|
+
});
|
|
1641
|
+
const recheck = await this.checkAuth();
|
|
1642
|
+
if (recheck.authenticated && recheck.username) {
|
|
1643
|
+
prompts.log.success(colors.green(`Authenticated as ${colors.bold(recheck.username)} on npm.`));
|
|
1644
|
+
return recheck.username;
|
|
1645
|
+
}
|
|
1646
|
+
throw new NpmAuthError({
|
|
1647
|
+
message: "npm login completed, but npm authentication could not be verified."
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1510
1650
|
async prepare(ctx) {
|
|
1511
1651
|
try {
|
|
1512
1652
|
await execFileAsync4("npm", ["pack", "--dry-run"], {
|
|
1513
1653
|
cwd: ctx.packagePlan.directory
|
|
1514
1654
|
});
|
|
1515
1655
|
} catch (err) {
|
|
1516
|
-
throw new
|
|
1656
|
+
throw new PublishError({
|
|
1657
|
+
packageName: ctx.packagePlan.name,
|
|
1658
|
+
message: `Failed packing verification for package ${ctx.packagePlan.name}: ${err.message || String(err)}`
|
|
1659
|
+
});
|
|
1517
1660
|
}
|
|
1518
|
-
if (!ctx.dryRun
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
}
|
|
1522
|
-
throw new Error(`You are not logged in to npm (npm whoami failed with 401). Please run 'npm login' first.`);
|
|
1523
|
-
}
|
|
1661
|
+
if (!ctx.dryRun) {
|
|
1662
|
+
await this.ensureAuth({
|
|
1663
|
+
interactive: this.options.interactive ?? (!process.env.CI && process.stdin.isTTY)
|
|
1664
|
+
});
|
|
1524
1665
|
}
|
|
1525
1666
|
}
|
|
1526
1667
|
async publish(ctx) {
|
|
@@ -1545,8 +1686,7 @@ var NpmPublishProvider = class {
|
|
|
1545
1686
|
}
|
|
1546
1687
|
const isInteractive = this.options.interactive ?? (!ctx.dryRun && !process.env.CI && process.stdin.isTTY);
|
|
1547
1688
|
if (isInteractive) {
|
|
1548
|
-
|
|
1549
|
-
\u{1F510} Interactive terminal detected for npm publish:`);
|
|
1689
|
+
prompts.log.info(colors.cyan("Terminal ready for npm publish authentication:"));
|
|
1550
1690
|
console.log(` \u2022 WebAuthn / Passkey / Touch ID: press ENTER when prompted to open browser.`);
|
|
1551
1691
|
console.log(` \u2022 Authenticator App: enter your 6-digit TOTP code if prompted.
|
|
1552
1692
|
`);
|
|
@@ -1619,25 +1759,22 @@ ${err.message || ""}`;
|
|
|
1619
1759
|
if (authUrlMatch && doneUrlMatch && !ctx.dryRun) {
|
|
1620
1760
|
const authUrl = authUrlMatch[0];
|
|
1621
1761
|
const doneUrl = doneUrlMatch[0];
|
|
1622
|
-
|
|
1623
|
-
\x1B[36m\u{1F511} WebAuthn / Security Key 2FA Detected!\x1B[0m`);
|
|
1762
|
+
prompts.log.info(colors.cyan(colors.bold("\u{1F511} WebAuthn / Security Key 2FA Detected!")));
|
|
1624
1763
|
console.log(` Your npm account uses Touch ID / security key (WebAuthn) instead of a 6-digit code.`);
|
|
1625
1764
|
console.log(` Opening browser to authenticate:`);
|
|
1626
|
-
console.log(` \u{1F449}
|
|
1765
|
+
console.log(` \u{1F449} ${colors.underline(authUrl)}
|
|
1627
1766
|
`);
|
|
1628
1767
|
openBrowser(authUrl);
|
|
1629
|
-
|
|
1768
|
+
prompts.log.info("Waiting for Touch ID / security key approval in your browser (up to 90s)...");
|
|
1630
1769
|
const token = await pollForWebAuthToken(doneUrl, 9e4);
|
|
1631
1770
|
if (token) {
|
|
1632
|
-
|
|
1633
|
-
`);
|
|
1771
|
+
prompts.log.success(colors.green("\u2713 Touch ID / Passkey approved! Completing publish..."));
|
|
1634
1772
|
return this.publish({
|
|
1635
1773
|
...ctx,
|
|
1636
1774
|
extraArgs: [...ctx.extraArgs || [], "--otp", token]
|
|
1637
1775
|
});
|
|
1638
1776
|
} else {
|
|
1639
|
-
|
|
1640
|
-
`);
|
|
1777
|
+
prompts.log.error(colors.red("\u274C WebAuthn approval timed out or was cancelled."));
|
|
1641
1778
|
}
|
|
1642
1779
|
}
|
|
1643
1780
|
return {
|
|
@@ -1653,17 +1790,29 @@ ${err.message || ""}`;
|
|
|
1653
1790
|
};
|
|
1654
1791
|
}
|
|
1655
1792
|
}
|
|
1793
|
+
/**
|
|
1794
|
+
* Resiliently fetches published versions using EffectTS retry schedule
|
|
1795
|
+
*/
|
|
1656
1796
|
async getPublishedVersions(packageName) {
|
|
1797
|
+
const fetchVersions = Effect2.tryPromise({
|
|
1798
|
+
try: async () => {
|
|
1799
|
+
const { stdout } = await execFileAsync4("npm", ["view", packageName, "versions", "--json"]);
|
|
1800
|
+
const parsed = JSON.parse(stdout.trim());
|
|
1801
|
+
if (Array.isArray(parsed)) {
|
|
1802
|
+
return parsed;
|
|
1803
|
+
}
|
|
1804
|
+
if (typeof parsed === "string") {
|
|
1805
|
+
return [parsed];
|
|
1806
|
+
}
|
|
1807
|
+
return [];
|
|
1808
|
+
},
|
|
1809
|
+
catch: (err) => err
|
|
1810
|
+
});
|
|
1811
|
+
const withRetry = Effect2.retry(fetchVersions, {
|
|
1812
|
+
schedule: Schedule.recurs(2)
|
|
1813
|
+
});
|
|
1657
1814
|
try {
|
|
1658
|
-
|
|
1659
|
-
const parsed = JSON.parse(stdout.trim());
|
|
1660
|
-
if (Array.isArray(parsed)) {
|
|
1661
|
-
return parsed;
|
|
1662
|
-
}
|
|
1663
|
-
if (typeof parsed === "string") {
|
|
1664
|
-
return [parsed];
|
|
1665
|
-
}
|
|
1666
|
-
return [];
|
|
1815
|
+
return await Effect2.runPromise(withRetry);
|
|
1667
1816
|
} catch {
|
|
1668
1817
|
return [];
|
|
1669
1818
|
}
|
|
@@ -1671,12 +1820,12 @@ ${err.message || ""}`;
|
|
|
1671
1820
|
};
|
|
1672
1821
|
|
|
1673
1822
|
// ../config/dist/config.js
|
|
1674
|
-
import { stat as
|
|
1675
|
-
import { join as
|
|
1823
|
+
import { stat as stat7 } from "fs/promises";
|
|
1824
|
+
import { join as join11 } from "path";
|
|
1676
1825
|
import { pathToFileURL } from "url";
|
|
1677
1826
|
async function fileExists6(path) {
|
|
1678
1827
|
try {
|
|
1679
|
-
const s = await
|
|
1828
|
+
const s = await stat7(path);
|
|
1680
1829
|
return s.isFile();
|
|
1681
1830
|
} catch {
|
|
1682
1831
|
return false;
|
|
@@ -1694,7 +1843,7 @@ async function loadConfig(rootDir) {
|
|
|
1694
1843
|
"autoversion.config.mjs"
|
|
1695
1844
|
];
|
|
1696
1845
|
for (const name of candidateNames) {
|
|
1697
|
-
const fullPath =
|
|
1846
|
+
const fullPath = join11(rootDir, name);
|
|
1698
1847
|
if (await fileExists6(fullPath)) {
|
|
1699
1848
|
try {
|
|
1700
1849
|
const fileUrl = pathToFileURL(fullPath).href;
|
|
@@ -1740,8 +1889,8 @@ var MigrationScriptSchema = z.object({
|
|
|
1740
1889
|
});
|
|
1741
1890
|
|
|
1742
1891
|
// ../ai/dist/changelog.js
|
|
1743
|
-
function renderChangelogMarkdown(
|
|
1744
|
-
const parts = [`# ${
|
|
1892
|
+
function renderChangelogMarkdown(version2, date, data) {
|
|
1893
|
+
const parts = [`# ${version2} (${date})`, ""];
|
|
1745
1894
|
if (data.breakingChanges.length > 0) {
|
|
1746
1895
|
parts.push("## \u26A0\uFE0F Breaking Changes");
|
|
1747
1896
|
for (const item of data.breakingChanges) {
|
|
@@ -1949,10 +2098,10 @@ walkAndTransform(targetDir);
|
|
|
1949
2098
|
}
|
|
1950
2099
|
|
|
1951
2100
|
// ../ai/dist/validation.js
|
|
1952
|
-
import { cp, mkdtemp as mkdtemp4, rm as rm3, writeFile as
|
|
2101
|
+
import { cp, mkdtemp as mkdtemp4, rm as rm3, writeFile as writeFile5, mkdir as mkdir5, symlink as symlink2, readdir as readdir3 } from "fs/promises";
|
|
1953
2102
|
import { execFile as execFile5 } from "child_process";
|
|
1954
2103
|
import { promisify as promisify5 } from "util";
|
|
1955
|
-
import { join as
|
|
2104
|
+
import { join as join12 } from "path";
|
|
1956
2105
|
import { tmpdir as tmpdir4 } from "os";
|
|
1957
2106
|
import * as tar3 from "tar";
|
|
1958
2107
|
var execFileAsync5 = promisify5(execFile5);
|
|
@@ -2165,8 +2314,8 @@ var CommandTestAdapter = class {
|
|
|
2165
2314
|
};
|
|
2166
2315
|
|
|
2167
2316
|
// ../test-adapter/dist/test-suite-analyzer.js
|
|
2168
|
-
import { readFile as
|
|
2169
|
-
import { join as
|
|
2317
|
+
import { readFile as readFile7, readdir as readdir4 } from "fs/promises";
|
|
2318
|
+
import { join as join13 } from "path";
|
|
2170
2319
|
var TestSuiteAnalyzer = class {
|
|
2171
2320
|
name = "test-suite-analyzer";
|
|
2172
2321
|
adapters;
|
|
@@ -2183,7 +2332,7 @@ var TestSuiteAnalyzer = class {
|
|
|
2183
2332
|
const { packageName, packagePath } = context;
|
|
2184
2333
|
let manifest;
|
|
2185
2334
|
try {
|
|
2186
|
-
const raw = await
|
|
2335
|
+
const raw = await readFile7(join13(packagePath, "package.json"), "utf8");
|
|
2187
2336
|
manifest = JSON.parse(raw);
|
|
2188
2337
|
} catch {
|
|
2189
2338
|
return [];
|
|
@@ -2784,30 +2933,21 @@ var DenoTestAdapter = class {
|
|
|
2784
2933
|
|
|
2785
2934
|
// src/cli.ts
|
|
2786
2935
|
import semver4 from "semver";
|
|
2787
|
-
async function promptConfirm(question, defaultYes = false) {
|
|
2788
|
-
const rl = createInterface({
|
|
2789
|
-
input: process.stdin,
|
|
2790
|
-
output: process.stdout
|
|
2791
|
-
});
|
|
2792
|
-
try {
|
|
2793
|
-
const answer = (await rl.question(question)).trim().toLowerCase();
|
|
2794
|
-
if (!answer) return defaultYes;
|
|
2795
|
-
return answer === "y" || answer === "yes";
|
|
2796
|
-
} finally {
|
|
2797
|
-
rl.close();
|
|
2798
|
-
}
|
|
2799
|
-
}
|
|
2800
2936
|
var VereleaseCli = class {
|
|
2801
2937
|
cwd;
|
|
2802
2938
|
constructor(options = {}) {
|
|
2803
2939
|
this.cwd = resolve2(options.cwd || process.cwd());
|
|
2804
2940
|
}
|
|
2805
2941
|
async createPlan(options = {}) {
|
|
2942
|
+
const isInteractive = process.stdout.isTTY && !options.json;
|
|
2943
|
+
const spin = isInteractive ? prompts2.spinner() : void 0;
|
|
2944
|
+
spin?.start("Resolving workspace package graph");
|
|
2806
2945
|
const config = await loadConfig(this.cwd);
|
|
2807
2946
|
const resolver = new DefaultWorkspaceResolver();
|
|
2808
2947
|
const discovery = await resolver.resolve(this.cwd);
|
|
2948
|
+
spin?.stop(`Discovered ${colors2.bold(discovery.packages.length)} workspace package${discovery.packages.length === 1 ? "" : "s"}`);
|
|
2809
2949
|
const contractStore = new FileContractStore(
|
|
2810
|
-
|
|
2950
|
+
join14(this.cwd, config.contracts?.directory || ".verelease/contracts")
|
|
2811
2951
|
);
|
|
2812
2952
|
const contractAnalyzer = new ContractCompatibilityAnalyzer(contractStore);
|
|
2813
2953
|
const apiDiffAnalyzer = new ApiDiffAnalyzer();
|
|
@@ -2826,8 +2966,9 @@ var VereleaseCli = class {
|
|
|
2826
2966
|
const npmPublisher = new NpmPublishProvider();
|
|
2827
2967
|
const evidenceMap = /* @__PURE__ */ new Map();
|
|
2828
2968
|
const initialBumps = /* @__PURE__ */ new Map();
|
|
2829
|
-
const baselineTmpDir = await mkdtemp5(
|
|
2969
|
+
const baselineTmpDir = await mkdtemp5(join14(tmpdir5(), "verelease-baseline-"));
|
|
2830
2970
|
try {
|
|
2971
|
+
spin?.start("Evaluating compatibility evidence (API diffs, tests & contracts)");
|
|
2831
2972
|
for (const pkg of discovery.packages) {
|
|
2832
2973
|
const evidence = [];
|
|
2833
2974
|
try {
|
|
@@ -2930,10 +3071,12 @@ var VereleaseCli = class {
|
|
|
2930
3071
|
reasons
|
|
2931
3072
|
});
|
|
2932
3073
|
}
|
|
3074
|
+
spin?.stop("Compatibility evidence evaluated");
|
|
2933
3075
|
} finally {
|
|
2934
3076
|
await rm4(baselineTmpDir, { recursive: true, force: true }).catch(() => {
|
|
2935
3077
|
});
|
|
2936
3078
|
}
|
|
3079
|
+
spin?.start("Propagating dependency updates across workspace graph");
|
|
2937
3080
|
const propagator = new VersionPropagator(discovery.graph);
|
|
2938
3081
|
const { bumps: finalBumps, dependencyUpdates } = propagator.propagate(initialBumps);
|
|
2939
3082
|
const publishOrder = discovery.graph.getTopologicalOrder();
|
|
@@ -2973,6 +3116,7 @@ var VereleaseCli = class {
|
|
|
2973
3116
|
publish: !pkg.isPrivate && bumpTarget.bump !== "none"
|
|
2974
3117
|
});
|
|
2975
3118
|
}
|
|
3119
|
+
spin?.stop(`Calculated release plan [Channel: ${colors2.cyan(options.channel || "stable")}]`);
|
|
2976
3120
|
const plan = {
|
|
2977
3121
|
schemaVersion: 1,
|
|
2978
3122
|
id: `plan-${Date.now()}`,
|
|
@@ -2993,6 +3137,11 @@ var VereleaseCli = class {
|
|
|
2993
3137
|
return { plan, evidenceMap };
|
|
2994
3138
|
}
|
|
2995
3139
|
async runPlanCommand(options = {}) {
|
|
3140
|
+
if (!options.json) {
|
|
3141
|
+
prompts2.intro(
|
|
3142
|
+
colors2.cyan(colors2.bold("\u25C9 Verelease")) + colors2.dim(" evidence-based release plan")
|
|
3143
|
+
);
|
|
3144
|
+
}
|
|
2996
3145
|
const { plan } = await this.createPlan(options);
|
|
2997
3146
|
if (options.json) {
|
|
2998
3147
|
console.log(JSON.stringify(plan, null, 2));
|
|
@@ -3000,198 +3149,228 @@ var VereleaseCli = class {
|
|
|
3000
3149
|
}
|
|
3001
3150
|
this.printPlan(plan);
|
|
3002
3151
|
try {
|
|
3003
|
-
const verelDir =
|
|
3152
|
+
const verelDir = join14(this.cwd, ".verelease");
|
|
3004
3153
|
await mkdir6(verelDir, { recursive: true });
|
|
3005
|
-
await
|
|
3006
|
-
|
|
3154
|
+
await writeFile6(
|
|
3155
|
+
join14(verelDir, "release-plan.json"),
|
|
3007
3156
|
JSON.stringify(plan, null, 2),
|
|
3008
3157
|
"utf8"
|
|
3009
3158
|
);
|
|
3010
3159
|
} catch {
|
|
3011
3160
|
}
|
|
3161
|
+
prompts2.outro(
|
|
3162
|
+
plan.summary.bumpedPackages === 0 ? colors2.green("\u2728 All packages are up to date.") : colors2.cyan(
|
|
3163
|
+
`Plan ready: run ${colors2.bold(`verelease release --channel ${plan.channel}`)} to execute.`
|
|
3164
|
+
)
|
|
3165
|
+
);
|
|
3012
3166
|
return plan;
|
|
3013
3167
|
}
|
|
3014
3168
|
async runReleaseCommand(options = {}) {
|
|
3169
|
+
if (!options.json) {
|
|
3170
|
+
prompts2.intro(
|
|
3171
|
+
colors2.cyan(colors2.bold("\u25C9 Verelease")) + colors2.dim(
|
|
3172
|
+
` release pipeline [${colors2.bold(options.channel || "stable")}]` + (options.dryRun ? colors2.yellow(" (DRY RUN)") : "")
|
|
3173
|
+
)
|
|
3174
|
+
);
|
|
3175
|
+
}
|
|
3015
3176
|
const { plan, evidenceMap } = await this.createPlan(options);
|
|
3016
|
-
console.log(`
|
|
3017
|
-
\u{1F680} Verelease Pipeline [Channel: ${plan.channel}, Dry-run: ${!!options.dryRun}]
|
|
3018
|
-
`);
|
|
3019
3177
|
this.printPlan(plan);
|
|
3020
3178
|
if (plan.summary.bumpedPackages === 0) {
|
|
3021
|
-
|
|
3179
|
+
prompts2.outro(colors2.green("\u2728 No packages require release."));
|
|
3022
3180
|
return;
|
|
3023
3181
|
}
|
|
3024
3182
|
const publishable = plan.packages.filter((p) => p.publish && p.bump !== "none");
|
|
3025
3183
|
const internalBumps = plan.packages.filter((p) => !p.publish && p.bump !== "none");
|
|
3026
|
-
const promptDescription = publishable.length > 0 ? `publishing ${publishable.map((p) => `${p.name}@${p.proposedVersion}`).join(", ")}` + (internalBumps.length > 0 ? ` and bumping ${internalBumps.length} internal package(s)` : "") : `bumping ${internalBumps.length} internal package(s)`;
|
|
3027
|
-
const
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3184
|
+
const promptDescription = publishable.length > 0 ? `publishing ${publishable.map((p) => colors2.cyan(`${p.name}@${p.proposedVersion}`)).join(", ")}` + (internalBumps.length > 0 ? ` and bumping ${internalBumps.length} internal package(s)` : "") : `bumping ${internalBumps.length} internal package(s)`;
|
|
3185
|
+
const isInteractive = !options.dryRun && !options.yes && !process.env.CI && process.stdin.isTTY;
|
|
3186
|
+
const npmPublisher = new NpmPublishProvider();
|
|
3187
|
+
if (publishable.length > 0 && !options.dryRun) {
|
|
3188
|
+
const authStatus = await npmPublisher.checkAuth();
|
|
3189
|
+
if (!authStatus.authenticated) {
|
|
3190
|
+
if (isInteractive) {
|
|
3191
|
+
await npmPublisher.ensureAuth({ interactive: true });
|
|
3192
|
+
} else {
|
|
3193
|
+
throw new NpmAuthError({
|
|
3194
|
+
message: "You are not logged in to npm. Run 'npm login' or configure NPM_TOKEN / NODE_AUTH_TOKEN in CI."
|
|
3195
|
+
});
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
if (isInteractive) {
|
|
3200
|
+
const confirmed = await prompts2.confirm({
|
|
3201
|
+
message: `Proceed with ${promptDescription}?`,
|
|
3202
|
+
initialValue: true
|
|
3203
|
+
});
|
|
3204
|
+
if (prompts2.isCancel(confirmed) || !confirmed) {
|
|
3205
|
+
prompts2.cancel("Release cancelled by user. No manifests, lockfiles, or packages were modified.");
|
|
3035
3206
|
return;
|
|
3036
3207
|
}
|
|
3037
3208
|
}
|
|
3038
|
-
|
|
3209
|
+
if (options.dryRun) {
|
|
3210
|
+
prompts2.log.info(colors2.yellow("DRY RUN: Manifest mutations, changelogs, git tags, and registry publishing skipped."));
|
|
3211
|
+
prompts2.outro(colors2.green("Dry run completed successfully."));
|
|
3212
|
+
return;
|
|
3213
|
+
}
|
|
3039
3214
|
const lockfileAdapter = new AutoLockfileAdapter();
|
|
3040
|
-
const npmPublisher = new NpmPublishProvider();
|
|
3041
3215
|
const ai = new TanStackReleaseAI();
|
|
3042
3216
|
const git = new GitManager(this.cwd);
|
|
3043
|
-
|
|
3044
|
-
const
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
await transaction.rollback();
|
|
3055
|
-
console.log("\u21A9 Rolled back manifest updates");
|
|
3056
|
-
process.exit(1);
|
|
3057
|
-
}
|
|
3058
|
-
console.log("\u2713 Lockfile regenerated successfully");
|
|
3059
|
-
console.log("\n\u{1F916} Generating changelogs & migration documentation via AI layer...");
|
|
3060
|
-
for (const pkgPlan of plan.packages) {
|
|
3061
|
-
if (pkgPlan.bump === "none") continue;
|
|
3062
|
-
const evidence = evidenceMap.get(pkgPlan.name) || [];
|
|
3063
|
-
const changelog = await ai.generateChangelog({
|
|
3064
|
-
packageName: pkgPlan.name,
|
|
3065
|
-
previousVersion: pkgPlan.currentVersion,
|
|
3066
|
-
newVersion: pkgPlan.proposedVersion,
|
|
3067
|
-
evidence
|
|
3068
|
-
});
|
|
3069
|
-
const changelogPath = join13(pkgPlan.directory, "CHANGELOG.md");
|
|
3070
|
-
let existingChangelog = null;
|
|
3071
|
-
try {
|
|
3072
|
-
existingChangelog = await readFile7(changelogPath, "utf8");
|
|
3073
|
-
} catch {
|
|
3074
|
-
existingChangelog = null;
|
|
3217
|
+
const spin = process.stdout.isTTY ? prompts2.spinner() : void 0;
|
|
3218
|
+
const releaseProgram = executeReleaseTransaction(
|
|
3219
|
+
{
|
|
3220
|
+
plan,
|
|
3221
|
+
cwd: this.cwd,
|
|
3222
|
+
lockfileAdapter,
|
|
3223
|
+
onStep: (msg) => {
|
|
3224
|
+
spin?.message(msg);
|
|
3225
|
+
},
|
|
3226
|
+
onRollback: (msg) => {
|
|
3227
|
+
prompts2.log.warn(colors2.yellow(`\u21A9 ${msg}`));
|
|
3075
3228
|
}
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3229
|
+
},
|
|
3230
|
+
(scope) => Effect3.gen(function* () {
|
|
3231
|
+
spin?.start("Generating changelogs & migration documentation via AI layer");
|
|
3232
|
+
for (const pkgPlan of plan.packages) {
|
|
3233
|
+
if (pkgPlan.bump === "none") continue;
|
|
3234
|
+
const evidence = evidenceMap.get(pkgPlan.name) || [];
|
|
3235
|
+
const changelog = yield* Effect3.promise(
|
|
3236
|
+
() => ai.generateChangelog({
|
|
3237
|
+
packageName: pkgPlan.name,
|
|
3238
|
+
previousVersion: pkgPlan.currentVersion,
|
|
3239
|
+
newVersion: pkgPlan.proposedVersion,
|
|
3240
|
+
evidence
|
|
3241
|
+
})
|
|
3242
|
+
);
|
|
3243
|
+
const changelogPath = join14(pkgPlan.directory, "CHANGELOG.md");
|
|
3244
|
+
let existingChangelog = null;
|
|
3245
|
+
try {
|
|
3246
|
+
existingChangelog = yield* Effect3.promise(() => readFile8(changelogPath, "utf8"));
|
|
3247
|
+
} catch {
|
|
3248
|
+
existingChangelog = null;
|
|
3249
|
+
}
|
|
3250
|
+
scope.backupChangelog(changelogPath, existingChangelog);
|
|
3251
|
+
yield* Effect3.promise(
|
|
3252
|
+
() => writeFile6(
|
|
3253
|
+
changelogPath,
|
|
3254
|
+
`${changelog.markdown}
|
|
3080
3255
|
|
|
3081
3256
|
${existingChangelog || ""}`.trim() + "\n",
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
console.log(` \u2713 Updated ${pkgPlan.name} CHANGELOG.md`);
|
|
3085
|
-
if (pkgPlan.bump === "major") {
|
|
3086
|
-
const breakingEvidence = evidence.filter((e) => e.severity === "major");
|
|
3087
|
-
const guide = await ai.generateMigrationGuide({
|
|
3088
|
-
packageName: pkgPlan.name,
|
|
3089
|
-
previousVersion: pkgPlan.currentVersion,
|
|
3090
|
-
newVersion: pkgPlan.proposedVersion,
|
|
3091
|
-
breakingChanges: breakingEvidence
|
|
3092
|
-
});
|
|
3093
|
-
const migrationsDir = join13(pkgPlan.directory, "docs", "migrations");
|
|
3094
|
-
await mkdir6(migrationsDir, { recursive: true });
|
|
3095
|
-
const guidePath = join13(
|
|
3096
|
-
migrationsDir,
|
|
3097
|
-
`${pkgPlan.currentVersion.split(".")[0]}-to-${pkgPlan.proposedVersion.split(".")[0]}.md`
|
|
3257
|
+
"utf8"
|
|
3258
|
+
)
|
|
3098
3259
|
);
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
if (options.tag) {
|
|
3104
|
-
try {
|
|
3105
|
-
console.log("\n\u{1F3F7}\uFE0F Creating Git release commit and tags...");
|
|
3106
|
-
await git.createReleaseCommit([], `chore(release): publish version ${plan.id}`);
|
|
3107
|
-
for (const pkgPlan of plan.packages) {
|
|
3108
|
-
if (pkgPlan.publish) {
|
|
3109
|
-
const tagName = git.formatTagName({
|
|
3260
|
+
if (pkgPlan.bump === "major") {
|
|
3261
|
+
const breakingEvidence = evidence.filter((e) => e.severity === "major");
|
|
3262
|
+
const guide = yield* Effect3.promise(
|
|
3263
|
+
() => ai.generateMigrationGuide({
|
|
3110
3264
|
packageName: pkgPlan.name,
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3265
|
+
previousVersion: pkgPlan.currentVersion,
|
|
3266
|
+
newVersion: pkgPlan.proposedVersion,
|
|
3267
|
+
breakingChanges: breakingEvidence
|
|
3268
|
+
})
|
|
3269
|
+
);
|
|
3270
|
+
const migrationsDir = join14(pkgPlan.directory, "docs", "migrations");
|
|
3271
|
+
yield* Effect3.promise(async () => {
|
|
3272
|
+
await mkdir6(migrationsDir, { recursive: true });
|
|
3273
|
+
const guidePath = join14(
|
|
3274
|
+
migrationsDir,
|
|
3275
|
+
`${pkgPlan.currentVersion.split(".")[0]}-to-${pkgPlan.proposedVersion.split(".")[0]}.md`
|
|
3276
|
+
);
|
|
3277
|
+
await writeFile6(guidePath, guide.markdown, "utf8");
|
|
3278
|
+
});
|
|
3116
3279
|
}
|
|
3117
|
-
} catch (err) {
|
|
3118
|
-
console.warn(` \u26A0\uFE0F Git tagging warning: ${err.message || String(err)}`);
|
|
3119
3280
|
}
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
console.log("\n\u{1F4E4} Publishing packages in dependency order:");
|
|
3125
|
-
for (const pkgName of plan.publishOrder) {
|
|
3126
|
-
const pkgPlan = plan.packages.find((p) => p.name === pkgName);
|
|
3127
|
-
if (!pkgPlan || !pkgPlan.publish) continue;
|
|
3128
|
-
console.log(`
|
|
3129
|
-
\u2192 Publishing ${pkgPlan.name}@${pkgPlan.proposedVersion}...`);
|
|
3130
|
-
const result = await npmPublisher.publish({
|
|
3131
|
-
packagePlan: pkgPlan,
|
|
3132
|
-
fullPlan: plan,
|
|
3133
|
-
dryRun: !!options.dryRun,
|
|
3134
|
-
extraArgs: options.otp ? ["--otp", options.otp] : void 0
|
|
3135
|
-
});
|
|
3136
|
-
if (result.published) {
|
|
3137
|
-
console.log(` \u2713 ${pkgPlan.name}@${pkgPlan.proposedVersion} published [tag: ${result.distTag}]`);
|
|
3138
|
-
} else {
|
|
3139
|
-
console.error(` \u274C Failed to publish ${pkgPlan.name}: ${result.error}`);
|
|
3140
|
-
if (!options.dryRun) {
|
|
3141
|
-
console.log("\n\u21A9 Rolling back manifest updates due to publish failure...");
|
|
3142
|
-
if (transaction) {
|
|
3143
|
-
await transaction.rollback();
|
|
3144
|
-
console.log(" \u2713 Restored manifests to previous versions");
|
|
3145
|
-
}
|
|
3146
|
-
for (const [chPath, original] of changelogBackups) {
|
|
3281
|
+
spin?.stop("Changelogs and documentation generated");
|
|
3282
|
+
if (options.tag) {
|
|
3283
|
+
spin?.start("Creating Git release commit and tags");
|
|
3284
|
+
yield* Effect3.promise(async () => {
|
|
3147
3285
|
try {
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3286
|
+
await git.createReleaseCommit([], `chore(release): publish version ${plan.id}`);
|
|
3287
|
+
for (const pkgPlan of plan.packages) {
|
|
3288
|
+
if (pkgPlan.publish) {
|
|
3289
|
+
const tagName = git.formatTagName({
|
|
3290
|
+
packageName: pkgPlan.name,
|
|
3291
|
+
version: pkgPlan.proposedVersion
|
|
3292
|
+
});
|
|
3293
|
+
await git.createTag(tagName);
|
|
3294
|
+
}
|
|
3152
3295
|
}
|
|
3153
|
-
} catch {
|
|
3296
|
+
} catch (err) {
|
|
3297
|
+
prompts2.log.warn(`Git tagging warning: ${err.message || String(err)}`);
|
|
3154
3298
|
}
|
|
3299
|
+
});
|
|
3300
|
+
spin?.stop("Git commit and tags created");
|
|
3301
|
+
}
|
|
3302
|
+
spin?.start("Publishing packages to npm registry");
|
|
3303
|
+
for (const pkgName of plan.publishOrder) {
|
|
3304
|
+
const pkgPlan = plan.packages.find((p) => p.name === pkgName);
|
|
3305
|
+
if (!pkgPlan || !pkgPlan.publish) continue;
|
|
3306
|
+
spin?.message(`Publishing ${colors2.bold(pkgPlan.name)}@${pkgPlan.proposedVersion}`);
|
|
3307
|
+
const result = yield* Effect3.promise(
|
|
3308
|
+
() => npmPublisher.publish({
|
|
3309
|
+
packagePlan: pkgPlan,
|
|
3310
|
+
fullPlan: plan,
|
|
3311
|
+
dryRun: !!options.dryRun,
|
|
3312
|
+
extraArgs: options.otp ? ["--otp", options.otp] : void 0
|
|
3313
|
+
})
|
|
3314
|
+
);
|
|
3315
|
+
if (!result.published) {
|
|
3316
|
+
return yield* Effect3.fail(
|
|
3317
|
+
new PublishError({
|
|
3318
|
+
packageName: pkgPlan.name,
|
|
3319
|
+
message: result.error || "npm publish failed"
|
|
3320
|
+
})
|
|
3321
|
+
);
|
|
3155
3322
|
}
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3323
|
+
prompts2.log.success(
|
|
3324
|
+
colors2.green(
|
|
3325
|
+
`Published ${colors2.bold(pkgPlan.name)}@${pkgPlan.proposedVersion} [tag: ${colors2.cyan(result.distTag)}]`
|
|
3326
|
+
)
|
|
3327
|
+
);
|
|
3160
3328
|
}
|
|
3161
|
-
|
|
3329
|
+
spin?.stop("All packages published successfully");
|
|
3330
|
+
})
|
|
3331
|
+
);
|
|
3332
|
+
const exit = await Effect3.runPromiseExit(releaseProgram);
|
|
3333
|
+
if (Exit2.isFailure(exit)) {
|
|
3334
|
+
prompts2.log.error(
|
|
3335
|
+
colors2.red("Publish failed. Transactional rollback has restored all files to their clean state.")
|
|
3336
|
+
);
|
|
3337
|
+
process.exit(1);
|
|
3162
3338
|
}
|
|
3163
|
-
|
|
3339
|
+
prompts2.outro(colors2.green(colors2.bold("\u{1F389} Release completed successfully!")));
|
|
3164
3340
|
}
|
|
3165
3341
|
async runExplainCommand(packageName) {
|
|
3342
|
+
prompts2.intro(
|
|
3343
|
+
colors2.cyan(colors2.bold("\u25C9 Verelease")) + colors2.dim(` explanation for ${colors2.bold(packageName)}`)
|
|
3344
|
+
);
|
|
3166
3345
|
const { plan, evidenceMap } = await this.createPlan();
|
|
3167
3346
|
const pkgPlan = plan.packages.find((p) => p.name === packageName);
|
|
3168
3347
|
if (!pkgPlan) {
|
|
3169
|
-
|
|
3348
|
+
prompts2.log.error(`Package "${packageName}" not found in workspace.`);
|
|
3170
3349
|
return;
|
|
3171
3350
|
}
|
|
3172
3351
|
const evidence = evidenceMap.get(packageName) || [];
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
`);
|
|
3183
|
-
console.log(`Evidence items (${evidence.length}):`);
|
|
3352
|
+
const badge = pkgPlan.bump === "major" ? colors2.red("\u{1F534} MAJOR") : pkgPlan.bump === "minor" ? colors2.yellow("\u{1F7E1} MINOR") : pkgPlan.bump === "patch" ? colors2.green("\u{1F7E2} PATCH") : colors2.dim("\u26AA NONE");
|
|
3353
|
+
const lines = [
|
|
3354
|
+
`${colors2.bold("Current Version:")} ${pkgPlan.currentVersion}`,
|
|
3355
|
+
`${colors2.bold("Proposed Version:")} ${colors2.cyan(colors2.bold(pkgPlan.proposedVersion))} [${badge}]`,
|
|
3356
|
+
`${colors2.bold("Confidence:")} ${(pkgPlan.confidence * 100).toFixed(0)}%`,
|
|
3357
|
+
`${colors2.bold("Requires Review:")} ${pkgPlan.requiresApproval ? colors2.yellow("YES") : colors2.green("NO")}`,
|
|
3358
|
+
"",
|
|
3359
|
+
`${colors2.bold(`Evidence Items (${evidence.length}):`)}`
|
|
3360
|
+
];
|
|
3184
3361
|
for (const ev of evidence) {
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
`);
|
|
3362
|
+
lines.push(` \u2022 [${colors2.cyan(ev.source.toUpperCase())}] (${colors2.bold(ev.severity.toUpperCase())}) ${ev.title}`);
|
|
3363
|
+
lines.push(` ${colors2.dim(ev.description)}`);
|
|
3188
3364
|
}
|
|
3189
3365
|
if (pkgPlan.reasons.length > 0) {
|
|
3190
|
-
|
|
3366
|
+
lines.push("");
|
|
3367
|
+
lines.push(colors2.bold("Decision Reasons:"));
|
|
3191
3368
|
for (const reason of pkgPlan.reasons) {
|
|
3192
|
-
|
|
3369
|
+
lines.push(` \u2022 ${reason}`);
|
|
3193
3370
|
}
|
|
3194
3371
|
}
|
|
3372
|
+
prompts2.note(lines.join("\n"), `Evidence Breakdown`);
|
|
3373
|
+
prompts2.outro("Explanation complete.");
|
|
3195
3374
|
}
|
|
3196
3375
|
async runGuideCommand(options = {}) {
|
|
3197
3376
|
if (options.json) {
|
|
@@ -3206,11 +3385,11 @@ ${existingChangelog || ""}`.trim() + "\n",
|
|
|
3206
3385
|
"Pass --yes to bypass interactive prompts in CI"
|
|
3207
3386
|
],
|
|
3208
3387
|
commands: {
|
|
3209
|
-
plan: "
|
|
3210
|
-
release: "
|
|
3211
|
-
explain: "
|
|
3212
|
-
guide: "
|
|
3213
|
-
init: "
|
|
3388
|
+
plan: "verelease plan [--channel <channel>] [--json]",
|
|
3389
|
+
release: "verelease release [--channel <channel>] [-y, --yes]",
|
|
3390
|
+
explain: "verelease explain <pkg>",
|
|
3391
|
+
guide: "verelease guide [--json]",
|
|
3392
|
+
init: "verelease init"
|
|
3214
3393
|
},
|
|
3215
3394
|
configuration: "verelease.config.ts",
|
|
3216
3395
|
contractsDirectory: ".verelease/contracts",
|
|
@@ -3222,37 +3401,32 @@ ${existingChangelog || ""}`.trim() + "\n",
|
|
|
3222
3401
|
);
|
|
3223
3402
|
return;
|
|
3224
3403
|
}
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
## Configuration
|
|
3246
|
-
Config is loaded from \`verelease.config.ts\` or \`verelease.config.js\`.
|
|
3247
|
-
Contract test suites live in \`.verelease/contracts/\`.
|
|
3248
|
-
`);
|
|
3404
|
+
prompts2.intro(colors2.cyan(colors2.bold("\u25C9 Verelease")) + colors2.dim(" agent guide"));
|
|
3405
|
+
const content = [
|
|
3406
|
+
colors2.bold("Core Rules for AI Coding Agents:"),
|
|
3407
|
+
`1. ${colors2.bold("NEVER manually edit 'version' in package.json")}: Always let Verelease determine versions.`,
|
|
3408
|
+
`2. ${colors2.bold("Inspect first")}: Run ${colors2.cyan("verelease plan")} to preview proposed version bumps.`,
|
|
3409
|
+
`3. ${colors2.bold("Diagnose bumps")}: Run ${colors2.cyan("verelease explain <pkg>")} to inspect compatibility evidence.`,
|
|
3410
|
+
`4. ${colors2.bold("Automated releases")}: In CI or agents, run ${colors2.cyan("verelease release --yes")} to bypass prompts.`,
|
|
3411
|
+
`5. ${colors2.bold("Interactive releases")}: Locally, Verelease prompts [y/N] and supports WebAuthn / Touch ID.`,
|
|
3412
|
+
"",
|
|
3413
|
+
colors2.bold("Command Reference:"),
|
|
3414
|
+
`\u2022 ${colors2.cyan("verelease plan")} Preview release plan for stable channel`,
|
|
3415
|
+
`\u2022 ${colors2.cyan("verelease plan --channel alpha")} Preview pre-release bumps for alpha channel`,
|
|
3416
|
+
`\u2022 ${colors2.cyan("verelease release")} Interactive release pipeline`,
|
|
3417
|
+
`\u2022 ${colors2.cyan("verelease release -y")} Non-interactive release (for agents/CI)`,
|
|
3418
|
+
`\u2022 ${colors2.cyan("verelease explain <package>")} Inspect evidence and decision reasons`,
|
|
3419
|
+
`\u2022 ${colors2.cyan("verelease init")} Initialize config, contracts, and AGENTS.md`
|
|
3420
|
+
].join("\n");
|
|
3421
|
+
prompts2.note(content, "Agent Guide & Workflow");
|
|
3422
|
+
prompts2.outro("Guide complete.");
|
|
3249
3423
|
}
|
|
3250
3424
|
async runInitCommand(options = {}) {
|
|
3251
|
-
const configPath =
|
|
3252
|
-
const contractsDir =
|
|
3253
|
-
const agentsPath =
|
|
3254
|
-
const pkgJsonPath =
|
|
3255
|
-
|
|
3425
|
+
const configPath = join14(this.cwd, "verelease.config.ts");
|
|
3426
|
+
const contractsDir = join14(this.cwd, ".verelease", "contracts");
|
|
3427
|
+
const agentsPath = join14(this.cwd, "AGENTS.md");
|
|
3428
|
+
const pkgJsonPath = join14(this.cwd, "package.json");
|
|
3429
|
+
prompts2.intro(colors2.cyan(colors2.bold("\u25C9 Verelease")) + colors2.dim(" project initialization"));
|
|
3256
3430
|
if (!existsSync(configPath)) {
|
|
3257
3431
|
const sampleConfig = `import { defineConfig } from "verelease/config";
|
|
3258
3432
|
|
|
@@ -3275,21 +3449,21 @@ export default defineConfig({
|
|
|
3275
3449
|
},
|
|
3276
3450
|
});
|
|
3277
3451
|
`;
|
|
3278
|
-
await
|
|
3279
|
-
|
|
3452
|
+
await writeFile6(configPath, sampleConfig, "utf8");
|
|
3453
|
+
prompts2.log.success(colors2.green("Created verelease.config.ts"));
|
|
3280
3454
|
} else {
|
|
3281
|
-
|
|
3455
|
+
prompts2.log.info("verelease.config.ts already exists");
|
|
3282
3456
|
}
|
|
3283
3457
|
if (!existsSync(contractsDir)) {
|
|
3284
3458
|
await mkdir6(contractsDir, { recursive: true });
|
|
3285
|
-
await
|
|
3286
|
-
|
|
3459
|
+
await writeFile6(
|
|
3460
|
+
join14(contractsDir, "README.md"),
|
|
3287
3461
|
"# Verelease Contracts\n\nPlace consumer-driven contract definitions (.json or .yaml) here.\n",
|
|
3288
3462
|
"utf8"
|
|
3289
3463
|
);
|
|
3290
|
-
|
|
3464
|
+
prompts2.log.success(colors2.green("Created .verelease/contracts/"));
|
|
3291
3465
|
} else {
|
|
3292
|
-
|
|
3466
|
+
prompts2.log.info(".verelease/contracts/ already exists");
|
|
3293
3467
|
}
|
|
3294
3468
|
const agentGuidance = `
|
|
3295
3469
|
## Releases & Versioning (Verelease)
|
|
@@ -3301,24 +3475,24 @@ This repository uses [Verelease](https://github.com/ivanjeremic/verelease) for a
|
|
|
3301
3475
|
- **NEVER manually edit "version" in package.json.**
|
|
3302
3476
|
`;
|
|
3303
3477
|
if (!existsSync(agentsPath)) {
|
|
3304
|
-
await
|
|
3478
|
+
await writeFile6(agentsPath, `# Agent Guidelines
|
|
3305
3479
|
${agentGuidance}
|
|
3306
3480
|
`, "utf8");
|
|
3307
|
-
|
|
3481
|
+
prompts2.log.success(colors2.green("Created AGENTS.md with Verelease instructions"));
|
|
3308
3482
|
} else {
|
|
3309
|
-
const currentContent = await
|
|
3483
|
+
const currentContent = await readFile8(agentsPath, "utf8");
|
|
3310
3484
|
if (!currentContent.includes("Verelease")) {
|
|
3311
|
-
await
|
|
3485
|
+
await writeFile6(agentsPath, `${currentContent.trim()}
|
|
3312
3486
|
${agentGuidance}
|
|
3313
3487
|
`, "utf8");
|
|
3314
|
-
|
|
3488
|
+
prompts2.log.success(colors2.green("Appended Verelease instructions to AGENTS.md"));
|
|
3315
3489
|
} else {
|
|
3316
|
-
|
|
3490
|
+
prompts2.log.info("AGENTS.md already contains Verelease instructions");
|
|
3317
3491
|
}
|
|
3318
3492
|
}
|
|
3319
3493
|
if (existsSync(pkgJsonPath)) {
|
|
3320
3494
|
try {
|
|
3321
|
-
const pkg = JSON.parse(await
|
|
3495
|
+
const pkg = JSON.parse(await readFile8(pkgJsonPath, "utf8"));
|
|
3322
3496
|
pkg.scripts = pkg.scripts || {};
|
|
3323
3497
|
let modified = false;
|
|
3324
3498
|
if (!pkg.scripts.plan) {
|
|
@@ -3330,127 +3504,219 @@ ${agentGuidance}
|
|
|
3330
3504
|
modified = true;
|
|
3331
3505
|
}
|
|
3332
3506
|
if (modified) {
|
|
3333
|
-
await
|
|
3334
|
-
|
|
3507
|
+
await writeFile6(pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
3508
|
+
prompts2.log.success(colors2.green("Added 'plan' and 'release' scripts to package.json"));
|
|
3335
3509
|
}
|
|
3336
3510
|
} catch {
|
|
3337
3511
|
}
|
|
3338
3512
|
}
|
|
3339
|
-
|
|
3513
|
+
prompts2.outro(colors2.green(colors2.bold("\u2728 Verelease successfully initialized!")));
|
|
3340
3514
|
}
|
|
3341
3515
|
printPlan(plan) {
|
|
3342
|
-
|
|
3343
|
-
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
|
|
3344
|
-
console.log(` VERELEASE RELEASE PLAN`);
|
|
3345
|
-
console.log(` Channel: ${plan.channel} | Packages: ${plan.summary.totalPackages} | Bumps: ${plan.summary.bumpedPackages}`);
|
|
3346
|
-
console.log(`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
3347
|
-
`);
|
|
3516
|
+
const lines = [];
|
|
3348
3517
|
for (const pkg of plan.packages) {
|
|
3349
|
-
const
|
|
3350
|
-
|
|
3351
|
-
|
|
3518
|
+
const badge = pkg.bump === "major" ? colors2.red("\u{1F534} MAJOR") : pkg.bump === "minor" ? colors2.yellow("\u{1F7E1} MINOR") : pkg.bump === "patch" ? colors2.green("\u{1F7E2} PATCH") : colors2.dim("\u26AA NONE");
|
|
3519
|
+
const pubTag = pkg.publish ? colors2.cyan(" [npm publish]") : colors2.dim(" [internal]");
|
|
3520
|
+
lines.push(
|
|
3521
|
+
`${badge} ${colors2.bold(pkg.name)} ${colors2.dim(pkg.currentVersion)} \u2192 ${colors2.cyan(colors2.bold(pkg.proposedVersion))}${pubTag}`
|
|
3522
|
+
);
|
|
3352
3523
|
if (pkg.reasons.length > 0) {
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
console.log(` ${reason}`);
|
|
3524
|
+
for (const reason of pkg.reasons.slice(0, 2)) {
|
|
3525
|
+
lines.push(` ${colors2.dim("\u2022")} ${colors2.dim(reason)}`);
|
|
3356
3526
|
}
|
|
3357
3527
|
}
|
|
3358
3528
|
if (pkg.dependentUpdates.length > 0) {
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3529
|
+
for (const dep of pkg.dependentUpdates.slice(0, 2)) {
|
|
3530
|
+
lines.push(
|
|
3531
|
+
` ${colors2.dim("\u21B3")} ${colors2.dim(`${dep.dependentName} depends on ${dep.dependencyName} (${dep.previousRange} \u2192 ${dep.newRange})`)}`
|
|
3532
|
+
);
|
|
3362
3533
|
}
|
|
3363
3534
|
}
|
|
3364
|
-
|
|
3535
|
+
lines.push("");
|
|
3365
3536
|
}
|
|
3537
|
+
const summaryLine = [
|
|
3538
|
+
`${colors2.bold(plan.summary.totalPackages)} packages`,
|
|
3539
|
+
`${colors2.green(colors2.bold(plan.summary.bumpedPackages))} bumps`,
|
|
3540
|
+
`(${colors2.green(`${plan.summary.patchBumps} patch`)}, ${colors2.yellow(`${plan.summary.minorBumps} minor`)}, ${colors2.red(`${plan.summary.majorBumps} major`)})`,
|
|
3541
|
+
`Channel: ${colors2.cyan(plan.channel)}`
|
|
3542
|
+
].join(" | ");
|
|
3543
|
+
lines.push(summaryLine);
|
|
3544
|
+
prompts2.note(lines.join("\n"), `Release Plan Summary`);
|
|
3366
3545
|
}
|
|
3367
3546
|
};
|
|
3368
3547
|
|
|
3369
3548
|
// src/bin.ts
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
release Apply version bumps, generate changelogs, and publish
|
|
3380
|
-
explain <pkg> Explain compatibility evidence and bump decision for a package
|
|
3381
|
-
guide Show agent-optimized cheat sheet and machine-readable instructions
|
|
3382
|
-
init Initialize verelease.config.ts, contracts, and AGENTS.md
|
|
3383
|
-
|
|
3384
|
-
Options:
|
|
3385
|
-
--channel <name> Release channel (e.g. stable, alpha, beta, rc) [default: stable]
|
|
3386
|
-
--bump <type> Manual bump override (patch | minor | major)
|
|
3387
|
-
--dry-run Simulate pipeline without modifying manifests or publishing
|
|
3388
|
-
-y, --yes Skip interactive confirmation prompt
|
|
3389
|
-
--otp <code> One-time password for npm 2FA
|
|
3390
|
-
--tag Create git commit and tags for release
|
|
3391
|
-
--json Output machine-readable JSON
|
|
3392
|
-
-h, --help Show this help message
|
|
3393
|
-
`);
|
|
3394
|
-
return;
|
|
3549
|
+
var __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
3550
|
+
async function getCliVersion() {
|
|
3551
|
+
try {
|
|
3552
|
+
const pkgJsonPath = join15(__dirname2, "../package.json");
|
|
3553
|
+
const content = await readFile9(pkgJsonPath, "utf8");
|
|
3554
|
+
const parsed = JSON.parse(content);
|
|
3555
|
+
return parsed.version || "0.1.0";
|
|
3556
|
+
} catch {
|
|
3557
|
+
return "0.1.0";
|
|
3395
3558
|
}
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3559
|
+
}
|
|
3560
|
+
var commonArgs = {
|
|
3561
|
+
channel: {
|
|
3562
|
+
type: "string",
|
|
3563
|
+
description: "Release channel (e.g. stable, alpha, beta, rc)",
|
|
3564
|
+
default: "stable"
|
|
3565
|
+
},
|
|
3566
|
+
bump: {
|
|
3567
|
+
type: "string",
|
|
3568
|
+
description: "Manual bump override (patch | minor | major)"
|
|
3569
|
+
},
|
|
3570
|
+
dryRun: {
|
|
3571
|
+
type: "boolean",
|
|
3572
|
+
description: "Simulate pipeline without modifying manifests or publishing"
|
|
3573
|
+
},
|
|
3574
|
+
yes: {
|
|
3575
|
+
type: "boolean",
|
|
3576
|
+
alias: "y",
|
|
3577
|
+
description: "Skip interactive confirmation prompt"
|
|
3578
|
+
},
|
|
3579
|
+
json: {
|
|
3580
|
+
type: "boolean",
|
|
3581
|
+
description: "Output machine-readable JSON"
|
|
3582
|
+
},
|
|
3583
|
+
tag: {
|
|
3584
|
+
type: "boolean",
|
|
3585
|
+
description: "Create git commit and tags for release"
|
|
3586
|
+
},
|
|
3587
|
+
otp: {
|
|
3588
|
+
type: "string",
|
|
3589
|
+
description: "One-time password for npm 2FA"
|
|
3590
|
+
},
|
|
3591
|
+
cwd: {
|
|
3592
|
+
type: "string",
|
|
3593
|
+
description: "Working directory path"
|
|
3594
|
+
}
|
|
3595
|
+
};
|
|
3596
|
+
function toCliOptions(args) {
|
|
3597
|
+
return {
|
|
3598
|
+
channel: args.channel,
|
|
3599
|
+
defaultBump: args.bump,
|
|
3600
|
+
dryRun: Boolean(args.dryRun),
|
|
3601
|
+
yes: Boolean(args.yes),
|
|
3602
|
+
json: Boolean(args.json),
|
|
3603
|
+
tag: Boolean(args.tag),
|
|
3604
|
+
otp: args.otp,
|
|
3605
|
+
cwd: args.cwd
|
|
3401
3606
|
};
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3607
|
+
}
|
|
3608
|
+
var subcommandExecuted = false;
|
|
3609
|
+
var planCommand = defineCommand({
|
|
3610
|
+
meta: {
|
|
3611
|
+
name: "plan",
|
|
3612
|
+
description: "Preview the release plan without making changes"
|
|
3613
|
+
},
|
|
3614
|
+
args: commonArgs,
|
|
3615
|
+
async run({ args }) {
|
|
3616
|
+
subcommandExecuted = true;
|
|
3617
|
+
const opts = toCliOptions(args);
|
|
3618
|
+
const cli = new VereleaseCli(opts);
|
|
3619
|
+
await cli.runPlanCommand(opts);
|
|
3405
3620
|
}
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3621
|
+
});
|
|
3622
|
+
var releaseCommand = defineCommand({
|
|
3623
|
+
meta: {
|
|
3624
|
+
name: "release",
|
|
3625
|
+
description: "Apply version bumps, generate changelogs, and publish"
|
|
3626
|
+
},
|
|
3627
|
+
args: commonArgs,
|
|
3628
|
+
async run({ args }) {
|
|
3629
|
+
subcommandExecuted = true;
|
|
3630
|
+
const opts = toCliOptions(args);
|
|
3631
|
+
const cli = new VereleaseCli(opts);
|
|
3632
|
+
await cli.runReleaseCommand(opts);
|
|
3409
3633
|
}
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3634
|
+
});
|
|
3635
|
+
var explainCommand = defineCommand({
|
|
3636
|
+
meta: {
|
|
3637
|
+
name: "explain",
|
|
3638
|
+
description: "Explain compatibility evidence and bump decision for a package"
|
|
3639
|
+
},
|
|
3640
|
+
args: {
|
|
3641
|
+
package: {
|
|
3642
|
+
type: "positional",
|
|
3643
|
+
description: "Package name to explain (e.g. @verelease/core)",
|
|
3644
|
+
required: false
|
|
3645
|
+
},
|
|
3646
|
+
...commonArgs
|
|
3647
|
+
},
|
|
3648
|
+
async run({ args }) {
|
|
3649
|
+
subcommandExecuted = true;
|
|
3650
|
+
const opts = toCliOptions(args);
|
|
3651
|
+
const cli = new VereleaseCli(opts);
|
|
3652
|
+
const targetPkg = args.package || args._ && args._[0];
|
|
3653
|
+
if (!targetPkg) {
|
|
3654
|
+
console.error("\n\u274C Please specify a package name to explain, e.g.: verelease explain @verelease/core\n");
|
|
3655
|
+
process.exit(1);
|
|
3656
|
+
}
|
|
3657
|
+
await cli.runExplainCommand(targetPkg);
|
|
3658
|
+
}
|
|
3659
|
+
});
|
|
3660
|
+
var guideCommand = defineCommand({
|
|
3661
|
+
meta: {
|
|
3662
|
+
name: "guide",
|
|
3663
|
+
description: "Show agent-optimized cheat sheet and machine-readable instructions"
|
|
3664
|
+
},
|
|
3665
|
+
args: {
|
|
3666
|
+
json: {
|
|
3667
|
+
type: "boolean",
|
|
3668
|
+
description: "Output machine-readable JSON"
|
|
3669
|
+
},
|
|
3670
|
+
cwd: {
|
|
3671
|
+
type: "string",
|
|
3672
|
+
description: "Working directory path"
|
|
3673
|
+
}
|
|
3674
|
+
},
|
|
3675
|
+
async run({ args }) {
|
|
3676
|
+
subcommandExecuted = true;
|
|
3677
|
+
const cli = new VereleaseCli({ cwd: args.cwd });
|
|
3678
|
+
await cli.runGuideCommand({ json: Boolean(args.json) });
|
|
3413
3679
|
}
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3680
|
+
});
|
|
3681
|
+
var initCommand = defineCommand({
|
|
3682
|
+
meta: {
|
|
3683
|
+
name: "init",
|
|
3684
|
+
description: "Initialize verelease.config.ts, contracts, and AGENTS.md"
|
|
3685
|
+
},
|
|
3686
|
+
args: {
|
|
3687
|
+
cwd: {
|
|
3688
|
+
type: "string",
|
|
3689
|
+
description: "Working directory path"
|
|
3690
|
+
}
|
|
3691
|
+
},
|
|
3692
|
+
async run({ args }) {
|
|
3693
|
+
subcommandExecuted = true;
|
|
3694
|
+
const cli = new VereleaseCli({ cwd: args.cwd });
|
|
3695
|
+
await cli.runInitCommand({ cwd: args.cwd });
|
|
3417
3696
|
}
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
process.exit(1);
|
|
3441
|
-
}
|
|
3442
|
-
await cli.runExplainCommand(targetPkg);
|
|
3443
|
-
break;
|
|
3444
|
-
default:
|
|
3445
|
-
console.log(`Unknown command: ${command}`);
|
|
3446
|
-
console.log(`Run 'verelease --help' for available commands and options.`);
|
|
3447
|
-
process.exit(1);
|
|
3448
|
-
}
|
|
3449
|
-
} catch (err) {
|
|
3450
|
-
console.error(`
|
|
3451
|
-
\u274C Error: ${err.message || String(err)}`);
|
|
3452
|
-
process.exit(1);
|
|
3697
|
+
});
|
|
3698
|
+
var version = await getCliVersion();
|
|
3699
|
+
var mainCommand = defineCommand({
|
|
3700
|
+
meta: {
|
|
3701
|
+
name: "verelease",
|
|
3702
|
+
version,
|
|
3703
|
+
description: "Evidence-based semantic versioning release engine"
|
|
3704
|
+
},
|
|
3705
|
+
args: commonArgs,
|
|
3706
|
+
subCommands: {
|
|
3707
|
+
plan: planCommand,
|
|
3708
|
+
release: releaseCommand,
|
|
3709
|
+
version: releaseCommand,
|
|
3710
|
+
explain: explainCommand,
|
|
3711
|
+
guide: guideCommand,
|
|
3712
|
+
init: initCommand
|
|
3713
|
+
},
|
|
3714
|
+
async run({ args }) {
|
|
3715
|
+
if (subcommandExecuted) return;
|
|
3716
|
+
const opts = toCliOptions(args);
|
|
3717
|
+
const cli = new VereleaseCli(opts);
|
|
3718
|
+
await cli.runPlanCommand(opts);
|
|
3453
3719
|
}
|
|
3454
|
-
}
|
|
3455
|
-
|
|
3720
|
+
});
|
|
3721
|
+
void runMain(mainCommand);
|
|
3456
3722
|
//# sourceMappingURL=bin.js.map
|