@smoothbricks/cli 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +29 -24
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +19 -3
  4. package/dist/generate/index.d.ts +20 -0
  5. package/dist/generate/index.d.ts.map +1 -0
  6. package/dist/generate/index.js +47 -0
  7. package/dist/lib/run.d.ts +1 -0
  8. package/dist/lib/run.d.ts.map +1 -1
  9. package/dist/lib/run.js +19 -0
  10. package/dist/lib/workspace.d.ts +10 -0
  11. package/dist/lib/workspace.d.ts.map +1 -1
  12. package/dist/lib/workspace.js +55 -16
  13. package/dist/monorepo/lockfile.d.ts.map +1 -1
  14. package/dist/monorepo/lockfile.js +9 -0
  15. package/dist/monorepo/package-policy.d.ts +6 -17
  16. package/dist/monorepo/package-policy.d.ts.map +1 -1
  17. package/dist/monorepo/package-policy.js +63 -1127
  18. package/dist/monorepo/publish-workflow.js +2 -3
  19. package/dist/nx/index.d.ts +1 -0
  20. package/dist/nx/index.d.ts.map +1 -1
  21. package/dist/nx/index.js +23 -5
  22. package/dist/release/candidates.d.ts +1 -0
  23. package/dist/release/candidates.d.ts.map +1 -1
  24. package/dist/release/candidates.js +12 -0
  25. package/dist/release/core.d.ts +3 -1
  26. package/dist/release/core.d.ts.map +1 -1
  27. package/dist/release/core.js +76 -9
  28. package/dist/release/github-release.d.ts +1 -0
  29. package/dist/release/github-release.d.ts.map +1 -1
  30. package/dist/release/github-release.js +1 -1
  31. package/dist/release/index.d.ts +9 -3
  32. package/dist/release/index.d.ts.map +1 -1
  33. package/dist/release/index.js +287 -39
  34. package/dist/release/orchestration.d.ts +10 -2
  35. package/dist/release/orchestration.d.ts.map +1 -1
  36. package/dist/release/orchestration.js +33 -3
  37. package/managed/raw/tooling/git-hooks/commit-msg.sh +7 -1
  38. package/managed/raw/tooling/git-hooks/pre-commit.sh +7 -1
  39. package/managed/templates/github/workflows/ci.yml +3 -2
  40. package/package.json +7 -12
  41. package/src/cli.ts +21 -4
  42. package/src/generate/index.ts +92 -0
  43. package/src/lib/run.ts +25 -0
  44. package/src/lib/workspace.ts +72 -16
  45. package/src/monorepo/lockfile.ts +9 -0
  46. package/src/monorepo/package-policy.test.ts +254 -26
  47. package/src/monorepo/package-policy.ts +88 -1351
  48. package/src/monorepo/publish-workflow.ts +2 -3
  49. package/src/nx/index.test.ts +9 -0
  50. package/src/nx/index.ts +25 -5
  51. package/src/release/__tests__/candidates.test.ts +109 -8
  52. package/src/release/__tests__/core-properties.test.ts +2 -2
  53. package/src/release/__tests__/core-scenarios.test.ts +12 -15
  54. package/src/release/__tests__/core.test.ts +50 -3
  55. package/src/release/__tests__/fixture-repo.test.ts +68 -18
  56. package/src/release/__tests__/github-release.test.ts +9 -16
  57. package/src/release/__tests__/orchestration.test.ts +91 -5
  58. package/src/release/__tests__/retag-unpublished.test.ts +9 -9
  59. package/src/release/__tests__/trust-publisher.test.ts +65 -33
  60. package/src/release/candidates.ts +13 -0
  61. package/src/release/core.ts +95 -10
  62. package/src/release/github-release.ts +1 -1
  63. package/src/release/index.ts +366 -49
  64. package/src/release/orchestration.ts +50 -5
  65. package/dist/nx-version-actions.cjs +0 -25
  66. package/dist/nx-version-actions.d.cts +0 -6
  67. package/dist/nx-version-actions.d.cts.map +0 -1
  68. package/src/nx-version-actions.cts +0 -36
@@ -6,15 +6,15 @@ import { Writable } from 'node:stream';
6
6
  import { $ } from 'bun';
7
7
  import { withDevenvEnv } from '../lib/devenv.js';
8
8
  import { isRecord, readJsonObject, stringProperty } from '../lib/json.js';
9
- import { decode, run, runResult, runStatus } from '../lib/run.js';
9
+ import { decode, run, runInteractiveStatus, runResult, runStatus } from '../lib/run.js';
10
10
  import { listReleasePackages, readPackageJson, repositoryInfo } from '../lib/workspace.js';
11
11
  import { readPackedPackageJson, validatePackedWorkspaceDependencies } from '../monorepo/packed-manifest.js';
12
12
  import { bootstrapNpmPackages, NPM_BOOTSTRAP_DIST_TAG, NPM_BOOTSTRAP_VERSION, } from './bootstrap-npm-packages.js';
13
13
  import { autoReleaseCandidatePackages } from './candidates.js';
