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
@@ -2,6 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.applyStepEvent = applyStepEvent;
4
4
  exports.discardGeneratorRun = discardGeneratorRun;
5
+ exports.tallySteps = tallySteps;
6
+ exports.runTallies = runTallies;
7
+ exports.unresolvedFailureDetail = unresolvedFailureDetail;
8
+ exports.completionSummaryLines = completionSummaryLines;
9
+ exports.unresolvedCommitName = unresolvedCommitName;
5
10
  exports.markInstallFailed = markInstallFailed;
6
11
  exports.uncoveredFailedStepIds = uncoveredFailedStepIds;
7
12
  exports.hasPendingCommitDebt = hasPendingCommitDebt;
@@ -14,7 +19,9 @@ exports.commitReceipt = commitReceipt;
14
19
  exports.latestRound = latestRound;
15
20
  exports.splitMigrationId = splitMigrationId;
16
21
  exports.stepsToPendingMigrations = stepsToPendingMigrations;
22
+ exports.gitRan = gitRan;
17
23
  exports.commitResultToLedgerEntry = commitResultToLedgerEntry;
24
+ const text_1 = require("../text");
18
25
  const PROMPT_OUTCOME_TO_STEP_STATUS = {
19
26
  completed: 'succeeded',
20
27
  skipped: 'skipped',
@@ -130,14 +137,20 @@ function applyStepAction(state, index, step, action) {
130
137
  // exec side effect, or a crash mid-flush), so a reset-backed retry is
131
138
  // offered under the same guard as for a death.
132
139
  return commit(state, index, cleanRearm(state, step));
140
+ case 'adopt':
141
+ return commit(state, index, adopt(step));
133
142
  case 'skip':
143
+ case 'unresolved':
134
144
  if (commitMayBeInHistory(state, step)) {
135
145
  return {
136
146
  kind: 'error',
137
- reason: `Cannot apply action 'skip' to step '${step.id}': a commit of its changes landed or was started and never recorded, so the migration may be committed. Use 'retry' to finish it.`,
147
+ reason: `Cannot apply action '${action}' to step '${step.id}': a commit of its changes landed or was started and never recorded, so the migration may be committed. Use 'adopt' to record it as applied once the migration is finished.`,
138
148
  };
139
149
  }
140
- return commit(state, index, { ...step, status: 'skipped' });
150
+ return commit(state, index, {
151
+ ...step,
152
+ status: action === 'skip' ? 'skipped' : 'unresolved',
153
+ });
141
154
  }
142
155
  }
