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.
- package/README.md +1 -1
- package/dist/nx-cloud-internals.d.ts +1 -1
- package/dist/nx-cloud-internals.js +4 -4
- package/dist/src/command-line/affected/affected.js +20 -1
- package/dist/src/command-line/affected/command-object.js +1 -1
- package/dist/src/command-line/migrate/agentic/master/run-master-session.d.ts +6 -2
- package/dist/src/command-line/migrate/agentic/master/run-master-session.js +88 -14
- package/dist/src/command-line/migrate/command-object.d.ts +1 -0
- package/dist/src/command-line/migrate/command-object.js +6 -1
- package/dist/src/command-line/migrate/migrate-analytics.d.ts +34 -9
- package/dist/src/command-line/migrate/migrate-analytics.js +71 -10
- package/dist/src/command-line/migrate/migrate-config.d.ts +5 -2
- package/dist/src/command-line/migrate/migrate-config.js +19 -11
- package/dist/src/command-line/migrate/migrate.d.ts +2 -0
- package/dist/src/command-line/migrate/migrate.js +152 -45
- package/dist/src/command-line/migrate/resolve-package-version.js +2 -19
- package/dist/src/command-line/migrate/run/broker.d.ts +14 -2
- package/dist/src/command-line/migrate/run/broker.js +95 -34
- package/dist/src/command-line/migrate/run/existing-run-report.d.ts +55 -0
- package/dist/src/command-line/migrate/run/existing-run-report.js +225 -0
- package/dist/src/command-line/migrate/run/give-up.d.ts +41 -0
- package/dist/src/command-line/migrate/run/give-up.js +148 -0
- package/dist/src/command-line/migrate/run/index.d.ts +6 -4
- package/dist/src/command-line/migrate/run/index.js +15 -2
- package/dist/src/command-line/migrate/run/issues.d.ts +14 -0
- package/dist/src/command-line/migrate/run/issues.js +59 -0
- package/dist/src/command-line/migrate/run/orchestrator.d.ts +40 -1
- package/dist/src/command-line/migrate/run/orchestrator.js +509 -160
- package/dist/src/command-line/migrate/run/run-id.d.ts +0 -8
- package/dist/src/command-line/migrate/run/run-id.js +0 -30
- package/dist/src/command-line/migrate/run/run-state.d.ts +11 -9
- package/dist/src/command-line/migrate/run/run-state.js +25 -12
- package/dist/src/command-line/migrate/run/runbook.js +9 -4
- package/dist/src/command-line/migrate/run/state-lock.d.ts +31 -0
- package/dist/src/command-line/migrate/run/state-lock.js +126 -0
- package/dist/src/command-line/migrate/run/state-machine.d.ts +15 -0
- package/dist/src/command-line/migrate/run/state-machine.js +137 -10
- package/dist/src/command-line/migrate/run/util.d.ts +1 -0
- package/dist/src/command-line/migrate/run/util.js +21 -0
- package/dist/src/command-line/migrate/run/worker.js +3 -0
- package/dist/src/command-line/migrate/step-actions.d.ts +1 -1
- package/dist/src/command-line/migrate/step-actions.js +7 -1
- package/dist/src/command-line/migrate/text.d.ts +6 -0
- package/dist/src/command-line/migrate/text.js +25 -0
- package/dist/src/command-line/release/utils/release-graph.js +1 -1
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +2 -5
- package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.d.ts +2 -2
- package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.js +4 -5
- package/dist/src/command-line/release/version/version-actions.d.ts +3 -0
- package/dist/src/command-line/reset/reset.js +10 -4
- package/dist/src/command-line/show/command-object.js +1 -1
- package/dist/src/command-line/show/projects.js +22 -0
- package/dist/src/command-line/yargs-utils/shared-options.d.ts +4 -0
- package/dist/src/command-line/yargs-utils/shared-options.js +16 -0
- package/dist/src/config/task-graph.d.ts +1 -1
- package/dist/src/config/workspace-json-project-json.d.ts +4 -4
- package/dist/src/daemon/client/client.d.ts +3 -3
- package/dist/src/daemon/client/client.js +4 -4
- package/dist/src/daemon/message-types/select-affected-tasks.d.ts +3 -0
- package/dist/src/daemon/message-types/ultracache-configuration-version.d.ts +8 -0
- package/dist/src/daemon/server/handle-hash-tasks.d.ts +2 -2
- package/dist/src/daemon/server/handle-hash-tasks.js +3 -3
- package/dist/src/daemon/server/handle-select-affected-tasks.js +5 -2
- package/dist/src/daemon/server/ultracache-configuration-state.d.ts +7 -0
- package/dist/src/daemon/server/{io-snapshots-state.js → ultracache-configuration-state.js} +5 -4
- package/dist/src/devkit-internals.d.ts +3 -1
- package/dist/src/devkit-internals.js +8 -3
- package/dist/src/hasher/create-task-hasher.d.ts +3 -3
- package/dist/src/hasher/create-task-hasher.js +6 -6
- package/dist/src/hasher/hash-task.d.ts +2 -2
- package/dist/src/hasher/hash-task.js +6 -5
- package/dist/src/hasher/native-task-hasher-impl.d.ts +7 -7
- package/dist/src/hasher/native-task-hasher-impl.js +19 -19
- package/dist/src/hasher/task-hasher.d.ts +14 -14
- package/dist/src/hasher/task-hasher.js +13 -13
- package/dist/src/native/index.d.ts +151 -102
- package/dist/src/native/native-bindings.js +4 -5
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +15 -8
- package/dist/src/nx-cloud/update-manager.d.ts +3 -3
- package/dist/src/nx-cloud/update-manager.js +19 -13
- package/dist/src/nx-cloud/utilities/client.d.ts +1 -1
- package/dist/src/nx-cloud/utilities/client.js +1 -1
- package/dist/src/nx-cloud/utilities/is-workspace-claimed.js +2 -1
- package/dist/src/nx-cloud/utilities/nx-cloud-http-client.d.ts +5 -0
- package/dist/src/nx-cloud/utilities/nx-cloud-http-client.js +16 -0
- package/dist/src/nx-cloud/utilities/url-shorten.js +17 -10
- package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +22 -18
- package/dist/src/plugins/js/utils/register.js +11 -3
- package/dist/src/project-graph/affected/affected-reasons.d.ts +116 -0
- package/dist/src/project-graph/affected/affected-reasons.js +459 -0
- package/dist/src/project-graph/affected/affected-tasks.d.ts +23 -10
- package/dist/src/project-graph/affected/affected-tasks.js +203 -31
- package/dist/src/project-graph/affected/print-explanation.d.ts +11 -0
- package/dist/src/project-graph/affected/print-explanation.js +44 -0
- package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
- package/dist/src/tasks-runner/init-tasks-runner.d.ts +5 -5
- package/dist/src/tasks-runner/init-tasks-runner.js +8 -15
- package/dist/src/tasks-runner/run-command.d.ts +7 -7
- package/dist/src/tasks-runner/run-command.js +19 -23
- package/dist/src/tasks-runner/task-orchestrator.d.ts +7 -3
- package/dist/src/tasks-runner/task-orchestrator.js +9 -8
- package/dist/src/tasks-runner/tasks-runner.d.ts +2 -0
- package/dist/src/ultracache/config.d.ts +21 -0
- package/dist/src/ultracache/config.js +29 -0
- package/dist/src/ultracache/fetch.d.ts +21 -0
- package/dist/src/{io-snapshots → ultracache}/fetch.js +9 -9
- package/dist/src/ultracache/overrides.d.ts +17 -0
- package/dist/src/{io-snapshots → ultracache}/overrides.js +14 -13
- package/dist/src/ultracache/report.d.ts +13 -0
- package/dist/src/{io-snapshots → ultracache}/report.js +15 -21
- package/dist/src/ultracache/store.d.ts +40 -0
- package/dist/src/{io-snapshots → ultracache}/store.js +45 -45
- package/dist/src/utils/ab-testing.js +11 -12
- package/dist/src/utils/command-line-utils.d.ts +1 -0
- package/dist/src/utils/create-cli-options.d.ts +6 -0
- package/dist/src/utils/create-cli-options.js +29 -0
- package/dist/src/utils/db-connection.d.ts +1 -0
- package/dist/src/utils/db-connection.js +1 -0
- package/dist/src/utils/git-utils.d.ts +7 -0
- package/dist/src/utils/git-utils.js +21 -9
- package/dist/src/utils/http-client.d.ts +27 -0
- package/dist/src/utils/http-client.js +193 -0
- package/package.json +12 -38
- package/dist/src/daemon/message-types/io-snapshot-version.d.ts +0 -5
- package/dist/src/daemon/server/io-snapshots-state.d.ts +0 -7
- package/dist/src/io-snapshots/config.d.ts +0 -23
- package/dist/src/io-snapshots/config.js +0 -32
- package/dist/src/io-snapshots/fetch.d.ts +0 -21
- package/dist/src/io-snapshots/outputs.d.ts +0 -12
- package/dist/src/io-snapshots/outputs.js +0 -33
- package/dist/src/io-snapshots/overrides.d.ts +0 -17
- package/dist/src/io-snapshots/report.d.ts +0 -13
- package/dist/src/io-snapshots/store.d.ts +0 -37
- package/dist/src/nx-cloud/utilities/axios.d.ts +0 -4
- package/dist/src/nx-cloud/utilities/axios.js +0 -17
- /package/dist/src/daemon/message-types/{io-snapshot-version.js → ultracache-configuration-version.js} +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type AncestorStatus } from '../../../utils/git-utils';
|
|
2
|
+
import { type MigrateCommitLedgerEntry, type MigrateRunPolicy, type MigrateRunState } from './run-state';
|
|
3
|
+
import { type StepTally } from './state-machine';
|
|
4
|
+
export declare function progressLine(tally: StepTally): string;
|
|
5
|
+
export interface ExistingRunFacts {
|
|
6
|
+
runId: string;
|
|
7
|
+
createdAt: string;
|
|
8
|
+
recordedBranch: string | undefined;
|
|
9
|
+
currentBranch: string | null;
|
|
10
|
+
progress: StepTally;
|
|
11
|
+
unresolvedIssues: number;
|
|
12
|
+
policy: MigrateRunPolicy;
|
|
13
|
+
commits: {
|
|
14
|
+
recorded: number;
|
|
15
|
+
reachable: number;
|
|
16
|
+
unchecked: number;
|
|
17
|
+
newest: {
|
|
18
|
+
sha: string | null;
|
|
19
|
+
status: AncestorStatus;
|
|
20
|
+
} | null;
|
|
21
|
+
};
|
|
22
|
+
liveWorkers: {
|
|
23
|
+
pid: number;
|
|
24
|
+
stepId: string;
|
|
25
|
+
migrationId: string;
|
|
26
|
+
}[];
|
|
27
|
+
otherHolders: number[] | 'unknown';
|
|
28
|
+
otherActiveRuns: string[] | 'unknown';
|
|
29
|
+
appliedStillPlanned: number | undefined;
|
|
30
|
+
replacedRunId?: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function collectExistingRunFacts(root: string, runId: string, state: MigrateRunState, plannedMigrationIds?: readonly string[]): ExistingRunFacts;
|
|
33
|
+
export declare function liveWorkers(state: MigrateRunState): ExistingRunFacts['liveWorkers'];
|
|
34
|
+
export declare function recordedCommits(state: MigrateRunState): MigrateCommitLedgerEntry[];
|
|
35
|
+
export declare function renderContinueCommand(root: string, runId: string, policy: MigrateRunPolicy, agentId?: string): string;
|
|
36
|
+
export interface ExistingRunCommands {
|
|
37
|
+
continueCommand: string;
|
|
38
|
+
startFresh: {
|
|
39
|
+
command: string;
|
|
40
|
+
} | {
|
|
41
|
+
migrationsPath: string;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export declare function renderExistingRunCommands(root: string, facts: ExistingRunFacts, migrationsPath: string | undefined, agentId?: string): ExistingRunCommands;
|
|
45
|
+
/**
|
|
46
|
+
* Title plus body lines, in the shape the agent-output writers take. Every
|
|
47
|
+
* body line goes through `singleLine`: branch names and timestamps came from
|
|
48
|
+
* git and disk, and the lines end up on the stdout the agent scans for
|
|
49
|
+
* blocks. The title carries only the run id, which discovery validated.
|
|
50
|
+
*/
|
|
51
|
+
export declare function renderExistingRunReport(facts: ExistingRunFacts, commands?: ExistingRunCommands): {
|
|
52
|
+
title: string;
|
|
53
|
+
bodyLines: string[];
|
|
54
|
+
};
|
|
55
|
+
export declare function workersLine(workers: ExistingRunFacts['liveWorkers']): string;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The report on a run already active. Facts only: nothing here decides, and
|
|
3
|
+
// anything that could not be established says so instead of guessing.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.progressLine = progressLine;
|
|
6
|
+
exports.collectExistingRunFacts = collectExistingRunFacts;
|
|
7
|
+
exports.liveWorkers = liveWorkers;
|
|
8
|
+
exports.recordedCommits = recordedCommits;
|
|
9
|
+
exports.renderContinueCommand = renderContinueCommand;
|
|
10
|
+
exports.renderExistingRunCommands = renderExistingRunCommands;
|
|
11
|
+
exports.renderExistingRunReport = renderExistingRunReport;
|
|
12
|
+
exports.workersLine = workersLine;
|
|
13
|
+
const git_utils_1 = require("../../../utils/git-utils");
|
|
14
|
+
const text_1 = require("../text");
|
|
15
|
+
const run_state_1 = require("./run-state");
|
|
16
|
+
const issues_1 = require("./issues");
|
|
17
|
+
const state_lock_1 = require("./state-lock");
|
|
18
|
+
const state_machine_1 = require("./state-machine");
|
|
19
|
+
const util_1 = require("./util");
|
|
20
|
+
// The stalled count is a subset of `remaining`, called out separately: the
|
|
21
|
+
// count alone would read as work that has not been reached yet. Adopted and
|
|
22
|
+
// unresolved appear only when present, so the common line stays short.
|
|
23
|
+
function progressLine(tally) {
|
|
24
|
+
return `${tally.applied} applied, ${tally.adopted > 0 ? `${tally.adopted} adopted, ` : ''}${tally.skipped} skipped, ${tally.unresolved.length > 0 ? `${tally.unresolved.length} unresolved, ` : ''}${tally.remaining} remaining${tally.stalled > 0 ? ` (${tally.stalled} awaiting a decision)` : ''}`;
|
|
25
|
+
}
|
|
26
|
+
function collectExistingRunFacts(root, runId, state, plannedMigrationIds) {
|
|
27
|
+
const head = (0, git_utils_1.getLatestCommitSha)(root);
|
|
28
|
+
// A landed entry may have lost its sha to a transient rev-parse failure;
|
|
29
|
+
// it still counts, as a commit that could not be checked.
|
|
30
|
+
const commits = recordedCommits(state);
|
|
31
|
+
const statuses = commits.map((entry) => entry.sha && head
|
|
32
|
+
? (0, git_utils_1.getAncestorStatus)(entry.sha, head, root)
|
|
33
|
+
: 'unknown');
|
|
34
|
+
const newestIndex = commits.length - 1;
|
|
35
|
+
const planned = plannedMigrationIds ? new Set(plannedMigrationIds) : null;
|
|
36
|
+
return {
|
|
37
|
+
runId,
|
|
38
|
+
createdAt: state.createdAt,
|
|
39
|
+
recordedBranch: state.branch,
|
|
40
|
+
currentBranch: (0, git_utils_1.getGitCurrentBranch)(root),
|
|
41
|
+
progress: (0, state_machine_1.tallySteps)(state),
|
|
42
|
+
unresolvedIssues: (0, issues_1.unresolvedIssues)(state).length,
|
|
43
|
+
policy: {
|
|
44
|
+
createCommits: state.createCommits,
|
|
45
|
+
skipInstall: state.skipInstall === true,
|
|
46
|
+
},
|
|
47
|
+
commits: {
|
|
48
|
+
recorded: commits.length,
|
|
49
|
+
reachable: statuses.filter((s) => s === 'ancestor').length,
|
|
50
|
+
unchecked: statuses.filter((s) => s === 'unknown').length,
|
|
51
|
+
newest: newestIndex >= 0
|
|
52
|
+
? {
|
|
53
|
+
sha: commits[newestIndex].sha ?? null,
|
|
54
|
+
status: statuses[newestIndex],
|
|
55
|
+
}
|
|
56
|
+
: null,
|
|
57
|
+
},
|
|
58
|
+
liveWorkers: liveWorkers(state),
|
|
59
|
+
otherHolders: (0, state_lock_1.liveRunActivityPids)((0, run_state_1.runDir)(root, runId)),
|
|
60
|
+
otherActiveRuns: otherActiveRuns(root, runId),
|
|
61
|
+
appliedStillPlanned: planned
|
|
62
|
+
? state.steps.filter((s) => s.status === 'succeeded' && planned.has(s.migrationId)).length
|
|
63
|
+
: undefined,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Only a step recorded as running can own a worker; a died or finished step's
|
|
67
|
+
// pid may have been reused by an unrelated process since.
|
|
68
|
+
function liveWorkers(state) {
|
|
69
|
+
return state.steps
|
|
70
|
+
.filter((s) => s.status === 'running' && s.pid !== undefined && (0, util_1.isPidAlive)(s.pid))
|
|
71
|
+
.map((s) => ({ pid: s.pid, stepId: s.id, migrationId: s.migrationId }));
|
|
72
|
+
}
|
|
73
|
+
// The commits the run made, in ledger order.
|
|
74
|
+
function recordedCommits(state) {
|
|
75
|
+
return state.commits.filter((entry) => madeCommit(entry.kind));
|
|
76
|
+
}
|
|
77
|
+
function madeCommit(kind) {
|
|
78
|
+
switch (kind) {
|
|
79
|
+
case 'checkpoint':
|
|
80
|
+
case 'landed':
|
|
81
|
+
return true;
|
|
82
|
+
case 'failed':
|
|
83
|
+
return false;
|
|
84
|
+
default: {
|
|
85
|
+
const exhaustive = kind;
|
|
86
|
+
throw new Error(`Unhandled commit kind '${exhaustive}'.`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function otherActiveRuns(root, runId) {
|
|
91
|
+
try {
|
|
92
|
+
return (0, run_state_1.findActiveRun)(root).activeRunIds.filter((id) => id !== runId);
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
if (e instanceof run_state_1.NewerRunStateFormatError) {
|
|
96
|
+
return 'unknown';
|
|
97
|
+
}
|
|
98
|
+
throw e;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// The `--run-migrations --run-id` continue, the one entry that re-enters a
|
|
102
|
+
// run on both paths. The policy flags are always explicit: a continue must
|
|
103
|
+
// resolve to the run's recorded policy, and nx.json can flip the bare default
|
|
104
|
+
// either way. `--agentic` names the agent to spawn; bare, inside an agent, it
|
|
105
|
+
// only satisfies the parse and the run continues in place.
|
|
106
|
+
function renderContinueCommand(root, runId, policy, agentId) {
|
|
107
|
+
return `${(0, util_1.pmExecPrefix)(root)} nx migrate --run-migrations --agentic${agentId === undefined ? '' : `=${agentId}`} --run-id=${runId} ${policy.createCommits ? '--create-commits' : '--no-create-commits'}${policy.skipInstall ? ' --skip-install' : ''}`;
|
|
108
|
+
}
|
|
109
|
+
function renderExistingRunCommands(root, facts, migrationsPath, agentId) {
|
|
110
|
+
const flag = migrationsPath === undefined
|
|
111
|
+
? '--run-migrations'
|
|
112
|
+
: (0, util_1.runMigrationsFlag)(migrationsPath);
|
|
113
|
+
return {
|
|
114
|
+
continueCommand: renderContinueCommand(root, facts.runId, facts.policy, agentId),
|
|
115
|
+
startFresh: flag === null
|
|
116
|
+
? { migrationsPath }
|
|
117
|
+
: {
|
|
118
|
+
command: `${(0, util_1.pmExecPrefix)(root)} nx migrate ${flag}${agentId === undefined ? '' : ` --agentic=${agentId}`} --start-fresh --run-id=${facts.runId}`,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Title plus body lines, in the shape the agent-output writers take. Every
|
|
124
|
+
* body line goes through `singleLine`: branch names and timestamps came from
|
|
125
|
+
* git and disk, and the lines end up on the stdout the agent scans for
|
|
126
|
+
* blocks. The title carries only the run id, which discovery validated.
|
|
127
|
+
*/
|
|
128
|
+
function renderExistingRunReport(facts, commands) {
|
|
129
|
+
const lines = [
|
|
130
|
+
...(facts.replacedRunId !== undefined
|
|
131
|
+
? [
|
|
132
|
+
`Not deleting ${facts.replacedRunId}: the newest active run is ${facts.runId}.`,
|
|
133
|
+
]
|
|
134
|
+
: []),
|
|
135
|
+
` run: ${facts.runId}`,
|
|
136
|
+
` started: ${facts.createdAt} (${(0, text_1.formatAge)(facts.createdAt)})`,
|
|
137
|
+
` branch: ${branchLine(facts)}`,
|
|
138
|
+
` progress: ${progressLine(facts.progress)}`,
|
|
139
|
+
` policy: per-migration commits ${facts.policy.createCommits ? 'on' : 'off'}, installs ${facts.policy.skipInstall ? 'skipped' : 'on'}`,
|
|
140
|
+
` commits: ${commitsLine(facts.commits)}`,
|
|
141
|
+
` worker: ${workersLine(facts.liveWorkers)}`,
|
|
142
|
+
` activity: ${activityLine(facts.otherHolders)}`,
|
|
143
|
+
];
|
|
144
|
+
if (facts.unresolvedIssues > 0) {
|
|
145
|
+
lines.push(` issues: ${facts.unresolvedIssues} unresolved`);
|
|
146
|
+
}
|
|
147
|
+
if (facts.otherActiveRuns === 'unknown') {
|
|
148
|
+
lines.push(` other active runs on disk: unknown (a run written by a newer nx is on disk)`);
|
|
149
|
+
}
|
|
150
|
+
else if (facts.otherActiveRuns.length > 0) {
|
|
151
|
+
lines.push(` other active runs on disk: ${facts.otherActiveRuns.join(', ')}`);
|
|
152
|
+
}
|
|
153
|
+
if (facts.appliedStillPlanned !== undefined) {
|
|
154
|
+
lines.push(` plan overlap: ${facts.appliedStillPlanned} of the applied migrations ${facts.appliedStillPlanned === 1 ? 'is' : 'are'} still in the plan; a new run applies ${facts.appliedStillPlanned === 1 ? 'it' : 'them'} again`);
|
|
155
|
+
}
|
|
156
|
+
if (commands) {
|
|
157
|
+
lines.push(``, `To continue the run: ${commands.continueCommand}`, `To start fresh (deletes the run record, then runs the whole plan again): ${'command' in commands.startFresh
|
|
158
|
+
? commands.startFresh.command
|
|
159
|
+
: `re-run this command with --start-fresh --run-id=${facts.runId}, keeping the --run-migrations argument, which names ${displayPath(commands.startFresh.migrationsPath)}; that path cannot be rendered as a command for this shell`}`, `Run either command with NX_MIGRATE_ORCHESTRATOR=true set in the environment.`);
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
title: `A migrate run is already active: ${facts.runId}`,
|
|
163
|
+
bodyLines: lines.map(text_1.singleLine),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// Prose, not a command, so a path goes out verbatim. A path with a line break
|
|
167
|
+
// goes out JSON-escaped: singleLine below would otherwise turn the break into
|
|
168
|
+
// a space and name a different path. JSON.stringify leaves U+0085, U+2028 and
|
|
169
|
+
// U+2029 literal, so those are escaped by hand.
|
|
170
|
+
function displayPath(migrationsPath) {
|
|
171
|
+
if (!(0, text_1.hasLineBreak)(migrationsPath)) {
|
|
172
|
+
return migrationsPath;
|
|
173
|
+
}
|
|
174
|
+
return JSON.stringify(migrationsPath).replace(/[\u0085\u2028\u2029]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
|
|
175
|
+
}
|
|
176
|
+
// A null probe is 'unknown' on both sides: getGitCurrentBranch folds a
|
|
177
|
+
// detached HEAD and a failed git call into one null.
|
|
178
|
+
function branchLine(facts) {
|
|
179
|
+
const recorded = facts.recordedBranch ?? 'unknown';
|
|
180
|
+
const current = facts.currentBranch ?? 'unknown';
|
|
181
|
+
return recorded === current
|
|
182
|
+
? recorded
|
|
183
|
+
: `started on ${recorded}, currently on ${current}`;
|
|
184
|
+
}
|
|
185
|
+
function commitsLine(commits) {
|
|
186
|
+
if (!commits.newest) {
|
|
187
|
+
return 'none recorded';
|
|
188
|
+
}
|
|
189
|
+
const newest = commits.newest.sha === null
|
|
190
|
+
? 'newest recorded commit has no recorded sha and could not be checked'
|
|
191
|
+
: `newest recorded commit ${commits.newest.sha.slice(0, 10)} is ${describeStatus(commits.newest.status)}`;
|
|
192
|
+
const tally = `${commits.reachable} of ${commits.recorded} reachable${commits.unchecked > 0 ? `, ${commits.unchecked} could not be checked` : ''}`;
|
|
193
|
+
return `${newest} (${tally})`;
|
|
194
|
+
}
|
|
195
|
+
function describeStatus(status) {
|
|
196
|
+
switch (status) {
|
|
197
|
+
case 'ancestor':
|
|
198
|
+
return 'reachable from HEAD';
|
|
199
|
+
case 'not-ancestor':
|
|
200
|
+
return 'not reachable from HEAD';
|
|
201
|
+
case 'unknown':
|
|
202
|
+
return 'of unknown reachability from HEAD';
|
|
203
|
+
default: {
|
|
204
|
+
const exhaustive = status;
|
|
205
|
+
throw new Error(`Unhandled ancestor status '${exhaustive}'.`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function activityLine(holders) {
|
|
210
|
+
if (holders === 'unknown') {
|
|
211
|
+
return 'unknown whether another nx migrate process is working on it';
|
|
212
|
+
}
|
|
213
|
+
if (holders.length === 0) {
|
|
214
|
+
return 'no other nx migrate process is working on it';
|
|
215
|
+
}
|
|
216
|
+
return (0, state_lock_1.describeHolders)(holders);
|
|
217
|
+
}
|
|
218
|
+
function workersLine(workers) {
|
|
219
|
+
if (workers.length === 0) {
|
|
220
|
+
return 'none running';
|
|
221
|
+
}
|
|
222
|
+
return workers
|
|
223
|
+
.map((w) => `pid ${w.pid} is still running ${w.stepId} (${w.migrationId})`)
|
|
224
|
+
.join('; ');
|
|
225
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type CommitResult } from '../migrate-commits';
|
|
2
|
+
import type { MigrateOutputSink } from '../deferred-output';
|
|
3
|
+
import type { TreeLease } from './broker';
|
|
4
|
+
import { type MigrateRunState, type MigrateStep } from './run-state';
|
|
5
|
+
export type GiveUpOutcome = {
|
|
6
|
+
kind: 'given-up';
|
|
7
|
+
commit: CommitResult | {
|
|
8
|
+
status: 'install-failed';
|
|
9
|
+
reason: string;
|
|
10
|
+
};
|
|
11
|
+
archiveError?: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'refused';
|
|
14
|
+
reason: string;
|
|
15
|
+
};
|
|
16
|
+
export interface GiveUpInput {
|
|
17
|
+
root: string;
|
|
18
|
+
dir: string;
|
|
19
|
+
step: MigrateStep;
|
|
20
|
+
lease: TreeLease;
|
|
21
|
+
skipInstall: boolean;
|
|
22
|
+
reconcileCommand: string;
|
|
23
|
+
output?: MigrateOutputSink;
|
|
24
|
+
}
|
|
25
|
+
export declare function giveUpWithCommit(input: GiveUpInput): Promise<GiveUpOutcome>;
|
|
26
|
+
/**
|
|
27
|
+
* Mints the issue that carries a given-up step's failure and points the step
|
|
28
|
+
* at it, on state where the step is already unresolved. The archive file is
|
|
29
|
+
* best-effort: run.json is authoritative, and undoing the transition for a
|
|
30
|
+
* lost detail file would make the agent re-issue an action the run already
|
|
31
|
+
* took.
|
|
32
|
+
*/
|
|
33
|
+
export declare function recordUnresolvedIssue(dir: string, state: MigrateRunState, stepId: string): {
|
|
34
|
+
state: MigrateRunState;
|
|
35
|
+
archiveError?: string;
|
|
36
|
+
};
|
|
37
|
+
export declare function warnUnresolvedNotArchived(migrationId: string, archiveError: string): void;
|
|
38
|
+
/** What the reconcile owes the agent for a give-up that went through. */
|
|
39
|
+
export declare function warnAboutGiveUp(step: MigrateStep, outcome: Extract<GiveUpOutcome, {
|
|
40
|
+
kind: 'given-up';
|
|
41
|
+
}>): void;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Giving a step up while the run commits is one operation under one tree
|
|
3
|
+
// reservation, run by the process that holds the tree (a session's parent or
|
|
4
|
+
// the reconcile): split across two, the transition would find the give-up's
|
|
5
|
+
// own commit in the ledger and refuse it as a commit of the migration.
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.giveUpWithCommit = giveUpWithCommit;
|
|
8
|
+
exports.recordUnresolvedIssue = recordUnresolvedIssue;
|
|
9
|
+
exports.warnUnresolvedNotArchived = warnUnresolvedNotArchived;
|
|
10
|
+
exports.warnAboutGiveUp = warnAboutGiveUp;
|
|
11
|
+
const migrate_commits_1 = require("../migrate-commits");
|
|
12
|
+
const agent_output_1 = require("./agent-output");
|
|
13
|
+
const issues_1 = require("./issues");
|
|
14
|
+
const run_state_1 = require("./run-state");
|
|
15
|
+
const state_lock_1 = require("./state-lock");
|
|
16
|
+
const state_machine_1 = require("./state-machine");
|
|
17
|
+
const util_1 = require("./util");
|
|
18
|
+
async function giveUpWithCommit(input) {
|
|
19
|
+
const { root, dir, step, lease } = input;
|
|
20
|
+
const ownMark = lease.markedStepId === step.id;
|
|
21
|
+
const event = {
|
|
22
|
+
type: 'stepAction',
|
|
23
|
+
stepId: step.id,
|
|
24
|
+
action: 'unresolved',
|
|
25
|
+
attempt: step.attempt,
|
|
26
|
+
};
|
|
27
|
+
const state = (0, run_state_1.readRunState)(dir);
|
|
28
|
+
// Checked under the reservation, before git runs: a landed commit of the
|
|
29
|
+
// step, or a mark another operation left, refuses the give-up with nothing
|
|
30
|
+
// to undo. The mark this reservation set is not evidence against it.
|
|
31
|
+
const accepted = (0, state_machine_1.applyStepEvent)(ownMark ? (0, state_machine_1.clearCommitStarted)(state, step.id) : state, event);
|
|
32
|
+
if (accepted.kind === 'error') {
|
|
33
|
+
return { kind: 'refused', reason: accepted.reason };
|
|
34
|
+
}
|
|
35
|
+
const absorbedStepIds = (0, state_machine_1.uncoveredFailedStepIds)(state).filter((id) => id !== step.id);
|
|
36
|
+
let commit;
|
|
37
|
+
let entry;
|
|
38
|
+
try {
|
|
39
|
+
const result = await (0, migrate_commits_1.commitMigrationIfRequested)(root, { name: (0, state_machine_1.unresolvedCommitName)(step) }, true, state.commitPrefix, () => (0, util_1.installDepsChangedSinceDispense)(root, dir, step, input.skipInstall, input.reconcileCommand, input.output), (0, state_machine_1.stepsToPendingMigrations)(state, absorbedStepIds), undefined, input.output);
|
|
40
|
+
commit = result;
|
|
41
|
+
entry = (0, state_machine_1.commitResultToLedgerEntry)(result, step.id, absorbedStepIds);
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
// The install is the only thrower: the commit reports through its result.
|
|
45
|
+
// The debt cannot stand in for the install failure, since a later commit
|
|
46
|
+
// absorbing this diff clears the debt while the dependencies are still
|
|
47
|
+
// missing.
|
|
48
|
+
commit = { status: 'install-failed', reason: (0, util_1.summarizeError)(e) };
|
|
49
|
+
entry = { kind: 'failed', stepIds: [step.id] };
|
|
50
|
+
}
|
|
51
|
+
const ran = commit.status !== 'install-failed' && (0, state_machine_1.gitRan)(commit);
|
|
52
|
+
if (ran)
|
|
53
|
+
lease.markedStepId = undefined;
|
|
54
|
+
let archiveError;
|
|
55
|
+
let refusal;
|
|
56
|
+
(0, state_lock_1.updateRunState)(dir, (fresh) => {
|
|
57
|
+
// Re-applied on fresh state. The reservation kept every other commit of
|
|
58
|
+
// the step out, so only run state edited from outside can refuse it now;
|
|
59
|
+
// the commit then stays unrecorded, with the mark standing for an adopt.
|
|
60
|
+
const applied = (0, state_machine_1.applyStepEvent)(ownMark ? (0, state_machine_1.clearCommitStarted)(fresh, step.id) : fresh, event);
|
|
61
|
+
if (applied.kind === 'error') {
|
|
62
|
+
refusal = applied.reason;
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
// A failure once git ran keeps the mark: only a landed entry accounts
|
|
66
|
+
// for the commit it may have made.
|
|
67
|
+
const recorded = recordUnresolvedIssue(dir, ownMark && commit.status === 'failed'
|
|
68
|
+
? (0, state_machine_1.markCommitStarted)(applied.state, step.id)
|
|
69
|
+
: applied.state, step.id);
|
|
70
|
+
archiveError = recorded.archiveError;
|
|
71
|
+
const next = commit.status === 'install-failed'
|
|
72
|
+
? (0, state_machine_1.markInstallFailed)(recorded.state, step.id)
|
|
73
|
+
: recorded.state;
|
|
74
|
+
return entry
|
|
75
|
+
? (0, state_machine_1.appendCommit)(next, (0, issues_1.attachIssueIdsToCommitEntry)(next, entry))
|
|
76
|
+
: next;
|
|
77
|
+
});
|
|
78
|
+
if (refusal !== undefined) {
|
|
79
|
+
return {
|
|
80
|
+
kind: 'refused',
|
|
81
|
+
reason: entry?.kind === 'landed' && entry.sha
|
|
82
|
+
? `${refusal} Note: this action's commit ${entry.sha} had already landed and stays in history; resolve the step against the tree as it stands now.`
|
|
83
|
+
: refusal,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
kind: 'given-up',
|
|
88
|
+
commit,
|
|
89
|
+
...(archiveError !== undefined ? { archiveError } : {}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Mints the issue that carries a given-up step's failure and points the step
|
|
94
|
+
* at it, on state where the step is already unresolved. The archive file is
|
|
95
|
+
* best-effort: run.json is authoritative, and undoing the transition for a
|
|
96
|
+
* lost detail file would make the agent re-issue an action the run already
|
|
97
|
+
* took.
|
|
98
|
+
*/
|
|
99
|
+
function recordUnresolvedIssue(dir, state, stepId) {
|
|
100
|
+
// Minted from the transitioned step: a died one only gains its failure in
|
|
101
|
+
// the transition.
|
|
102
|
+
const minted = (0, issues_1.mintUnresolvedIssue)(state, state.steps.find((s) => s.id === stepId));
|
|
103
|
+
let archiveError;
|
|
104
|
+
try {
|
|
105
|
+
(0, issues_1.archiveIssues)(dir, minted.application);
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
archiveError = (0, util_1.summarizeError)(e);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
state: {
|
|
112
|
+
...minted.application.state,
|
|
113
|
+
steps: minted.application.state.steps.map((s) => s.id === stepId ? { ...s, unresolvedIssueId: minted.issueId } : s),
|
|
114
|
+
},
|
|
115
|
+
...(archiveError !== undefined ? { archiveError } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function warnUnresolvedNotArchived(migrationId, archiveError) {
|
|
119
|
+
(0, agent_output_1.warnToAgent)({
|
|
120
|
+
title: `The issue recording that ${migrationId} was left unresolved could not be archived (${archiveError}).`,
|
|
121
|
+
bodyLines: [
|
|
122
|
+
`run.json stays authoritative: the step is unresolved and the issue is in its ledger; only the archived file under the run's issues directory is missing.`,
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/** What the reconcile owes the agent for a give-up that went through. */
|
|
127
|
+
function warnAboutGiveUp(step, outcome) {
|
|
128
|
+
const name = (0, state_machine_1.unresolvedCommitName)(step);
|
|
129
|
+
switch (outcome.commit.status) {
|
|
130
|
+
case 'install-failed':
|
|
131
|
+
(0, util_1.warnCommitFailed)(name, outcome.commit.reason);
|
|
132
|
+
break;
|
|
133
|
+
case 'failed':
|
|
134
|
+
(0, util_1.warnCommitFailed)(name);
|
|
135
|
+
break;
|
|
136
|
+
case 'committed':
|
|
137
|
+
case 'no-changes':
|
|
138
|
+
case 'disabled':
|
|
139
|
+
break;
|
|
140
|
+
default: {
|
|
141
|
+
const exhaustive = outcome.commit;
|
|
142
|
+
throw new Error(`Unhandled commit result: ${JSON.stringify(exhaustive)}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (outcome.archiveError !== undefined) {
|
|
146
|
+
warnUnresolvedNotArchived(step.migrationId, outcome.archiveError);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export { CURRENT_RUN_STATE_FORMAT_VERSION, NewerRunStateFormatError, migrateRunsDir, runDir, runHandoffsDir, readRunState, writeRunState, findActiveRun, createRun, } from './run-state';
|
|
2
2
|
export type { MigrateRunStatus, MigrateRunRound, MigrateStepStatus, PromptOutcomeStatus, MigrateStepAwaitingKind, MigrateStepOutcome, MigrateStepPromptOutcome, MigrateStep, MigrateCommitKind, MigrateCommitLedgerEntry, MigrateIssueDisposition, MigrateRunIssue, MigrateRunAnalytics, MigrateRunNoProgress, MigrateRunPolicy, MigrateRunState, } from './run-state';
|
|
3
|
-
export { applyStepEvent, hasPendingCommitDebt } from './state-machine';
|
|
3
|
+
export { applyStepEvent, completionSummaryLines, hasPendingCommitDebt, latestRound, runTallies, tallySteps, } from './state-machine';
|
|
4
|
+
export { hasUnresolvedIssues } from './issues';
|
|
4
5
|
export type { StepAction, StepEvent, ApplyStepEventResult, } from './state-machine';
|
|
5
|
-
export { createRunId
|
|
6
|
+
export { createRunId } from './run-id';
|
|
6
7
|
export { runSingleMigrationWorker } from './worker';
|
|
7
8
|
export type { RunSingleMigrationWorkerInput } from './worker';
|
|
8
|
-
export { completionWarnings, runOrchestratorInit, runOrchestratorReconcile, } from './orchestrator';
|
|
9
|
-
export type { RunOrchestratorInitInput, RunOrchestratorReconcileInput, } from './orchestrator';
|
|
9
|
+
export { activeRunToReplace, completionWarnings, holdRunToContinue, releaseRunToHandOff, runOrchestratorInit, runOrchestratorReconcile, runOrchestratorResume, } from './orchestrator';
|
|
10
|
+
export type { OrchestratorInitResult, RunOrchestratorInitInput, RunOrchestratorReconcileInput, } from './orchestrator';
|
|
11
|
+
export { renderContinueCommand, renderExistingRunCommands, renderExistingRunReport, } from './existing-run-report';
|
|
10
12
|
export { BROKER_ENV_VAR, MigrateCommitBroker, treeOperationLabel, } from './broker';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.treeOperationLabel = exports.MigrateCommitBroker = exports.BROKER_ENV_VAR = exports.runOrchestratorReconcile = exports.runOrchestratorInit = exports.completionWarnings = exports.
|
|
3
|
+
exports.treeOperationLabel = exports.MigrateCommitBroker = exports.BROKER_ENV_VAR = exports.renderExistingRunReport = exports.renderExistingRunCommands = exports.renderContinueCommand = exports.runOrchestratorResume = exports.runOrchestratorReconcile = exports.runOrchestratorInit = exports.releaseRunToHandOff = exports.holdRunToContinue = exports.completionWarnings = exports.activeRunToReplace = exports.runSingleMigrationWorker = exports.createRunId = exports.hasUnresolvedIssues = exports.tallySteps = exports.runTallies = exports.latestRound = exports.hasPendingCommitDebt = exports.completionSummaryLines = exports.applyStepEvent = exports.createRun = exports.findActiveRun = exports.writeRunState = exports.readRunState = exports.runHandoffsDir = exports.runDir = exports.migrateRunsDir = exports.NewerRunStateFormatError = exports.CURRENT_RUN_STATE_FORMAT_VERSION = void 0;
|
|
4
4
|
var run_state_1 = require("./run-state");
|
|
5
5
|
Object.defineProperty(exports, "CURRENT_RUN_STATE_FORMAT_VERSION", { enumerable: true, get: function () { return run_state_1.CURRENT_RUN_STATE_FORMAT_VERSION; } });
|
|
6
6
|
Object.defineProperty(exports, "NewerRunStateFormatError", { enumerable: true, get: function () { return run_state_1.NewerRunStateFormatError; } });
|
|
@@ -13,16 +13,29 @@ Object.defineProperty(exports, "findActiveRun", { enumerable: true, get: functio
|
|
|
13
13
|
Object.defineProperty(exports, "createRun", { enumerable: true, get: function () { return run_state_1.createRun; } });
|
|
14
14
|
var state_machine_1 = require("./state-machine");
|
|
15
15
|
Object.defineProperty(exports, "applyStepEvent", { enumerable: true, get: function () { return state_machine_1.applyStepEvent; } });
|
|
16
|
+
Object.defineProperty(exports, "completionSummaryLines", { enumerable: true, get: function () { return state_machine_1.completionSummaryLines; } });
|
|
16
17
|
Object.defineProperty(exports, "hasPendingCommitDebt", { enumerable: true, get: function () { return state_machine_1.hasPendingCommitDebt; } });
|
|
18
|
+
Object.defineProperty(exports, "latestRound", { enumerable: true, get: function () { return state_machine_1.latestRound; } });
|
|
19
|
+
Object.defineProperty(exports, "runTallies", { enumerable: true, get: function () { return state_machine_1.runTallies; } });
|
|
20
|
+
Object.defineProperty(exports, "tallySteps", { enumerable: true, get: function () { return state_machine_1.tallySteps; } });
|
|
21
|
+
var issues_1 = require("./issues");
|
|
22
|
+
Object.defineProperty(exports, "hasUnresolvedIssues", { enumerable: true, get: function () { return issues_1.hasUnresolvedIssues; } });
|
|
17
23
|
var run_id_1 = require("./run-id");
|
|
18
24
|
Object.defineProperty(exports, "createRunId", { enumerable: true, get: function () { return run_id_1.createRunId; } });
|
|
19
|
-
Object.defineProperty(exports, "computePlanHash", { enumerable: true, get: function () { return run_id_1.computePlanHash; } });
|
|
20
25
|
var worker_1 = require("./worker");
|
|
21
26
|
Object.defineProperty(exports, "runSingleMigrationWorker", { enumerable: true, get: function () { return worker_1.runSingleMigrationWorker; } });
|
|
22
27
|
var orchestrator_1 = require("./orchestrator");
|
|
28
|
+
Object.defineProperty(exports, "activeRunToReplace", { enumerable: true, get: function () { return orchestrator_1.activeRunToReplace; } });
|
|
23
29
|
Object.defineProperty(exports, "completionWarnings", { enumerable: true, get: function () { return orchestrator_1.completionWarnings; } });
|
|
30
|
+
Object.defineProperty(exports, "holdRunToContinue", { enumerable: true, get: function () { return orchestrator_1.holdRunToContinue; } });
|
|
31
|
+
Object.defineProperty(exports, "releaseRunToHandOff", { enumerable: true, get: function () { return orchestrator_1.releaseRunToHandOff; } });
|
|
24
32
|
Object.defineProperty(exports, "runOrchestratorInit", { enumerable: true, get: function () { return orchestrator_1.runOrchestratorInit; } });
|
|
25
33
|
Object.defineProperty(exports, "runOrchestratorReconcile", { enumerable: true, get: function () { return orchestrator_1.runOrchestratorReconcile; } });
|
|
34
|
+
Object.defineProperty(exports, "runOrchestratorResume", { enumerable: true, get: function () { return orchestrator_1.runOrchestratorResume; } });
|
|
35
|
+
var existing_run_report_1 = require("./existing-run-report");
|
|
36
|
+
Object.defineProperty(exports, "renderContinueCommand", { enumerable: true, get: function () { return existing_run_report_1.renderContinueCommand; } });
|
|
37
|
+
Object.defineProperty(exports, "renderExistingRunCommands", { enumerable: true, get: function () { return existing_run_report_1.renderExistingRunCommands; } });
|
|
38
|
+
Object.defineProperty(exports, "renderExistingRunReport", { enumerable: true, get: function () { return existing_run_report_1.renderExistingRunReport; } });
|
|
26
39
|
var broker_1 = require("./broker");
|
|
27
40
|
Object.defineProperty(exports, "BROKER_ENV_VAR", { enumerable: true, get: function () { return broker_1.BROKER_ENV_VAR; } });
|
|
28
41
|
Object.defineProperty(exports, "MigrateCommitBroker", { enumerable: true, get: function () { return broker_1.MigrateCommitBroker; } });
|
|
@@ -43,6 +43,18 @@ export interface IssueApplication {
|
|
|
43
43
|
}[];
|
|
44
44
|
updates: IssueArchiveUpdate[];
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Mints the run issue carrying a given-up step's last failure to the completion
|
|
48
|
+
* report: unscoped and deferred, so no later step claims it, with the migration
|
|
49
|
+
* id in the summary so identical failures stay apart. A report with the same
|
|
50
|
+
* text is taken over, not merged: the fingerprint is derived from the summary,
|
|
51
|
+
* so the ledger cannot hold both, and a merge would keep the report's scope.
|
|
52
|
+
* Same locking contract as {@link applyReportedIssues}.
|
|
53
|
+
*/
|
|
54
|
+
export declare function mintUnresolvedIssue(state: MigrateRunState, step: MigrateStep): {
|
|
55
|
+
application: IssueApplication;
|
|
56
|
+
issueId: string;
|
|
57
|
+
};
|
|
46
58
|
/**
|
|
47
59
|
* Pure. Must run on state read fresh inside the fold's locked write, or ids
|
|
48
60
|
* collide and the claim checks test state that is no longer on disk.
|
|
@@ -94,6 +106,8 @@ export declare function settleUnclaimableIssues(state: MigrateRunState): Migrate
|
|
|
94
106
|
*/
|
|
95
107
|
export declare function renderIssueDigestLines(state: MigrateRunState, currentStepId: string, runId: string): string[];
|
|
96
108
|
export declare function renderUnresolvedIssueLines(state: MigrateRunState, runId: string): string[];
|
|
109
|
+
export declare function unresolvedIssues(state: MigrateRunState): MigrateRunIssue[];
|
|
110
|
+
export declare function hasUnresolvedIssues(state: MigrateRunState): boolean;
|
|
97
111
|
export declare function issueArchivePath(runDirPath: string, issueId: string): string;
|
|
98
112
|
export declare function warnReconstructedArchives(issueIds: string[]): void;
|
|
99
113
|
/**
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.issueFingerprint = void 0;
|
|
6
6
|
exports.parseHandoffIssues = parseHandoffIssues;
|
|
7
|
+
exports.mintUnresolvedIssue = mintUnresolvedIssue;
|
|
7
8
|
exports.applyReportedIssues = applyReportedIssues;
|
|
8
9
|
exports.issueIdsForCommit = issueIdsForCommit;
|
|
9
10
|
exports.attachIssueIdsToCommitEntry = attachIssueIdsToCommitEntry;
|
|
@@ -13,10 +14,13 @@ exports.claimIssuesForStep = claimIssuesForStep;
|
|
|
13
14
|
exports.settleUnclaimableIssues = settleUnclaimableIssues;
|
|
14
15
|
exports.renderIssueDigestLines = renderIssueDigestLines;
|
|
15
16
|
exports.renderUnresolvedIssueLines = renderUnresolvedIssueLines;
|
|
17
|
+
exports.unresolvedIssues = unresolvedIssues;
|
|
18
|
+
exports.hasUnresolvedIssues = hasUnresolvedIssues;
|
|
16
19
|
exports.issueArchivePath = issueArchivePath;
|
|
17
20
|
exports.warnReconstructedArchives = warnReconstructedArchives;
|
|
18
21
|
exports.archiveIssues = archiveIssues;
|
|
19
22
|
exports.applicationArchivesIntact = applicationArchivesIntact;
|
|
23
|
+
const crypto_1 = require("crypto");
|
|
20
24
|
const fs_1 = require("fs");
|
|
21
25
|
const path_1 = require("path");
|
|
22
26
|
const fileutils_1 = require("../../../utils/fileutils");
|
|
@@ -27,6 +31,8 @@ const agent_output_1 = require("./agent-output");
|
|
|
27
31
|
const run_state_1 = require("./run-state");
|
|
28
32
|
Object.defineProperty(exports, "issueFingerprint", { enumerable: true, get: function () { return run_state_1.issueFingerprint; } });
|
|
29
33
|
const state_machine_1 = require("./state-machine");
|
|
34
|
+
// Reserves summary space for the attempt count and the failure.
|
|
35
|
+
const MAX_UNRESOLVED_ID_CHARS = 200;
|
|
30
36
|
const ISSUES_DIR_NAME = 'issues';
|
|
31
37
|
// Bounds on one agent-written handoff's issue report. A report over a bound is
|
|
32
38
|
// rejected whole for the agent to fix, never trimmed.
|
|
@@ -241,6 +247,46 @@ function mappedStepIds(identifier, state) {
|
|
|
241
247
|
.filter((s) => (0, state_machine_1.splitMigrationId)(s.migrationId).package === identifier)
|
|
242
248
|
.map((s) => s.id);
|
|
243
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* Mints the run issue carrying a given-up step's last failure to the completion
|
|
252
|
+
* report: unscoped and deferred, so no later step claims it, with the migration
|
|
253
|
+
* id in the summary so identical failures stay apart. A report with the same
|
|
254
|
+
* text is taken over, not merged: the fingerprint is derived from the summary,
|
|
255
|
+
* so the ledger cannot hold both, and a merge would keep the report's scope.
|
|
256
|
+
* Same locking contract as {@link applyReportedIssues}.
|
|
257
|
+
*/
|
|
258
|
+
function mintUnresolvedIssue(state, step) {
|
|
259
|
+
const attempts = `${step.attempt} attempt${step.attempt === 1 ? '' : 's'}`;
|
|
260
|
+
const prefix = `Migration ${abbreviatedMigrationId(step.migrationId)} was left unresolved after ${attempts}: `;
|
|
261
|
+
const room = MAX_SUMMARY_CHARS - prefix.length;
|
|
262
|
+
const detail = (0, state_machine_1.unresolvedFailureDetail)(step);
|
|
263
|
+
const summary = prefix +
|
|
264
|
+
(detail.length > room ? `${detail.slice(0, room - 3)}...` : detail);
|
|
265
|
+
const ledger = state.issues ?? [];
|
|
266
|
+
const index = ledger.findIndex((i) => i.fingerprint === (0, run_state_1.issueFingerprint)(summary));
|
|
267
|
+
if (index === -1) {
|
|
268
|
+
const application = applyReportedIssues(state, step, [{ summary, applicableMigrations: 'unknown' }], []);
|
|
269
|
+
return { application, issueId: application.newIssues[0].entry.id };
|
|
270
|
+
}
|
|
271
|
+
const { claimedByStepId: _claim, resolvedByStepId: _credit, resolvedAtCommitCount: _fence, ...rest } = ledger[index];
|
|
272
|
+
const entry = {
|
|
273
|
+
...rest,
|
|
274
|
+
applicableStepIds: 'unknown',
|
|
275
|
+
disposition: 'deferred-final',
|
|
276
|
+
};
|
|
277
|
+
const issues = [...ledger];
|
|
278
|
+
issues[index] = entry;
|
|
279
|
+
return {
|
|
280
|
+
application: {
|
|
281
|
+
state: { ...state, issues },
|
|
282
|
+
newIssues: [],
|
|
283
|
+
updates: [
|
|
284
|
+
{ issueId: entry.id, stepId: step.id, disposition: 'deferred-final' },
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
issueId: entry.id,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
244
290
|
/**
|
|
245
291
|
* Pure. Must run on state read fresh inside the fold's locked write, or ids
|
|
246
292
|
* collide and the claim checks test state that is no longer on disk.
|
|
@@ -731,6 +777,9 @@ function renderUnresolvedIssueLines(state, runId) {
|
|
|
731
777
|
function unresolvedIssues(state) {
|
|
732
778
|
return (state.issues ?? []).filter((i) => i.disposition !== 'resolved');
|
|
733
779
|
}
|
|
780
|
+
function hasUnresolvedIssues(state) {
|
|
781
|
+
return unresolvedIssues(state).length > 0;
|
|
782
|
+
}
|
|
734
783
|
function stepDigestHeading(runId) {
|
|
735
784
|
return `Known issues reported earlier in this run (details under ${issuesDirRef(runId)}):`;
|
|
736
785
|
}
|
|
@@ -886,6 +935,16 @@ const NEW_ISSUE_ARCHIVE_KEYS = [
|
|
|
886
935
|
'disposition',
|
|
887
936
|
'detail',
|
|
888
937
|
];
|
|
938
|
+
// Hashes the full id so ids sharing a visible prefix stay distinct.
|
|
939
|
+
function abbreviatedMigrationId(migrationId) {
|
|
940
|
+
if (migrationId.length <= MAX_UNRESOLVED_ID_CHARS)
|
|
941
|
+
return migrationId;
|
|
942
|
+
const digest = (0, crypto_1.createHash)('sha256')
|
|
943
|
+
.update(migrationId)
|
|
944
|
+
.digest('hex')
|
|
945
|
+
.slice(0, 8);
|
|
946
|
+
return `${migrationId.slice(0, MAX_UNRESOLVED_ID_CHARS - 12)}...[${digest}]`;
|
|
947
|
+
}
|
|
889
948
|
// Shared by the write path and the intactness check so their shapes cannot
|
|
890
949
|
// drift.
|
|
891
950
|
function newIssueArchiveRecord(entry, report) {
|