14
- import { collectOwnedReleaseTagRecords, pendingReleaseTargets, releaseTag, } from './core.js';
15
- import { createOrUpdateGithubRelease, renderNxProjectChangelogContents } from './github-release.js';
14
+ import { collectOwnedReleaseTagRecords, pendingReleaseTargets, releaseTag, releaseTagAliases, } from './core.js';
15
+ import { createOrUpdateGithubRelease, renderNxProjectChangelogContents, withNxWorkspaceRoot, } from './github-release.js';
16
16
  import { publishWithAuthDiagnostics } from './npm-auth.js';
17
- import { completeReleaseAtHead as completeReleaseAtHeadWithShell, repairPendingTargets, runReleaseVersion, } from './orchestration.js';
17
+ import { bumpStableReleaseToNext, completeReleaseAtHead as completeReleaseAtHeadWithShell, repairPendingTargets, runReleaseVersion, } from './orchestration.js';
18
18
  import { retagUnpublished } from './retag-unpublished.js';
19
19
  export async function releaseVersion(root, options) {
20
20
  const bump = releaseBumpArg(options.bump);
@@ -33,6 +33,9 @@ export async function releaseVersion(root, options) {
33
33
  else if (result.status === 'no-release-needed') {
34
34
  console.log('Nx did not create a release commit; no release needed.');
35
35
  }
36
+ if (result.status === 'dry-run') {
37
+ await writeReleasePreviewSummary(root, packages, result.packages);
38
+ }
36
39
  await writeReleaseGithubOutput(options.githubOutput, result.packages, result.mode);
37
40
  }
38
41
  export async function releasePublish(root, options) {
@@ -40,15 +43,20 @@ export async function releasePublish(root, options) {
40
43
  const packages = await releasePackagesAtHead(root, releasePackages(root));
41
44
  if (packages.length === 0) {
42
45
  if (bump === 'auto') {
46
+ if (options.dryRun === true) {
47
+ console.log('No release tags found at HEAD because dry-run versioning does not create release commits or tags.');
48
+ return;
49
+ }
43
50
  console.log('No release tags found at HEAD; no release to publish.');
44
51
  const summary = {
45
52
  sha: await gitHead(root),
46
- dryRun: options.dryRun === true,
53
+ dryRun: false,
47
54
  packages,
48
55
  pushed: false,
49
56
  published: [],
50
57
  alreadyPublished: [],
51
58
  githubReleases: [],
59
+ githubReleaseLinks: [],
52
60
  rerunRequired: false,
53
61
  noRelease: true,
54
62
  };
@@ -59,6 +67,10 @@ export async function releasePublish(root, options) {
59
67
  }
60
68
  const summary = await completeReleaseAtHeadWithShell(releaseCompletionShell(root), packages, options.dryRun === true, await newerCommitsRemain(root));
61
69
  await writeReleaseSummary(summary);
70
+ const bumpedPackages = await bumpStableReleaseToNext(releaseNextShell(root), packages, options.dryRun === true, summary.rerunRequired);
71
+ if (bumpedPackages.length > 0) {
72
+ console.log(`Prepared next prerelease for ${packageSummary(bumpedPackages)}.`);
73
+ }
62
74
  }
63
75
  export async function releaseRepairPending(root, options) {
64
76
  const branch = await releaseBranch(root);
@@ -72,6 +84,9 @@ export async function releaseRepairPending(root, options) {
72
84
  console.log(targets.length === 0
73
85
  ? 'Repair pending releases: no pending durable state repairs found.'
74
86
  : `Repair pending releases: ${targets.length} release target${targets.length === 1 ? '' : 's'} need repair.`);
87
+ for (const target of targets) {
88
+ console.log(`Repair pending releases: target ${target.sha.slice(0, 12)} needs ${repairTargetSummary(target)}.`);
89
+ }
75
90
  const summaries = await repairPendingTargets(releaseRepairShell(root), targets, restoreRef, options.dryRun === true);
76
91
  await writeRepairSummary(summaries, options.dryRun === true);
77
92
  }
@@ -91,7 +106,6 @@ export async function releaseTrustPublisher(root, options) {
91
106
  promptOtp: (packageName) => promptForNpmOtp(packageName),
92
107
  log: (message) => console.log(message),
93
108
  }, bootstrapOptions),
94
- login: () => runLatestNpm(root, ['login', '--auth-type=web']),
95
109
  trustPublisher: (pkg, dryRun, env) => {
96
110
  const args = ['trust', 'github', pkg.name, '--file', workflow, '--repo', repository, '--yes'];
97
111
  if (dryRun) {
@@ -99,28 +113,27 @@ export async function releaseTrustPublisher(root, options) {
99
113
  }
100
114
  return runLatestNpmTrust(root, args, env);
101
115
  },
102
- promptOtp: (packageName) => promptForNpmOtp(packageName),
116
+ trustedPublishers: (pkg) => listTrustedPublishers(root, pkg),
103
117
  log: (message) => console.log(message),
104
118
  error: (message) => console.error(message),
105
119
  }, options);
106
120
  }
107
121
  export async function configureTrustedPublishers(shell, options) {
108
122
  const packages = shell.listReleasePackages();
109
- if (packages.length === 0) {
123
+ const selectedPackages = selectedTrustPublisherPackages(packages, options.packages ?? []);
124
+ if (selectedPackages.length === 0) {
110
125
  throw new Error('No owned release packages found.');
111
126
  }
112
- let bootstrapLoggedIn = false;
113
127
  if (options.bootstrap) {
114
- const bootstrapped = await shell.bootstrapNpmPackages({
128
+ await shell.bootstrapNpmPackages({
115
129
  dryRun: options.dryRun === true,
116
130
  skipLogin: options.skipLogin === true,
117
131
  otp: options.bootstrapOtp,
118
- packages: [],
132
+ packages: options.packages ?? [],
119
133
  });
120
- bootstrapLoggedIn = bootstrapped.length > 0 && !options.dryRun && !options.skipLogin;
121
134
  }
122
135
  if (!options.dryRun) {
123
- const packageStates = await Promise.all(packages.map(async (pkg) => ((await shell.packageExists(pkg.name)) ? null : pkg.name)));
136
+ const packageStates = await Promise.all(selectedPackages.map(async (pkg) => ((await shell.packageExists(pkg.name)) ? null : pkg.name)));
124
137
  const missingPackages = packageStates.filter((name) => name !== null);
125
138
  if (missingPackages.length > 0) {
126
139
  throw new Error('npm trusted publishing can only be configured after packages exist on the registry. ' +
@@ -128,16 +141,23 @@ export async function configureTrustedPublishers(shell, options) {
128
141
  `Missing packages: ${missingPackages.join(', ')}`);
129
142
  }
130
143
  }
131
- if (!options.dryRun && !options.skipLogin && !bootstrapLoggedIn) {
132
- await shell.login();
133
- }
134
144
  const failedPackages = [];
135
- for (const pkg of packages) {
145
+ for (const pkg of selectedPackages) {
146
+ if (!options.dryRun) {
147
+ const trustedPublishers = await shell.trustedPublishers(pkg);
148
+ if (hasMatchingGithubTrustedPublisher(trustedPublishers, shell.repository, shell.workflow)) {
149
+ shell.log(`${pkg.name}: npm trusted publisher is already configured; skipping.`);
150
+ continue;
151
+ }
152
+ if (trustedPublishers.length > 0) {
153
+ throw new Error(`${pkg.name}: npm trusted publisher exists but does not match GitHub Actions ${shell.repository}/${shell.workflow}. ` +
154
+ 'Use npm trust list to inspect it and npm trust revoke --id <trust-id> before reconfiguring.');
155
+ }
156
+ }
136
157
  shell.log(`${pkg.name}: trusting GitHub Actions ${shell.repository}/${shell.workflow}`);
137
158
  while (true) {
138
- const otp = options.dryRun ? undefined : (options.otp ?? (await shell.promptOtp(pkg.name)));
139
159
  try {
140
- const result = await shell.trustPublisher(pkg, options.dryRun === true, otp ? { NPM_CONFIG_OTP: otp } : undefined);
160
+ const result = await shell.trustPublisher(pkg, options.dryRun === true);
141
161
  if (result === 'already-configured') {
142
162
  shell.log(`${pkg.name}: npm trusted publisher is already configured; skipping.`);
143
163
  }
@@ -147,9 +167,9 @@ export async function configureTrustedPublishers(shell, options) {
147
167
  shell.error(`${pkg.name}: npm trusted publisher setup failed.`);
148
168
  shell.error(error instanceof Error ? error.message : String(error));
149
169
  if (!options.dryRun) {
150
- shell.error('npm OTP codes are single-use. Generate a fresh OTP before retrying this package.');
170
+ shell.error('npm owns trusted publishing authentication. Complete the npm browser challenge, then retry.');
151
171
  }
152
- if (options.otp || options.dryRun) {
172
+ if (options.dryRun) {
153
173
  failedPackages.push(pkg.name);
154
174
  break;
155
175
  }
@@ -159,10 +179,33 @@ export async function configureTrustedPublishers(shell, options) {
159
179
  }
160
180
  if (failedPackages.length > 0) {
161
181
  shell.error(`Trusted publishing was not configured for: ${failedPackages.join(', ')}`);
162
- if (options.otp) {
163
- shell.error('Rerun smoo release trust-publisher without --otp to enter a fresh OTP for each failed package.');
182
+ }
183
+ }
184
+ function selectedTrustPublisherPackages(packages, selections) {
185
+ if (selections.length === 0) {
186
+ return packages;
187
+ }
188
+ const byName = new Map(packages.map((pkg) => [pkg.name, pkg]));
189
+ const selected = [];
190
+ const unknown = [];
191
+ for (const name of selections) {
192
+ const pkg = byName.get(name);
193
+ if (pkg) {
194
+ selected.push(pkg);
195
+ }
196
+ else {
197
+ unknown.push(name);
164
198
  }
165
199
  }
200
+ if (unknown.length > 0) {
201
+ throw new Error(`Unknown owned release package selection: ${unknown.join(', ')}`);
202
+ }
203
+ return selected;
204
+ }
205
+ function hasMatchingGithubTrustedPublisher(trustedPublishers, repository, workflow) {
206
+ return trustedPublishers.some((trustedPublisher) => trustedPublisher.type === 'github' &&
207
+ trustedPublisher.repository === repository &&
208
+ trustedPublisher.file === workflow);
166
209
  }
167
210
  export async function releaseBootstrapNpmPackages(root, options) {
168
211
  await bootstrapNpmPackages({
@@ -198,6 +241,7 @@ export async function releaseRetagUnpublished(root, options) {
198
241
  export async function printReleaseState(root) {
199
242
  console.log(JSON.stringify(await getReleaseState(root), null, 2));
200
243
  }
244
+ const releasePreviewChangelogs = new Map();
201
245
  async function getReleaseState(root) {
202
246
  return getReleaseStateForPackages(root, listReleasePackages(root));
203
247
  }
@@ -293,6 +337,7 @@ async function releaseVersionPackages(root, packages, bump) {
293
337
  }
294
338
  return autoReleaseCandidatePackages({
295
339
  gitRefExists: (ref) => gitRefExists(root, ref),
340
+ latestStableReleaseRef: (projectName) => latestStableReleaseRef(root, projectName),
296
341
  packageChangedFilesSince: (ref, packagePath) => packageChangedFilesSince(root, ref, packagePath),
297
342
  packageJsonAtRef: (ref, packagePath) => packageJsonAtRef(root, ref, packagePath),
298
343
  currentPackageJson: (packagePath) => currentPackageJson(root, packagePath),
@@ -301,6 +346,9 @@ async function releaseVersionPackages(root, packages, bump) {
301
346
  }, packages);
302
347
  }
303
348
  async function runNxReleaseVersion(root, projects, bump, dryRun) {
349
+ if (dryRun) {
350
+ return runNxReleaseVersionPreview(root, projects, bump);
351
+ }
304
352
  // Nx owns local release mutation: package versions, bun.lock updates, the
305
353
  // release commit, and annotated tags. smoo owns remote publication after the
306
354
  // workflow validates the exact release commit Nx produced.
@@ -309,10 +357,66 @@ async function runNxReleaseVersion(root, projects, bump, dryRun) {
309
357
  nxArgs.push(bump);
310
358
  }
311
359
  nxArgs.push(`--projects=${projects}`, '--git-commit=true', '--git-tag=true', '--git-push=false');
312
- if (dryRun) {
313
- nxArgs.push('--dry-run');
314
- }
315
360
  await run('nx', nxArgs, root);
361
+ return [];
362
+ }
363
+ async function runNxReleaseVersionPreview(root, projects, bump) {
364
+ return withNxWorkspaceRoot(root, async () => {
365
+ const { createAPI } = await import('nx/src/command-line/release/version.js');
366
+ const { createAPI: createChangelogAPI } = await import('nx/src/command-line/release/changelog.js');
367
+ const projectNames = projects.split(',').filter(Boolean);
368
+ const result = await createAPI({}, false)({
369
+ specifier: bump === 'auto' ? undefined : bump,
370
+ projects: projectNames,
371
+ gitCommit: true,
372
+ gitTag: true,
373
+ gitPush: false,
374
+ dryRun: true,
375
+ });
376
+ const changelogResult = await createChangelogAPI({
377
+ changelog: {
378
+ workspaceChangelog: false,
379
+ projectChangelogs: { createRelease: false, file: false },
380
+ },
381
+ }, false)({
382
+ projects: projectNames,
383
+ versionData: result.projectsVersionData,
384
+ releaseGraph: result.releaseGraph,
385
+ gitCommit: false,
386
+ gitTag: false,
387
+ gitPush: false,
388
+ stageChanges: false,
389
+ createRelease: false,
390
+ dryRun: true,
391
+ });
392
+ releasePreviewChangelogs.clear();
393
+ for (const [projectName, changelog] of Object.entries(changelogResult.projectChangelogs ?? {})) {
394
+ releasePreviewChangelogs.set(projectName, changelog.contents);
395
+ }
396
+ const packagesByProject = new Map(releasePackages(root).map((pkg) => [pkg.projectName, pkg]));
397
+ return Object.entries(result.projectsVersionData)
398
+ .map(([projectName, data]) => {
399
+ const pkg = packagesByProject.get(projectName);
400
+ if (!pkg || !data.newVersion) {
401
+ return null;
402
+ }
403
+ return { ...pkg, version: data.newVersion };
404
+ })
405
+ .filter((pkg) => pkg !== null);
406
+ });
407
+ }
408
+ async function runNxNextPrereleaseVersion(root, projects) {
409
+ await run('nx', [
410
+ 'release',
411
+ 'version',
412
+ 'prerelease',
413
+ `--projects=${projects}`,
414
+ '--preid=next',
415
+ '--git-commit=true',
416
+ '--git-tag=false',
417
+ '--git-push=false',
418
+ '--git-commit-message=chore(release): prepare next prerelease',
419
+ ], root);
316
420
  }
317
421
  async function releasePackagesAtHead(root, packages) {
318
422
  return releasePackagesAtRef(root, packages, 'HEAD');
@@ -381,7 +485,7 @@ async function gitTagsAtRef(root, ref) {
381
485
  .filter(Boolean);
382
486
  }
383
487
  async function previousReleaseTag(root, pkg, currentTag) {
384
- const result = await $ `git tag --list ${`${pkg.name}@*`} --sort=-v:refname --merged ${currentTag}`
488
+ const result = await $ `git tag --list ${`${pkg.projectName}@*`} --sort=-v:refname --merged ${currentTag}`
385
489
  .cwd(root)
386
490
  .quiet()
387
491
  .nothrow();
@@ -429,6 +533,11 @@ async function pushReleaseRefs(root, packages) {
429
533
  await run('git', ['push', '--atomic', remote, ...refspecs], root);
430
534
  return true;
431
535
  }
536
+ async function pushCurrentBranch(root) {
537
+ const branch = await releaseBranch(root);
538
+ const remote = await releaseRemote(root, branch);
539
+ await run('git', ['push', remote, `HEAD:refs/heads/${branch}`], root);
540
+ }
432
541
  function releaseCompletionShell(root) {
433
542
  return {
434
543
  gitHead: () => gitHead(root),
@@ -446,6 +555,14 @@ function releaseCompletionShell(root) {
446
555
  createGithubRelease: (pkg, dryRun) => createGithubRelease(root, pkg, dryRun),
447
556
  };
448
557
  }
558
+ function releaseNextShell(root) {
559
+ return {
560
+ bumpStablePackagesToNext: async (packages) => {
561
+ await runNxNextPrereleaseVersion(root, releasePackageProjects(packages));
562
+ await pushCurrentBranch(root);
563
+ },
564
+ };
565
+ }
449
566
  function releaseRepairShell(root) {
450
567
  return {
451
568
  ...releaseCompletionShell(root),
@@ -490,15 +607,23 @@ async function listOwnedReleaseTagRecords(root, ref) {
490
607
  }
491
608
  async function durableReleaseTagState(root, pkg, tag) {
492
609
  const packageVersion = `${pkg.name}@${pkg.version}`;
610
+ const githubReleaseTags = releaseTagAliases({ ...pkg, projectName: projectNameFromReleaseTag(tag) });
493
611
  const start = Date.now();
494
- console.log(`${packageVersion}: checking durable state (npm + GitHub Release ${tag}).`);
612
+ console.log(`${packageVersion}: checking durable state (npm + GitHub Release ${githubReleaseTags.join(' or ')}).`);
495
613
  const [npmPublished, githubReleasePresent] = await Promise.all([
496
614
  npmVersionExists(root, pkg.name, pkg.version),
497
- githubReleaseExists(root, tag),
615
+ anyGithubReleaseExists(root, githubReleaseTags),
498
616
  ]);
499
617
  console.log(`${packageVersion}: durable state npm=${yesNo(npmPublished)} github=${yesNo(githubReleasePresent)} (${Date.now() - start}ms).`);
500
618
  return { npmPublished, githubReleaseExists: githubReleasePresent };
501
619
  }
620
+ function projectNameFromReleaseTag(tag) {
621
+ const versionSeparator = tag.lastIndexOf('@');
622
+ if (versionSeparator <= 0) {
623
+ return tag;
624
+ }
625
+ return tag.slice(0, versionSeparator);
626
+ }
502
627
  async function gitReleaseTagsByCreatorDate(root) {
503
628
  const result = await $ `git for-each-ref --sort=-creatordate --format=${'%(refname:short)%09%(creatordate:unix)%09%(*objectname)%09%(objectname)'} refs/tags`
504
629
  .cwd(root)
@@ -537,7 +662,7 @@ async function createGithubRelease(root, pkg, dryRun) {
537
662
  const previousTag = await previousReleaseTag(root, pkg, currentTag);
538
663
  if (dryRun) {
539
664
  await renderNxProjectChangelogContents({ root, pkg, previousTag, dryRun });
540
- return;
665
+ return null;
541
666
  }
542
667
  const contents = await renderNxProjectChangelogContents({ root, pkg, previousTag, dryRun });
543
668
  await createOrUpdateGithubRelease(pkg, contents, {
@@ -545,6 +670,7 @@ async function createGithubRelease(root, pkg, dryRun) {
545
670
  runGhRelease: (args) => run('gh', args, root),
546
671
  log: (message) => console.log(message),
547
672
  });
673
+ return githubReleaseUrl(root, currentTag);
548
674
  }
549
675
  function envPresence(name) {
550
676
  return `${name}=${process.env[name] ? 'present' : 'missing'}`;
@@ -579,7 +705,7 @@ async function writeReleaseSummary(summary) {
579
705
  lines.push(`- Git refs pushed: ${summary.pushed ? 'yes' : 'already current'}`);
580
706
  lines.push(`- npm published: ${packageSummary(summary.published)}`);
581
707
  lines.push(`- npm already published: ${packageSummary(summary.alreadyPublished)}`);
582
- lines.push(`- GitHub Releases created/updated: ${packageSummary(summary.githubReleases)}`);
708
+ lines.push(`- GitHub Releases created/updated: ${githubReleaseSummary(summary)}`);
583
709
  }
584
710
  if (summary.rerunRequired) {
585
711
  const message = 'A previous incomplete release was repaired; newer commits remain. Run Publish again.';
@@ -593,6 +719,32 @@ async function writeReleaseSummary(summary) {
593
719
  await appendFile(summaryPath, `${text}\n`);
594
720
  }
595
721
  }
722
+ async function writeReleasePreviewSummary(root, currentPackages, previewPackages) {
723
+ const lines = ['## Release Preview', '', `- Commit: \`${(await gitHead(root)).slice(0, 12)}\``, '- Mode: dry run'];
724
+ if (previewPackages.length === 0) {
725
+ lines.push('- Result: no release needed');
726
+ }
727
+ else {
728
+ lines.push('- Packages:');
729
+ const currentByName = new Map(currentPackages.map((pkg) => [pkg.name, pkg]));
730
+ for (const pkg of previewPackages) {
731
+ lines.push(` - \`${pkg.name}\`: \`${currentByName.get(pkg.name)?.version ?? 'unknown'}\` -> \`${pkg.version}\``);
732
+ }
733
+ for (const pkg of previewPackages) {
734
+ const contents = releasePreviewChangelogs.get(pkg.projectName);
735
+ if (!contents) {
736
+ throw new Error(`Nx did not generate a project changelog for ${pkg.projectName}.`);
737
+ }
738
+ lines.push('', `### ${pkg.name} ${pkg.version}`, '', contents.trim());
739
+ }
740
+ }
741
+ const text = `${lines.join('\n')}\n`;
742
+ console.log(text.trimEnd());
743
+ const summaryPath = process.env.GITHUB_STEP_SUMMARY;
744
+ if (summaryPath) {
745
+ await appendFile(summaryPath, `${text}\n`);
746
+ }
747
+ }
596
748
  async function writeRepairSummary(summaries, dryRun) {
597
749
  const lines = ['## Pending Release Repair', '', `- Mode: ${dryRun ? 'dry run' : 'release'}`];
598
750
  if (summaries.length === 0) {
@@ -600,7 +752,7 @@ async function writeRepairSummary(summaries, dryRun) {
600
752
  }
601
753
  else {
602
754
  for (const summary of summaries) {
603
- lines.push(`- Repaired \`${summary.sha.slice(0, 12)}\`: ${packageSummary(summary.packages)}`);
755
+ lines.push(`- ${dryRun ? 'Would repair' : 'Repaired'} \`${summary.sha.slice(0, 12)}\`: ${packageSummary(summary.packages)}`);
604
756
  }
605
757
  }
606
758
  const text = `${lines.join('\n')}\n`;
@@ -616,6 +768,27 @@ function packageSummary(packages) {
616
768
  }
617
769
  return packages.map((pkg) => `${pkg.name}@${pkg.version}`).join(', ');
618
770
  }
771
+ function githubReleaseSummary(summary) {
772
+ if (summary.githubReleaseLinks.length > 0) {
773
+ return summary.githubReleaseLinks.map(({ pkg, url }) => `[${pkg.name}@${pkg.version}](${url})`).join(', ');
774
+ }
775
+ return packageSummary(summary.githubReleases);
776
+ }
777
+ function repairTargetSummary(target) {
778
+ return target.packages
779
+ .map((pkg) => `${pkg.name}@${pkg.version} (${repairReasons(target, pkg).join(' + ')})`)
780
+ .join(', ');
781
+ }
782
+ function repairReasons(target, pkg) {
783
+ const reasons = [];
784
+ if (target.npmPackages.includes(pkg)) {
785
+ reasons.push('npm missing');
786
+ }
787
+ if (target.githubPackages.includes(pkg)) {
788
+ reasons.push('GitHub Release missing');
789
+ }
790
+ return reasons;
791
+ }
619
792
  async function fetchReleaseRefs(root, remote, branch) {
620
793
  await run('git', ['fetch', '--tags', remote, branch], root);
621
794
  }
@@ -623,6 +796,27 @@ async function gitRefExists(root, ref) {
623
796
  const result = await $ `git rev-parse --verify ${ref}`.cwd(root).quiet().nothrow();
624
797
  return result.exitCode === 0;
625
798
  }
799
+ async function latestStableReleaseRef(root, projectName) {
800
+ // List tags matching <projectName>@* sorted newest-first by version, then
801
+ // return the first non-prerelease tag as a full ref.
802
+ const pattern = `${projectName}@*`;
803
+ const result = await $ `git tag --list ${pattern} --sort=-v:refname`.cwd(root).quiet().nothrow();
804
+ if (result.exitCode !== 0) {
805
+ return null;
806
+ }
807
+ const tags = decode(result.stdout)
808
+ .split('\n')
809
+ .map((t) => t.trim())
810
+ .filter(Boolean);
811
+ const prefix = `${projectName}@`;
812
+ for (const tagName of tags) {
813
+ const version = tagName.slice(prefix.length);
814
+ if (version && !version.includes('-')) {
815
+ return `refs/tags/${tagName}`;
816
+ }
817
+ }
818
+ return null;
819
+ }
626
820
  async function packageChangedFilesSince(root, ref, packagePath) {
627
821
  const result = await $ `git diff --name-only ${`${ref}..HEAD`} -- ${packagePath}`.cwd(root).quiet().nothrow();
628
822
  if (result.exitCode !== 0) {
@@ -806,6 +1000,17 @@ async function pushRetaggedReleaseTags(root, remote, updates) {
806
1000
  async function githubReleaseExists(root, tag) {
807
1001
  return (await runStatus('gh', ['release', 'view', tag, '--json', 'tagName'], root, true)) === 0;
808
1002
  }
1003
+ function githubReleaseUrl(root, tag) {
1004
+ return `https://github.com/${githubRepositoryFromRootPackage(root)}/releases/tag/${encodeURIComponent(tag)}`;
1005
+ }
1006
+ async function anyGithubReleaseExists(root, tags) {
1007
+ for (const tag of tags) {
1008
+ if (await githubReleaseExists(root, tag)) {
1009
+ return true;
1010
+ }
1011
+ }
1012
+ return false;
1013
+ }
809
1014
  async function assertRemoteTagExists(root, tag) {
810
1015
  const branch = await releaseBranch(root);
811
1016
  const remote = await releaseRemote(root, branch);
@@ -917,15 +1122,58 @@ async function runLatestNpm(root, npmArgs, env) {
917
1122
  await run('nix', ['shell', 'nixpkgs#nodejs_latest', '-c', 'npm', ...npmArgs], root, env);
918
1123
  }
919
1124
  async function runLatestNpmTrust(root, npmArgs, env) {
920
- const result = await runResult('nix', ['shell', 'nixpkgs#nodejs_latest', '-c', 'npm', ...npmArgs], root, env);
921
- if (result.exitCode === 0) {
1125
+ const status = await runInteractiveStatus('nix', ['shell', 'nixpkgs#nodejs_latest', '-c', 'npm', ...npmArgs], root, env);
1126
+ if (status === 0) {
922
1127
  return 'configured';
923
1128
  }
924
- const output = `${result.stdout}\n${result.stderr}`;
925
- if (/\bE409\b|\b409 Conflict\b/.test(output)) {
926
- return 'already-configured';
1129
+ throw new Error(`nix shell nixpkgs#nodejs_latest -c npm ${npmArgs.join(' ')} failed with exit code ${status}`);
1130
+ }
1131
+ async function listTrustedPublishers(root, pkg) {
1132
+ const args = ['trust', 'list', pkg.name, '--json'];
1133
+ let result = await runResult('nix', ['shell', 'nixpkgs#nodejs_latest', '-c', 'npm', ...args], root);
1134
+ if (result.exitCode !== 0 && /\bEOTP\b/.test(`${result.stdout}\n${result.stderr}`)) {
1135
+ const status = await runInteractiveStatus('nix', ['shell', 'nixpkgs#nodejs_latest', '-c', 'npm', ...args], root);
1136
+ if (status !== 0) {
1137
+ throw new Error(`nix shell nixpkgs#nodejs_latest -c npm ${args.join(' ')} failed with exit code ${status}`);
1138
+ }
1139
+ result = await runResult('nix', ['shell', 'nixpkgs#nodejs_latest', '-c', 'npm', ...args], root);
1140
+ }
1141
+ if (result.exitCode !== 0) {
1142
+ throw new Error(`nix shell nixpkgs#nodejs_latest -c npm ${args.join(' ')} failed with exit code ${result.exitCode}`);
1143
+ }
1144
+ return parseTrustedPublishers(result.stdout, pkg.name);
1145
+ }
1146
+ export function parseTrustedPublishers(stdout, packageName) {
1147
+ if (stdout.trim().length === 0) {
1148
+ return [];
1149
+ }
1150
+ const parsed = JSON.parse(stdout);
1151
+ if (parsed === null) {
1152
+ return [];
1153
+ }
1154
+ if (Array.isArray(parsed)) {
1155
+ return parsed.map((value) => parseTrustedPublisher(value, packageName));
1156
+ }
1157
+ if (isRecord(parsed)) {
1158
+ return [parseTrustedPublisher(parsed, packageName)];
927
1159
  }
928
- throw new Error(`nix shell nixpkgs#nodejs_latest -c npm ${npmArgs.join(' ')} failed with exit code ${result.exitCode}`);
1160
+ throw new Error(`${packageName}: npm trust list returned invalid JSON.`);
1161
+ }
1162
+ function parseTrustedPublisher(value, packageName) {
1163
+ if (!isRecord(value)) {
1164
+ throw new Error(`${packageName}: npm trust list returned invalid trusted publisher entry.`);
1165
+ }
1166
+ const id = stringProperty(value, 'id');
1167
+ const type = stringProperty(value, 'type');
1168
+ if (!id || !type) {
1169
+ throw new Error(`${packageName}: npm trust list returned a trusted publisher without id or type.`);
1170
+ }
1171
+ return {
1172
+ id,
1173
+ type,
1174
+ file: stringProperty(value, 'file') ?? undefined,
1175
+ repository: stringProperty(value, 'repository') ?? undefined,
1176
+ };
929
1177
  }
930
1178
  async function runLatestNpmPublish(root, npmArgs) {
931
1179
  await runLatestNpm(root, npmArgs, { NODE_AUTH_TOKEN: '', NPM_TOKEN: '' });
@@ -935,7 +1183,7 @@ function missingNpmPackagePublishGuidance(pkg) {
935
1183
  }
936
1184
  async function promptForNpmOtp(packageName) {
937
1185
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
938
- throw new Error(`npm trust requires a one-time password for ${packageName}. Pass --otp <code> in non-interactive shells.`);
1186
+ throw new Error(`npm requires a one-time password for ${packageName}. Pass --otp <code> in non-interactive shells.`);
939
1187
  }
940
1188
  const mutedOutput = new Writable({
941
1189
  write(_chunk, _encoding, callback) {
@@ -8,6 +8,10 @@ export interface ReleaseSummary<Package extends ReleasePackageInfo = ReleasePack
8
8
  published: Package[];
9
9
  alreadyPublished: Package[];
10
10
  githubReleases: Package[];
11
+ githubReleaseLinks: Array<{
12
+ pkg: Package;
13
+ url: string;
14
+ }>;
11
15
  rerunRequired: boolean;
12
16
  noRelease: boolean;
13
17
  }
@@ -18,7 +22,10 @@ export interface ReleaseCompletionShell<Package extends ReleasePackageInfo = Rel
18
22
  buildReleaseCandidate(packages: Package[]): Promise<void>;
19
23
  publishPackage(pkg: Package, distTag: string, dryRun: boolean): Promise<void>;
20
24
  listGithubMissingPackages(packages: Package[]): Promise<Package[]>;
21
- createGithubRelease(pkg: Package, dryRun: boolean): Promise<void>;
25
+ createGithubRelease(pkg: Package, dryRun: boolean): Promise<string | null>;
26
+ }
27
+ export interface ReleaseNextShell<Package extends ReleasePackageInfo = ReleasePackageInfo> {
28
+ bumpStablePackagesToNext(packages: Package[]): Promise<void>;
22
29
  }
23
30
  export interface ReleaseRepairShell<Package extends ReleasePackageInfo = ReleasePackageInfo> extends ReleaseCompletionShell<Package> {
24
31
  checkout(ref: string): Promise<void>;
@@ -31,7 +38,7 @@ export interface ReleaseVersionShell<Package extends ReleasePackageInfo = Releas
31
38
  releaseVersionPackages(bump: string): Promise<Package[]>;
32
39
  ensureLocalReleaseTags(packages: Package[]): Promise<void>;
33
40
  gitHead(): Promise<string>;
34
- runNxReleaseVersion(packages: Package[], bump: string, dryRun: boolean): Promise<void>;
41
+ runNxReleaseVersion(packages: Package[], bump: string, dryRun: boolean): Promise<Package[]>;
35
42
  assertCleanGitTree(): Promise<void>;
36
43
  }
37
44
  export interface ReleaseVersionResult<Package extends ReleasePackageInfo = ReleasePackageInfo> {
@@ -44,6 +51,7 @@ export declare function runReleaseVersion<Package extends ReleasePackageInfo>(sh
44
51
  dryRun: boolean;
45
52
  }): Promise<ReleaseVersionResult<Package>>;
46
53
  export declare function completeReleaseAtHead<Package extends ReleasePackageInfo>(shell: ReleaseCompletionShell<Package>, packages: Package[], dryRun: boolean, rerunRequired: boolean): Promise<ReleaseSummary<Package>>;
54
+ export declare function bumpStableReleaseToNext<Package extends ReleasePackageInfo>(shell: ReleaseNextShell<Package>, packages: Package[], dryRun: boolean, rerunRequired: boolean): Promise<Package[]>;
47
55
  export declare function repairPendingTargets<Package extends ReleasePackageInfo>(shell: ReleaseRepairShell<Package>, targets: Array<ReleaseTarget<Package>>, restoreRef: string, dryRun: boolean): Promise<Array<ReleaseSummary<Package>>>;
48
56
  export declare function completeRepairTargetAtHead<Package extends ReleasePackageInfo>(shell: ReleaseCompletionShell<Package>, target: ReleaseTarget<Package>, dryRun: boolean): Promise<ReleaseSummary<Package>>;
49
57
  //# sourceMappingURL=orchestration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"orchestration.d.ts","sourceRoot":"","sources":["../../src/release/orchestration.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"orchestration.d.ts","sourceRoot":"","sources":["../../src/release/orchestration.ts"],"names":[],"mappings":"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"}