143
156
  if (step.status === 'died') {
@@ -155,16 +168,12 @@ function applyStepAction(state, index, step, action) {
155
168
  kind: 'error',
156
169
  reason: `Cannot apply action 'retry' to step '${step.id}': the worker died before recording that its generator ran, so keeping the current tree could apply the migration twice. Use ${commitMayBeInHistory(state, step)
157
170
  ? `'retry-clean' where offered, or 'adopt'`
158
- : `'retry-clean', 'adopt' or 'skip'`} instead.`,
171
+ : `'retry-clean', 'adopt', 'skip' or 'unresolved'`} instead.`,
159
172
  };
160
173
  case 'retry-clean':
161
174
  return commit(state, index, cleanRearm(state, step));
162
175
  case 'adopt':
163
- return commit(state, index, {
164
- ...step,
165
- status: 'succeeded',
166
- outcome: { ...step.outcome, summary: adoptedSummary(step) },
167
- });
176
+ return commit(state, index, adopt(step));
168
177
  case 'skip': {
169
178
  if (coveringLandedEntries(state, step.id).length > 0) {
170
179
  return {
@@ -181,6 +190,20 @@ function applyStepAction(state, index, step, action) {
181
190
  // Same as skipping a failure: the tree stays as the worker left it.
182
191
  return commit(state, index, { ...step, status: 'skipped' });
183
192
  }
193
+ case 'unresolved':
194
+ if (commitMayBeInHistory(state, step)) {
195
+ return {
196
+ kind: 'error',
197
+ reason: `Cannot apply action 'unresolved' to step '${step.id}': a commit of its changes landed or was started and never recorded, so the migration may be committed. Use 'adopt' to record it as applied once the migration is finished.`,
198
+ };
199
+ }
200
+ // A death records no outcome, so the failure given up on is the death
201
+ // itself.
202
+ return commit(state, index, {
203
+ ...step,
204
+ status: 'unresolved',
205
+ outcome: { summary: workerDiedSummary(step) },
206
+ });
184
207
  }
185
208
  }
186
209
  return {
@@ -188,6 +211,9 @@ function applyStepAction(state, index, step, action) {
188
211
  reason: `Cannot apply action '${action}' to step '${step.id}' in status '${step.status}'.`,
189
212
  };
190
213
  }
214
+ function workerDiedSummary(step) {
215
+ return `the worker process${step.pid === undefined ? '' : ` (pid ${step.pid})`} died before recording an outcome`;
216
+ }
191
217
  // Re-arms for a retry that resets the tree first. The reset target predates
192
218
  // the generator unless a commit of this step's own already carries it, so the
193
219
  // marker only survives when such a commit exists; otherwise the reset discards
@@ -195,9 +221,18 @@ function applyStepAction(state, index, step, action) {
195
221
  function cleanRearm(state, step) {
196
222
  return rearm(step, coveringLandedEntries(state, step.id).length > 0);
197
223
  }
198
- // An adopted death records how far the worker got, since 'succeeded' alone
199
- // says the migration was applied and cannot say by what.
224
+ function adopt(step) {
225
+ return {
226
+ ...step,
227
+ status: 'succeeded',
228
+ adopted: true,
229
+ outcome: { ...step.outcome, summary: adoptedSummary(step) },
230
+ };
231
+ }
200
232
  function adoptedSummary(step) {
233
+ if (step.status === 'failed') {
234
+ return "Adopted after the attempt failed: the working tree as it stood was taken as this migration's result.";
235
+ }
201
236
  return step.generatorCompleted === true
202
237
  ? "Adopted after the worker died: its generator had run, and the working tree it left was taken as this migration's result."
203
238
  : "Adopted after the worker died before recording that its generator had run; the working tree it left was taken as this migration's result.";
@@ -264,6 +299,80 @@ function discardGeneratorRun(state, stepId) {
264
299
  }),
265
300
  };
266
301
  }
302
+ function tallySteps(state) {
303
+ const tally = {
304
+ applied: 0,
305
+ adopted: 0,
306
+ skipped: 0,
307
+ unresolved: [],
308
+ remaining: 0,
309
+ stalled: 0,
310
+ };
311
+ for (const step of state.steps) {
312
+ switch (step.status) {
313
+ case 'succeeded':
314
+ if (step.adopted)
315
+ tally.adopted++;
316
+ else
317
+ tally.applied++;
318
+ break;
319
+ case 'skipped':
320
+ tally.skipped++;
321
+ break;
322
+ case 'unresolved':
323
+ tally.unresolved.push(step);
324
+ break;
325
+ case 'failed':
326
+ case 'died':
327
+ tally.stalled++;
328
+ tally.remaining++;
329
+ break;
330
+ case 'pending':
331
+ case 'dispensed':
332
+ case 'running':
333
+ case 'awaiting-prompt-outcome':
334
+ tally.remaining++;
335
+ break;
336
+ default: {
337
+ const exhaustive = step.status;
338
+ throw new Error(`Unhandled step status '${exhaustive}'.`);
339
+ }
340
+ }
341
+ }
342
+ return tally;
343
+ }
344
+ function runTallies(state) {
345
+ const tally = tallySteps(state);
346
+ return {
347
+ completed: tally.applied + tally.adopted,
348
+ skipped: tally.skipped,
349
+ unresolved: tally.unresolved.length,
350
+ dispenseCount: state.steps.reduce((n, s) => n + s.dispenseCount, 0),
351
+ };
352
+ }
353
+ // The failure a given-up step is reported with, in the issue ledger and the
354
+ // completion report alike. Agent text is collapsed to one line so a break
355
+ // inside it cannot open a block at a line start; an accepted handoff may carry
356
+ // an empty summary, which gets the fallback.
357
+ function unresolvedFailureDetail(step) {
358
+ const failure = (0, text_1.singleLine)(step.outcome?.summary ?? step.promptOutcome?.summary ?? '').trim();
359
+ return failure.length === 0 ? 'no failure detail was recorded' : failure;
360
+ }
361
+ function completionSummaryLines(state) {
362
+ const tally = tallySteps(state);
363
+ return [
364
+ ` applied: ${tally.applied}`,
365
+ ` adopted: ${tally.adopted}`,
366
+ ` skipped: ${tally.skipped}`,
367
+ ` unresolved: ${tally.unresolved.length}`,
368
+ ...tally.unresolved.map((step) => ` - ${step.migrationId}: ${unresolvedFailureDetail(step)}`),
369
+ ];
370
+ }
371
+ // Suffixed so history does not read the partial result as the migration
372
+ // applied.
373
+ function unresolvedCommitName(step) {
374
+ return `${splitMigrationId(step.migrationId).name} (unresolved)`;
375
+ }
267
376
  // A guarded transition whose observation was made against an earlier attempt
268
377
  // of the same step: the status recurred, so the observation says nothing about
269
378
  // the attempt on disk now.
@@ -405,6 +514,24 @@ function stepsToPendingMigrations(state, stepIds) {
405
514
  }
406
515
  return pending;
407
516
  }
517
+ // Whether git may have written history for this result. A landed commit's
518
+ // mark is the ledger entry's to clear, and a failure reported once git ran
519
+ // (a hook's output overflowing the subprocess buffer after the commit) cannot
520
+ // vouch that nothing landed, so only the other results release the mark.
521
+ function gitRan(result) {
522
+ switch (result.status) {
523
+ case 'committed':
524
+ case 'failed':
525
+ return true;
526
+ case 'no-changes':
527
+ case 'disabled':
528
+ return false;
529
+ default: {
530
+ const exhaustive = result;
531
+ throw new Error(`Unhandled commit result: ${JSON.stringify(exhaustive)}`);
532
+ }
533
+ }
534
+ }
408
535
  /**
409
536
  * Classifies a commit attempt into the ledger entry to record, or null when
410
537
  * there is nothing to record ('no-changes' / 'disabled'). A landed entry
@@ -57,4 +57,5 @@ export declare function isPidAlive(pid: number): boolean;
57
57
  export declare function summarizeError(e: unknown): string;
58
58
  export declare function warnCommitFailed(name: string, cause?: unknown): void;
59
59
  export declare function pmExecPrefix(root: string): string;
60
+ export declare function runMigrationsFlag(migrationsPath: string): string | null;
60
61
  export declare function pmInstallCommand(root: string): string;
@@ -9,12 +9,15 @@ exports.isPidAlive = isPidAlive;
9
9
  exports.summarizeError = summarizeError;
10
10
  exports.warnCommitFailed = warnCommitFailed;
11
11
  exports.pmExecPrefix = pmExecPrefix;
12
+ exports.runMigrationsFlag = runMigrationsFlag;
12
13
  exports.pmInstallCommand = pmInstallCommand;
13
14
  const crypto_1 = require("crypto");
15
+ const shell_quoting_1 = require("../../../utils/shell-quoting");
14
16
  const package_manager_1 = require("../../../utils/package-manager");
15
17
  const execute_migration_1 = require("../execute-migration");
16
18
  const state_lock_1 = require("./state-lock");
17
19
  const agent_output_1 = require("./agent-output");
20
+ const text_1 = require("../text");
18
21
  function nowIso() {
19
22
  return new Date().toISOString();
20
23
  }
@@ -143,6 +146,24 @@ function pmCommands(root) {
143
146
  function pmExecPrefix(root) {
144
147
  return pmCommands(root).exec;
145
148
  }
149
+ const DEFAULT_MIGRATIONS_FILE = 'migrations.json';
150
+ // The --run-migrations flag a rendered command repeats; null when the path
151
+ // cannot go out as a one-line command for this shell. On Windows the paste
152
+ // target may be cmd.exe or PowerShell, whose quoting and expansion conflict
153
+ // (`%VAR%` expands in cmd, `$(...)` runs in PowerShell).
154
+ function runMigrationsFlag(migrationsPath) {
155
+ if (migrationsPath === DEFAULT_MIGRATIONS_FILE) {
156
+ return '--run-migrations';
157
+ }
158
+ if ((0, text_1.hasLineBreak)(migrationsPath)) {
159
+ return null;
160
+ }
161
+ const arg = `--run-migrations=${migrationsPath}`;
162
+ if (process.platform !== 'win32') {
163
+ return (0, shell_quoting_1.quoteShellArg)(arg);
164
+ }
165
+ return !(0, shell_quoting_1.needsShellQuoting)(arg) && !/[%^]/.test(arg) ? arg : null;
166
+ }
146
167
  function pmInstallCommand(root) {
147
168
  return pmCommands(root).install;
148
169
  }
@@ -105,6 +105,9 @@ function readMigrationsSource(root, runId) {
105
105
  if (!(0, run_state_1.hasRunState)(dir)) {
106
106
  throw new Error(`No migrate run '${runId}' was found under ${types_1.MIGRATE_RUNS_RELATIVE_DIR}.`);
107
107
  }
108
+ // Before the state and plan reads: a concurrent start-fresh must not
109
+ // delete the run while this worker resolves its migration.
110
+ (0, state_lock_1.holdRunActivity)(root, runId);
108
111
  // Version refusal (NewerRunStateFormatError) propagates.
109
112
  const state = (0, run_state_1.readRunState)(dir);
110
113
  const round = (0, state_machine_1.latestRound)(state);
@@ -1,5 +1,5 @@
1
1
  /** Allowed values for `--step-action`, the orchestrated reconcile's decision relay. */
2
- export declare const STEP_ACTIONS: readonly ['retry', 'skip', 'retry-clean', 'adopt'];
2
+ export declare const STEP_ACTIONS: readonly ['retry', 'skip', 'retry-clean', 'adopt', 'unresolved'];
3
3
  export type StepAction = (typeof STEP_ACTIONS)[number];
4
4
  /** Runtime guard backed by {@link STEP_ACTIONS} so the two can't drift apart. */
5
5
  export declare function isStepAction(value: unknown): value is StepAction;
@@ -5,7 +5,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.STEP_ACTIONS = void 0;
6
6
  exports.isStepAction = isStepAction;
7
7
  /** Allowed values for `--step-action`, the orchestrated reconcile's decision relay. */
8
- exports.STEP_ACTIONS = ['retry', 'skip', 'retry-clean', 'adopt'];
8
+ exports.STEP_ACTIONS = [
9
+ 'retry',
10
+ 'skip',
11
+ 'retry-clean',
12
+ 'adopt',
13
+ 'unresolved',
14
+ ];
9
15
  /** Runtime guard backed by {@link STEP_ACTIONS} so the two can't drift apart. */
10
16
  function isStepAction(value) {
11
17
  return exports.STEP_ACTIONS.includes(value);
@@ -16,3 +16,9 @@
16
16
  * real terminator can put a value at the start of a line.
17
17
  */
18
18
  export declare function singleLine(value: string): string;
19
+ export declare function hasLineBreak(value: string): boolean;
20
+ /**
21
+ * Renders an ISO timestamp as a human age ("6 hours ago"), falling back to
22
+ * the raw value when it is not parseable or lies in the future.
23
+ */
24
+ export declare function formatAge(timestamp: string): string;
@@ -4,6 +4,8 @@
4
4
  // run/util.ts without a cycle through run/state-lock.ts.
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.singleLine = singleLine;
7
+ exports.hasLineBreak = hasLineBreak;
8
+ exports.formatAge = formatAge;
7
9
  /**
8
10
  * Collapses every character a reader could take for a line break, so a value
9
11
  * cannot break out of the line it is rendered on. The driving agent scans the
@@ -24,3 +26,26 @@ exports.singleLine = singleLine;
24
26
  function singleLine(value) {
25
27
  return value.replace(/[\r\n\u000b\u000c\u0085\u2028\u2029]+/g, ' ');
26
28
  }
29
+ function hasLineBreak(value) {
30
+ return singleLine(value) !== value;
31
+ }
32
+ /**
33
+ * Renders an ISO timestamp as a human age ("6 hours ago"), falling back to
34
+ * the raw value when it is not parseable or lies in the future.
35
+ */
36
+ function formatAge(timestamp) {
37
+ const elapsedMs = Date.now() - Date.parse(timestamp);
38
+ if (!Number.isFinite(elapsedMs) || elapsedMs < 0) {
39
+ return timestamp;
40
+ }
41
+ const minutes = Math.floor(elapsedMs / 60_000);
42
+ if (minutes < 60) {
43
+ return `${minutes} minute${minutes === 1 ? '' : 's'} ago`;
44
+ }
45
+ const hours = Math.floor(elapsedMs / 3_600_000);
46
+ if (hours < 48) {
47
+ return `${hours} hour${hours === 1 ? '' : 's'} ago`;
48
+ }
49
+ const days = Math.floor(elapsedMs / 86_400_000);
50
+ return `${days} day${days === 1 ? '' : 's'} ago`;
51
+ }
@@ -437,7 +437,7 @@ class ReleaseGraph {
437
437
  if (!releaseGroup ||
438
438
  !this.projectsToVersionActions.has(projectName) ||
439
439
  !this.finalConfigsByProject.has(projectName)) {
440
- throw new Error(`Cannot resolve a concrete current version for dependency project "${projectName}" because it is not configured for Nx Release. Include the project in an Nx Release group so that its current version resolver can be configured.`);
440
+ throw Object.assign(new Error(`Cannot resolve a concrete current version for dependency project "${projectName}" because it is not configured for Nx Release. Include the project in an Nx Release group so that its current version resolver can be configured.`), { code: 'NX_RELEASE_PROJECT_NOT_CONFIGURED' });
441
441
  }
442
442
  let currentVersion;
443
443
  try {
@@ -11,8 +11,7 @@ const output_1 = require("../../../../utils/output");
11
11
  const path_1 = require("../../../../utils/path");
12
12
  const extract_repo_slug_1 = require("./extract-repo-slug");
13
13
  const remote_release_client_1 = require("./remote-release-client");
14
- // Use default import with esModuleInterop
15
- const axios_1 = tslib_1.__importDefault(require("axios"));
14
+ const http_client_1 = require("../../../../utils/http-client");
16
15
  exports.defaultCreateReleaseProvider = {
17
16
  provider: 'github',
18
17
  hostname: 'github.com',
@@ -117,9 +116,7 @@ class GithubRemoteReleaseClient extends remote_release_client_1.RemoteReleaseCli
117
116
  break;
118
117
  }
119
118
  }
120
- const { data } = await axios_1.default
121
- .get(`https://ungh.cc/users/find/${email}`)
122
- .catch(() => ({ data: { user: null } }));
119
+ const { data } = await (0, http_client_1.httpRequest)(`https://ungh.cc/users/find/${email}`).catch(() => ({ data: { user: null } }));
123
120
  if (data?.user?.username) {
124
121
  meta.username = data.user.username;
125
122
  break;
@@ -1,7 +1,7 @@
1
- import type { AxiosRequestConfig } from 'axios';
2
1
  import type { PostGitTask } from '../../changelog';
3
2
  import { ResolvedCreateRemoteReleaseProvider } from '../../config/config';
4
3
  import type { Reference } from '../git';
4
+ import { type HttpRequestConfig } from '../../../../utils/http-client';
5
5
  import { type ReleaseVersion } from '../shared';
6
6
  import type { GithubRemoteReleaseClient } from './github';
7
7
  import type { GitLabRemoteReleaseClient } from './gitlab';
@@ -62,7 +62,7 @@ export declare abstract class RemoteReleaseClient<RemoteRelease extends Record<s
62
62
  /**
63
63
  * Make an (optionally authenticated) API request to the remote release provider
64
64
  */
65
- protected makeRequest(url: string, opts?: AxiosRequestConfig): Promise<any>;
65
+ protected makeRequest(url: string, opts?: HttpRequestConfig): Promise<any>;
66
66
  createOrUpdateRelease(releaseVersion: ReleaseVersion, changelogContents: string, latestCommit: string, { dryRun }: {
67
67
  dryRun: boolean;
68
68
  }): Promise<void>;
@@ -2,10 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RemoteReleaseClient = void 0;
4
4
  exports.createRemoteReleaseClient = createRemoteReleaseClient;
5
- const tslib_1 = require("tslib");
6
5
  const node_util_1 = require("node:util");
7
- const axios_1 = tslib_1.__importDefault(require("axios"));
8
6
  const handle_import_1 = require("../../../../utils/handle-import");
7
+ const http_client_1 = require("../../../../utils/http-client");
9
8
  const print_changes_1 = require("../print-changes");
10
9
  const shared_1 = require("../shared");
11
10
  /**
@@ -33,10 +32,10 @@ class RemoteReleaseClient {
33
32
  }
34
33
  inspectWithRedactedToken(error) {
35
34
  const inspected = (0, node_util_1.inspect)(error);
36
- // The dump can hold a different string than the raw token: axios trims
35
+ // The dump can hold a different string than the raw token: fetch trims
37
36
  // header values and inspect() escapes the result, while message/stack keep
38
37
  // it bare. Redact every rendering; the Set collapses them for an ordinary
39
- // token. The CR/LF needle is belt-and-braces — node rejects such headers.
38
+ // token. The CR/LF needle is belt-and-braces - fetch rejects such headers.
40
39
  const token = this.tokenData?.token?.trim();
41
40
  if (!token) {
42
41
  return inspected;
@@ -81,7 +80,7 @@ class RemoteReleaseClient {
81
80
  ...this.tokenHeader,
82
81
  },
83
82
  };
84
- return (await (0, axios_1.default)(url, config)).data;
83
+ return (await (0, http_client_1.httpRequest)(url, config)).data;
85
84
  }
86
85
  async createOrUpdateRelease(releaseVersion, changelogContents, latestCommit, { dryRun }) {
87
86
  let existingRelease;
@@ -15,6 +15,9 @@ export type SemverBumpType = ReleaseType | 'none';
15
15
  * when it is part of the release, or its current version when it is not being
16
16
  * versioned.
17
17
  *
18
+ * When the project is not configured for Nx Release, the returned promise
19
+ * rejects with an error whose `code` is `NX_RELEASE_PROJECT_NOT_CONFIGURED`.
20
+ *
18
21
  * @public
19
22
  */
20
23
  export type ResolveVersionForDependency = (dependencyProjectName: string) => Promise<string>;
@@ -12,6 +12,7 @@ const client_2 = require("../../nx-cloud/utilities/client");
12
12
  const get_cloud_options_1 = require("../../nx-cloud/utilities/get-cloud-options");
13
13
  const nx_cloud_utils_1 = require("../../utils/nx-cloud-utils");
14
14
  const configuration_1 = require("../../config/configuration");
15
+ const db_connection_1 = require("../../utils/db-connection");
15
16
  const update_manager_1 = require("../../nx-cloud/update-manager");
16
17
  // Wait at max 5 seconds before giving up on a failing operation.
17
18
  const INCREMENTAL_BACKOFF_MAX_DURATION = 5000;
@@ -82,6 +83,11 @@ async function resetHandler(args) {
82
83
  if ((cloudEnabled && all) || args.onlyCloud) {
83
84
  try {
84
85
  await resetCloudClient();
86
+ }
87
+ catch (e) {
88
+ errors.push('Failed to clean up the Nx Cloud client.', e.toString());
89
+ }
90
+ try {
85
91
  await removeInstalledNxCloudClient();
86
92
  }
87
93
  catch (e) {
@@ -117,10 +123,8 @@ function cleanupDaemonWorkspaceData() {
117
123
  async function resetCloudClient() {
118
124
  // Remove nx cloud marker files. This helps if the use happens to run `nx-cloud start-ci-run` or
119
125
  // similar commands on their local machine.
120
- try {
121
- (await (0, client_2.getCloudClient)((0, get_cloud_options_1.getCloudOptions)())).invoke('cleanup');
122
- }
123
- catch { }
126
+ // `exit` would end the process here, before reset reports its result.
127
+ await (await (0, client_2.getCloudClient)((0, get_cloud_options_1.getCloudOptions)())).invoke('cleanup', false);
124
128
  }
125
129
  function removeInstalledNxCloudClient() {
126
130
  return incrementalBackoff(INCREMENTAL_BACKOFF_FIRST_DELAY, INCREMENTAL_BACKOFF_MAX_DURATION, () => {
@@ -155,6 +159,8 @@ function cleanupNativeFileCache() {
155
159
  }
156
160
  function cleanupWorkspaceData() {
157
161
  return incrementalBackoff(INCREMENTAL_BACKOFF_FIRST_DELAY, INCREMENTAL_BACKOFF_MAX_DURATION, () => {
162
+ // Analytics opened the DB at startup; Windows cannot delete an open file.
163
+ (0, db_connection_1.removeDbConnections)();
158
164
  (0, node_fs_1.rmSync)(cache_directory_1.workspaceDataDirectory, { recursive: true, force: true });
159
165
  // Also clean wherever the DB actually lives, which is outside this
160
166
  // checkout whenever the shared root is reachable. Resolved through the
@@ -37,7 +37,7 @@ exports.yargsShowCommand = {
37
37
  const showProjectsCommand = {
38
38
  command: 'projects',
39
39
  describe: 'Show a list of projects in the workspace.',
40
- builder: (yargs) => (0, shared_options_1.withVerbose)((0, shared_options_1.withAffectedOptions)(yargs))
40
+ builder: (yargs) => (0, shared_options_1.withVerbose)((0, shared_options_1.withExplainOption)((0, shared_options_1.withAffectedOptions)(yargs)))
41
41
  .option('affected', {
42
42
  type: 'boolean',
43
43
  description: 'Show only affected projects.',
@@ -5,6 +5,8 @@ const output_1 = require("../../utils/output");
5
5
  const nx_json_1 = require("../../config/nx-json");
6
6
  const affected_project_graph_1 = require("../../project-graph/affected/affected-project-graph");
7
7
  const affected_tasks_1 = require("../../project-graph/affected/affected-tasks");
8
+ const print_explanation_1 = require("../../project-graph/affected/print-explanation");
9
+ const affected_reasons_1 = require("../../project-graph/affected/affected-reasons");
8
10
  const file_utils_1 = require("../../project-graph/file-utils");
9
11
  const operators_1 = require("../../project-graph/operators");
10
12
  const project_graph_1 = require("../../project-graph/project-graph");
@@ -18,6 +20,17 @@ async function showProjectsHandler(args) {
18
20
  const { nxArgs } = (0, command_line_utils_1.splitArgsIntoNxArgsAndOverrides)(args, 'affected', {
19
21
  printWarnings: false,
20
22
  }, nxJson);
23
+ const explainsTasks = args.affected &&
24
+ (0, affected_tasks_1.selectsAffectedTasks)() &&
25
+ !!args.withTarget?.length &&
26
+ !args.projects;
27
+ if ((0, affected_reasons_1.isExplaining)(nxArgs.explain) && !explainsTasks) {
28
+ throw new Error((0, affected_reasons_1.explainUnavailable)([
29
+ !(0, affected_tasks_1.selectsAffectedTasks)() && 'NX_LEGACY_AFFECTED=false',
30
+ !args.affected && '--affected',
31
+ !args.withTarget?.length && 'targets passed with --withTarget (-t)',
32
+ ].filter(Boolean), args.projects ? ['--projects'] : []));
33
+ }
21
34
  // Affected touches dependencies so it needs to be processed first.
22
35
  if (args.affected) {
23
36
  const touchedFiles = await getTouchedFiles(nxArgs);
@@ -33,8 +46,17 @@ async function showProjectsHandler(args) {
33
46
  head: nxArgs.head,
34
47
  files: nxArgs.files,
35
48
  },
49
+ explain: (0, affected_reasons_1.isExplaining)(nxArgs.explain),
36
50
  ...(await runCommandModule().runnerInputsForSelection(nxArgs, nxJson)),
37
51
  });
52
+ if ((0, affected_reasons_1.isExplaining)(nxArgs.explain)) {
53
+ (0, print_explanation_1.printAffectedExplanation)(affectedTasks.explanation, 'Affected tasks',
54
+ // show projects declares its own --json, which has no executor to
55
+ // pass through to.
56
+ args.json ? 'stdout' : nxArgs.explain, { verbose: args.verbose });
57
+ await output_1.output.drain();
58
+ return;
59
+ }
38
60
  const { taskGraph, initiatingTaskIds } = affectedTasks.taskSelection;
39
61
  const owning = new Set(initiatingTaskIds.map((id) => taskGraph.tasks[id].target.project));
40
62
  // The graph selection ran against: with the daemon on, the daemon's, not `graph`.
@@ -54,6 +54,10 @@ export declare function withAffectedOptions(yargs: Argv): Argv<ExcludeOptions &
54
54
  } & {
55
55
  head: string;
56
56
  }>;
57
+ /** Only on the commands that report instead of running when it is passed. */
58
+ export declare function withExplainOption<T>(yargs: Argv<T>): Argv<T & {
59
+ explain?: string | boolean;
60
+ }>;
57
61
  export interface RunManyOptions extends RunOptions {
58
62
  projects: string[];
59
63
  /**
@@ -9,6 +9,7 @@ exports.withConfiguration = withConfiguration;
9
9
  exports.withVerbose = withVerbose;
10
10
  exports.withBatch = withBatch;
11
11
  exports.withAffectedOptions = withAffectedOptions;
12
+ exports.withExplainOption = withExplainOption;
12
13
  exports.withRunManyOptions = withRunManyOptions;
13
14
  exports.withOverrides = withOverrides;
14
15
  exports.withOutputStyleOption = withOutputStyleOption;
@@ -244,6 +245,21 @@ function withAffectedOptions(yargs) {
244
245
  }
245
246
  }, true);
246
247
  }
248
+ /** Only on the commands that report instead of running when it is passed. */
249
+ function withExplainOption(yargs) {
250
+ return yargs.option('explain', {
251
+ type: 'string',
252
+ describe: 'Print why each task was considered affected, instead of running anything. Needs task selection (NX_LEGACY_AFFECTED=false). Pass a file path to save the reasons as JSON instead. Pass "stdout" to print the JSON to the terminal.',
253
+ // Same shape as `--graph`: the value is a destination, and a bare
254
+ // `--explain` has to arrive as a boolean. `nx affected` leaves `--json`
255
+ // undeclared so it still reaches the executor.
256
+ coerce: (value) => value === '' || value === 'true' || value === true
257
+ ? true
258
+ : value === 'false' || value === false
259
+ ? false
260
+ : value,
261
+ });
262
+ }
247
263
  function withRunManyOptions(yargs) {
248
264
  return withRunOptions(yargs)
249
265
  .parserConfiguration(exports.defaultYargsParserConfiguration)
@@ -3,4 +3,4 @@
3
3
  * `packages/nx/src/native/tasks/types.rs` and exposed to TypeScript via NAPI.
4
4
  * This file re-exports them so existing imports keep working.
5
5
  */
6
- export type { Task, TaskGraph, TaskTarget, TaskHashDetails, TaskUltracacheConfiguration, } from '../native';
6
+ export type { Task, TaskGraph, TaskTarget, TaskHashDetails, TaskUltracacheSettings, } from '../native';
@@ -1,4 +1,4 @@
1
- import type { JsonInput, TaskUltracacheConfiguration } from '../native';
1
+ import type { JsonInput, TaskUltracacheSettings } from '../native';
2
2
  import type { PackageJson } from '../utils/package-json';
3
3
  import type { NxJsonConfiguration, NxReleaseDockerConfiguration, NxReleaseVersionConfiguration } from './nx-json';
4
4
  /**
@@ -156,13 +156,13 @@ export type Spreadable<T> = T & {
156
156
  '...'?: true;
157
157
  };
158
158
  /**
159
- * Ultracache configuration for a target's tasks.
159
+ * Ultracache settings for a target's tasks.
160
160
  *
161
161
  * The resolved shape rides on each Task instance (`Task['ultracache']`), which
162
162
  * is why it is shared with the native task definition. Authoring additionally
163
163
  * permits `'...'`, which target merging resolves away.
164
164
  */
165
- export type TargetUltracacheConfiguration = Spreadable<TaskUltracacheConfiguration>;
165
+ export type TargetUltracacheSettings = Spreadable<TaskUltracacheSettings>;
166
166
  export interface TargetDependencyConfig {
167
167
  /**
168
168
  * A list of projects that have `target`.
@@ -263,7 +263,7 @@ export interface TargetConfiguration<T = any> {
263
263
  /**
264
264
  * Configures ultracache for tasks of this target. Nx Cloud only.
265
265
  */
266
- ultracache?: TargetUltracacheConfiguration;
266
+ ultracache?: TargetUltracacheSettings;
267
267
  /**
268
268
  * Metadata about the target
269
269
  */
@@ -1,7 +1,7 @@
1
1
  import { ChildProcess } from 'child_process';
2
2
  import { FileData, ProjectGraph } from '../../config/project-graph';
3
3
  import { Task, TaskGraph } from '../../config/task-graph';
4
- import type { IoSnapshotVersion } from '../message-types/io-snapshot-version';
4
+ import type { UltracacheConfigurationVersion } from '../message-types/ultracache-configuration-version';
5
5
  import { Hash } from '../../hasher/task-hasher';
6
6
  import { NxWorkspaceFiles, TaskRun, TaskTarget } from '../../native';
7
7
  import { PreTasksExecutionContext } from '../../project-graph/plugins/public-api';
@@ -61,8 +61,8 @@ export declare class DaemonClient {
61
61
  sourceMaps: ConfigurationSourceMaps;
62
62
  }>;
63
63
  getAllFileData(): Promise<FileData[]>;
64
- hashTasks(runnerOptions: any, tasks: Task[], taskGraph: TaskGraph, perTaskEnvs: Record<string, NodeJS.ProcessEnv>, cwd: string, collectInputs?: boolean, ioSnapshots?: IoSnapshotVersion): Promise<Hash[]>;
65
- hashTasksUpfront(runnerOptions: any, tasks: Task[], taskGraph: TaskGraph, perTaskEnvs: Record<string, NodeJS.ProcessEnv>, cwd: string, collectInputs?: boolean, ioSnapshots?: IoSnapshotVersion): Promise<Record<string, Hash>>;
64
+ hashTasks(runnerOptions: any, tasks: Task[], taskGraph: TaskGraph, perTaskEnvs: Record<string, NodeJS.ProcessEnv>, cwd: string, collectInputs?: boolean, ultracacheConfigurationsVersion?: UltracacheConfigurationVersion): Promise<Hash[]>;
65
+ hashTasksUpfront(runnerOptions: any, tasks: Task[], taskGraph: TaskGraph, perTaskEnvs: Record<string, NodeJS.ProcessEnv>, cwd: string, collectInputs?: boolean, ultracacheConfigurationsVersion?: UltracacheConfigurationVersion): Promise<Record<string, Hash>>;
66
66
  registerFileWatcher(config: {
67
67
  watchProjects: string[] | 'all';
68
68
  includeGlobalWorkspaceFiles?: boolean;