nx 23.0.0-beta.21 → 23.0.0-beta.22
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/src/adapter/compat.d.ts +1 -1
- package/dist/src/adapter/compat.js +1 -0
- package/dist/src/command-line/examples.js +4 -4
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.d.ts +5 -0
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +1 -0
- package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.d.ts +5 -0
- package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.js +1 -0
- package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.d.ts +5 -0
- package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.js +1 -0
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.d.ts +1 -0
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.js +15 -0
- package/dist/src/command-line/migrate/agentic/run-step.d.ts +7 -0
- package/dist/src/command-line/migrate/agentic/run-step.js +3 -1
- package/dist/src/command-line/migrate/agentic/select.js +120 -32
- package/dist/src/command-line/migrate/command-object.d.ts +42 -0
- package/dist/src/command-line/migrate/command-object.js +37 -7
- package/dist/src/command-line/migrate/migrate-config.d.ts +27 -0
- package/dist/src/command-line/migrate/migrate-config.js +103 -0
- package/dist/src/command-line/migrate/migrate.d.ts +37 -2
- package/dist/src/command-line/migrate/migrate.js +97 -8
- package/dist/src/command-line/release/changelog/version-plan-filtering.d.ts +3 -1
- package/dist/src/command-line/release/changelog/version-plan-filtering.js +7 -3
- package/dist/src/command-line/release/changelog.d.ts +7 -0
- package/dist/src/command-line/release/changelog.js +22 -9
- package/dist/src/command-line/release/release.js +65 -55
- package/dist/src/command-line/release/utils/git.d.ts +6 -0
- package/dist/src/command-line/release/utils/git.js +33 -0
- package/dist/src/command-line/release/version/derive-specifier-from-conventional-commits.js +3 -2
- package/dist/src/command-line/release/version.d.ts +3 -0
- package/dist/src/command-line/release/version.js +13 -3
- package/dist/src/config/misc-interfaces.d.ts +8 -0
- package/dist/src/config/nx-json.d.ts +49 -0
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/plugins/js/lock-file/lock-file.d.ts +5 -0
- package/dist/src/plugins/js/lock-file/lock-file.js +34 -24
- package/dist/src/plugins/js/project-graph/affected/lock-file-changes.d.ts +2 -4
- package/dist/src/plugins/js/project-graph/affected/lock-file-changes.js +121 -43
- package/dist/src/project-graph/file-utils.d.ts +7 -0
- package/dist/src/project-graph/file-utils.js +78 -10
- package/dist/src/tasks-runner/init-tasks-runner.d.ts +2 -2
- package/dist/src/tasks-runner/init-tasks-runner.js +6 -6
- package/dist/src/tasks-runner/task-orchestrator.d.ts +2 -2
- package/dist/src/tasks-runner/task-orchestrator.js +6 -6
- package/migrations.json +18 -9
- package/package.json +11 -11
- package/schemas/nx-schema.json +41 -0
|
@@ -74,6 +74,7 @@ export declare class Migrator {
|
|
|
74
74
|
factory?: string;
|
|
75
75
|
prompt?: string;
|
|
76
76
|
requires?: Record<string, string>;
|
|
77
|
+
documentation?: string;
|
|
77
78
|
}[];
|
|
78
79
|
}>;
|
|
79
80
|
private createMigrateJson;
|
|
@@ -118,7 +119,7 @@ export declare function resolveCanonicalNxPackage(targetVersion: string): 'nx' |
|
|
|
118
119
|
export declare function resolveMode(mode: MigrateMode | undefined, targetPackage: string, targetVersion: string, context?: {
|
|
119
120
|
hasFrom: boolean;
|
|
120
121
|
hasExcludeAppliedMigrations: boolean;
|
|
121
|
-
}): Promise<MigrateMode>;
|
|
122
|
+
}, configuredMode?: MigrateMode): Promise<MigrateMode>;
|
|
122
123
|
type GenerateMigrations = {
|
|
123
124
|
type: 'generateMigrations';
|
|
124
125
|
targetPackage: string;
|
|
@@ -172,6 +173,7 @@ type ExecutableMigration = {
|
|
|
172
173
|
implementation?: string;
|
|
173
174
|
factory?: string;
|
|
174
175
|
prompt?: string;
|
|
176
|
+
documentation?: string;
|
|
175
177
|
};
|
|
176
178
|
export { isPromptOnlyMigration, isHybridMigration };
|
|
177
179
|
export declare function resolveAgenticRunId(migrations: ExecutableMigration[]): string;
|
|
@@ -235,7 +237,10 @@ export declare function runNxOrAngularMigration(root: string, migration: {
|
|
|
235
237
|
name: string;
|
|
236
238
|
description?: string;
|
|
237
239
|
version: string;
|
|
238
|
-
}, isVerbose: boolean, captureGeneratorOutput?: boolean
|
|
240
|
+
}, isVerbose: boolean, captureGeneratorOutput?: boolean, resolvedCollection?: {
|
|
241
|
+
collection: MigrationsJson;
|
|
242
|
+
collectionPath: string;
|
|
243
|
+
}): Promise<{
|
|
239
244
|
changes: FileChange[];
|
|
240
245
|
nextSteps: string[];
|
|
241
246
|
agentContext: string[];
|
|
@@ -258,4 +263,34 @@ export declare function getImplementationPath(collection: MigrationsJson, collec
|
|
|
258
263
|
path: string;
|
|
259
264
|
fnSymbol: string;
|
|
260
265
|
};
|
|
266
|
+
/**
|
|
267
|
+
* Resolves a migration's collection once and derives everything the run loop
|
|
268
|
+
* needs from that single read: the implementation context (`collection` +
|
|
269
|
+
* `collectionPath`, handed to `runNxOrAngularMigration`) and, for agentic runs,
|
|
270
|
+
* the workspace-relative documentation path handed to the agent.
|
|
271
|
+
*
|
|
272
|
+
* Read fresh per migration (not cached across the loop) so a prior migration's
|
|
273
|
+
* reinstall is reflected, exactly as before. Error handling matches each field's
|
|
274
|
+
* role:
|
|
275
|
+
* - Migrations that run an implementation REQUIRE the collection; an unreadable
|
|
276
|
+
* collection throws and aborts that migration (caught by the run loop).
|
|
277
|
+
* - Prompt-only migrations don't run an implementation, so the collection is
|
|
278
|
+
* read only to resolve documentation - a failure there is non-fatal: the
|
|
279
|
+
* prompt still runs and the supplementary doc is skipped with a warning.
|
|
280
|
+
*/
|
|
281
|
+
export declare function resolveMigrationForRun(root: string, migration: {
|
|
282
|
+
package: string;
|
|
283
|
+
name: string;
|
|
284
|
+
documentation?: string;
|
|
285
|
+
implementation?: string;
|
|
286
|
+
factory?: string;
|
|
287
|
+
prompt?: string;
|
|
288
|
+
}, resolveDocumentation: boolean): {
|
|
289
|
+
resolvedCollection?: {
|
|
290
|
+
collection: MigrationsJson;
|
|
291
|
+
collectionPath: string;
|
|
292
|
+
};
|
|
293
|
+
documentationPath?: string;
|
|
294
|
+
};
|
|
295
|
+
export declare function resolveDocumentationFileToWorkspacePath(root: string, migrationsDir: string, documentation: string): string | undefined;
|
|
261
296
|
export declare function nxCliPath(nxWorkspaceRoot?: string): Promise<string>;
|
|
@@ -17,6 +17,8 @@ exports.migrate = migrate;
|
|
|
17
17
|
exports.runMigration = runMigration;
|
|
18
18
|
exports.readMigrationCollection = readMigrationCollection;
|
|
19
19
|
exports.getImplementationPath = getImplementationPath;
|
|
20
|
+
exports.resolveMigrationForRun = resolveMigrationForRun;
|
|
21
|
+
exports.resolveDocumentationFileToWorkspacePath = resolveDocumentationFileToWorkspacePath;
|
|
20
22
|
exports.nxCliPath = nxCliPath;
|
|
21
23
|
const tslib_1 = require("tslib");
|
|
22
24
|
const pc = tslib_1.__importStar(require("picocolors"));
|
|
@@ -56,6 +58,7 @@ const catalog_1 = require("../../utils/catalog");
|
|
|
56
58
|
const multi_major_1 = require("./multi-major");
|
|
57
59
|
const prompt_files_1 = require("./prompt-files");
|
|
58
60
|
const command_object_1 = require("./command-object");
|
|
61
|
+
const migrate_config_1 = require("./migrate-config");
|
|
59
62
|
const handoff_gitignore_1 = require("./agentic/handoff-gitignore");
|
|
60
63
|
const migrate_commits_1 = require("./migrate-commits");
|
|
61
64
|
const migrate_output_1 = require("./migrate-output");
|
|
@@ -529,13 +532,18 @@ function resolveCanonicalNxPackage(targetVersion) {
|
|
|
529
532
|
async function resolveMode(mode, targetPackage, targetVersion, context = {
|
|
530
533
|
hasFrom: false,
|
|
531
534
|
hasExcludeAppliedMigrations: false,
|
|
532
|
-
}) {
|
|
535
|
+
}, configuredMode) {
|
|
533
536
|
if (mode) {
|
|
534
537
|
return mode;
|
|
535
538
|
}
|
|
536
539
|
if (!(0, version_utils_1.isNxEquivalentTarget)(targetPackage, targetVersion)) {
|
|
537
540
|
return 'all';
|
|
538
541
|
}
|
|
542
|
+
// nx.json `migrate.mode` pre-selects the value the interactive prompt would
|
|
543
|
+
// ask for; it applies only to Nx targets (non-Nx returned 'all' above).
|
|
544
|
+
if (configuredMode) {
|
|
545
|
+
return configuredMode;
|
|
546
|
+
}
|
|
539
547
|
if (!process.stdin.isTTY || (0, is_ci_1.isCI)()) {
|
|
540
548
|
return 'all';
|
|
541
549
|
}
|
|
@@ -656,6 +664,13 @@ async function parseMigrationsOptions(options) {
|
|
|
656
664
|
});
|
|
657
665
|
targetVersion = multiMajorResult.chosen;
|
|
658
666
|
if (mode === 'third-party') {
|
|
667
|
+
// `mode` can resolve to third-party via nx.json, which bypasses the early
|
|
668
|
+
// CLI-only check above; re-assert against the resolved mode.
|
|
669
|
+
assertThirdPartyModeFlagCompatibility({
|
|
670
|
+
mode,
|
|
671
|
+
from: options.from,
|
|
672
|
+
excludeAppliedMigrations: options.excludeAppliedMigrations,
|
|
673
|
+
});
|
|
659
674
|
assertThirdPartyTargetBounds({
|
|
660
675
|
targetPackage,
|
|
661
676
|
targetVersion,
|
|
@@ -706,7 +721,7 @@ async function resolveTargetAndMode(args) {
|
|
|
706
721
|
const mode = await resolveMode(options.mode, targetPackage ?? 'nx', targetVersion ?? 'latest', {
|
|
707
722
|
hasFrom: Object.keys(from).length > 0,
|
|
708
723
|
hasExcludeAppliedMigrations: options.excludeAppliedMigrations === true,
|
|
709
|
-
});
|
|
724
|
+
}, options.modeFromConfig);
|
|
710
725
|
let installedNxVersion;
|
|
711
726
|
// For third-party, anchor `targetPackage`/`targetVersion` to the installed
|
|
712
727
|
// canonical when the positional was either omitted or a bare package name
|
|
@@ -1572,9 +1587,16 @@ function resolveCreateCommits(args) {
|
|
|
1572
1587
|
}
|
|
1573
1588
|
return { effective: true, agenticHasDiffContext: true };
|
|
1574
1589
|
}
|
|
1590
|
+
// Commits aren't enabled here. A custom prefix only reaches this path via
|
|
1591
|
+
// nx.json (e.g. `migrate.commitPrefix` + `migrate.agentic` when the agentic
|
|
1592
|
+
// flow resolves to disabled); surface that it has no effect rather than
|
|
1593
|
+
// dropping it silently.
|
|
1575
1594
|
return {
|
|
1576
1595
|
effective: createCommits === true,
|
|
1577
1596
|
agenticHasDiffContext: false,
|
|
1597
|
+
warning: commitPrefixIsCustom && createCommits !== true
|
|
1598
|
+
? 'A custom migrate commit prefix is configured, but commits are not enabled for this run, so it has no effect. Set `migrate.createCommits` to `true` (or pass `--create-commits`) to create a commit per migration.'
|
|
1599
|
+
: undefined,
|
|
1578
1600
|
};
|
|
1579
1601
|
}
|
|
1580
1602
|
/**
|
|
@@ -1716,6 +1738,11 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1716
1738
|
// already-dirty shared file like `package.json`) doesn't collapse.
|
|
1717
1739
|
const baselineWorkingTreeSnapshot = (0, git_utils_1.getUncommittedChangesSnapshot)(root);
|
|
1718
1740
|
try {
|
|
1741
|
+
// Read this migration's collection once and derive everything from it:
|
|
1742
|
+
// the implementation context (passed to runNxOrAngularMigration) and the
|
|
1743
|
+
// documentation path (passed to the agent). Read fresh per iteration so a
|
|
1744
|
+
// prior migration's reinstall is reflected.
|
|
1745
|
+
const { resolvedCollection, documentationPath } = resolveMigrationForRun(root, m, !!agenticRun);
|
|
1719
1746
|
let outcome;
|
|
1720
1747
|
let commit = { kind: 'none' };
|
|
1721
1748
|
if ((0, migration_shape_1.isPromptOnlyMigration)(m)) {
|
|
@@ -1726,6 +1753,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1726
1753
|
agentic: agenticRun.agentic,
|
|
1727
1754
|
runDir: agenticRun.runDir,
|
|
1728
1755
|
installDepsIfChanged,
|
|
1756
|
+
documentationPath,
|
|
1729
1757
|
});
|
|
1730
1758
|
commit = await attemptMigrationCommit(m);
|
|
1731
1759
|
(0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous ? 'Marked complete by user' : 'Applied', commit.kind === 'landed' ? commit.sha : null, stepResult.summary);
|
|
@@ -1740,7 +1768,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1740
1768
|
}
|
|
1741
1769
|
else if ((0, migration_shape_1.isHybridMigration)(m)) {
|
|
1742
1770
|
const { changes, nextSteps, agentContext, logs, madeChanges } = await runNxOrAngularMigration(root, m, isVerbose,
|
|
1743
|
-
/* captureGeneratorOutput: */ !!agenticRun);
|
|
1771
|
+
/* captureGeneratorOutput: */ !!agenticRun, resolvedCollection);
|
|
1744
1772
|
migrationEmittedNextSteps.push(...nextSteps);
|
|
1745
1773
|
if (agenticRun) {
|
|
1746
1774
|
// Install any deps the deterministic phase added/bumped before the
|
|
@@ -1753,6 +1781,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1753
1781
|
agentic: agenticRun.agentic,
|
|
1754
1782
|
runDir: agenticRun.runDir,
|
|
1755
1783
|
installDepsIfChanged,
|
|
1784
|
+
documentationPath,
|
|
1756
1785
|
implContext: {
|
|
1757
1786
|
logs,
|
|
1758
1787
|
changes,
|
|
@@ -1801,7 +1830,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1801
1830
|
// changes uncommitted in the working tree for the user to review.
|
|
1802
1831
|
const validationRun = agenticRun && shouldRunValidation ? agenticRun : undefined;
|
|
1803
1832
|
const { changes, nextSteps, agentContext, logs, madeChanges } = await runNxOrAngularMigration(root, m, isVerbose,
|
|
1804
|
-
/* captureGeneratorOutput: */ !!validationRun);
|
|
1833
|
+
/* captureGeneratorOutput: */ !!validationRun, resolvedCollection);
|
|
1805
1834
|
migrationEmittedNextSteps.push(...nextSteps);
|
|
1806
1835
|
const canRunValidation = !!validationRun && changes.length > 0;
|
|
1807
1836
|
if (canRunValidation) {
|
|
@@ -1814,6 +1843,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1814
1843
|
agentic: validationRun.agentic,
|
|
1815
1844
|
runDir: validationRun.runDir,
|
|
1816
1845
|
installDepsIfChanged,
|
|
1846
|
+
documentationPath,
|
|
1817
1847
|
implContext: {
|
|
1818
1848
|
logs,
|
|
1819
1849
|
changes,
|
|
@@ -1957,8 +1987,8 @@ function logSkippedPostMigrationInstall(root) {
|
|
|
1957
1987
|
bodyLines: [`Run "${installCommand}" to install the updated dependencies.`],
|
|
1958
1988
|
});
|
|
1959
1989
|
}
|
|
1960
|
-
async function runNxOrAngularMigration(root, migration, isVerbose, captureGeneratorOutput = false) {
|
|
1961
|
-
const { collection, collectionPath } = readMigrationCollection(migration.package, root);
|
|
1990
|
+
async function runNxOrAngularMigration(root, migration, isVerbose, captureGeneratorOutput = false, resolvedCollection) {
|
|
1991
|
+
const { collection, collectionPath } = resolvedCollection ?? readMigrationCollection(migration.package, root);
|
|
1962
1992
|
let changes = [];
|
|
1963
1993
|
let nextSteps = [];
|
|
1964
1994
|
let agentContext = [];
|
|
@@ -2219,13 +2249,15 @@ function filterStrings(value) {
|
|
|
2219
2249
|
async function migrate(root, args, rawArgs) {
|
|
2220
2250
|
await client_1.daemonClient.stop();
|
|
2221
2251
|
return (0, handle_errors_1.handleErrors)(process.env.NX_VERBOSE_LOGGING === 'true', async () => {
|
|
2222
|
-
const
|
|
2252
|
+
const mergedArgs = (0, migrate_config_1.applyNxJsonMigrateDefaults)(args, (0, configuration_1.readNxJson)().migrate);
|
|
2253
|
+
(0, migrate_config_1.assertCommitPrefixHasCommits)(mergedArgs);
|
|
2254
|
+
const opts = await parseMigrationsOptions(mergedArgs);
|
|
2223
2255
|
if (opts.type === 'generateMigrations') {
|
|
2224
2256
|
await generateMigrationsJsonAndUpdatePackageJson(root, opts);
|
|
2225
2257
|
}
|
|
2226
2258
|
else {
|
|
2227
2259
|
try {
|
|
2228
|
-
return await runMigrations(root, opts, rawArgs,
|
|
2260
|
+
return await runMigrations(root, opts, rawArgs, mergedArgs['verbose'], mergedArgs['createCommits'], mergedArgs['commitPrefix'] ?? command_object_1.DEFAULT_MIGRATION_COMMIT_PREFIX, mergedArgs['skipInstall']);
|
|
2229
2261
|
}
|
|
2230
2262
|
catch (e) {
|
|
2231
2263
|
// The remediation guidance is already logged by `runInstall`; swallow
|
|
@@ -2297,6 +2329,63 @@ function getImplementationPath(collection, collectionPath, name, migrationVersio
|
|
|
2297
2329
|
}
|
|
2298
2330
|
return { path: implPath, fnSymbol };
|
|
2299
2331
|
}
|
|
2332
|
+
/**
|
|
2333
|
+
* Resolves a migration's collection once and derives everything the run loop
|
|
2334
|
+
* needs from that single read: the implementation context (`collection` +
|
|
2335
|
+
* `collectionPath`, handed to `runNxOrAngularMigration`) and, for agentic runs,
|
|
2336
|
+
* the workspace-relative documentation path handed to the agent.
|
|
2337
|
+
*
|
|
2338
|
+
* Read fresh per migration (not cached across the loop) so a prior migration's
|
|
2339
|
+
* reinstall is reflected, exactly as before. Error handling matches each field's
|
|
2340
|
+
* role:
|
|
2341
|
+
* - Migrations that run an implementation REQUIRE the collection; an unreadable
|
|
2342
|
+
* collection throws and aborts that migration (caught by the run loop).
|
|
2343
|
+
* - Prompt-only migrations don't run an implementation, so the collection is
|
|
2344
|
+
* read only to resolve documentation - a failure there is non-fatal: the
|
|
2345
|
+
* prompt still runs and the supplementary doc is skipped with a warning.
|
|
2346
|
+
*/
|
|
2347
|
+
function resolveMigrationForRun(root, migration, resolveDocumentation) {
|
|
2348
|
+
let resolvedCollection;
|
|
2349
|
+
if (!(0, migration_shape_1.isPromptOnlyMigration)(migration)) {
|
|
2350
|
+
resolvedCollection = readMigrationCollection(migration.package, root);
|
|
2351
|
+
}
|
|
2352
|
+
else if (resolveDocumentation && migration.documentation) {
|
|
2353
|
+
try {
|
|
2354
|
+
resolvedCollection = readMigrationCollection(migration.package, root);
|
|
2355
|
+
}
|
|
2356
|
+
catch {
|
|
2357
|
+
// Non-fatal: documentation is supplementary; the warning below fires.
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
let documentationPath;
|
|
2361
|
+
if (resolveDocumentation && migration.documentation) {
|
|
2362
|
+
documentationPath = resolvedCollection
|
|
2363
|
+
? resolveDocumentationFileToWorkspacePath(root, (0, path_1.dirname)(resolvedCollection.collectionPath), migration.documentation)
|
|
2364
|
+
: undefined;
|
|
2365
|
+
if (!documentationPath) {
|
|
2366
|
+
logger_1.logger.warn(`Could not resolve the "documentation" file "${migration.documentation}" declared for migration "${migration.package}: ${migration.name}". It will be skipped as additional context for the AI agent.`);
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
return { resolvedCollection, documentationPath };
|
|
2370
|
+
}
|
|
2371
|
+
// Resolves a `documentation` path (relative to the package's migrations dir) to
|
|
2372
|
+
// a workspace-relative path - or the absolute path when it resolves outside the
|
|
2373
|
+
// workspace (unusual hoisted/symlinked layouts). The agent runs with cwd =
|
|
2374
|
+
// workspace root, so the workspace-relative form is preferred. Returns
|
|
2375
|
+
// undefined when the file can't be resolved.
|
|
2376
|
+
function resolveDocumentationFileToWorkspacePath(root, migrationsDir, documentation) {
|
|
2377
|
+
let documentationFile;
|
|
2378
|
+
try {
|
|
2379
|
+
documentationFile = require.resolve(documentation, {
|
|
2380
|
+
paths: [migrationsDir],
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2383
|
+
catch {
|
|
2384
|
+
return undefined;
|
|
2385
|
+
}
|
|
2386
|
+
const relativePath = (0, path_1.relative)(root, documentationFile);
|
|
2387
|
+
return relativePath.startsWith('..') ? documentationFile : relativePath;
|
|
2388
|
+
}
|
|
2300
2389
|
class MigrationImplementationMissingError extends Error {
|
|
2301
2390
|
constructor(baseMessage, collectionPath, migrationVersion) {
|
|
2302
2391
|
super(buildMigrationMissingMessage(baseMessage, collectionPath, migrationVersion));
|
|
@@ -16,7 +16,7 @@ export declare function filterVersionPlansByCommitRange(versionPlans: RawVersion
|
|
|
16
16
|
* Resolves the "from SHA" for changelog purposes.
|
|
17
17
|
* This determines the starting point for changelog generation and optional version plan filtering.
|
|
18
18
|
*/
|
|
19
|
-
export declare function resolveChangelogFromSHA({ fromRef, tagPattern, tagPatternValues, checkAllBranchesWhen, preid, requireSemver, strictPreid, useAutomaticFromRef, resolveRepositoryTags, }: {
|
|
19
|
+
export declare function resolveChangelogFromSHA({ fromRef, tagPattern, tagPatternValues, checkAllBranchesWhen, preid, requireSemver, strictPreid, useAutomaticFromRef, resolveRepositoryTags, projectRoot, }: {
|
|
20
20
|
fromRef?: string;
|
|
21
21
|
tagPattern: string;
|
|
22
22
|
tagPatternValues: Record<string, string>;
|
|
@@ -26,6 +26,8 @@ export declare function resolveChangelogFromSHA({ fromRef, tagPattern, tagPatter
|
|
|
26
26
|
strictPreid: boolean;
|
|
27
27
|
useAutomaticFromRef: boolean;
|
|
28
28
|
resolveRepositoryTags: RepoGitTags['resolveTags'];
|
|
29
|
+
/** When provided, scopes the fallback to the project's first commit instead of the repo's first commit */
|
|
30
|
+
projectRoot?: string;
|
|
29
31
|
}): Promise<string | null>;
|
|
30
32
|
/**
|
|
31
33
|
* Helper function for workspace-level "from SHA" resolution.
|
|
@@ -89,7 +89,7 @@ async function getFilesAddedInCommitRange(fromSHA, toSHA, isVerbose) {
|
|
|
89
89
|
* Resolves the "from SHA" for changelog purposes.
|
|
90
90
|
* This determines the starting point for changelog generation and optional version plan filtering.
|
|
91
91
|
*/
|
|
92
|
-
async function resolveChangelogFromSHA({ fromRef, tagPattern, tagPatternValues, checkAllBranchesWhen, preid, requireSemver, strictPreid, useAutomaticFromRef, resolveRepositoryTags, }) {
|
|
92
|
+
async function resolveChangelogFromSHA({ fromRef, tagPattern, tagPatternValues, checkAllBranchesWhen, preid, requireSemver, strictPreid, useAutomaticFromRef, resolveRepositoryTags, projectRoot, }) {
|
|
93
93
|
// If user provided a from ref, resolve it to a SHA
|
|
94
94
|
if (fromRef) {
|
|
95
95
|
return await (0, git_1.getCommitHash)(fromRef);
|
|
@@ -104,9 +104,13 @@ async function resolveChangelogFromSHA({ fromRef, tagPattern, tagPatternValues,
|
|
|
104
104
|
if (latestTag?.tag) {
|
|
105
105
|
return await (0, git_1.getCommitHash)(latestTag.tag);
|
|
106
106
|
}
|
|
107
|
-
// Finally, if automatic from ref is enabled, use the first commit as a fallback
|
|
107
|
+
// Finally, if automatic from ref is enabled, use the first commit as a fallback.
|
|
108
|
+
// When a projectRoot is provided, scope the fallback to the project's first commit
|
|
109
|
+
// to avoid scanning the entire repo history for projects added after the repo was created.
|
|
108
110
|
if (useAutomaticFromRef) {
|
|
109
|
-
return
|
|
111
|
+
return projectRoot
|
|
112
|
+
? await (0, git_1.getFirstProjectCommit)(projectRoot)
|
|
113
|
+
: await (0, git_1.getFirstGitCommit)();
|
|
110
114
|
}
|
|
111
115
|
return null;
|
|
112
116
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NxReleaseConfiguration } from '../../config/nx-json';
|
|
2
2
|
import { ChangelogOptions } from './command-object';
|
|
3
|
+
import { NxReleaseConfig } from './config/config';
|
|
3
4
|
import { ReleaseVersion } from './utils/shared';
|
|
4
5
|
export interface NxReleaseChangelogResult {
|
|
5
6
|
workspaceChangelog?: {
|
|
@@ -17,5 +18,11 @@ export interface NxReleaseChangelogResult {
|
|
|
17
18
|
}
|
|
18
19
|
export type { ChangelogChange } from './changelog/version-plan-utils';
|
|
19
20
|
export type PostGitTask = (latestCommit: string) => Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Determines whether a changelog configuration will actually produce any output.
|
|
23
|
+
* A changelog config is effectively enabled when it would produce a changelog file
|
|
24
|
+
* or create a remote release.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isChangelogEffectivelyEnabled(config?: NxReleaseConfig['changelog']['workspaceChangelog'] | NxReleaseConfig['groups'][string]['changelog']): boolean | undefined;
|
|
20
27
|
export declare const releaseChangelogCLIHandler: (args: ChangelogOptions) => Promise<number>;
|
|
21
28
|
export declare function createAPI(overrideReleaseConfig: NxReleaseConfiguration, ignoreNxJsonConfig: boolean): (args: ChangelogOptions) => Promise<NxReleaseChangelogResult>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.releaseChangelogCLIHandler = void 0;
|
|
4
|
+
exports.isChangelogEffectivelyEnabled = isChangelogEffectivelyEnabled;
|
|
4
5
|
exports.createAPI = createAPI;
|
|
5
6
|
const tslib_1 = require("tslib");
|
|
6
7
|
const pc = tslib_1.__importStar(require("picocolors"));
|
|
@@ -35,6 +36,17 @@ const resolve_changelog_renderer_1 = require("./utils/resolve-changelog-renderer
|
|
|
35
36
|
const resolve_nx_json_error_message_1 = require("./utils/resolve-nx-json-error-message");
|
|
36
37
|
const shared_1 = require("./utils/shared");
|
|
37
38
|
const version_plan_utils_2 = require("./utils/version-plan-utils");
|
|
39
|
+
/**
|
|
40
|
+
* Determines whether a changelog configuration will actually produce any output.
|
|
41
|
+
* A changelog config is effectively enabled when it would produce a changelog file
|
|
42
|
+
* or create a remote release.
|
|
43
|
+
*/
|
|
44
|
+
function isChangelogEffectivelyEnabled(config) {
|
|
45
|
+
if (!config) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return config.file !== false || config.createRelease !== false;
|
|
49
|
+
}
|
|
38
50
|
const releaseChangelogCLIHandler = (args) => (0, handle_errors_1.handleErrors)(args.verbose, () => createAPI({}, false)(args));
|
|
39
51
|
exports.releaseChangelogCLIHandler = releaseChangelogCLIHandler;
|
|
40
52
|
function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
|
|
@@ -100,13 +112,8 @@ function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
|
|
|
100
112
|
process.env.NX_RELEASE_INTERNAL_SUPPRESS_FILTER_LOG !== 'true') {
|
|
101
113
|
output_1.output.note(releaseGraph.filterLog);
|
|
102
114
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// default to deleting version plans in this command instead of after versioning
|
|
106
|
-
args.deleteVersionPlans = true;
|
|
107
|
-
}
|
|
108
|
-
const changelogGenerationEnabled = !!nxReleaseConfig.changelog.workspaceChangelog ||
|
|
109
|
-
Object.values(nxReleaseConfig.groups).some((g) => g.changelog);
|
|
115
|
+
const changelogGenerationEnabled = isChangelogEffectivelyEnabled(nxReleaseConfig.changelog.workspaceChangelog) ||
|
|
116
|
+
Object.values(nxReleaseConfig.groups).some((g) => isChangelogEffectivelyEnabled(g.changelog));
|
|
110
117
|
if (!changelogGenerationEnabled) {
|
|
111
118
|
output_1.output.warn({
|
|
112
119
|
title: `Changelogs are disabled. No changelog entries will be generated`,
|
|
@@ -116,6 +123,11 @@ function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
|
|
|
116
123
|
});
|
|
117
124
|
return {};
|
|
118
125
|
}
|
|
126
|
+
let rawVersionPlans = await (0, version_plans_1.readRawVersionPlans)();
|
|
127
|
+
if (args.deleteVersionPlans === undefined) {
|
|
128
|
+
// default to deleting version plans in this command instead of after versioning
|
|
129
|
+
args.deleteVersionPlans = true;
|
|
130
|
+
}
|
|
119
131
|
const useAutomaticFromRef = nxReleaseConfig.changelog?.automaticFromRef || args.firstRelease;
|
|
120
132
|
/**
|
|
121
133
|
* For determining the versions to use within changelog files, there are a few different possibilities:
|
|
@@ -214,7 +226,7 @@ function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
|
|
|
214
226
|
fromSHACache.set('workspace', fromSHA);
|
|
215
227
|
}
|
|
216
228
|
// Helper function to get cached from SHA or resolve and cache it
|
|
217
|
-
const getCachedFromSHA = async (cacheKey, pattern, templateValues, preid, checkAllBranchesWhen, requireSemver, strictPreid) => {
|
|
229
|
+
const getCachedFromSHA = async (cacheKey, pattern, templateValues, preid, checkAllBranchesWhen, requireSemver, strictPreid, projectRoot) => {
|
|
218
230
|
if (fromSHACache.has(cacheKey)) {
|
|
219
231
|
return fromSHACache.get(cacheKey);
|
|
220
232
|
}
|
|
@@ -228,6 +240,7 @@ function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
|
|
|
228
240
|
requireSemver,
|
|
229
241
|
strictPreid,
|
|
230
242
|
useAutomaticFromRef,
|
|
243
|
+
projectRoot,
|
|
231
244
|
});
|
|
232
245
|
fromSHACache.set(cacheKey, sha);
|
|
233
246
|
return sha;
|
|
@@ -296,7 +309,7 @@ function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
|
|
|
296
309
|
const fromSHA = await getCachedFromSHA(projectCacheKey, releaseGroup.releaseTag.pattern, {
|
|
297
310
|
projectName: project.name,
|
|
298
311
|
releaseGroupName: releaseGroup.name,
|
|
299
|
-
}, projectsPreid[project.name], releaseGroup.releaseTag.checkAllBranchesWhen, releaseGroup.releaseTag.requireSemver, releaseGroup.releaseTag.strictPreid);
|
|
312
|
+
}, projectsPreid[project.name], releaseGroup.releaseTag.checkAllBranchesWhen, releaseGroup.releaseTag.requireSemver, releaseGroup.releaseTag.strictPreid, project.data.root);
|
|
300
313
|
let commits;
|
|
301
314
|
let fromRef = fromSHA;
|
|
302
315
|
if (!fromRef && useAutomaticFromRef) {
|
|
@@ -84,19 +84,6 @@ function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
|
|
|
84
84
|
});
|
|
85
85
|
// Suppress the filter log for the changelog command as it would have already been printed by the version command
|
|
86
86
|
process.env.NX_RELEASE_INTERNAL_SUPPRESS_FILTER_LOG = 'true';
|
|
87
|
-
const changelogResult = await releaseChangelog({
|
|
88
|
-
...args,
|
|
89
|
-
// Re-use existing release graph
|
|
90
|
-
releaseGraph,
|
|
91
|
-
versionData: projectsVersionData,
|
|
92
|
-
version: workspaceVersion,
|
|
93
|
-
stageChanges: shouldStage,
|
|
94
|
-
gitCommit: false,
|
|
95
|
-
gitTag: false,
|
|
96
|
-
gitPush: false,
|
|
97
|
-
createRelease: false,
|
|
98
|
-
deleteVersionPlans: false,
|
|
99
|
-
});
|
|
100
87
|
await (0, version_plans_1.setResolvedVersionPlansOnGroups)(rawVersionPlans, releaseGraph.releaseGroups, Object.keys(projectGraph.nodes), args.verbose);
|
|
101
88
|
// Validate version plans against the filter after resolution
|
|
102
89
|
const versionPlanValidationError = (0, version_plan_utils_1.validateResolvedVersionPlansAgainstFilter)(releaseGraph.releaseGroups, releaseGraph.releaseGroupToFilteredProjects);
|
|
@@ -150,6 +137,27 @@ function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
|
|
|
150
137
|
verbose: args.verbose,
|
|
151
138
|
});
|
|
152
139
|
}
|
|
140
|
+
// Check if any changelog generation is actually enabled before calling releaseChangelog,
|
|
141
|
+
// to avoid expensive operations (project graph recreation, git log, etc.) when changelogs are disabled
|
|
142
|
+
const changelogGenerationEnabled = (0, changelog_1.isChangelogEffectivelyEnabled)(nxReleaseConfig.changelog.workspaceChangelog) ||
|
|
143
|
+
releaseGraph.releaseGroups.some((g) => (0, changelog_1.isChangelogEffectivelyEnabled)(g.changelog));
|
|
144
|
+
// Run changelog generation before git commit/tag so that changelog files are
|
|
145
|
+
// included in the same commit as the version bump
|
|
146
|
+
const changelogResult = changelogGenerationEnabled
|
|
147
|
+
? await releaseChangelog({
|
|
148
|
+
...args,
|
|
149
|
+
// Re-use existing release graph
|
|
150
|
+
releaseGraph,
|
|
151
|
+
versionData: projectsVersionData,
|
|
152
|
+
version: workspaceVersion,
|
|
153
|
+
stageChanges: shouldStage,
|
|
154
|
+
gitCommit: false,
|
|
155
|
+
gitTag: false,
|
|
156
|
+
gitPush: false,
|
|
157
|
+
createRelease: false,
|
|
158
|
+
deleteVersionPlans: false,
|
|
159
|
+
})
|
|
160
|
+
: undefined;
|
|
153
161
|
if (shouldCommit) {
|
|
154
162
|
output_1.output.logSingleLine(`Committing changes with git`);
|
|
155
163
|
const commitMessage = nxReleaseConfig.git.commitMessage;
|
|
@@ -190,51 +198,53 @@ function createAPI(overrideReleaseConfig, ignoreNxJsonConfig) {
|
|
|
190
198
|
});
|
|
191
199
|
hasPushedChanges = true;
|
|
192
200
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
// shouldCreateWorkspaceRemoteRelease() ensures that the createRelease property exists and is not false
|
|
198
|
-
nxReleaseConfig.changelog.workspaceChangelog
|
|
199
|
-
.createRelease);
|
|
200
|
-
if (!hasPushedChanges) {
|
|
201
|
-
throw new Error(`It is not possible to create a ${remoteReleaseClient.remoteReleaseProviderName} release for the workspace without pushing the changes to the remote, please ensure that you have not disabled git push in your nx release config`);
|
|
202
|
-
}
|
|
203
|
-
output_1.output.logSingleLine(`Creating ${remoteReleaseClient.remoteReleaseProviderName} Release`);
|
|
204
|
-
latestCommit = await (0, git_1.getCommitHash)('HEAD');
|
|
205
|
-
await remoteReleaseClient.createOrUpdateRelease(changelogResult.workspaceChangelog.releaseVersion, changelogResult.workspaceChangelog.contents, latestCommit, { dryRun: args.dryRun });
|
|
206
|
-
}
|
|
207
|
-
for (const releaseGroupName of releaseGraph.sortedReleaseGroups) {
|
|
208
|
-
const releaseGroup = releaseGraph.releaseGroups.find((g) => g.name === releaseGroupName);
|
|
209
|
-
if (!releaseGroup) {
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
const shouldCreateProjectRemoteReleases = shouldCreateRemoteRelease(releaseGroup.changelog);
|
|
213
|
-
if (shouldCreateProjectRemoteReleases &&
|
|
214
|
-
changelogResult.projectChangelogs) {
|
|
201
|
+
if (changelogResult) {
|
|
202
|
+
let latestCommit;
|
|
203
|
+
if (shouldCreateWorkspaceRemoteRelease &&
|
|
204
|
+
changelogResult.workspaceChangelog) {
|
|
215
205
|
const remoteReleaseClient = await (0, remote_release_client_1.createRemoteReleaseClient)(
|
|
216
|
-
//
|
|
217
|
-
|
|
206
|
+
// shouldCreateWorkspaceRemoteRelease() ensures that the createRelease property exists and is not false
|
|
207
|
+
nxReleaseConfig.changelog.workspaceChangelog
|
|
218
208
|
.createRelease);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
209
|
+
if (!hasPushedChanges) {
|
|
210
|
+
throw new Error(`It is not possible to create a ${remoteReleaseClient.remoteReleaseProviderName} release for the workspace without pushing the changes to the remote, please ensure that you have not disabled git push in your nx release config`);
|
|
211
|
+
}
|
|
212
|
+
output_1.output.logSingleLine(`Creating ${remoteReleaseClient.remoteReleaseProviderName} Release`);
|
|
213
|
+
latestCommit = await (0, git_1.getCommitHash)('HEAD');
|
|
214
|
+
await remoteReleaseClient.createOrUpdateRelease(changelogResult.workspaceChangelog.releaseVersion, changelogResult.workspaceChangelog.contents, latestCommit, { dryRun: args.dryRun });
|
|
215
|
+
}
|
|
216
|
+
for (const releaseGroupName of releaseGraph.sortedReleaseGroups) {
|
|
217
|
+
const releaseGroup = releaseGraph.releaseGroups.find((g) => g.name === releaseGroupName);
|
|
218
|
+
if (!releaseGroup) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const shouldCreateProjectRemoteReleases = shouldCreateRemoteRelease(releaseGroup.changelog);
|
|
222
|
+
if (shouldCreateProjectRemoteReleases &&
|
|
223
|
+
changelogResult.projectChangelogs) {
|
|
224
|
+
const remoteReleaseClient = await (0, remote_release_client_1.createRemoteReleaseClient)(
|
|
225
|
+
// shouldCreateProjectRemoteReleases() ensures that the createRelease property exists and is not false
|
|
226
|
+
releaseGroup.changelog
|
|
227
|
+
.createRelease);
|
|
228
|
+
const projects = args.projects?.length
|
|
229
|
+
? // If the user has passed a list of projects, we need to use the filtered list of projects within the release group
|
|
230
|
+
Array.from(releaseGraph.releaseGroupToFilteredProjects.get(releaseGroup))
|
|
231
|
+
: // Otherwise, we use the full list of projects within the release group
|
|
232
|
+
releaseGroup.projects;
|
|
233
|
+
const projectNodes = projects.map((name) => projectGraph.nodes[name]);
|
|
234
|
+
for (const project of projectNodes) {
|
|
235
|
+
const changelog = changelogResult.projectChangelogs[project.name];
|
|
236
|
+
if (!changelog) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (!hasPushedChanges) {
|
|
240
|
+
throw new Error(`It is not possible to create a ${remoteReleaseClient.remoteReleaseProviderName} release for the project without pushing the changes to the remote, please ensure that you have not disabled git push in your nx release config`);
|
|
241
|
+
}
|
|
242
|
+
output_1.output.logSingleLine(`Creating ${remoteReleaseClient.remoteReleaseProviderName} Release`);
|
|
243
|
+
if (!latestCommit) {
|
|
244
|
+
latestCommit = await (0, git_1.getCommitHash)('HEAD');
|
|
245
|
+
}
|
|
246
|
+
await remoteReleaseClient.createOrUpdateRelease(changelog.releaseVersion, changelog.contents, latestCommit, { dryRun: args.dryRun });
|
|
236
247
|
}
|
|
237
|
-
await remoteReleaseClient.createOrUpdateRelease(changelog.releaseVersion, changelog.contents, latestCommit, { dryRun: args.dryRun });
|
|
238
248
|
}
|
|
239
249
|
}
|
|
240
250
|
}
|
|
@@ -118,3 +118,9 @@ export declare function parseVersionPlanCommit(commit: RawGitCommit): {
|
|
|
118
118
|
export declare function parseGitCommit(commit: RawGitCommit): GitCommit | null;
|
|
119
119
|
export declare function getCommitHash(ref: string): Promise<string>;
|
|
120
120
|
export declare function getFirstGitCommit(): Promise<string>;
|
|
121
|
+
/**
|
|
122
|
+
* Returns the parent of the first commit that touched the given project root,
|
|
123
|
+
* so that `from..HEAD` ranges include the project's creation commit.
|
|
124
|
+
* Falls back to getFirstGitCommit() if the project history cannot be determined.
|
|
125
|
+
*/
|
|
126
|
+
export declare function getFirstProjectCommit(projectRoot: string): Promise<string>;
|
|
@@ -15,6 +15,7 @@ exports.parseVersionPlanCommit = parseVersionPlanCommit;
|
|
|
15
15
|
exports.parseGitCommit = parseGitCommit;
|
|
16
16
|
exports.getCommitHash = getCommitHash;
|
|
17
17
|
exports.getFirstGitCommit = getFirstGitCommit;
|
|
18
|
+
exports.getFirstProjectCommit = getFirstProjectCommit;
|
|
18
19
|
/**
|
|
19
20
|
* Special thanks to changelogen for the original inspiration for many of these utilities:
|
|
20
21
|
* https://github.com/unjs/changelogen
|
|
@@ -542,6 +543,38 @@ async function getFirstGitCommit() {
|
|
|
542
543
|
throw new Error(`Unable to find first commit in git history`);
|
|
543
544
|
}
|
|
544
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* Returns the parent of the first commit that touched the given project root,
|
|
548
|
+
* so that `from..HEAD` ranges include the project's creation commit.
|
|
549
|
+
* Falls back to getFirstGitCommit() if the project history cannot be determined.
|
|
550
|
+
*/
|
|
551
|
+
async function getFirstProjectCommit(projectRoot) {
|
|
552
|
+
try {
|
|
553
|
+
const result = (await (0, exec_command_1.execCommand)('git', [
|
|
554
|
+
'rev-list',
|
|
555
|
+
'--reverse',
|
|
556
|
+
'HEAD',
|
|
557
|
+
'--first-parent',
|
|
558
|
+
'--',
|
|
559
|
+
projectRoot,
|
|
560
|
+
])).trim();
|
|
561
|
+
const firstCommit = result.split('\n')[0];
|
|
562
|
+
if (firstCommit) {
|
|
563
|
+
// Return the parent so the creation commit is included in from..to ranges
|
|
564
|
+
try {
|
|
565
|
+
return (await (0, exec_command_1.execCommand)('git', ['rev-parse', `${firstCommit}~1`])).trim();
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
// No parent (project was added in the repo's very first commit)
|
|
569
|
+
return firstCommit;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
// fall through to fallback
|
|
575
|
+
}
|
|
576
|
+
return getFirstGitCommit();
|
|
577
|
+
}
|
|
545
578
|
async function getGitRoot() {
|
|
546
579
|
try {
|
|
547
580
|
return (await (0, exec_command_1.execCommand)('git', ['rev-parse', '--show-toplevel'])).trim();
|