nx 23.0.0-beta.20 → 23.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +3 -11
- package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.js +3 -11
- package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.js +5 -8
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.d.ts +9 -0
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.js +43 -0
- package/dist/src/command-line/migrate/agentic/runner.js +3 -0
- package/dist/src/command-line/migrate/migrate-output.d.ts +70 -11
- package/dist/src/command-line/migrate/migrate-output.js +52 -35
- package/dist/src/command-line/migrate/migrate.js +83 -80
- package/dist/src/command-line/migrate/run-migration-process.js +9 -1
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/utils/git-utils.d.ts +1 -0
- package/dist/src/utils/git-utils.js +65 -0
- package/package.json +12 -12
|
@@ -1638,20 +1638,13 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1638
1638
|
// render them distinctly per resolution mode.
|
|
1639
1639
|
const migrationEmittedNextSteps = [];
|
|
1640
1640
|
const skippedPrompts = [];
|
|
1641
|
-
// One record per migration
|
|
1642
|
-
//
|
|
1643
|
-
//
|
|
1641
|
+
// One record per migration the loop touched. `status: 'completed'` records
|
|
1642
|
+
// are pushed at the end of each successful iteration; `status: 'aborted'`
|
|
1643
|
+
// is pushed by the catch block when a migration throws mid-iteration, so
|
|
1644
|
+
// `outcomes` is the single source of truth for the recap and tally — no
|
|
1645
|
+
// parallel "pending" list. `outcomes.length` always equals `migrationIndex`
|
|
1646
|
+
// after the loop body runs.
|
|
1644
1647
|
const outcomes = [];
|
|
1645
|
-
// Migrations whose own commits failed and whose diffs therefore sit
|
|
1646
|
-
// uncommitted in the working tree. The next successful commit will absorb
|
|
1647
|
-
// them via `git add -A`; we annotate that commit's body with this list
|
|
1648
|
-
// and back-annotate the absorbed outcomes with `committedAsPartOf`.
|
|
1649
|
-
//
|
|
1650
|
-
// While non-empty, the working tree carries prior-migration state, so the
|
|
1651
|
-
// `hasDiffContext` flag in the hybrid-agentic and validation-agentic
|
|
1652
|
-
// prompt branches is suppressed (the prompt-only-with-agentic branch
|
|
1653
|
-
// doesn't use `hasDiffContext`). See call sites below.
|
|
1654
|
-
const pendingMigrations = [];
|
|
1655
1648
|
// Prompt-only migrations whose agent never ran. Hybrid migrations with a
|
|
1656
1649
|
// skipped prompt are NOT counted here — their deterministic half still ran.
|
|
1657
1650
|
let notRunMigrationsCount = 0;
|
|
@@ -1659,69 +1652,72 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1659
1652
|
? 'deferred to the AI agent driving this run'
|
|
1660
1653
|
: 'agentic flow disabled';
|
|
1661
1654
|
const installDepsIfChanged = () => changedDepInstaller.installDepsIfChanged();
|
|
1662
|
-
//
|
|
1663
|
-
//
|
|
1664
|
-
//
|
|
1665
|
-
//
|
|
1666
|
-
//
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1655
|
+
// Returns the migrations whose own commits failed and whose diffs are
|
|
1656
|
+
// still sitting in the working tree — derived live from `outcomes`. The
|
|
1657
|
+
// next successful commit absorbs them via `git add -A`; its commit body
|
|
1658
|
+
// lists them so a reader of `git log -p` can see which prior migrations'
|
|
1659
|
+
// diffs got pulled in.
|
|
1660
|
+
const pendingForCommitBody = () => outcomes
|
|
1661
|
+
.filter((o) => o.commit.kind === 'failed')
|
|
1662
|
+
.map((o) => ({ package: o.migration.package, name: o.migration.name }));
|
|
1663
|
+
// True while at least one prior migration's commit has failed and its
|
|
1664
|
+
// diff hasn't been absorbed yet. While true, the working tree carries
|
|
1665
|
+
// prior-migration state, so the `hasDiffContext` flag in the hybrid-
|
|
1666
|
+
// agentic and validation-agentic prompt branches is suppressed (the
|
|
1667
|
+
// prompt-only-with-agentic branch doesn't use `hasDiffContext`).
|
|
1668
|
+
const hasPendingCommitDebt = () => outcomes.some((o) => o.commit.kind === 'failed');
|
|
1669
|
+
// Single funnel for per-migration commit attempts. Returns the
|
|
1670
|
+
// `CommitState` to record on the migration's outcome. On `committed`,
|
|
1671
|
+
// back-annotates any prior failed-commit outcomes to `kind: 'absorbed'`
|
|
1672
|
+
// (their diffs were just rolled into this commit via `git add -A`).
|
|
1670
1673
|
async function attemptMigrationCommit(m) {
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
result = await (0, migrate_commits_1.commitMigrationIfRequested)(root, m, shouldCreateCommits, commitPrefix, installDepsIfChanged, pendingMigrations);
|
|
1674
|
-
}
|
|
1675
|
-
catch (err) {
|
|
1676
|
-
// `commitMigrationIfRequested` awaits `installDepsIfChanged` before
|
|
1677
|
-
// any commit attempt; a `runInstall` failure (e.g. NpmPeerDepsInstall)
|
|
1678
|
-
// throws past it. Treat that as a commit failure for tracking
|
|
1679
|
-
// purposes — the migration's diff is on disk, and pretending we never
|
|
1680
|
-
// attempted would silently drop it from the retained-state recap and
|
|
1681
|
-
// body annotations — then re-raise so the outer loop runs its
|
|
1682
|
-
// failure path.
|
|
1683
|
-
pendingMigrations.push({ package: m.package, name: m.name });
|
|
1684
|
-
throw err;
|
|
1685
|
-
}
|
|
1674
|
+
const pending = pendingForCommitBody();
|
|
1675
|
+
const result = await (0, migrate_commits_1.commitMigrationIfRequested)(root, m, shouldCreateCommits, commitPrefix, installDepsIfChanged, pending);
|
|
1686
1676
|
if (result.status === 'committed') {
|
|
1687
|
-
//
|
|
1688
|
-
//
|
|
1689
|
-
//
|
|
1677
|
+
// This commit absorbed every pending failed-commit migration's diff.
|
|
1678
|
+
// Transition their `commit.kind: 'failed'` records to `'absorbed'` so
|
|
1679
|
+
// the failure recap (if a later migration throws) can anchor them
|
|
1680
|
+
// and the retained-state filter no longer counts them as
|
|
1681
|
+
// uncommitted.
|
|
1690
1682
|
//
|
|
1691
1683
|
// The key is `package:name`; matching on `name` alone would conflate
|
|
1692
|
-
// across packages. Guard
|
|
1693
|
-
//
|
|
1694
|
-
if (
|
|
1695
|
-
const absorbedKeys = new Set(
|
|
1684
|
+
// across packages. Guard the kind check so a subsequent absorption-
|
|
1685
|
+
// of-same-name cannot overwrite an earlier annotation.
|
|
1686
|
+
if (pending.length > 0) {
|
|
1687
|
+
const absorbedKeys = new Set(pending.map((p) => `${p.package}:${p.name}`));
|
|
1696
1688
|
for (const o of outcomes) {
|
|
1697
1689
|
const key = `${o.migration.package}:${o.migration.name}`;
|
|
1698
|
-
if (absorbedKeys.has(key) &&
|
|
1699
|
-
o.
|
|
1690
|
+
if (absorbedKeys.has(key) && o.commit.kind === 'failed') {
|
|
1691
|
+
o.commit = {
|
|
1692
|
+
kind: 'absorbed',
|
|
1693
|
+
into: { name: m.name, sha: result.sha },
|
|
1694
|
+
};
|
|
1700
1695
|
}
|
|
1701
1696
|
}
|
|
1702
1697
|
}
|
|
1703
|
-
|
|
1704
|
-
return { sha: result.sha, failed: false };
|
|
1698
|
+
return { kind: 'landed', sha: result.sha };
|
|
1705
1699
|
}
|
|
1706
1700
|
if (result.status === 'failed') {
|
|
1707
1701
|
// Diff is still in WT. Subsequent prompts cannot claim git-isolation
|
|
1708
1702
|
// until a later commit absorbs the backlog.
|
|
1709
|
-
|
|
1710
|
-
return { sha: null, failed: true };
|
|
1703
|
+
return { kind: 'failed' };
|
|
1711
1704
|
}
|
|
1712
|
-
// `no-changes` and `disabled`
|
|
1713
|
-
//
|
|
1714
|
-
return {
|
|
1705
|
+
// `no-changes` and `disabled` — no commit attempted, nothing to record
|
|
1706
|
+
// as a commit failure.
|
|
1707
|
+
return { kind: 'none' };
|
|
1715
1708
|
}
|
|
1716
1709
|
const totalMigrations = sortedMigrations.length;
|
|
1717
1710
|
let migrationIndex = 0;
|
|
1718
1711
|
for (const m of sortedMigrations) {
|
|
1719
1712
|
migrationIndex++;
|
|
1720
1713
|
(0, migrate_output_1.logMigrationBoundary)(migrationIndex, totalMigrations, m.package, m.name);
|
|
1714
|
+
// Snapshot the WT for before/after comparison in the catch block.
|
|
1715
|
+
// Content-sensitive so a dirty→dirty case (this migration mutating an
|
|
1716
|
+
// already-dirty shared file like `package.json`) doesn't collapse.
|
|
1717
|
+
const baselineWorkingTreeSnapshot = (0, git_utils_1.getUncommittedChangesSnapshot)(root);
|
|
1721
1718
|
try {
|
|
1722
1719
|
let outcome;
|
|
1723
|
-
let
|
|
1724
|
-
let commitFailed = false;
|
|
1720
|
+
let commit = { kind: 'none' };
|
|
1725
1721
|
if ((0, migration_shape_1.isPromptOnlyMigration)(m)) {
|
|
1726
1722
|
if (agenticRun) {
|
|
1727
1723
|
const stepResult = await agenticRun.runStep({
|
|
@@ -1731,9 +1727,8 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1731
1727
|
runDir: agenticRun.runDir,
|
|
1732
1728
|
installDepsIfChanged,
|
|
1733
1729
|
});
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
(0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous ? 'Marked complete by user' : 'Applied', committedSha, stepResult.summary);
|
|
1730
|
+
commit = await attemptMigrationCommit(m);
|
|
1731
|
+
(0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous ? 'Marked complete by user' : 'Applied', commit.kind === 'landed' ? commit.sha : null, stepResult.summary);
|
|
1737
1732
|
outcome = 'applied';
|
|
1738
1733
|
}
|
|
1739
1734
|
else {
|
|
@@ -1765,12 +1760,11 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1765
1760
|
// When prior commits failed, the working tree carries their
|
|
1766
1761
|
// diff. The git-inspect path of the prompt would mislead the
|
|
1767
1762
|
// agent in that case; fall back to embedded `<files_changed>`.
|
|
1768
|
-
hasDiffContext: agenticHasDiffContext &&
|
|
1763
|
+
hasDiffContext: agenticHasDiffContext && !hasPendingCommitDebt(),
|
|
1769
1764
|
},
|
|
1770
1765
|
});
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
(0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous ? 'Marked complete by user' : 'Applied', committedSha, stepResult.summary);
|
|
1766
|
+
commit = await attemptMigrationCommit(m);
|
|
1767
|
+
(0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous ? 'Marked complete by user' : 'Applied', commit.kind === 'landed' ? commit.sha : null, stepResult.summary);
|
|
1774
1768
|
outcome = 'applied';
|
|
1775
1769
|
}
|
|
1776
1770
|
else {
|
|
@@ -1794,11 +1788,10 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1794
1788
|
// diffs — confusing `git log` / `git blame` attribution. Pending
|
|
1795
1789
|
// stays pending and the next change-producing migration absorbs.
|
|
1796
1790
|
if (madeChanges) {
|
|
1797
|
-
|
|
1798
|
-
await attemptMigrationCommit(m));
|
|
1791
|
+
commit = await attemptMigrationCommit(m);
|
|
1799
1792
|
}
|
|
1800
|
-
if (
|
|
1801
|
-
logger_1.logger.info(pc.dim(`Committed as ${
|
|
1793
|
+
if (commit.kind === 'landed' && commit.sha) {
|
|
1794
|
+
logger_1.logger.info(pc.dim(`Committed as ${commit.sha}`));
|
|
1802
1795
|
}
|
|
1803
1796
|
outcome = 'deferred';
|
|
1804
1797
|
}
|
|
@@ -1826,16 +1819,15 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1826
1819
|
changes,
|
|
1827
1820
|
agentContext,
|
|
1828
1821
|
// See the hybrid agentic branch above for the rationale on
|
|
1829
|
-
// why
|
|
1830
|
-
hasDiffContext: agenticHasDiffContext &&
|
|
1822
|
+
// why pending commit debt gates git-inspect context.
|
|
1823
|
+
hasDiffContext: agenticHasDiffContext && !hasPendingCommitDebt(),
|
|
1831
1824
|
},
|
|
1832
1825
|
mode: 'generic-validation',
|
|
1833
1826
|
});
|
|
1834
|
-
|
|
1835
|
-
await attemptMigrationCommit(m));
|
|
1827
|
+
commit = await attemptMigrationCommit(m);
|
|
1836
1828
|
(0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous
|
|
1837
1829
|
? 'Marked complete by user'
|
|
1838
|
-
: 'Validation passed',
|
|
1830
|
+
: 'Validation passed', commit.kind === 'landed' ? commit.sha : null, stepResult.summary);
|
|
1839
1831
|
outcome = 'applied';
|
|
1840
1832
|
}
|
|
1841
1833
|
else {
|
|
@@ -1849,10 +1841,9 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1849
1841
|
outcome = 'no-changes';
|
|
1850
1842
|
}
|
|
1851
1843
|
else {
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
logger_1.logger.info(pc.dim(`Committed as ${committedSha}`));
|
|
1844
|
+
commit = await attemptMigrationCommit(m);
|
|
1845
|
+
if (commit.kind === 'landed' && commit.sha) {
|
|
1846
|
+
logger_1.logger.info(pc.dim(`Committed as ${commit.sha}`));
|
|
1856
1847
|
}
|
|
1857
1848
|
outcome = 'applied';
|
|
1858
1849
|
}
|
|
@@ -1860,13 +1851,26 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1860
1851
|
}
|
|
1861
1852
|
outcomes.push({
|
|
1862
1853
|
migration: { package: m.package, name: m.name },
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1854
|
+
status: 'completed',
|
|
1855
|
+
kind: outcome,
|
|
1856
|
+
commit,
|
|
1866
1857
|
});
|
|
1867
1858
|
logger_1.logger.info('');
|
|
1868
1859
|
}
|
|
1869
1860
|
catch (e) {
|
|
1861
|
+
// Record the in-flight migration as `aborted` so the recap and tally
|
|
1862
|
+
// see it. `commit: 'failed'` requires both: (1) commits were
|
|
1863
|
+
// requested — otherwise the "could not be created" recap line is
|
|
1864
|
+
// false; (2) the WT snapshot diverged from the iteration baseline —
|
|
1865
|
+
// net-new state, not the pre-existing pending diff. Else `'none'`.
|
|
1866
|
+
const leftNewDiff = (0, git_utils_1.getUncommittedChangesSnapshot)(root) !== baselineWorkingTreeSnapshot;
|
|
1867
|
+
outcomes.push({
|
|
1868
|
+
migration: { package: m.package, name: m.name },
|
|
1869
|
+
status: 'aborted',
|
|
1870
|
+
commit: shouldCreateCommits && leftNewDiff
|
|
1871
|
+
? { kind: 'failed' }
|
|
1872
|
+
: { kind: 'none' },
|
|
1873
|
+
});
|
|
1870
1874
|
if (!(e instanceof NpmPeerDepsInstallError)) {
|
|
1871
1875
|
// `withGeneratorOutputCapture` attaches the generator's `console.*`
|
|
1872
1876
|
// output as `capturedLogs` (best-effort; may be absent). Surface it
|
|
@@ -1887,7 +1891,6 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1887
1891
|
migrationIndex,
|
|
1888
1892
|
totalMigrations,
|
|
1889
1893
|
outcomes,
|
|
1890
|
-
pendingMigrations,
|
|
1891
1894
|
migrationEmittedNextSteps,
|
|
1892
1895
|
insideAgent: agentic?.kind === 'inside-agent',
|
|
1893
1896
|
});
|
|
@@ -1914,12 +1917,12 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
|
|
|
1914
1917
|
nextSteps: combinedNextSteps,
|
|
1915
1918
|
skippedPrompts,
|
|
1916
1919
|
migrationEmittedNextSteps,
|
|
1917
|
-
committedShasCount:
|
|
1920
|
+
committedShasCount: (0, migrate_output_1.countLandedCommits)(outcomes),
|
|
1918
1921
|
// Migrations whose commits failed and never got absorbed by a later
|
|
1919
1922
|
// commit. The caller surfaces them so a successful run doesn't claim
|
|
1920
1923
|
// "up to date" while leaving uncommitted diffs in the working tree.
|
|
1921
1924
|
// Formatted as `package: name` for direct display.
|
|
1922
|
-
retainedAtSuccess:
|
|
1925
|
+
retainedAtSuccess: (0, migrate_output_1.retainedMigrations)(outcomes).map((p) => `${p.package}: ${p.name}`),
|
|
1923
1926
|
};
|
|
1924
1927
|
}
|
|
1925
1928
|
class ChangedDepInstaller {
|
|
@@ -46,13 +46,21 @@ async function runMigrationProcess() {
|
|
|
46
46
|
);
|
|
47
47
|
|
|
48
48
|
if (configuration.createCommits) {
|
|
49
|
-
await commitMigrationIfRequested(
|
|
49
|
+
const commitResult = await commitMigrationIfRequested(
|
|
50
50
|
workspacePath,
|
|
51
51
|
migration,
|
|
52
52
|
true,
|
|
53
53
|
configuration.commitPrefix,
|
|
54
54
|
installDepsIfChanged
|
|
55
55
|
);
|
|
56
|
+
if (commitResult.status === 'failed') {
|
|
57
|
+
// Single-migration UI child surfaces the failure via stdout (logged
|
|
58
|
+
// inside commitMigrationIfRequested) and continues with success-
|
|
59
|
+
// with-warning. The executor's absorption flow does not apply here:
|
|
60
|
+
// there is no later migration that could pick up this migration's
|
|
61
|
+
// diff, so the working tree is left in its post-migration state
|
|
62
|
+
// for the user to commit or revert through the UI.
|
|
63
|
+
}
|
|
56
64
|
} else {
|
|
57
65
|
await installDepsIfChanged();
|
|
58
66
|
}
|