nx 23.2.0-beta.4 → 23.2.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/nx.js +1 -0
- package/dist/src/command-line/affected/affected.js +1 -0
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +2 -2
- package/dist/src/command-line/init/init-v2.js +2 -2
- package/dist/src/command-line/migrate/agentic/definitions.js +2 -2
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +15 -19
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +19 -31
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +4 -6
- package/dist/src/command-line/migrate/agentic/handoff.js +9 -8
- package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.d.ts +7 -12
- package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.js +7 -0
- package/dist/src/command-line/migrate/agentic/run-step.d.ts +10 -0
- package/dist/src/command-line/migrate/agentic/select.d.ts +10 -2
- package/dist/src/command-line/migrate/agentic/select.js +10 -2
- package/dist/src/command-line/migrate/agentic/types.d.ts +17 -3
- package/dist/src/command-line/migrate/agentic/types.js +26 -0
- package/dist/src/command-line/migrate/command-object.d.ts +3 -0
- package/dist/src/command-line/migrate/command-object.js +6 -1
- package/dist/src/command-line/migrate/execute-migration.d.ts +14 -9
- package/dist/src/command-line/migrate/execute-migration.js +82 -18
- package/dist/src/command-line/migrate/migrate-analytics.d.ts +7 -0
- package/dist/src/command-line/migrate/migrate-analytics.js +14 -0
- package/dist/src/command-line/migrate/migrate-commits.d.ts +32 -8
- package/dist/src/command-line/migrate/migrate-commits.js +77 -9
- package/dist/src/command-line/migrate/migrate-config.d.ts +11 -12
- package/dist/src/command-line/migrate/migrate-config.js +14 -14
- package/dist/src/command-line/migrate/migrate-output.d.ts +45 -7
- package/dist/src/command-line/migrate/migrate-output.js +52 -8
- package/dist/src/command-line/migrate/migrate-ui-api.d.ts +2 -1
- package/dist/src/command-line/migrate/migrate-ui-api.js +16 -7
- package/dist/src/command-line/migrate/migrate.d.ts +17 -68
- package/dist/src/command-line/migrate/migrate.js +411 -229
- package/dist/src/command-line/migrate/migration-shape.d.ts +8 -0
- package/dist/src/command-line/migrate/run/index.d.ts +2 -0
- package/dist/src/command-line/migrate/run/index.js +5 -0
- package/dist/src/command-line/migrate/run/util.d.ts +1 -0
- package/dist/src/command-line/migrate/run/util.js +16 -0
- package/dist/src/command-line/migrate/run/worker.d.ts +15 -0
- package/dist/src/command-line/migrate/run/worker.js +419 -0
- package/dist/src/command-line/migrate/run-migration-process.js +6 -5
- package/dist/src/command-line/migrate/sort-migrations.d.ts +16 -0
- package/dist/src/command-line/migrate/sort-migrations.js +32 -0
- package/dist/src/command-line/migrate/version-skew-guard.d.ts +58 -0
- package/dist/src/command-line/migrate/version-skew-guard.js +155 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/command-object.d.ts +2 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/command-object.js +16 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/start-nx-agents.d.ts +4 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/start-nx-agents.js +13 -0
- package/dist/src/command-line/nx-commands.js +26 -24
- package/dist/src/command-line/release/changelog.js +5 -0
- package/dist/src/command-line/release/utils/git.js +1 -1
- package/dist/src/command-line/release/utils/release-graph.d.ts +1 -0
- package/dist/src/command-line/release/utils/release-graph.js +15 -3
- package/dist/src/command-line/release/version/version-actions.d.ts +8 -0
- package/dist/src/command-line/release/version/version-actions.js +8 -0
- package/dist/src/command-line/release/version.js +9 -5
- package/dist/src/command-line/reset/reset.js +6 -1
- package/dist/src/command-line/run/run-one.js +1 -0
- package/dist/src/command-line/run-many/run-many.js +1 -0
- package/dist/src/config/misc-interfaces.d.ts +19 -7
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/core/graph/styles.js +1 -1
- package/dist/src/daemon/client/client.d.ts +45 -1
- package/dist/src/daemon/client/client.js +128 -32
- package/dist/src/daemon/client/daemon-socket-messenger.js +4 -0
- package/dist/src/daemon/message-types/daemon-message.d.ts +32 -0
- package/dist/src/daemon/message-types/daemon-message.js +25 -0
- package/dist/src/daemon/server/latest-nx.js +5 -2
- package/dist/src/daemon/server/server.js +22 -0
- package/dist/src/daemon/server/shutdown-utils.d.ts +5 -0
- package/dist/src/daemon/server/shutdown-utils.js +11 -4
- package/dist/src/daemon/socket-utils.d.ts +1 -0
- package/dist/src/daemon/socket-utils.js +24 -3
- package/dist/src/daemon/tmp-dir.d.ts +43 -3
- package/dist/src/daemon/tmp-dir.js +437 -15
- package/dist/src/devkit-internals.d.ts +92 -3
- package/dist/src/devkit-internals.js +189 -5
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.d.ts +5 -0
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +9 -2
- package/dist/src/generators/tree.d.ts +4 -2
- package/dist/src/native/index.d.ts +10 -0
- package/dist/src/native/index.js +71 -28
- package/dist/src/native/native-bindings.js +1 -0
- package/dist/src/native/native-file-cache-location.d.ts +17 -1
- package/dist/src/native/native-file-cache-location.js +86 -12
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/nx-cloud/utilities/get-cloud-options.js +5 -2
- package/dist/src/nx-cloud/utilities/onboarding.js +5 -2
- package/dist/src/nx-cloud/utilities/url-shorten.d.ts +1 -4
- package/dist/src/nx-cloud/utilities/url-shorten.js +9 -8
- package/dist/src/plugins/js/lock-file/project-graph-pruning.js +14 -8
- package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +2 -0
- package/dist/src/project-graph/affected/affected-project-graph-models.d.ts +1 -1
- package/dist/src/project-graph/affected/affected-project-graph.d.ts +1 -1
- package/dist/src/project-graph/affected/affected-project-graph.js +2 -2
- package/dist/src/project-graph/affected/locators/project-glob-changes.js +5 -2
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +18 -1
- package/dist/src/project-graph/plugins/isolation/messaging.d.ts +16 -3
- package/dist/src/project-graph/plugins/isolation/messaging.js +3 -0
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +58 -8
- package/dist/src/project-graph/project-graph.js +20 -0
- package/dist/src/tasks-runner/life-cycles/performance-report.d.ts +12 -0
- package/dist/src/tasks-runner/life-cycles/performance-report.js +62 -3
- package/dist/src/tasks-runner/run-command.js +4 -0
- package/dist/src/utils/assert-workspace-validity.js +7 -2
- package/dist/src/utils/child-process.d.ts +31 -1
- package/dist/src/utils/child-process.js +103 -0
- package/dist/src/utils/ignore.d.ts +128 -1
- package/dist/src/utils/ignore.js +182 -12
- package/dist/src/utils/nx-tmp-dir.d.ts +18 -0
- package/dist/src/utils/nx-tmp-dir.js +40 -0
- package/dist/src/utils/owned-private-dir.d.ts +132 -0
- package/dist/src/utils/owned-private-dir.js +323 -0
- package/dist/src/utils/shell-quoting.d.ts +15 -0
- package/dist/src/utils/shell-quoting.js +35 -0
- package/dist/src/utils/wait-for-socket-connection.d.ts +11 -0
- package/dist/src/utils/wait-for-socket-connection.js +14 -8
- package/package.json +12 -12
|
@@ -7,16 +7,13 @@ exports.resolveInclude = resolveInclude;
|
|
|
7
7
|
exports.parseMigrationsOptions = parseMigrationsOptions;
|
|
8
8
|
exports.createFetcher = createFetcher;
|
|
9
9
|
exports.generateMigrationsJsonAndUpdatePackageJson = generateMigrationsJsonAndUpdatePackageJson;
|
|
10
|
-
exports.resolveAgenticRunId = resolveAgenticRunId;
|
|
11
10
|
exports.formatSkippedPromptsNextStep = formatSkippedPromptsNextStep;
|
|
12
|
-
exports.resolveCreateCommits = resolveCreateCommits;
|
|
13
|
-
exports.confirmCommitsOnDefaultBranch = confirmCommitsOnDefaultBranch;
|
|
14
|
-
exports.resolveShouldRunValidation = resolveShouldRunValidation;
|
|
15
11
|
exports.executeMigrations = executeMigrations;
|
|
12
|
+
exports.isSingleMigrationInvocation = isSingleMigrationInvocation;
|
|
16
13
|
exports.migrate = migrate;
|
|
14
|
+
exports.readLocalNxVersion = readLocalNxVersion;
|
|
17
15
|
exports.runMigration = runMigration;
|
|
18
16
|
exports.resolveMigrationForRun = resolveMigrationForRun;
|
|
19
|
-
exports.resolveDocumentationFileToWorkspacePath = resolveDocumentationFileToWorkspacePath;
|
|
20
17
|
exports.nxCliPath = nxCliPath;
|
|
21
18
|
const tslib_1 = require("tslib");
|
|
22
19
|
const pc = tslib_1.__importStar(require("picocolors"));
|
|
@@ -31,6 +28,7 @@ const util_1 = require("util");
|
|
|
31
28
|
const fileutils_1 = require("../../utils/fileutils");
|
|
32
29
|
const tar_1 = require("../../utils/tar");
|
|
33
30
|
const write_formatted_json_file_1 = require("../../utils/write-formatted-json-file");
|
|
31
|
+
const shell_quoting_1 = require("../../utils/shell-quoting");
|
|
34
32
|
const logger_1 = require("../../utils/logger");
|
|
35
33
|
const git_utils_1 = require("../../utils/git-utils");
|
|
36
34
|
const command_line_utils_1 = require("../../utils/command-line-utils");
|
|
@@ -58,7 +56,6 @@ const multi_major_1 = require("./multi-major");
|
|
|
58
56
|
const prompt_files_1 = require("./prompt-files");
|
|
59
57
|
const command_object_1 = require("./command-object");
|
|
60
58
|
const migrate_config_1 = require("./migrate-config");
|
|
61
|
-
const handoff_gitignore_1 = require("./agentic/handoff-gitignore");
|
|
62
59
|
const migrate_commits_1 = require("./migrate-commits");
|
|
63
60
|
const migrate_output_1 = require("./migrate-output");
|
|
64
61
|
const migration_shape_1 = require("./migration-shape");
|
|
@@ -69,6 +66,9 @@ Object.defineProperty(exports, "filterDowngradedUpdates", { enumerable: true, ge
|
|
|
69
66
|
const version_utils_1 = require("./version-utils");
|
|
70
67
|
Object.defineProperty(exports, "normalizeVersion", { enumerable: true, get: function () { return version_utils_1.normalizeVersion; } });
|
|
71
68
|
const execute_migration_1 = require("./execute-migration");
|
|
69
|
+
const sort_migrations_1 = require("./sort-migrations");
|
|
70
|
+
const version_skew_guard_1 = require("./version-skew-guard");
|
|
71
|
+
const versions_1 = require("../../utils/versions");
|
|
72
72
|
tslib_1.__exportStar(require("./execute-migration"), exports);
|
|
73
73
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
74
74
|
function formatCommandFailure(command, error) {
|
|
@@ -671,6 +671,32 @@ async function parseTargetPackageAndVersion(args) {
|
|
|
671
671
|
return { targetPackage: args, targetVersion: 'latest' };
|
|
672
672
|
}
|
|
673
673
|
async function parseMigrationsOptions(options, fetch) {
|
|
674
|
+
if (options.runMigration !== undefined) {
|
|
675
|
+
if (options.runMigration === '') {
|
|
676
|
+
throw new Error(`Error: '--run-migration' requires a migration id, e.g. '--run-migration=@nx/js:my-migration'.`);
|
|
677
|
+
}
|
|
678
|
+
if (options.runMigrations !== undefined) {
|
|
679
|
+
throw new Error(`Error: '--run-migration' (run a single migration) cannot be combined with '--run-migrations' (run the whole migrations file).`);
|
|
680
|
+
}
|
|
681
|
+
if (options.include) {
|
|
682
|
+
throw new Error(`Error: '--run-migration' cannot be combined with '--include'.`);
|
|
683
|
+
}
|
|
684
|
+
if (options.multiMajorMode) {
|
|
685
|
+
throw new Error(`Error: '--run-migration' cannot be combined with '--multi-major-mode'.`);
|
|
686
|
+
}
|
|
687
|
+
// `--if-exists` only applies to a whole-file run, so an explicit "on"
|
|
688
|
+
// conflicts. Its yargs default (false) is always present and is tolerated.
|
|
689
|
+
if (options.ifExists === true) {
|
|
690
|
+
throw new Error(`Error: '--run-migration' cannot be combined with '--if-exists'.`);
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
type: 'runSingleMigration',
|
|
694
|
+
runMigration: options.runMigration,
|
|
695
|
+
agentic: options.agentic,
|
|
696
|
+
validate: options.validate,
|
|
697
|
+
interactive: options.interactive,
|
|
698
|
+
};
|
|
699
|
+
}
|
|
674
700
|
if (options.runMigrations === '') {
|
|
675
701
|
options.runMigrations = 'migrations.json';
|
|
676
702
|
}
|
|
@@ -1593,136 +1619,22 @@ function showConnectToCloudMessage() {
|
|
|
1593
1619
|
return false;
|
|
1594
1620
|
}
|
|
1595
1621
|
}
|
|
1596
|
-
function resolveAgenticRunId(migrations) {
|
|
1597
|
-
return (0, semver_1.rsort)(migrations.map((m) => (0, version_utils_1.normalizeVersion)(m.version)))[0];
|
|
1598
|
-
}
|
|
1599
1622
|
function formatSkippedPromptsNextStep(skipped) {
|
|
1600
1623
|
return [
|
|
1601
1624
|
'Some prompt migrations were skipped. Review and apply each of the following prompt files to the workspace, in the listed order:',
|
|
1602
1625
|
...skipped.map((m) => ` - ${m.prompt}`),
|
|
1603
1626
|
].join('\n');
|
|
1604
1627
|
}
|
|
1605
|
-
/**
|
|
1606
|
-
* Resolves the effective `--create-commits` state once the agentic flow has
|
|
1607
|
-
* been resolved. The agent's outer prompt only embeds the impl-phase file list
|
|
1608
|
-
* when per-migration commits isolate each migration's diff, so the diff-context
|
|
1609
|
-
* flag returned here gates that section.
|
|
1610
|
-
*/
|
|
1611
|
-
function resolveCreateCommits(args) {
|
|
1612
|
-
const { createCommits, agenticKind, isGitRepo, commitPrefixIsCustom } = args;
|
|
1613
|
-
// Explicit `--create-commits` without git is a hard error — the user asked
|
|
1614
|
-
// for something we cannot deliver.
|
|
1615
|
-
if (createCommits === true && !isGitRepo) {
|
|
1616
|
-
return {
|
|
1617
|
-
effective: false,
|
|
1618
|
-
agenticHasDiffContext: false,
|
|
1619
|
-
error: '`--create-commits` requires a git repository. Run `git init` first, or omit the flag.',
|
|
1620
|
-
};
|
|
1621
|
-
}
|
|
1622
|
-
if (agenticKind === 'enabled') {
|
|
1623
|
-
if (createCommits === false) {
|
|
1624
|
-
return {
|
|
1625
|
-
effective: false,
|
|
1626
|
-
agenticHasDiffContext: false,
|
|
1627
|
-
warning: "--no-create-commits was passed alongside --agentic. Without per-migration commits, the agent can't isolate the current migration's changes from earlier migrations in this run. Drop --no-create-commits for accurate per-migration review." +
|
|
1628
|
-
(commitPrefixIsCustom
|
|
1629
|
-
? ' Note: the custom --commit-prefix value will have no effect because commits are disabled.'
|
|
1630
|
-
: ''),
|
|
1631
|
-
};
|
|
1632
|
-
}
|
|
1633
|
-
// Without git we cannot soft-force commits the user didn't explicitly
|
|
1634
|
-
// opt into. Degrade rather than error: continue the agentic run, but
|
|
1635
|
-
// without per-file diff context (which depends on per-migration commits).
|
|
1636
|
-
if (!isGitRepo) {
|
|
1637
|
-
return {
|
|
1638
|
-
effective: false,
|
|
1639
|
-
agenticHasDiffContext: false,
|
|
1640
|
-
warning: '`--agentic` enables per-migration commits by default, but the workspace is not a git repository. Continuing without commits — the agent will not receive per-file diff context. Run `git init` to enable.' +
|
|
1641
|
-
(commitPrefixIsCustom
|
|
1642
|
-
? ' The custom --commit-prefix value will have no effect.'
|
|
1643
|
-
: ''),
|
|
1644
|
-
};
|
|
1645
|
-
}
|
|
1646
|
-
return { effective: true, agenticHasDiffContext: true };
|
|
1647
|
-
}
|
|
1648
|
-
// Commits aren't enabled here. A custom prefix only reaches this path via
|
|
1649
|
-
// nx.json (e.g. `migrate.commitPrefix` + `migrate.agentic` when the agentic
|
|
1650
|
-
// flow resolves to disabled); surface that it has no effect rather than
|
|
1651
|
-
// dropping it silently.
|
|
1652
|
-
return {
|
|
1653
|
-
effective: createCommits === true,
|
|
1654
|
-
agenticHasDiffContext: false,
|
|
1655
|
-
warning: commitPrefixIsCustom && createCommits !== true
|
|
1656
|
-
? '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.'
|
|
1657
|
-
: undefined,
|
|
1658
|
-
};
|
|
1659
|
-
}
|
|
1660
|
-
/**
|
|
1661
|
-
* Confirms before creating per-migration commits while the user sits on the
|
|
1662
|
-
* repository's default branch, so migrations don't silently commit there (most
|
|
1663
|
-
* relevant since `--agentic` enables commits by default). Only the default
|
|
1664
|
-
* branch triggers a prompt; a detached HEAD, a different branch, or an
|
|
1665
|
-
* unresolved default branch all proceed untouched. Callers gate this on
|
|
1666
|
-
* commits being effective and prompting being possible, so non-interactive
|
|
1667
|
-
* runs (CI, `--no-interactive`) never reach here.
|
|
1668
|
-
*/
|
|
1669
|
-
async function confirmCommitsOnDefaultBranch(args) {
|
|
1670
|
-
const { currentBranch, defaultBranch } = args;
|
|
1671
|
-
if (!currentBranch || !defaultBranch || currentBranch !== defaultBranch) {
|
|
1672
|
-
return true;
|
|
1673
|
-
}
|
|
1674
|
-
const { proceed } = await (0, safe_prompt_1.migratePrompt)([
|
|
1675
|
-
{
|
|
1676
|
-
name: 'proceed',
|
|
1677
|
-
type: 'confirm',
|
|
1678
|
-
message: `You're on the default branch '${currentBranch}'. nx migrate will create a commit for each migration on this branch. Continue?`,
|
|
1679
|
-
initial: false,
|
|
1680
|
-
},
|
|
1681
|
-
]);
|
|
1682
|
-
return proceed;
|
|
1683
|
-
}
|
|
1684
|
-
/**
|
|
1685
|
-
* Resolves whether the framework-owned generic-validation agent step should run
|
|
1686
|
-
* after generator-only migrations.
|
|
1687
|
-
*
|
|
1688
|
-
* Default-on when the agentic flow resolved to `enabled`; silently ignored
|
|
1689
|
-
* otherwise (no warning emitted) — `--validate` requires an active agent
|
|
1690
|
-
* session by definition. An explicit `--no-validate` (`validate === false`)
|
|
1691
|
-
* opts out even when agentic is enabled.
|
|
1692
|
-
*/
|
|
1693
|
-
function resolveShouldRunValidation(args) {
|
|
1694
|
-
return args.validate !== false && args.agenticKind === 'enabled';
|
|
1695
|
-
}
|
|
1696
1628
|
async function executeMigrations(root, migrations, isVerbose, shouldCreateCommits, commitPrefix, shouldSkipInstall = false, agentic, agenticHasDiffContext = false, shouldRunValidation = false) {
|
|
1697
1629
|
const changedDepInstaller = new execute_migration_1.ChangedDepInstaller(root, shouldSkipInstall);
|
|
1698
1630
|
const migrationsWithNoChanges = [];
|
|
1699
|
-
const sortedMigrations =
|
|
1700
|
-
|
|
1701
|
-
// `.nx/migrate-runs` to position 0 so its .gitignore update lands
|
|
1702
|
-
// before any per-migration commit absorbs the run's handoff scratch.
|
|
1703
|
-
// See `agentic/handoff-gitignore.ts` for the full rationale and the
|
|
1704
|
-
// inline-fallback path that covers intra-pre-v23 agentic runs.
|
|
1705
|
-
if (agentic?.kind === 'enabled') {
|
|
1706
|
-
if ((0, handoff_gitignore_1.isHandoffGitignoreMigration)(a))
|
|
1707
|
-
return -1;
|
|
1708
|
-
if ((0, handoff_gitignore_1.isHandoffGitignoreMigration)(b))
|
|
1709
|
-
return 1;
|
|
1710
|
-
}
|
|
1711
|
-
// special case for the split configuration migration to run first
|
|
1712
|
-
if (a.name === '15-7-0-split-configuration-into-project-json-files') {
|
|
1713
|
-
return -1;
|
|
1714
|
-
}
|
|
1715
|
-
if (b.name === '15-7-0-split-configuration-into-project-json-files') {
|
|
1716
|
-
return 1;
|
|
1717
|
-
}
|
|
1718
|
-
return (0, semver_1.lt)((0, version_utils_1.normalizeVersion)(a.version), (0, version_utils_1.normalizeVersion)(b.version))
|
|
1719
|
-
? -1
|
|
1720
|
-
: 1;
|
|
1631
|
+
const sortedMigrations = (0, sort_migrations_1.sortMigrations)(migrations, {
|
|
1632
|
+
hoistHandoffGitignore: agentic?.kind === 'enabled',
|
|
1721
1633
|
});
|
|
1722
1634
|
// Lazy-load the agentic chain so non-agentic runs don't pay its startup cost.
|
|
1723
1635
|
let agenticRun;
|
|
1724
1636
|
if (agentic?.kind === 'enabled' && sortedMigrations.length > 0) {
|
|
1725
|
-
const { initRunDir } = require('./agentic/handoff');
|
|
1637
|
+
const { initRunDir, resolveAgenticRunId } = require('./agentic/handoff');
|
|
1726
1638
|
const { runAgenticPromptStep } = require('./agentic/run-step');
|
|
1727
1639
|
agenticRun = {
|
|
1728
1640
|
agentic,
|
|
@@ -1810,6 +1722,23 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1810
1722
|
// as a commit failure.
|
|
1811
1723
|
return { kind: 'none' };
|
|
1812
1724
|
}
|
|
1725
|
+
// Records what a migration's deterministic phase produced. A migration that
|
|
1726
|
+
// changed nothing must not attempt a commit: the absorbing `git add -A`
|
|
1727
|
+
// would build a commit subject naming this no-op migration even though its
|
|
1728
|
+
// content is entirely prior pending diffs, confusing `git log` / `git blame`
|
|
1729
|
+
// attribution. Pending stays pending and the next change-producing migration
|
|
1730
|
+
// absorbs it.
|
|
1731
|
+
async function commitOrRecordNoChanges(m, madeChanges) {
|
|
1732
|
+
if (!madeChanges) {
|
|
1733
|
+
migrationsWithNoChanges.push(m);
|
|
1734
|
+
return { kind: 'none' };
|
|
1735
|
+
}
|
|
1736
|
+
const commit = await attemptMigrationCommit(m);
|
|
1737
|
+
if (commit.kind === 'landed' && commit.sha) {
|
|
1738
|
+
logger_1.logger.info(pc.dim(`Committed as ${commit.sha}`));
|
|
1739
|
+
}
|
|
1740
|
+
return commit;
|
|
1741
|
+
}
|
|
1813
1742
|
const totalMigrations = sortedMigrations.length;
|
|
1814
1743
|
let migrationIndex = 0;
|
|
1815
1744
|
for (const m of sortedMigrations) {
|
|
@@ -1830,6 +1759,11 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1830
1759
|
const { resolvedCollection, documentationPath } = resolveMigrationForRun(root, m, !!agenticRun);
|
|
1831
1760
|
let outcome;
|
|
1832
1761
|
let commit = { kind: 'none' };
|
|
1762
|
+
// Set when the migration returned `skipAgentic: true` and something was
|
|
1763
|
+
// actually waived, so the end-of-run recaps can report it. A hybrid's
|
|
1764
|
+
// prompt is owed in every agentic mode, so waiving it always counts; a
|
|
1765
|
+
// generator-only migration only counts when validation would have run.
|
|
1766
|
+
let waivedAgenticStep = false;
|
|
1833
1767
|
if ((0, migration_shape_1.isPromptOnlyMigration)(m)) {
|
|
1834
1768
|
if (agenticRun) {
|
|
1835
1769
|
inAgenticStep = true;
|
|
@@ -1854,10 +1788,19 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1854
1788
|
}
|
|
1855
1789
|
}
|
|
1856
1790
|
else if ((0, migration_shape_1.isHybridMigration)(m)) {
|
|
1857
|
-
const { changes, nextSteps, agentContext, logs, madeChanges } = await (0, execute_migration_1.runNxOrAngularMigration)(root, m, isVerbose,
|
|
1791
|
+
const { changes, nextSteps, agentContext, skipAgentic, logs, madeChanges, } = await (0, execute_migration_1.runNxOrAngularMigration)(root, m, isVerbose,
|
|
1858
1792
|
/* captureGeneratorOutput: */ !!agenticRun, resolvedCollection);
|
|
1859
1793
|
migrationEmittedNextSteps.push(...nextSteps);
|
|
1860
|
-
if (
|
|
1794
|
+
if (skipAgentic) {
|
|
1795
|
+
// The generator reported the prompt half unnecessary, so nothing is
|
|
1796
|
+
// owed: no agent run, and no next-steps entry telling the user to
|
|
1797
|
+
// run the prompt themselves. Runs in all three agentic modes.
|
|
1798
|
+
(0, migrate_output_1.logWaivedAgenticStep)(m, agentContext);
|
|
1799
|
+
waivedAgenticStep = true;
|
|
1800
|
+
commit = await commitOrRecordNoChanges(m, madeChanges);
|
|
1801
|
+
outcome = madeChanges ? 'applied' : 'no-changes';
|
|
1802
|
+
}
|
|
1803
|
+
else if (agenticRun) {
|
|
1861
1804
|
// Install any deps the deterministic phase added/bumped before the
|
|
1862
1805
|
// agent runs — the prompt half may depend on them being present in
|
|
1863
1806
|
// node_modules.
|
|
@@ -1896,21 +1839,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1896
1839
|
printDroppedAgentContext({ migration: m, agentContext });
|
|
1897
1840
|
}
|
|
1898
1841
|
skippedPrompts.push(m);
|
|
1899
|
-
|
|
1900
|
-
migrationsWithNoChanges.push(m);
|
|
1901
|
-
}
|
|
1902
|
-
// Only attempt a commit when this migration's deterministic
|
|
1903
|
-
// phase actually produced changes. Otherwise the absorbing
|
|
1904
|
-
// `git add -A` would build a commit subject naming this no-op
|
|
1905
|
-
// migration even though its content is entirely prior pending
|
|
1906
|
-
// diffs — confusing `git log` / `git blame` attribution. Pending
|
|
1907
|
-
// stays pending and the next change-producing migration absorbs.
|
|
1908
|
-
if (madeChanges) {
|
|
1909
|
-
commit = await attemptMigrationCommit(m);
|
|
1910
|
-
}
|
|
1911
|
-
if (commit.kind === 'landed' && commit.sha) {
|
|
1912
|
-
logger_1.logger.info(pc.dim(`Committed as ${commit.sha}`));
|
|
1913
|
-
}
|
|
1842
|
+
commit = await commitOrRecordNoChanges(m, madeChanges);
|
|
1914
1843
|
outcome = 'deferred';
|
|
1915
1844
|
}
|
|
1916
1845
|
}
|
|
@@ -1918,10 +1847,14 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1918
1847
|
// Defer commit until validation succeeds; failed validation leaves
|
|
1919
1848
|
// changes uncommitted in the working tree for the user to review.
|
|
1920
1849
|
const validationRun = agenticRun && shouldRunValidation ? agenticRun : undefined;
|
|
1921
|
-
const { changes, nextSteps, agentContext, logs, madeChanges } = await (0, execute_migration_1.runNxOrAngularMigration)(root, m, isVerbose,
|
|
1850
|
+
const { changes, nextSteps, agentContext, skipAgentic, logs, madeChanges, } = await (0, execute_migration_1.runNxOrAngularMigration)(root, m, isVerbose,
|
|
1922
1851
|
/* captureGeneratorOutput: */ !!validationRun, resolvedCollection);
|
|
1923
1852
|
migrationEmittedNextSteps.push(...nextSteps);
|
|
1924
|
-
|
|
1853
|
+
// Whether a validation step was on the table at all. `skipAgentic`
|
|
1854
|
+
// waives nothing when it wasn't, so the whole waived path hangs off
|
|
1855
|
+
// this, not just the log line.
|
|
1856
|
+
const validationApplies = !!validationRun && changes.length > 0;
|
|
1857
|
+
const canRunValidation = validationApplies && !skipAgentic;
|
|
1925
1858
|
if (canRunValidation) {
|
|
1926
1859
|
// Install any deps the deterministic phase added/bumped before the
|
|
1927
1860
|
// validation agent runs — the agent may run tasks that need them.
|
|
@@ -1952,22 +1885,17 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1952
1885
|
outcome = 'applied';
|
|
1953
1886
|
}
|
|
1954
1887
|
else {
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
printDroppedAgentContext({ migration: m, agentContext });
|
|
1888
|
+
if (skipAgentic && validationApplies) {
|
|
1889
|
+
(0, migrate_output_1.logWaivedAgenticStep)(m, agentContext);
|
|
1890
|
+
waivedAgenticStep = true;
|
|
1959
1891
|
}
|
|
1960
|
-
if (
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
else {
|
|
1965
|
-
commit = await attemptMigrationCommit(m);
|
|
1966
|
-
if (commit.kind === 'landed' && commit.sha) {
|
|
1967
|
-
logger_1.logger.info(pc.dim(`Committed as ${commit.sha}`));
|
|
1968
|
-
}
|
|
1969
|
-
outcome = 'applied';
|
|
1892
|
+
else if (printDroppedAgentContext && agentContext.length > 0) {
|
|
1893
|
+
// Inner validation step didn't run. Surface `agentContext` under
|
|
1894
|
+
// `inside-agent` so the outer driving agent can ingest it.
|
|
1895
|
+
printDroppedAgentContext({ migration: m, agentContext });
|
|
1970
1896
|
}
|
|
1897
|
+
commit = await commitOrRecordNoChanges(m, madeChanges);
|
|
1898
|
+
outcome = madeChanges ? 'applied' : 'no-changes';
|
|
1971
1899
|
}
|
|
1972
1900
|
}
|
|
1973
1901
|
outcomes.push({
|
|
@@ -1975,6 +1903,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1975
1903
|
status: 'completed',
|
|
1976
1904
|
kind: outcome,
|
|
1977
1905
|
commit,
|
|
1906
|
+
waivedAgenticStep,
|
|
1978
1907
|
});
|
|
1979
1908
|
logger_1.logger.info('');
|
|
1980
1909
|
}
|
|
@@ -2037,7 +1966,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
2037
1966
|
await installDepsIfChanged();
|
|
2038
1967
|
}
|
|
2039
1968
|
if (changedDepInstaller.skippedInstall) {
|
|
2040
|
-
logSkippedPostMigrationInstall(root);
|
|
1969
|
+
(0, execute_migration_1.logSkippedPostMigrationInstall)(root);
|
|
2041
1970
|
}
|
|
2042
1971
|
// Combined-view next-steps array kept for back-compat with repair.ts, which
|
|
2043
1972
|
// consumes the single `nextSteps` field.
|
|
@@ -2053,6 +1982,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
2053
1982
|
skippedPrompts,
|
|
2054
1983
|
migrationEmittedNextSteps,
|
|
2055
1984
|
committedShasCount: (0, migrate_output_1.countLandedCommits)(outcomes),
|
|
1985
|
+
waivedAgenticStepsCount: (0, migrate_output_1.countWaivedAgenticSteps)(outcomes),
|
|
2056
1986
|
// Migrations whose commits failed and never got absorbed by a later
|
|
2057
1987
|
// commit. The caller surfaces them so a successful run doesn't claim
|
|
2058
1988
|
// "up to date" while leaving uncommitted diffs in the working tree.
|
|
@@ -2060,13 +1990,18 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
2060
1990
|
retainedAtSuccess: (0, migrate_output_1.retainedMigrations)(outcomes).map((p) => `${p.package}: ${p.name}`),
|
|
2061
1991
|
};
|
|
2062
1992
|
}
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
1993
|
+
// nx is located at spawn time, after the gated pre-install, so the child runs
|
|
1994
|
+
// the bytes that install put in place.
|
|
1995
|
+
function handOffToLocalNx(args) {
|
|
1996
|
+
const exitCode = runOrReturnExitCode(() => (0, child_process_2.runNxArgvSync)(['migrate', ...args], {
|
|
1997
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
1998
|
+
env: {
|
|
1999
|
+
...process.env,
|
|
2000
|
+
NX_MIGRATE_SKIP_INSTALL: 'true',
|
|
2001
|
+
NX_MIGRATE_USE_LOCAL: 'true',
|
|
2002
|
+
},
|
|
2003
|
+
}));
|
|
2004
|
+
return exitCode !== 0 ? exitCode : undefined;
|
|
2070
2005
|
}
|
|
2071
2006
|
async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, commitPrefix, shouldSkipInstall = false) {
|
|
2072
2007
|
if (!shouldSkipInstall && !process.env.NX_MIGRATE_SKIP_INSTALL) {
|
|
@@ -2075,18 +2010,7 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2075
2010
|
if (!__dirname.startsWith(workspace_root_1.workspaceRoot)) {
|
|
2076
2011
|
// we are running from a temp installation with nx latest, switch to running
|
|
2077
2012
|
// from local installation
|
|
2078
|
-
|
|
2079
|
-
stdio: ['inherit', 'inherit', 'inherit'],
|
|
2080
|
-
env: {
|
|
2081
|
-
...process.env,
|
|
2082
|
-
NX_MIGRATE_SKIP_INSTALL: 'true',
|
|
2083
|
-
NX_MIGRATE_USE_LOCAL: 'true',
|
|
2084
|
-
},
|
|
2085
|
-
}));
|
|
2086
|
-
if (exitCode !== 0) {
|
|
2087
|
-
return exitCode;
|
|
2088
|
-
}
|
|
2089
|
-
return;
|
|
2013
|
+
return handOffToLocalNx(args);
|
|
2090
2014
|
}
|
|
2091
2015
|
const migrationsExists = (0, fileutils_1.fileExists)(opts.runMigrations);
|
|
2092
2016
|
if (opts.ifExists && !migrationsExists) {
|
|
@@ -2098,12 +2022,13 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2098
2022
|
else if (!opts.ifExists && !migrationsExists) {
|
|
2099
2023
|
throw new Error(`File '${opts.runMigrations}' doesn't exist, can't run migrations. Use flag --if-exists to run migrations only if the file exists`);
|
|
2100
2024
|
}
|
|
2101
|
-
const
|
|
2025
|
+
const migrationsJson = (0, fileutils_1.readJsonFile)((0, path_1.join)(root, opts.runMigrations));
|
|
2026
|
+
const migrations = migrationsJson.migrations;
|
|
2102
2027
|
(0, migrate_analytics_1.reportMigrateRunStart)({
|
|
2103
2028
|
createCommits: shouldCreateCommits ?? false,
|
|
2104
2029
|
migrationCount: migrations.length,
|
|
2105
2030
|
});
|
|
2106
|
-
const { resolveAgentic } = require('./agentic/select');
|
|
2031
|
+
const { resolveAgentic, resolveShouldRunValidation } = require('./agentic/select');
|
|
2107
2032
|
let agentic;
|
|
2108
2033
|
try {
|
|
2109
2034
|
agentic = await resolveAgentic({
|
|
@@ -2116,7 +2041,7 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2116
2041
|
(0, migrate_analytics_1.reportMigrateRunError)({ code: 'agentic', error: e });
|
|
2117
2042
|
throw e;
|
|
2118
2043
|
}
|
|
2119
|
-
const { effective: effectiveCreateCommits, agenticHasDiffContext, warning: createCommitsWarning, error: createCommitsError, } = resolveCreateCommits({
|
|
2044
|
+
const { effective: effectiveCreateCommits, agenticHasDiffContext, warning: createCommitsWarning, error: createCommitsError, } = (0, migrate_commits_1.resolveCreateCommits)({
|
|
2120
2045
|
createCommits: shouldCreateCommits,
|
|
2121
2046
|
agenticKind: agentic.kind,
|
|
2122
2047
|
isGitRepo: (0, git_utils_1.isGitRepository)(root),
|
|
@@ -2133,7 +2058,7 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2133
2058
|
// `getBaseRef` may carry an `origin/` prefix (set by the CI-workflow
|
|
2134
2059
|
// generator); compare against the local branch name.
|
|
2135
2060
|
const defaultBranch = (0, command_line_utils_1.getBaseRef)((0, configuration_1.readNxJson)(root)).replace(/^origin\//, '');
|
|
2136
|
-
const proceed = await confirmCommitsOnDefaultBranch({
|
|
2061
|
+
const proceed = await (0, migrate_commits_1.confirmCommitsOnDefaultBranch)({
|
|
2137
2062
|
currentBranch,
|
|
2138
2063
|
defaultBranch,
|
|
2139
2064
|
});
|
|
@@ -2161,8 +2086,9 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2161
2086
|
(0, migrate_commits_1.commitCheckpointBeforeMigrations)(root, commitPrefix);
|
|
2162
2087
|
}
|
|
2163
2088
|
if (agentic.kind === 'enabled') {
|
|
2089
|
+
const { applyAgenticHandoffGitignoreFallback } = require('./agentic/handoff-gitignore');
|
|
2164
2090
|
const { packageJson: nxPackageJson } = (0, package_json_1.readModulePackageJson)('nx', (0, installation_directory_1.getNxRequirePaths)(root));
|
|
2165
|
-
await
|
|
2091
|
+
await applyAgenticHandoffGitignoreFallback({
|
|
2166
2092
|
migrations,
|
|
2167
2093
|
installedNxVersion: nxPackageJson.version,
|
|
2168
2094
|
effectiveCreateCommits,
|
|
@@ -2170,7 +2096,7 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2170
2096
|
root,
|
|
2171
2097
|
});
|
|
2172
2098
|
}
|
|
2173
|
-
const { migrationsWithNoChanges, skippedPromptsCount, notRunMigrationsCount, skippedPrompts, migrationEmittedNextSteps, committedShasCount, retainedAtSuccess, } = await executeMigrations(root, migrations, isVerbose, effectiveCreateCommits, commitPrefix, shouldSkipInstall, agentic, agenticHasDiffContext, shouldRunValidation);
|
|
2099
|
+
const { migrationsWithNoChanges, skippedPromptsCount, notRunMigrationsCount, skippedPrompts, migrationEmittedNextSteps, committedShasCount, waivedAgenticStepsCount, retainedAtSuccess, } = await executeMigrations(root, migrations, isVerbose, effectiveCreateCommits, commitPrefix, shouldSkipInstall, agentic, agenticHasDiffContext, shouldRunValidation);
|
|
2174
2100
|
const ranWithChangesCount = migrations.length - notRunMigrationsCount - migrationsWithNoChanges.length;
|
|
2175
2101
|
// The "applied" tally counts fully-completed migrations — those that
|
|
2176
2102
|
// left no deferred work behind. Hybrid migrations whose prompt half was
|
|
@@ -2181,6 +2107,7 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2181
2107
|
appliedCount,
|
|
2182
2108
|
committedShasCount,
|
|
2183
2109
|
skippedPromptsCount,
|
|
2110
|
+
waivedAgenticStepsCount,
|
|
2184
2111
|
insideAgent,
|
|
2185
2112
|
});
|
|
2186
2113
|
const tallyBody = tallyLine ? [tallyLine] : undefined;
|
|
@@ -2262,18 +2189,26 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
|
|
|
2262
2189
|
appliedCount,
|
|
2263
2190
|
});
|
|
2264
2191
|
}
|
|
2192
|
+
function isSingleMigrationInvocation(args) {
|
|
2193
|
+
return args.runMigration !== undefined;
|
|
2194
|
+
}
|
|
2265
2195
|
async function migrate(root, args, rawArgs) {
|
|
2266
2196
|
await client_1.daemonClient.stop();
|
|
2267
2197
|
return (0, handle_errors_1.handleErrors)(process.env.NX_VERBOSE_LOGGING === 'true', async () => {
|
|
2268
2198
|
const mergedArgs = (0, migrate_config_1.applyNxJsonMigrateDefaults)(args, (0, configuration_1.readNxJson)().migrate);
|
|
2269
|
-
|
|
2199
|
+
// A single-migration invocation resolves its commit config downstream via
|
|
2200
|
+
// resolveCreateCommits, so this assert must not fire for it.
|
|
2201
|
+
const singleMigration = isSingleMigrationInvocation(mergedArgs);
|
|
2202
|
+
if (!singleMigration) {
|
|
2203
|
+
(0, migrate_config_1.assertCommitPrefixHasCommits)(mergedArgs);
|
|
2204
|
+
}
|
|
2270
2205
|
// One fetcher (registry-first, install fallback) shared by the `--include`
|
|
2271
2206
|
// eligibility gate and the migration cascade so package metadata is fetched
|
|
2272
2207
|
// at most once per package/version.
|
|
2273
2208
|
const fetch = createFetcher((0, package_manager_1.getPackageManagerCommand)());
|
|
2274
|
-
// `--run-migrations` without a value parses as ''
|
|
2275
|
-
// generate phase.
|
|
2276
|
-
const isGenerateInvocation = mergedArgs['runMigrations'] === undefined;
|
|
2209
|
+
// `--run-migrations` without a value parses as '', so only undefined (and
|
|
2210
|
+
// no single-migration flag) means the generate phase.
|
|
2211
|
+
const isGenerateInvocation = mergedArgs['runMigrations'] === undefined && !singleMigration;
|
|
2277
2212
|
let opts;
|
|
2278
2213
|
try {
|
|
2279
2214
|
opts = await parseMigrationsOptions(mergedArgs, fetch);
|
|
@@ -2282,36 +2217,289 @@ async function migrate(root, args, rawArgs) {
|
|
|
2282
2217
|
if (isGenerateInvocation) {
|
|
2283
2218
|
(0, migrate_analytics_1.reportMigrateGenerateError)('resolve_version', e);
|
|
2284
2219
|
}
|
|
2220
|
+
else if (singleMigration) {
|
|
2221
|
+
(0, migrate_analytics_1.reportMigrateRunError)({ code: 'other', error: e });
|
|
2222
|
+
}
|
|
2285
2223
|
throw e;
|
|
2286
2224
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2225
|
+
switch (opts.type) {
|
|
2226
|
+
case 'generateMigrations':
|
|
2227
|
+
await generateMigrationsJsonAndUpdatePackageJson(root, opts, fetch);
|
|
2228
|
+
return;
|
|
2229
|
+
case 'runSingleMigration':
|
|
2230
|
+
try {
|
|
2231
|
+
return await runSingleMigrationFromCli(root, opts, rawArgs, mergedArgs);
|
|
2232
|
+
}
|
|
2233
|
+
catch (e) {
|
|
2234
|
+
// `runInstall` already logged the remediation; returning instead of
|
|
2235
|
+
// rethrowing keeps `handleErrors` from printing a stack over it.
|
|
2236
|
+
if (e instanceof execute_migration_1.NpmPeerDepsInstallError) {
|
|
2237
|
+
(0, migrate_analytics_1.reportMigrateRunError)({ code: 'npm_install', error: e });
|
|
2238
|
+
return 1;
|
|
2239
|
+
}
|
|
2240
|
+
(0, migrate_analytics_1.reportMigrateRunError)({ code: 'other', error: e });
|
|
2241
|
+
throw e;
|
|
2242
|
+
}
|
|
2243
|
+
case 'runMigrations':
|
|
2244
|
+
try {
|
|
2245
|
+
return await runMigrations(root, opts, rawArgs, mergedArgs['verbose'], mergedArgs['createCommits'], mergedArgs['commitPrefix'] ?? command_object_1.DEFAULT_MIGRATION_COMMIT_PREFIX, mergedArgs['skipInstall']);
|
|
2246
|
+
}
|
|
2247
|
+
catch (e) {
|
|
2248
|
+
if (e instanceof execute_migration_1.NpmPeerDepsInstallError) {
|
|
2249
|
+
(0, migrate_analytics_1.reportMigrateRunError)({ code: 'npm_install', error: e });
|
|
2250
|
+
return 1;
|
|
2251
|
+
}
|
|
2252
|
+
(0, migrate_analytics_1.reportMigrateRunError)({ code: 'other', error: e });
|
|
2253
|
+
throw e;
|
|
2254
|
+
}
|
|
2255
|
+
default: {
|
|
2256
|
+
// `unhandled: never` makes a new invocation type a compile error here
|
|
2257
|
+
// rather than a silent fall-through.
|
|
2258
|
+
const unhandled = opts;
|
|
2259
|
+
throw new Error(`Unhandled migrate invocation type: ${JSON.stringify(unhandled)}`);
|
|
2260
|
+
}
|
|
2289
2261
|
}
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2262
|
+
});
|
|
2263
|
+
}
|
|
2264
|
+
// Evaluated manifest code can throw any value: a null-prototype object defeats
|
|
2265
|
+
// String() (the Object.prototype.toString rung catches it), and a revoked
|
|
2266
|
+
// proxy defeats that too. Interpolating either in a catch would throw again
|
|
2267
|
+
// and escape the catch.
|
|
2268
|
+
function stringifyCaught(e) {
|
|
2269
|
+
try {
|
|
2270
|
+
return String(e);
|
|
2271
|
+
}
|
|
2272
|
+
catch {
|
|
2273
|
+
try {
|
|
2274
|
+
return Object.prototype.toString.call(e);
|
|
2275
|
+
}
|
|
2276
|
+
catch {
|
|
2277
|
+
return '<unprintable value>';
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
// A resolver-based lookup (including `resolvePackageJsonWithoutCachePollution`,
|
|
2282
|
+
// which does defeat Node's package self-reference) is the wrong tool here:
|
|
2283
|
+
// resolvers fall back to NODE_PATH after the explicit paths, and NODE_PATH
|
|
2284
|
+
// names the temp installation when this runs there. The scan below reads its
|
|
2285
|
+
// candidate directories itself, so neither NODE_PATH nor self-reference can
|
|
2286
|
+
// reach it; `getNxBin` (utils/child-process.ts), which locates the nx the
|
|
2287
|
+
// hand-off spawns, avoids resolvers for the same reason, over a narrower set
|
|
2288
|
+
// of directories: it skips `.nx/installation`, declines a root without a
|
|
2289
|
+
// package.json, and stops at the nearest install rather than reading past an
|
|
2290
|
+
// unusable one. The PnP branch below stays bespoke either way: a resolver
|
|
2291
|
+
// cannot see into the zip cache without the PnP runtime.
|
|
2292
|
+
function readLocalNxVersion(root) {
|
|
2293
|
+
// A PnP manifest is consulted only when yarn drives the hand-off:
|
|
2294
|
+
// `getRunNxBaseCommand` builds the hand-off command through this same
|
|
2295
|
+
// detectPackageManager call (which answers from nx.json's
|
|
2296
|
+
// cli.packageManager first, before any lockfile), and yarn is the one
|
|
2297
|
+
// package manager that executes the manifest's nx. That mirror alone
|
|
2298
|
+
// cannot see a switch off yarn, because yarn.lock survives one and
|
|
2299
|
+
// outranks all but bun's lockfiles in the detection order, while .pnp.cjs
|
|
2300
|
+
// lingers only on a switch to another package manager (yarn removes it
|
|
2301
|
+
// when moving to node_modules linking). A rival lockfile beside the
|
|
2302
|
+
// manifest is therefore read as evidence of such a switch, though not
|
|
2303
|
+
// proof, since `pnpm install --lockfile-only` writes one without
|
|
2304
|
+
// installing: on a scan miss the manifest still answers rather than
|
|
2305
|
+
// failing the guards, and a rival install over a still-live PnP
|
|
2306
|
+
// workspace is read over the PnP nx the yarn spawn executes, because
|
|
2307
|
+
// preferring the manifest would judge the dead install that a completed
|
|
2308
|
+
// switch leaves behind and refuse persistently. The known misread left
|
|
2309
|
+
// is yarn classic driving a workspace with a leftover Berry manifest
|
|
2310
|
+
// and no other lockfile; telling the two yarns apart from disk is not
|
|
2311
|
+
// worth the fragility, and a `yarn node` runtime probe was rejected
|
|
2312
|
+
// because its stdout is forgeable through an inherited NODE_OPTIONS
|
|
2313
|
+
// preload and a hung descendant holding the output pipe outlives a
|
|
2314
|
+
// subprocess timeout on this synchronous path.
|
|
2315
|
+
const pnpManifest = ['.pnp.cjs', '.pnp.js']
|
|
2316
|
+
.map((f) => (0, path_1.join)(root, f))
|
|
2317
|
+
.find((f) => (0, fs_1.existsSync)(f));
|
|
2318
|
+
if (pnpManifest) {
|
|
2319
|
+
const competingLockfile = [
|
|
2320
|
+
'package-lock.json',
|
|
2321
|
+
'npm-shrinkwrap.json',
|
|
2322
|
+
'pnpm-lock.yaml',
|
|
2323
|
+
'bun.lockb',
|
|
2324
|
+
'bun.lock',
|
|
2325
|
+
].some((f) => (0, fs_1.existsSync)((0, path_1.join)(root, f)));
|
|
2326
|
+
if (competingLockfile) {
|
|
2327
|
+
const version = scanNodeModulesForNxVersion(root);
|
|
2328
|
+
if (version) {
|
|
2329
|
+
return version;
|
|
2293
2330
|
}
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
//
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2331
|
+
}
|
|
2332
|
+
try {
|
|
2333
|
+
if ((0, package_manager_1.detectPackageManager)(root) === 'yarn') {
|
|
2334
|
+
// Compiled manually rather than required: a require would serve the
|
|
2335
|
+
// stale cached copy the runtime loaded at startup. new Function does
|
|
2336
|
+
// not strip a leading shebang like the CJS loader, so drop it here.
|
|
2337
|
+
const manifestModule = {
|
|
2338
|
+
exports: {},
|
|
2339
|
+
};
|
|
2340
|
+
new Function('module', 'exports', 'require', '__dirname', '__filename', (0, fs_1.readFileSync)(pnpManifest, 'utf-8').replace(/^#!.*/, ''))(manifestModule, manifestModule.exports, require, root, pnpManifest);
|
|
2341
|
+
const packageJsonPath = manifestModule.exports.resolveRequest('nx/package.json', (0, path_1.join)(root, 'package.json'));
|
|
2342
|
+
if (packageJsonPath) {
|
|
2343
|
+
// A resolving manifest names the install the yarn spawn runs, so
|
|
2344
|
+
// every branch below returns rather than falling through to the
|
|
2345
|
+
// ancestor scan.
|
|
2346
|
+
try {
|
|
2347
|
+
const { version } = (0, fileutils_1.readJsonFile)(packageJsonPath);
|
|
2348
|
+
if (typeof version === 'string') {
|
|
2349
|
+
logger_1.logger.verbose(`Read the workspace's nx version ${version} from '${packageJsonPath}'.`);
|
|
2350
|
+
return version;
|
|
2351
|
+
}
|
|
2352
|
+
logger_1.logger.verbose(`'${packageJsonPath}' does not declare a usable version.`);
|
|
2353
|
+
return undefined;
|
|
2354
|
+
}
|
|
2355
|
+
catch (readError) {
|
|
2356
|
+
// A zip-served path (installs with scripts disabled keep nx
|
|
2357
|
+
// zipped) cannot be read without the PnP runtime's zip fs, but
|
|
2358
|
+
// the manifest's own locator still names the version (e.g.
|
|
2359
|
+
// 'virtual:<hash>#npm:23.1.0').
|
|
2360
|
+
let version;
|
|
2361
|
+
try {
|
|
2362
|
+
const reference = manifestModule.exports.findPackageLocator?.(packageJsonPath)?.reference;
|
|
2363
|
+
version =
|
|
2364
|
+
typeof reference === 'string'
|
|
2365
|
+
? reference.match(/npm:([^#:]+)/)?.[1]
|
|
2366
|
+
: undefined;
|
|
2367
|
+
}
|
|
2368
|
+
catch (locatorError) {
|
|
2369
|
+
logger_1.logger.verbose(`Could not read the nx locator from the PnP manifest '${pnpManifest}': ${stringifyCaught(locatorError)}`);
|
|
2370
|
+
}
|
|
2371
|
+
if (version && (0, semver_1.valid)(version)) {
|
|
2372
|
+
logger_1.logger.verbose(`Read the workspace's nx version ${version} from the locator in the PnP manifest '${pnpManifest}'.`);
|
|
2373
|
+
return version;
|
|
2374
|
+
}
|
|
2375
|
+
logger_1.logger.verbose(`Could not read the workspace's nx version through the PnP manifest '${pnpManifest}': ${stringifyCaught(readError)}`);
|
|
2376
|
+
return undefined;
|
|
2377
|
+
}
|
|
2301
2378
|
}
|
|
2302
|
-
(0, migrate_analytics_1.reportMigrateRunError)({ code: 'other', error: e });
|
|
2303
|
-
throw e;
|
|
2304
2379
|
}
|
|
2305
2380
|
}
|
|
2381
|
+
catch (e) {
|
|
2382
|
+
// Degrade to the full scan: neither a failed detection nor an
|
|
2383
|
+
// unevaluable manifest can name the spawn's nx.
|
|
2384
|
+
logger_1.logger.verbose(`Could not read the workspace's nx version through the PnP manifest '${pnpManifest}': ${stringifyCaught(e)}`);
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
return scanNodeModulesForNxVersion(root);
|
|
2388
|
+
}
|
|
2389
|
+
// The workspace's own install locations first (.nx/installation, root), then
|
|
2390
|
+
// ancestor directories: package-manager executable lookup ascends (npx and
|
|
2391
|
+
// bun always, pnpm and yarn inside an outer workspace) and hoisted layouts
|
|
2392
|
+
// install nx above the workspace root. `getNxBin` ascends too, so an ancestor
|
|
2393
|
+
// read here is usually the one the hand-off spawns. They diverge for a
|
|
2394
|
+
// `.nx/installation` workspace, which `getNxBin` declines, and where the only
|
|
2395
|
+
// nx found sits past the package manager's own lookup boundary and the spawn
|
|
2396
|
+
// has fallen back to that manager: yarn then reports "command not found"
|
|
2397
|
+
// while pnpm exec lands on an nx on PATH. The version read here is therefore
|
|
2398
|
+
// the readable answer, not necessarily the executed one.
|
|
2399
|
+
function scanNodeModulesForNxVersion(root) {
|
|
2400
|
+
const searchPaths = [...(0, installation_directory_1.getNxRequirePaths)(root)];
|
|
2401
|
+
for (let dir = (0, path_1.dirname)(root);; dir = (0, path_1.dirname)(dir)) {
|
|
2402
|
+
searchPaths.push(dir);
|
|
2403
|
+
if (dir === (0, path_1.dirname)(dir)) {
|
|
2404
|
+
break;
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
return readNxVersionFromNodeModules(searchPaths);
|
|
2408
|
+
}
|
|
2409
|
+
function readNxVersionFromNodeModules(searchPaths) {
|
|
2410
|
+
for (const searchPath of searchPaths) {
|
|
2411
|
+
const packageJsonPath = (0, path_1.join)(searchPath, 'node_modules', 'nx', 'package.json');
|
|
2412
|
+
try {
|
|
2413
|
+
if ((0, fs_1.existsSync)(packageJsonPath)) {
|
|
2414
|
+
const { version } = (0, fileutils_1.readJsonFile)(packageJsonPath);
|
|
2415
|
+
if (typeof version === 'string') {
|
|
2416
|
+
logger_1.logger.verbose(`Resolved nx version ${version} from '${packageJsonPath}'.`);
|
|
2417
|
+
return version;
|
|
2418
|
+
}
|
|
2419
|
+
logger_1.logger.verbose(`'${packageJsonPath}' does not declare a usable version; continuing the search.`);
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
catch (e) {
|
|
2423
|
+
logger_1.logger.verbose(`Could not read the workspace's installed nx version from '${packageJsonPath}': ${e}`);
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
return undefined;
|
|
2427
|
+
}
|
|
2428
|
+
// Keeps `--run-migration` behaving like `--run-migrations` when invoked from
|
|
2429
|
+
// a temp `nx@latest` install: same pre-install, same local-nx hand-off.
|
|
2430
|
+
async function runSingleMigrationFromCli(root, opts, args, mergedArgs) {
|
|
2431
|
+
const shouldSkipInstall = mergedArgs['skipInstall'] ?? false;
|
|
2432
|
+
if (!shouldSkipInstall && !process.env.NX_MIGRATE_SKIP_INSTALL) {
|
|
2433
|
+
await (0, execute_migration_1.runInstall)(undefined, 'pre-migration', (0, execute_migration_1.formatSingleMigrationRerunCommand)(opts.runMigration));
|
|
2434
|
+
}
|
|
2435
|
+
if (!__dirname.startsWith(workspace_root_1.workspaceRoot)) {
|
|
2436
|
+
// Must stay after the pre-install: it reads the version that install may
|
|
2437
|
+
// have just updated, so checking earlier would falsely refuse a workspace
|
|
2438
|
+
// whose package.json bump was not installed yet.
|
|
2439
|
+
(0, version_skew_guard_1.assertWorkspaceNxSupportsNewMigrateFlags)({
|
|
2440
|
+
argv: args,
|
|
2441
|
+
// `workspaceRoot`, not `root`: the migrate flow's `root` is the
|
|
2442
|
+
// invocation directory, which may sit below the workspace the hand-off
|
|
2443
|
+
// resolves nx in.
|
|
2444
|
+
readLocalNxVersion: () => readLocalNxVersion(workspace_root_1.workspaceRoot),
|
|
2445
|
+
});
|
|
2446
|
+
return handOffToLocalNx(args);
|
|
2447
|
+
}
|
|
2448
|
+
// The worker resolves the agentic flow, the effective commit config, and
|
|
2449
|
+
// the default-branch confirmation itself; hand it the raw flags.
|
|
2450
|
+
// Lazy-load run/ so plain migrate and repair runs don't pay for the agentic
|
|
2451
|
+
// selection chain its barrel pulls in eagerly.
|
|
2452
|
+
const { runSingleMigrationWorker } = require('./run');
|
|
2453
|
+
await runSingleMigrationWorker({
|
|
2454
|
+
root,
|
|
2455
|
+
runMigration: opts.runMigration,
|
|
2456
|
+
agentic: opts.agentic,
|
|
2457
|
+
validate: opts.validate,
|
|
2458
|
+
createCommits: mergedArgs['createCommits'],
|
|
2459
|
+
commitPrefix: mergedArgs['commitPrefix'] ??
|
|
2460
|
+
command_object_1.DEFAULT_MIGRATION_COMMIT_PREFIX,
|
|
2461
|
+
interactive: opts.interactive,
|
|
2462
|
+
skipInstall: shouldSkipInstall,
|
|
2463
|
+
isVerbose: mergedArgs['verbose'] ?? false,
|
|
2306
2464
|
});
|
|
2307
2465
|
}
|
|
2308
2466
|
async function runMigration() {
|
|
2309
2467
|
return (0, handle_errors_1.handleErrors)(process.env.NX_VERBOSE_LOGGING === 'true', async () => {
|
|
2310
|
-
|
|
2468
|
+
// Spawning `nx _migrate` re-resolves the workspace's nx, normally the
|
|
2469
|
+
// very installation running this code: bin/nx.ts delegated the outer
|
|
2470
|
+
// `migrate` command to it, while `_migrate` skips that delegation. A
|
|
2471
|
+
// new-flag invocation reaching this fallback therefore lands on an nx
|
|
2472
|
+
// that parsed the flags, so it needs no capability check. Two lookups
|
|
2473
|
+
// can still diverge from the running install, both properties of the
|
|
2474
|
+
// spawn machinery rather than of this fallback: a `.nx/installation`
|
|
2475
|
+
// beside a root package.json, and, once the spawn falls back to the
|
|
2476
|
+
// package manager, a lookup that misses the workspace install and lands
|
|
2477
|
+
// on an nx on PATH (pnpm exec).
|
|
2478
|
+
const forwardedArgv = process.argv.slice(3);
|
|
2479
|
+
const runLocalMigrate = () => runOrReturnExitCode(() => (0, child_process_2.runNxArgvSync)(['_migrate', ...forwardedArgv], {
|
|
2311
2480
|
stdio: ['inherit', 'inherit', 'inherit'],
|
|
2312
2481
|
}));
|
|
2313
2482
|
if (process.env.NX_USE_LOCAL !== 'true' &&
|
|
2314
2483
|
process.env.NX_MIGRATE_USE_LOCAL === undefined) {
|
|
2484
|
+
// The version spec mirrors nxCliPath's; keep the two in sync.
|
|
2485
|
+
const cliVersionSpec = process.env.NX_MIGRATE_CLI_VERSION || 'latest';
|
|
2486
|
+
const target = await (0, version_skew_guard_1.resolveNewMigrateFlagsRunTarget)({
|
|
2487
|
+
argv: process.argv.slice(3),
|
|
2488
|
+
cliVersionSpec,
|
|
2489
|
+
fromEnvOverride: !!process.env.NX_MIGRATE_CLI_VERSION,
|
|
2490
|
+
ownNxVersion: versions_1.nxVersion,
|
|
2491
|
+
resolveVersion: (spec) =>
|
|
2492
|
+
// A probe must not prompt, write pnpm excludes, or install; a
|
|
2493
|
+
// minimum-release-age violation surfaces as a rejection and
|
|
2494
|
+
// routes local.
|
|
2495
|
+
(0, resolve_package_version_1.resolvePackageVersionRespectingMinReleaseAge)('nx', spec, {
|
|
2496
|
+
applySideEffects: false,
|
|
2497
|
+
}),
|
|
2498
|
+
readLocalNxVersion: () => readLocalNxVersion(workspace_root_1.workspaceRoot),
|
|
2499
|
+
});
|
|
2500
|
+
if (target === 'local-nx') {
|
|
2501
|
+
return runLocalMigrate();
|
|
2502
|
+
}
|
|
2315
2503
|
const p = await nxCliPath();
|
|
2316
2504
|
if (p === null) {
|
|
2317
2505
|
return runLocalMigrate();
|
|
@@ -2322,14 +2510,26 @@ async function runMigration() {
|
|
|
2322
2510
|
process.env.npm_config_registry.match(/^https:\/\/registry\.(npmjs\.org|yarnpkg\.com)/)) {
|
|
2323
2511
|
delete process.env.npm_config_registry;
|
|
2324
2512
|
}
|
|
2325
|
-
// Intentionally not
|
|
2326
|
-
// temp dir by nxCliPath() (latest, or
|
|
2327
|
-
// migrations run with an up-to-date migrate
|
|
2328
|
-
//
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2513
|
+
// Intentionally not the workspace's nx: `p` is an nx CLI freshly
|
|
2514
|
+
// installed into a temp dir by nxCliPath() (latest, or
|
|
2515
|
+
// NX_MIGRATE_CLI_VERSION), so migrations run with an up-to-date migrate
|
|
2516
|
+
// implementation. `p` is <tmpDir>/node_modules/.bin/nx, a shell shim;
|
|
2517
|
+
// spawning the entry point that install's own manifest names instead
|
|
2518
|
+
// keeps the forwarded argv out of a shell. That install declares nx
|
|
2519
|
+
// itself, so it is read without ascending: an nx above the temp dir is
|
|
2520
|
+
// one nothing asked for, and the shim below is the answer instead.
|
|
2521
|
+
const tempNxEntry = (0, child_process_2.readInstalledNxBin)((0, path_1.dirname)((0, path_1.dirname)((0, path_1.dirname)(p))));
|
|
2522
|
+
return runOrReturnExitCode(() => tempNxEntry
|
|
2523
|
+
? (0, child_process_2.runNxArgvSync)(['_migrate', ...forwardedArgv], {
|
|
2524
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
2525
|
+
nxBin: tempNxEntry,
|
|
2526
|
+
})
|
|
2527
|
+
: (0, child_process_1.execSync)(`${(0, shell_quoting_1.quoteShellArg)(p)} _migrate ${forwardedArgv
|
|
2528
|
+
.map(shell_quoting_1.quoteShellArg)
|
|
2529
|
+
.join(' ')}`, {
|
|
2530
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
2531
|
+
windowsHide: true,
|
|
2532
|
+
}));
|
|
2333
2533
|
}
|
|
2334
2534
|
return runLocalMigrate();
|
|
2335
2535
|
});
|
|
@@ -2365,7 +2565,7 @@ function resolveMigrationForRun(root, migration, resolveDocumentation) {
|
|
|
2365
2565
|
let documentationPath;
|
|
2366
2566
|
if (resolveDocumentation && migration.documentation) {
|
|
2367
2567
|
documentationPath = resolvedCollection
|
|
2368
|
-
? resolveDocumentationFileToWorkspacePath(root, (0, path_1.dirname)(resolvedCollection.collectionPath), migration.documentation)
|
|
2568
|
+
? (0, execute_migration_1.resolveDocumentationFileToWorkspacePath)(root, (0, path_1.dirname)(resolvedCollection.collectionPath), migration.documentation)
|
|
2369
2569
|
: undefined;
|
|
2370
2570
|
if (!documentationPath) {
|
|
2371
2571
|
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.`);
|
|
@@ -2373,24 +2573,6 @@ function resolveMigrationForRun(root, migration, resolveDocumentation) {
|
|
|
2373
2573
|
}
|
|
2374
2574
|
return { resolvedCollection, documentationPath };
|
|
2375
2575
|
}
|
|
2376
|
-
// Resolves a `documentation` path (relative to the package's migrations dir) to
|
|
2377
|
-
// a workspace-relative path - or the absolute path when it resolves outside the
|
|
2378
|
-
// workspace (unusual hoisted/symlinked layouts). The agent runs with cwd =
|
|
2379
|
-
// workspace root, so the workspace-relative form is preferred. Returns
|
|
2380
|
-
// undefined when the file can't be resolved.
|
|
2381
|
-
function resolveDocumentationFileToWorkspacePath(root, migrationsDir, documentation) {
|
|
2382
|
-
let documentationFile;
|
|
2383
|
-
try {
|
|
2384
|
-
documentationFile = require.resolve(documentation, {
|
|
2385
|
-
paths: [migrationsDir],
|
|
2386
|
-
});
|
|
2387
|
-
}
|
|
2388
|
-
catch {
|
|
2389
|
-
return undefined;
|
|
2390
|
-
}
|
|
2391
|
-
const relativePath = (0, path_1.relative)(root, documentationFile);
|
|
2392
|
-
return relativePath.startsWith('..') ? documentationFile : relativePath;
|
|
2393
|
-
}
|
|
2394
2576
|
async function nxCliPath(nxWorkspaceRoot) {
|
|
2395
2577
|
const version = process.env.NX_MIGRATE_CLI_VERSION || 'latest';
|
|
2396
2578
|
const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true';
|
|
@@ -2441,7 +2623,7 @@ async function nxCliPath(nxWorkspaceRoot) {
|
|
|
2441
2623
|
return (0, path_1.join)(tmpDir, `node_modules`, '.bin', 'nx');
|
|
2442
2624
|
}
|
|
2443
2625
|
catch (e) {
|
|
2444
|
-
console.error(`Failed to install the ${version} version of the migration script.
|
|
2626
|
+
console.error(`Failed to install the ${version} version of the migration script. Falling back to the workspace's installed nx.`);
|
|
2445
2627
|
if (isVerbose) {
|
|
2446
2628
|
console.error(e);
|
|
2447
2629
|
}
|