nx 23.3.0-beta.7 → 23.3.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +1 -1
  2. package/dist/nx-cloud-internals.d.ts +1 -1
  3. package/dist/nx-cloud-internals.js +4 -4
  4. package/dist/src/command-line/affected/affected.js +20 -1
  5. package/dist/src/command-line/affected/command-object.js +1 -1
  6. package/dist/src/command-line/migrate/agentic/master/run-master-session.d.ts +6 -2
  7. package/dist/src/command-line/migrate/agentic/master/run-master-session.js +88 -14
  8. package/dist/src/command-line/migrate/command-object.d.ts +1 -0
  9. package/dist/src/command-line/migrate/command-object.js +6 -1
  10. package/dist/src/command-line/migrate/migrate-analytics.d.ts +34 -9
  11. package/dist/src/command-line/migrate/migrate-analytics.js +71 -10
  12. package/dist/src/command-line/migrate/migrate-config.d.ts +5 -2
  13. package/dist/src/command-line/migrate/migrate-config.js +19 -11
  14. package/dist/src/command-line/migrate/migrate.d.ts +2 -0
  15. package/dist/src/command-line/migrate/migrate.js +152 -45
  16. package/dist/src/command-line/migrate/resolve-package-version.js +2 -19
  17. package/dist/src/command-line/migrate/run/broker.d.ts +14 -2
  18. package/dist/src/command-line/migrate/run/broker.js +95 -34
  19. package/dist/src/command-line/migrate/run/existing-run-report.d.ts +55 -0
  20. package/dist/src/command-line/migrate/run/existing-run-report.js +225 -0
  21. package/dist/src/command-line/migrate/run/give-up.d.ts +41 -0
  22. package/dist/src/command-line/migrate/run/give-up.js +148 -0
  23. package/dist/src/command-line/migrate/run/index.d.ts +6 -4
  24. package/dist/src/command-line/migrate/run/index.js +15 -2
  25. package/dist/src/command-line/migrate/run/issues.d.ts +14 -0
  26. package/dist/src/command-line/migrate/run/issues.js +59 -0
  27. package/dist/src/command-line/migrate/run/orchestrator.d.ts +40 -1
  28. package/dist/src/command-line/migrate/run/orchestrator.js +509 -160
  29. package/dist/src/command-line/migrate/run/run-id.d.ts +0 -8
  30. package/dist/src/command-line/migrate/run/run-id.js +0 -30
  31. package/dist/src/command-line/migrate/run/run-state.d.ts +11 -9
  32. package/dist/src/command-line/migrate/run/run-state.js +25 -12
  33. package/dist/src/command-line/migrate/run/runbook.js +9 -4
  34. package/dist/src/command-line/migrate/run/state-lock.d.ts +31 -0
  35. package/dist/src/command-line/migrate/run/state-lock.js +126 -0
  36. package/dist/src/command-line/migrate/run/state-machine.d.ts +15 -0
  37. package/dist/src/command-line/migrate/run/state-machine.js +137 -10
  38. package/dist/src/command-line/migrate/run/util.d.ts +1 -0
  39. package/dist/src/command-line/migrate/run/util.js +21 -0
  40. package/dist/src/command-line/migrate/run/worker.js +3 -0
  41. package/dist/src/command-line/migrate/step-actions.d.ts +1 -1
  42. package/dist/src/command-line/migrate/step-actions.js +7 -1
  43. package/dist/src/command-line/migrate/text.d.ts +6 -0
  44. package/dist/src/command-line/migrate/text.js +25 -0
  45. package/dist/src/command-line/release/utils/release-graph.js +1 -1
  46. package/dist/src/command-line/release/utils/remote-release-clients/github.js +2 -5
  47. package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.d.ts +2 -2
  48. package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.js +4 -5
  49. package/dist/src/command-line/release/version/version-actions.d.ts +3 -0
  50. package/dist/src/command-line/reset/reset.js +10 -4
  51. package/dist/src/command-line/show/command-object.js +1 -1
  52. package/dist/src/command-line/show/projects.js +22 -0
  53. package/dist/src/command-line/yargs-utils/shared-options.d.ts +4 -0
  54. package/dist/src/command-line/yargs-utils/shared-options.js +16 -0
  55. package/dist/src/config/task-graph.d.ts +1 -1
  56. package/dist/src/config/workspace-json-project-json.d.ts +4 -4
  57. package/dist/src/daemon/client/client.d.ts +3 -3
  58. package/dist/src/daemon/client/client.js +4 -4
  59. package/dist/src/daemon/message-types/select-affected-tasks.d.ts +3 -0
  60. package/dist/src/daemon/message-types/ultracache-configuration-version.d.ts +8 -0
  61. package/dist/src/daemon/server/handle-hash-tasks.d.ts +2 -2
  62. package/dist/src/daemon/server/handle-hash-tasks.js +3 -3
  63. package/dist/src/daemon/server/handle-select-affected-tasks.js +5 -2
  64. package/dist/src/daemon/server/ultracache-configuration-state.d.ts +7 -0
  65. package/dist/src/daemon/server/{io-snapshots-state.js → ultracache-configuration-state.js} +5 -4
  66. package/dist/src/devkit-internals.d.ts +3 -1
  67. package/dist/src/devkit-internals.js +8 -3
  68. package/dist/src/hasher/create-task-hasher.d.ts +3 -3
  69. package/dist/src/hasher/create-task-hasher.js +6 -6
  70. package/dist/src/hasher/hash-task.d.ts +2 -2
  71. package/dist/src/hasher/hash-task.js +6 -5
  72. package/dist/src/hasher/native-task-hasher-impl.d.ts +7 -7
  73. package/dist/src/hasher/native-task-hasher-impl.js +19 -19
  74. package/dist/src/hasher/task-hasher.d.ts +14 -14
  75. package/dist/src/hasher/task-hasher.js +13 -13
  76. package/dist/src/native/index.d.ts +151 -102
  77. package/dist/src/native/native-bindings.js +4 -5
  78. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  79. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  80. package/dist/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +15 -8
  81. package/dist/src/nx-cloud/update-manager.d.ts +3 -3
  82. package/dist/src/nx-cloud/update-manager.js +19 -13
  83. package/dist/src/nx-cloud/utilities/client.d.ts +1 -1
  84. package/dist/src/nx-cloud/utilities/client.js +1 -1
  85. package/dist/src/nx-cloud/utilities/is-workspace-claimed.js +2 -1
  86. package/dist/src/nx-cloud/utilities/nx-cloud-http-client.d.ts +5 -0
  87. package/dist/src/nx-cloud/utilities/nx-cloud-http-client.js +16 -0
  88. package/dist/src/nx-cloud/utilities/url-shorten.js +17 -10
  89. package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +22 -18
  90. package/dist/src/plugins/js/utils/register.js +11 -3
  91. package/dist/src/project-graph/affected/affected-reasons.d.ts +116 -0
  92. package/dist/src/project-graph/affected/affected-reasons.js +459 -0
  93. package/dist/src/project-graph/affected/affected-tasks.d.ts +23 -10
  94. package/dist/src/project-graph/affected/affected-tasks.js +203 -31
  95. package/dist/src/project-graph/affected/print-explanation.d.ts +11 -0
  96. package/dist/src/project-graph/affected/print-explanation.js +44 -0
  97. package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
  98. package/dist/src/tasks-runner/init-tasks-runner.d.ts +5 -5
  99. package/dist/src/tasks-runner/init-tasks-runner.js +8 -15
  100. package/dist/src/tasks-runner/run-command.d.ts +7 -7
  101. package/dist/src/tasks-runner/run-command.js +19 -23
  102. package/dist/src/tasks-runner/task-orchestrator.d.ts +7 -3
  103. package/dist/src/tasks-runner/task-orchestrator.js +9 -8
  104. package/dist/src/tasks-runner/tasks-runner.d.ts +2 -0
  105. package/dist/src/ultracache/config.d.ts +21 -0
  106. package/dist/src/ultracache/config.js +29 -0
  107. package/dist/src/ultracache/fetch.d.ts +21 -0
  108. package/dist/src/{io-snapshots → ultracache}/fetch.js +9 -9
  109. package/dist/src/ultracache/overrides.d.ts +17 -0
  110. package/dist/src/{io-snapshots → ultracache}/overrides.js +14 -13
  111. package/dist/src/ultracache/report.d.ts +13 -0
  112. package/dist/src/{io-snapshots → ultracache}/report.js +15 -21
  113. package/dist/src/ultracache/store.d.ts +40 -0
  114. package/dist/src/{io-snapshots → ultracache}/store.js +45 -45
  115. package/dist/src/utils/ab-testing.js +11 -12
  116. package/dist/src/utils/command-line-utils.d.ts +1 -0
  117. package/dist/src/utils/create-cli-options.d.ts +6 -0
  118. package/dist/src/utils/create-cli-options.js +29 -0
  119. package/dist/src/utils/db-connection.d.ts +1 -0
  120. package/dist/src/utils/db-connection.js +1 -0
  121. package/dist/src/utils/git-utils.d.ts +7 -0
  122. package/dist/src/utils/git-utils.js +21 -9
  123. package/dist/src/utils/http-client.d.ts +27 -0
  124. package/dist/src/utils/http-client.js +193 -0
  125. package/package.json +12 -38
  126. package/dist/src/daemon/message-types/io-snapshot-version.d.ts +0 -5
  127. package/dist/src/daemon/server/io-snapshots-state.d.ts +0 -7
  128. package/dist/src/io-snapshots/config.d.ts +0 -23
  129. package/dist/src/io-snapshots/config.js +0 -32
  130. package/dist/src/io-snapshots/fetch.d.ts +0 -21
  131. package/dist/src/io-snapshots/outputs.d.ts +0 -12
  132. package/dist/src/io-snapshots/outputs.js +0 -33
  133. package/dist/src/io-snapshots/overrides.d.ts +0 -17
  134. package/dist/src/io-snapshots/report.d.ts +0 -13
  135. package/dist/src/io-snapshots/store.d.ts +0 -37
  136. package/dist/src/nx-cloud/utilities/axios.d.ts +0 -4
  137. package/dist/src/nx-cloud/utilities/axios.js +0 -17
  138. /package/dist/src/daemon/message-types/{io-snapshot-version.js → ultracache-configuration-version.js} +0 -0
@@ -710,13 +710,24 @@ async function parseTargetPackageAndVersion(args) {
710
710
  async function parseMigrationsOptions(options, fetch) {
711
711
  // A run recorded or reconciled via `--run-id` is driven by the outer agent;
712
712
  // spawning another agent from it would double-drive the run. Only the
713
- // explicit "on" values conflict; the nx.json default is ignored for
714
- // `--run-id` invocations instead.
713
+ // explicit "on" values conflict; the nx.json default is not applied to
714
+ // those invocations instead. The `--run-migrations` shape is the
715
+ // exception: there `--run-id` names the run a continue re-enters, or the
716
+ // one a `--start-fresh` replaces.
715
717
  if (options.runId !== undefined &&
716
718
  options.agentic !== undefined &&
717
- options.agentic !== false) {
719
+ options.agentic !== false &&
720
+ options.runMigrations === undefined) {
718
721
  throw new Error(`Error: '--agentic' cannot be combined with '--run-id'.`);
719
722
  }
723
+ if (options.startFresh === true) {
724
+ if (options.runMigrations === undefined) {
725
+ throw new Error(`Error: '--start-fresh' requires '--run-migrations'.`);
726
+ }
727
+ if (options.runId === undefined) {
728
+ throw new Error(`Error: '--start-fresh' requires '--run-id=<id>' naming the run to replace; the report prints it.`);
729
+ }
730
+ }
720
731
  if (options.runMigration !== undefined) {
721
732
  if (options.runMigration === '') {
722
733
  throw new Error(`Error: '--run-migration' requires a migration id, e.g. '--run-migration=@nx/js:my-migration'.`);
@@ -755,8 +766,16 @@ async function parseMigrationsOptions(options, fetch) {
755
766
  if (options.runId === '') {
756
767
  throw new Error(`Error: '--run-id' requires the id of the migrate run to record into.`);
757
768
  }
758
- if (options.runMigrations !== undefined) {
759
- throw new Error(`Error: '--run-id' (reconcile an orchestrated run) cannot be combined with '--run-migrations' (run the whole migrations file).`);
769
+ // With `--run-migrations`, `--run-id` continues the named run, which needs
770
+ // `--agentic`; a start-fresh names the run it replaces instead.
771
+ if (options.runMigrations !== undefined &&
772
+ options.startFresh !== true &&
773
+ (options.agentic === undefined || options.agentic === false)) {
774
+ throw new Error(`Error: '--run-id' (reconcile an orchestrated run) cannot be combined with '--run-migrations' (run the whole migrations file). To continue the run, pass '--agentic' (or '--agentic=<agent>') as well.`);
775
+ }
776
+ if (options.runMigrations !== undefined &&
777
+ options.stepAction !== undefined) {
778
+ throw new Error(`Error: '--step-action' cannot be combined with '--run-migrations'.`);
760
779
  }
761
780
  // A bare '--run-id' reconciles the run it names. Ungated, unlike init:
762
781
  // the id has to name a run directory that exists, and only a gated init
@@ -766,13 +785,15 @@ async function parseMigrationsOptions(options, fetch) {
766
785
  if (options.stepAction !== undefined && !(0, step_actions_1.isStepAction)(options.stepAction)) {
767
786
  throw new Error(`Error: '--step-action' must be one of ${step_actions_1.STEP_ACTIONS.join(', ')}.`);
768
787
  }
769
- return {
770
- type: 'orchestratorReconcile',
771
- runId: options.runId,
772
- ...(options.stepAction !== undefined
773
- ? { stepAction: options.stepAction }
774
- : {}),
775
- };
788
+ if (options.runMigrations === undefined) {
789
+ return {
790
+ type: 'orchestratorReconcile',
791
+ runId: options.runId,
792
+ ...(options.stepAction !== undefined
793
+ ? { stepAction: options.stepAction }
794
+ : {}),
795
+ };
796
+ }
776
797
  }
777
798
  if (options.stepAction !== undefined) {
778
799
  throw new Error(`Error: '--step-action' requires '--run-id'.`);
@@ -794,6 +815,8 @@ async function parseMigrationsOptions(options, fetch) {
794
815
  agentic: options.agentic,
795
816
  validate: options.validate,
796
817
  interactive: options.interactive,
818
+ ...(options.runId !== undefined ? { runId: options.runId } : {}),
819
+ ...(options.startFresh === true ? { startFresh: true } : {}),
797
820
  };
798
821
  }
799
822
  assertOptionalIncludeFlagCompatibility(options);
@@ -2093,6 +2116,9 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
2093
2116
  retainedAtSuccess: (0, migrate_output_1.retainedMigrations)(outcomes).map((p) => `${p.package}: ${p.name}`),
2094
2117
  };
2095
2118
  }
2119
+ function orchestratorFlagNeedsOrchestrator(flag) {
2120
+ return `'${flag}' acts on an orchestrated migrate run (NX_MIGRATE_ORCHESTRATOR=true with an enabled agent), and this invocation is not orchestrated.`;
2121
+ }
2096
2122
  // nx is located at spawn time, after the gated pre-install, so the child runs
2097
2123
  // the bytes that install put in place.
2098
2124
  function handOffToLocalNx(args) {
@@ -2107,30 +2133,75 @@ function handOffToLocalNx(args) {
2107
2133
  return exitCode !== 0 ? exitCode : undefined;
2108
2134
  }
2109
2135
  async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, commitPrefix, shouldSkipInstall = false) {
2136
+ // Both flags act on an orchestrated run: refuse them where none can run
2137
+ // (an explicit --agentic=false outside an agent cannot reach one either),
2138
+ // before the install and before --if-exists could return silently.
2139
+ const orchestratorFlag = opts.startFresh === true
2140
+ ? '--start-fresh'
2141
+ : opts.runId !== undefined
2142
+ ? '--run-id'
2143
+ : undefined;
2144
+ if (orchestratorFlag !== undefined &&
2145
+ (process.env.NX_MIGRATE_ORCHESTRATOR !== 'true' ||
2146
+ (opts.agentic === false && !(0, inception_1.isInsideAgent)()))) {
2147
+ throw new Error(orchestratorFlagNeedsOrchestrator(orchestratorFlag));
2148
+ }
2149
+ const isContinue = opts.runId !== undefined && opts.startFresh !== true;
2150
+ let continued;
2151
+ if (isContinue) {
2152
+ // Before the install: a concurrent start-fresh must not delete the run
2153
+ // this command is about to continue.
2154
+ const { holdRunToContinue } = require('./run');
2155
+ continued = holdRunToContinue(root, opts.runId);
2156
+ }
2157
+ else if (opts.startFresh === true) {
2158
+ // Before the install too: with no active run there is nothing to replace.
2159
+ const { activeRunToReplace } = require('./run');
2160
+ activeRunToReplace(root, opts.runId);
2161
+ }
2110
2162
  if (!shouldSkipInstall && !process.env.NX_MIGRATE_SKIP_INSTALL) {
2111
2163
  await (0, execute_migration_1.runInstall)();
2112
2164
  }
2113
2165
  if (!__dirname.startsWith(workspace_root_1.workspaceRoot)) {
2114
2166
  // we are running from a temp installation with nx latest, switch to running
2115
2167
  // from local installation
2168
+ if (isContinue) {
2169
+ // The child holds the run itself and refuses while this process does.
2170
+ const { releaseRunToHandOff } = require('./run');
2171
+ releaseRunToHandOff(root, opts.runId);
2172
+ }
2116
2173
  return handOffToLocalNx(args);
2117
2174
  }
2118
- const migrationsExists = (0, fileutils_1.fileExists)(opts.runMigrations);
2119
- if (opts.ifExists && !migrationsExists) {
2120
- output_1.output.log({
2121
- title: `Migrations file '${opts.runMigrations}' doesn't exist`,
2122
- });
2123
- return;
2175
+ let migrationsJson;
2176
+ if (isContinue) {
2177
+ // A continue runs the plan the run recorded, not whatever the workspace's
2178
+ // migrations file holds now (it may be gone, or belong to another plan).
2179
+ const { latestRound, runDir } = require('./run');
2180
+ const round = latestRound(continued);
2181
+ if (!round) {
2182
+ throw new Error(`Migrate run '${opts.runId}' records no plan.`);
2183
+ }
2184
+ migrationsJson = (0, fileutils_1.readJsonFile)((0, path_1.join)(runDir(root, opts.runId), round.planSnapshot));
2124
2185
  }
2125
- else if (!opts.ifExists && !migrationsExists) {
2126
- 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`);
2186
+ else {
2187
+ const migrationsExists = (0, fileutils_1.fileExists)(opts.runMigrations);
2188
+ if (opts.ifExists && !migrationsExists) {
2189
+ output_1.output.log({
2190
+ title: `Migrations file '${opts.runMigrations}' doesn't exist`,
2191
+ });
2192
+ return;
2193
+ }
2194
+ else if (!opts.ifExists && !migrationsExists) {
2195
+ 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`);
2196
+ }
2197
+ migrationsJson = (0, fileutils_1.readJsonFile)((0, path_1.join)(root, opts.runMigrations));
2127
2198
  }
2128
- const migrationsJson = (0, fileutils_1.readJsonFile)((0, path_1.join)(root, opts.runMigrations));
2129
2199
  const migrations = migrationsJson.migrations;
2130
2200
  // Defer the nx package lookup until an orchestrated branch needs this payload.
2131
2201
  const orchestratorInitInput = (createCommits) => ({
2132
2202
  root,
2133
2203
  migrationsJson,
2204
+ migrationsPath: opts.runMigrations,
2134
2205
  createCommits,
2135
2206
  commitPrefix,
2136
2207
  // The flag only, never NX_MIGRATE_SKIP_INSTALL: the wrapper's local
@@ -2142,10 +2213,11 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2142
2213
  validate: opts.validate,
2143
2214
  });
2144
2215
  // An outer agent drives the loop, so hand off to the orchestrator instead of
2145
- // the classic loop: init either starts a fresh run or resumes an already-
2146
- // active one. `--run-id` reconciles are dispatched separately and never
2147
- // reach here.
2216
+ // the classic loop: init starts a fresh run or reports an already-active
2217
+ // one; `--run-id` continues that run. Bare `--run-id` reconciles are
2218
+ // dispatched separately and never reach here.
2148
2219
  if (process.env.NX_MIGRATE_ORCHESTRATOR === 'true' && (0, inception_1.isInsideAgent)()) {
2220
+ const { runOrchestratorInit, runOrchestratorResume } = require('./run');
2149
2221
  // Orchestrated runs are agent-driven, so commits default on exactly as they
2150
2222
  // do under `--agentic=enabled`; the orchestrator replaces `resolveAgentic`.
2151
2223
  const { effective: effectiveCreateCommits, warning: createCommitsWarning, error: createCommitsError, } = (0, migrate_commits_1.resolveCreateCommits)({
@@ -2160,28 +2232,49 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2160
2232
  if (createCommitsWarning) {
2161
2233
  output_1.output.warn({ title: createCommitsWarning });
2162
2234
  }
2235
+ if (isContinue) {
2236
+ runOrchestratorResume({
2237
+ root,
2238
+ runId: opts.runId,
2239
+ policy: {
2240
+ createCommits: effectiveCreateCommits,
2241
+ skipInstall: shouldSkipInstall,
2242
+ },
2243
+ });
2244
+ return;
2245
+ }
2163
2246
  // The run commits on the user's behalf across many invocations and the
2164
2247
  // agent driving it cannot answer a terminal prompt, so a commit policy the
2165
2248
  // user never asked for stops the run on the default branch before any of
2166
2249
  // them. `--create-commits` or nx.json `migrate.createCommits` is that ask.
2167
- if (effectiveCreateCommits && shouldCreateCommits === undefined) {
2250
+ // Asked by init only once it is about to start a run: an active run is
2251
+ // reported instead, and keeps its own policy.
2252
+ const refuseCommitsOnDefaultBranch = async () => {
2253
+ if (!effectiveCreateCommits || shouldCreateCommits !== undefined) {
2254
+ return true;
2255
+ }
2168
2256
  const defaultBranch = (0, migrate_commits_1.currentBranchIfDefault)(root);
2169
- if (defaultBranch) {
2170
- output_1.output.log({
2171
- title: `Not starting the run: you are on the default branch '${defaultBranch}' and nx migrate would create a commit for each migration on it.`,
2172
- bodyLines: [
2173
- 'Ask the user how to proceed, then either:',
2174
- '- re-run with --create-commits to commit on this branch for this run,',
2175
- '- set "migrate": { "createCommits": true } in nx.json to always allow it, then re-run,',
2176
- '- or switch to another branch and re-run.',
2177
- ],
2178
- });
2179
- return;
2257
+ if (!defaultBranch) {
2258
+ return true;
2180
2259
  }
2181
- }
2260
+ output_1.output.log({
2261
+ title: `Not starting the run: you are on the default branch '${defaultBranch}' and nx migrate would create a commit for each migration on it.`,
2262
+ bodyLines: [
2263
+ 'Ask the user how to proceed, then either:',
2264
+ '- re-run with --create-commits to commit on this branch for this run,',
2265
+ '- set "migrate": { "createCommits": true } in nx.json to always allow it, then re-run,',
2266
+ '- or switch to another branch and re-run.',
2267
+ ],
2268
+ });
2269
+ return false;
2270
+ };
2182
2271
  const init = orchestratorInitInput(effectiveCreateCommits);
2183
- const { runOrchestratorInit } = require('./run');
2184
- await runOrchestratorInit(init);
2272
+ await runOrchestratorInit({
2273
+ ...init,
2274
+ onExistingRun: opts.startFresh === true ? 'start-fresh' : 'report',
2275
+ ...(opts.startFresh === true ? { replaceRunId: opts.runId } : {}),
2276
+ confirmStart: refuseCommitsOnDefaultBranch,
2277
+ });
2185
2278
  return;
2186
2279
  }
2187
2280
  (0, migrate_analytics_1.reportMigrateRunStart)({
@@ -2213,11 +2306,12 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2213
2306
  if (createCommitsWarning) {
2214
2307
  output_1.output.warn({ title: createCommitsWarning });
2215
2308
  }
2216
- if (effectiveCreateCommits &&
2217
- (0, safe_prompt_1.canPrompt)(opts.interactive) &&
2218
- !(await (0, migrate_commits_1.confirmMigrationCommitsOnDefaultBranch)(root, 'running migrations'))) {
2219
- return;
2220
- }
2309
+ // Asked only for a run this invocation starts: the master session hands it
2310
+ // to init, which asks once it is about to start one; a continued run keeps
2311
+ // the policy it was started with.
2312
+ const confirmNewRunCommits = async () => !effectiveCreateCommits ||
2313
+ !(0, safe_prompt_1.canPrompt)(opts.interactive) ||
2314
+ (0, migrate_commits_1.confirmMigrationCommitsOnDefaultBranch)(root, 'running migrations');
2221
2315
  // Dark: with the env var set, the agent drives the whole run through the
2222
2316
  // orchestrator from one session instead of being spawned per step. Not
2223
2317
  // under WASM, where the broker has no native lock to detect a dead parent.
@@ -2226,7 +2320,20 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2226
2320
  !native_1.IS_WASM) {
2227
2321
  const init = orchestratorInitInput(effectiveCreateCommits);
2228
2322
  const { runMasterSession } = require('./agentic/master/run-master-session');
2229
- return await runMasterSession({ ...init, agent: agentic.selectedAgent });
2323
+ return await runMasterSession({
2324
+ ...init,
2325
+ agent: agentic.selectedAgent,
2326
+ interactive: opts.interactive,
2327
+ runId: opts.runId,
2328
+ startFresh: opts.startFresh,
2329
+ confirmStart: confirmNewRunCommits,
2330
+ });
2331
+ }
2332
+ if (orchestratorFlag !== undefined) {
2333
+ throw new Error(orchestratorFlagNeedsOrchestrator(orchestratorFlag));
2334
+ }
2335
+ if (!(await confirmNewRunCommits())) {
2336
+ return;
2230
2337
  }
2231
2338
  const shouldRunValidation = resolveShouldRunValidation({
2232
2339
  validate: opts.validate,
@@ -15,6 +15,7 @@ const policy_1 = require("../../utils/min-release-age/policy");
15
15
  const pnpm_exclude_writer_1 = require("../../utils/min-release-age/pnpm-exclude-writer");
16
16
  const resolve_1 = require("../../utils/min-release-age/resolve");
17
17
  const safe_prompt_1 = require("./safe-prompt");
18
+ const text_1 = require("./text");
18
19
  /**
19
20
  * Whether nx migrate should resolve versions via the npm registry (fast) rather
20
21
  * than via a real package-manager install. Precedence, highest first:
@@ -195,7 +196,7 @@ async function handleViolation(packageName, error, policy) {
195
196
  // sorted newest-first, so the resolver's pick is the last entry. Mirror the
196
197
  // loose path: prompt for, exclude, and return that one version.
197
198
  const resolved = error.blocked[error.blocked.length - 1];
198
- const blockedLine = ` ${packageName}@${resolved.version} (published ${formatPublishAge(resolved.publishedAt)})`;
199
+ const blockedLine = ` ${packageName}@${resolved.version} (published ${(0, text_1.formatAge)(resolved.publishedAt)})`;
199
200
  // pnpm prompts once for the whole run; remember an approval for later picks.
200
201
  if (!strictApprovalGranted) {
201
202
  const approved = await (0, safe_prompt_1.migrateConfirm)({
@@ -225,21 +226,3 @@ async function handleViolation(packageName, error, policy) {
225
226
  ]);
226
227
  return resolved.version;
227
228
  }
228
- // Renders a registry ISO publish timestamp as a human age for the strict prompt
229
- // (e.g. "6 hours ago"), falling back to the raw value when it is not parseable.
230
- function formatPublishAge(publishedAt) {
231
- const elapsedMs = Date.now() - Date.parse(publishedAt);
232
- if (!Number.isFinite(elapsedMs) || elapsedMs < 0) {
233
- return publishedAt;
234
- }
235
- const minutes = Math.floor(elapsedMs / 60_000);
236
- if (minutes < 60) {
237
- return `${minutes} minute${minutes === 1 ? '' : 's'} ago`;
238
- }
239
- const hours = Math.floor(elapsedMs / 3_600_000);
240
- if (hours < 48) {
241
- return `${hours} hour${hours === 1 ? '' : 's'} ago`;
242
- }
243
- const days = Math.floor(elapsedMs / 86_400_000);
244
- return `${days} day${days === 1 ? '' : 's'} ago`;
245
- }
@@ -1,14 +1,16 @@
1
1
  import { type DeferredOutputRecord } from '../deferred-output';
2
2
  import { type CommitResult } from '../migrate-commits';
3
+ import { type GiveUpOutcome } from './give-up';
3
4
  import { type MigrateRunPolicy, type MigrateRunState, type MigrateStep, type MigrateTreeOperation } from './run-state';
4
5
  export declare const BROKER_ENV_VAR = "NX_MIGRATE_BROKER";
5
- export type BrokerRequestKind = 'commit' | 'install' | 'fold-install' | 'action-install' | 'reset';
6
+ export type BrokerRequestKind = 'commit' | 'install' | 'fold-install' | 'action-install' | 'reset' | 'give-up';
6
7
  export type InstallSeam = 'install' | 'fold-install' | 'action-install';
7
8
  export interface BrokerRequest {
8
9
  kind: BrokerRequestKind;
9
10
  stepId: string;
10
11
  attempt: number;
11
12
  invocation?: string;
13
+ commitAs?: 'adopt';
12
14
  }
13
15
  export type BrokerResult = {
14
16
  kind: 'commit';
@@ -26,6 +28,10 @@ export type BrokerResult = {
26
28
  } | {
27
29
  kind: 'reset';
28
30
  error?: string;
31
+ } | {
32
+ kind: 'give-up';
33
+ outcome: GiveUpOutcome;
34
+ output: DeferredOutputRecord[];
29
35
  } | {
30
36
  kind: 'stale';
31
37
  };
@@ -95,7 +101,7 @@ export declare function brokerDir(runDirPath: string): string;
95
101
  * collected, printed here. The absorbed step ids come from whichever side
96
102
  * ran the commit, so the ledger entry names what its `git add -A` took.
97
103
  */
98
- export declare function commitStepTree(dir: string, step: MigrateStep, absorbedStepIds: string[], commitInProcess: () => Promise<CommitResult>, scope: TreeScope): Promise<BrokeredCommit>;
104
+ export declare function commitStepTree(dir: string, step: MigrateStep, absorbedStepIds: string[], commitInProcess: () => Promise<CommitResult>, scope: TreeScope, commitAs?: 'adopt'): Promise<BrokeredCommit>;
99
105
  /**
100
106
  * The same for a step that owes only its install: no commit is due, or the
101
107
  * commit waits for a fold.
@@ -107,6 +113,12 @@ export declare function installStepTree(dir: string, step: MigrateStep, seam: In
107
113
  * against the state it reads then; a reset that could not run throws.
108
114
  */
109
115
  export declare function resetStepTree(dir: string, step: MigrateStep, resetInProcess: () => void, scope: TreeScope): Promise<void>;
116
+ /**
117
+ * The same for giving a failed or died step up while the run commits. Unlike
118
+ * the seams above, the operation settles the step itself, under the
119
+ * reservation it runs with, so the caller writes no transition of its own.
120
+ */
121
+ export declare function giveUpStepTree(root: string, dir: string, step: MigrateStep, skipInstall: boolean, reconcileCommand: string, scope: TreeScope): Promise<GiveUpOutcome>;
110
122
  /**
111
123
  * The parent side. Holds one exclusive lock for the session's lifetime so a
112
124
  * waiting step can tell a slow parent from a dead one, answers each request
@@ -18,6 +18,7 @@ exports.brokerDir = brokerDir;
18
18
  exports.commitStepTree = commitStepTree;
19
19
  exports.installStepTree = installStepTree;
20
20
  exports.resetStepTree = resetStepTree;
21
+ exports.giveUpStepTree = giveUpStepTree;
21
22
  const crypto_1 = require("crypto");
22
23
  const fs_1 = require("fs");
23
24
  const path_1 = require("path");
@@ -29,6 +30,7 @@ const deferred_output_1 = require("../deferred-output");
29
30
  const execute_migration_1 = require("../execute-migration");
30
31
  const migrate_commits_1 = require("../migrate-commits");
31
32
  const atomic_write_1 = require("./atomic-write");
33
+ const give_up_1 = require("./give-up");
32
34
  const run_state_1 = require("./run-state");
33
35
  const state_lock_1 = require("./state-lock");
34
36
  const state_machine_1 = require("./state-machine");
@@ -72,7 +74,7 @@ function acquireTreeOperation(dir, request, owner = (0, crypto_1.randomBytes)(4)
72
74
  if (held)
73
75
  throw new TreeBusyError(treeBusyMessage(held));
74
76
  marks =
75
- request.kind === 'commit' &&
77
+ (request.kind === 'commit' || request.kind === 'give-up') &&
76
78
  fresh.steps.find((s) => s.id === request.stepId)?.commitStarted !== true;
77
79
  return {
78
80
  ...(marks ? (0, state_machine_1.markCommitStarted)(fresh, request.stepId) : fresh),
@@ -115,9 +117,23 @@ function treeBusyMessage(held) {
115
117
  return `The working tree is held by process ${held.pid} for ${treeOperationLabel(held)}; run the reconcile again once it finishes.`;
116
118
  }
117
119
  function treeOperationLabel(held) {
118
- return held.kind === 'checkpoint'
119
- ? 'the checkpoint commit'
120
- : `the ${held.kind === 'commit' || held.kind === 'reset' ? held.kind : 'install'} of step '${held.stepId}'`;
120
+ switch (held.kind) {
121
+ case 'checkpoint':
122
+ return 'the checkpoint commit';
123
+ case 'commit':
124
+ case 'give-up':
125
+ return `the commit of step '${held.stepId}'`;
126
+ case 'reset':
127
+ return `the reset of step '${held.stepId}'`;
128
+ case 'install':
129
+ case 'fold-install':
130
+ case 'action-install':
131
+ return `the install of step '${held.stepId}'`;
132
+ default: {
133
+ const exhaustive = held.kind;
134
+ throw new Error(`Unhandled tree operation '${exhaustive}'.`);
135
+ }
136
+ }
121
137
  }
122
138
  function atSeam(state, request) {
123
139
  if (request.kind === 'checkpoint') {
@@ -131,16 +147,21 @@ function atSeam(state, request) {
131
147
  isAtSeam(state.steps.find((s) => s.id === request.stepId), request));
132
148
  }
133
149
  const SEAM_STATUSES = {
134
- commit: new Set(['running', 'awaiting-prompt-outcome', 'died']),
150
+ commit: new Set(['running', 'awaiting-prompt-outcome', 'failed', 'died']),
135
151
  install: new Set(['running']),
136
152
  'fold-install': new Set(['awaiting-prompt-outcome']),
137
153
  'action-install': new Set(['failed', 'died']),
138
154
  reset: new Set(['failed', 'died']),
155
+ 'give-up': new Set(['failed', 'died']),
139
156
  };
140
157
  function isAtSeam(step, request) {
141
- return (step !== undefined &&
142
- step.attempt === request.attempt &&
143
- SEAM_STATUSES[request.kind].has(step.status));
158
+ if (step === undefined || step.attempt !== request.attempt)
159
+ return false;
160
+ // A failed step commits only through its adopt.
161
+ if (request.kind === 'commit' && step.status === 'failed') {
162
+ return request.commitAs === 'adopt';
163
+ }
164
+ return SEAM_STATUSES[request.kind].has(step.status);
144
165
  }
145
166
  function brokerDir(runDirPath) {
146
167
  return (0, path_1.join)(runDirPath, BROKER_DIR_NAME);
@@ -167,18 +188,19 @@ function readRequestFile(filePath) {
167
188
  * collected, printed here. The absorbed step ids come from whichever side
168
189
  * ran the commit, so the ledger entry names what its `git add -A` took.
169
190
  */
170
- async function commitStepTree(dir, step, absorbedStepIds, commitInProcess, scope) {
191
+ async function commitStepTree(dir, step, absorbedStepIds, commitInProcess, scope, commitAs) {
171
192
  const request = {
172
193
  kind: 'commit',
173
194
  stepId: step.id,
174
195
  attempt: step.attempt,
196
+ ...(commitAs !== undefined ? { commitAs } : {}),
175
197
  };
176
198
  const nonce = process.env[exports.BROKER_ENV_VAR];
177
199
  if (!nonce) {
178
200
  const lease = acquireTreeOperation(dir, request);
179
201
  scope.lease = lease;
180
202
  const result = await commitInProcess();
181
- if (gitRan(result))
203
+ if ((0, state_machine_1.gitRan)(result))
182
204
  lease.markedStepId = undefined;
183
205
  return { result, absorbedStepIds, recorded: false };
184
206
  }
@@ -192,24 +214,6 @@ async function commitStepTree(dir, step, absorbedStepIds, commitInProcess, scope
192
214
  recorded: true,
193
215
  };
194
216
  }
195
- // Whether git may have written history for this result. A landed commit's
196
- // mark is the ledger entry's to clear, and a failure reported once git ran
197
- // (a hook's output overflowing the subprocess buffer after the commit) cannot
198
- // vouch that nothing landed, so only the other results release the mark.
199
- function gitRan(result) {
200
- switch (result.status) {
201
- case 'committed':
202
- case 'failed':
203
- return true;
204
- case 'no-changes':
205
- case 'disabled':
206
- return false;
207
- default: {
208
- const exhaustive = result;
209
- throw new Error(`Unhandled commit result: ${JSON.stringify(exhaustive)}`);
210
- }
211
- }
212
- }
213
217
  /**
214
218
  * The same for a step that owes only its install: no commit is due, or the
215
219
  * commit waits for a fold.
@@ -252,8 +256,38 @@ async function resetStepTree(dir, step, resetInProcess, scope) {
252
256
  throw new Error(`Unexpected '${answer.kind}' answer to a reset request.`);
253
257
  }
254
258
  }
259
+ /**
260
+ * The same for giving a failed or died step up while the run commits. Unlike
261
+ * the seams above, the operation settles the step itself, under the
262
+ * reservation it runs with, so the caller writes no transition of its own.
263
+ */
264
+ async function giveUpStepTree(root, dir, step, skipInstall, reconcileCommand, scope) {
265
+ const request = {
266
+ kind: 'give-up',
267
+ stepId: step.id,
268
+ attempt: step.attempt,
269
+ };
270
+ const nonce = process.env[exports.BROKER_ENV_VAR];
271
+ if (!nonce) {
272
+ const lease = acquireTreeOperation(dir, request);
273
+ scope.lease = lease;
274
+ return (0, give_up_1.giveUpWithCommit)({
275
+ root,
276
+ dir,
277
+ step,
278
+ lease,
279
+ skipInstall,
280
+ reconcileCommand,
281
+ });
282
+ }
283
+ const answer = await ask(dir, nonce, request);
284
+ if (answer.kind !== 'give-up') {
285
+ throw new Error(`Unexpected '${answer.kind}' answer to a give-up request.`);
286
+ }
287
+ return answer.outcome;
288
+ }
255
289
  async function ask(dir, nonce, request) {
256
- const id = `${nonce}-${request.stepId}-${request.attempt}-${request.kind}${request.invocation ? `-${request.invocation}` : ''}`;
290
+ const id = `${nonce}-${request.stepId}-${request.attempt}-${request.kind}${request.invocation ? `-${request.invocation}` : ''}${request.commitAs !== undefined ? `-${request.commitAs}` : ''}`;
257
291
  const path = resultPath(dir, id);
258
292
  // A repeat reads the first answer, whatever became of the session since.
259
293
  if ((0, fs_1.existsSync)(path)) {
@@ -320,6 +354,9 @@ function settle(result) {
320
354
  if (result.error !== undefined)
321
355
  throw new Error(result.error);
322
356
  return result;
357
+ case 'give-up':
358
+ (0, deferred_output_1.replayDeferredOutput)(result.output);
359
+ return result;
323
360
  case 'stale':
324
361
  throw new BrokerStaleRequestError(`The request for this step no longer matches its attempt; nothing was installed or committed.`);
325
362
  default: {
@@ -388,12 +425,12 @@ class MigrateCommitBroker {
388
425
  try {
389
426
  if (lease)
390
427
  this.inFlight = request;
391
- result = lease ? await this.answer(request) : { kind: 'stale' };
428
+ result = lease ? await this.answer(request, lease) : { kind: 'stale' };
392
429
  // Recorded by the process that ran the commit, before the answer: the
393
430
  // step reading it can die with the commit already in history. A failed
394
431
  // record throws and ends the session rather than losing the entry.
395
432
  if (result.kind === 'commit') {
396
- if (gitRan(result.result))
433
+ if ((0, state_machine_1.gitRan)(result.result))
397
434
  lease.markedStepId = undefined;
398
435
  this.record(request, result);
399
436
  }
@@ -424,15 +461,39 @@ class MigrateCommitBroker {
424
461
  };
425
462
  });
426
463
  }
427
- async answer(request) {
464
+ async answer(request, lease) {
428
465
  const state = (0, run_state_1.readRunState)(this.dir);
429
466
  const step = state.steps.find((s) => s.id === request.stepId);
430
467
  if (!Object.hasOwn(SEAM_STATUSES, request.kind) ||
431
468
  !isAtSeam(step, request) ||
432
- ((request.kind === 'commit' || request.kind === 'reset') &&
433
- !this.policy.createCommits)) {
469
+ ((request.kind === 'commit' ||
470
+ request.kind === 'reset' ||
471
+ request.kind === 'give-up') &&
472
+ !this.policy.createCommits) ||
473
+ (request.commitAs !== undefined && request.commitAs !== 'adopt')) {
434
474
  return { kind: 'stale' };
435
475
  }
476
+ if (request.kind === 'give-up') {
477
+ const output = new deferred_output_1.DeferredOutputCollector();
478
+ const outcome = await (0, give_up_1.giveUpWithCommit)({
479
+ root: this.root,
480
+ dir: this.dir,
481
+ step,
482
+ lease,
483
+ skipInstall: this.policy.skipInstall,
484
+ reconcileCommand: this.reconcileCommand,
485
+ output,
486
+ });
487
+ // Raw package-manager output only for a failed install, as for a commit.
488
+ return {
489
+ kind: 'give-up',
490
+ outcome,
491
+ output: output.render(outcome.kind === 'given-up' &&
492
+ outcome.commit.status === 'install-failed'
493
+ ? 'keep'
494
+ : 'drop'),
495
+ };
496
+ }
436
497
  if (request.kind === 'reset') {
437
498
  try {
438
499
  (0, clean_retry_1.resetForCleanRetry)(this.root, this.dir, step.id);