deepline 0.3.90 → 0.3.92
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/bundling-sources/sdk/src/client.ts +101 -6
- package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +2 -556
- package/dist/bundling-sources/sdk/src/monitor-fleets.ts +2 -2
- package/dist/bundling-sources/sdk/src/release.ts +7 -12
- package/dist/bundling-sources/sdk/src/types.ts +6 -1
- package/dist/bundling-sources/shared_libs/integration-contracts/monitor-fleet-contract.ts +549 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/errors.ts +96 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/limits.ts +8 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/plan.ts +320 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/verdict.ts +106 -0
- package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +5 -1
- package/dist/bundling-sources/shared_libs/monitors/monitor-fleet-limits.ts +2 -12
- package/dist/bundling-sources/shared_libs/monitors/package.json +107 -5
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +12 -7
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +48 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/db-session-lease-store.ts +578 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +521 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-receipt-gateway.ts +1378 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/runtime-topology.ts +1024 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-gateway-route-probe.ts +193 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machine-lifecycle.ts +104 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +1844 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/process-tree.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +262 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-surface.ts +98 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +303 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-publication-guardian.ts +70 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-recovery.ts +519 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-fly-rollback-alignment.ts +280 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-production-gate.ts +159 -0
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/memory-store.ts +114 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +44 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/child-play-resolver.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +3662 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/progress-sidecar.ts +47 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +84 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-gateway-transport.ts +91 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-heartbeat.ts +424 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +321 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-sheet-persistence-retry.ts +110 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +625 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +46 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/webhook-receipt-retry.ts +50 -0
- package/dist/bundling-sources/shared_libs/plays/bundling/repository-adapter.ts +24 -0
- package/dist/bundling-sources/shared_libs/plays/child-run-projection.ts +174 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/api-client.ts +267 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/control-plane-types.ts +73 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/manifest.ts +162 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/project-serializer.ts +87 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/source-file-path.ts +26 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/target-paths.ts +36 -0
- package/dist/bundling-sources/shared_libs/plays/launch/input-contract.ts +400 -0
- package/dist/bundling-sources/shared_libs/plays/launch/prepare.ts +96 -0
- package/dist/bundling-sources/shared_libs/plays/local-file-discovery.ts +385 -0
- package/dist/bundling-sources/shared_libs/plays/play-grep-search.ts +79 -6
- package/dist/bundling-sources/shared_libs/plays/play-reference.ts +117 -0
- package/dist/bundling-sources/shared_libs/plays/run-input-summary.ts +260 -0
- package/dist/bundling-sources/shared_libs/plays/run-summary-types.ts +49 -0
- package/dist/bundling-sources/shared_libs/plays/secrets/errors.ts +19 -0
- package/dist/bundling-sources/shared_libs/plays/sheet-filter.ts +347 -0
- package/dist/bundling-sources/shared_libs/plays/standard-webhooks.ts +117 -0
- package/dist/bundling-sources/shared_libs/plays/status-result.ts +388 -0
- package/dist/bundling-sources/shared_libs/plays/transform-factor-structure.ts +377 -0
- package/dist/bundling-sources/shared_libs/plays/webhook-payload.ts +113 -0
- package/dist/cli/index.js +369 -162
- package/dist/cli/index.mjs +369 -162
- package/dist/{compiler-manifest-BaJfE-5H.d.mts → compiler-manifest-x14KL83E.d.mts} +1 -1
- package/dist/{compiler-manifest-BaJfE-5H.d.ts → compiler-manifest-x14KL83E.d.ts} +1 -1
- package/dist/index.d.mts +45 -8
- package/dist/index.d.ts +45 -8
- package/dist/index.js +61 -14
- package/dist/index.mjs +61 -14
- package/dist/install-integrity.json +54 -2
- package/dist/monitor-fleet-contract.d.mts +3 -10
- package/dist/monitor-fleet-contract.d.ts +3 -10
- package/dist/monitor-fleet-contract.js +2 -0
- package/dist/monitor-fleet-contract.mjs +1 -1
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/release.d.mts +3 -3
- package/dist/release.d.ts +3 -3
- package/dist/release.js +7 -12
- package/dist/release.mjs +7 -12
- package/package.json +2 -2
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
export function exactFlyMachineRouteHeaders(
|
|
2
|
+
machineId: string,
|
|
3
|
+
): Record<'fly-force-instance-id', string> {
|
|
4
|
+
return { 'fly-force-instance-id': machineId };
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Fly Proxy can briefly lose an exact-instance route while an uncordon is
|
|
9
|
+
* propagating. A one-shot timeout is not evidence that an already-proven
|
|
10
|
+
* candidate is bad. Keep the bounded convergence policy here so receipt and
|
|
11
|
+
* execution cutovers do not invent different retry semantics.
|
|
12
|
+
*/
|
|
13
|
+
export const GATEWAY_ROUTE_PROBE_TIMEOUT_MS = 120_000;
|
|
14
|
+
export const GATEWAY_ROUTE_PROBE_RETRY_MS = 5_000;
|
|
15
|
+
|
|
16
|
+
export type GatewayRouteProbeRetry = Readonly<{
|
|
17
|
+
description: string;
|
|
18
|
+
attempt: number;
|
|
19
|
+
retryInMs: number;
|
|
20
|
+
remainingMs: number;
|
|
21
|
+
error: Error;
|
|
22
|
+
}>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Retry lines deliberately carry the current cause and remaining budget. They
|
|
26
|
+
* are concise enough for the normal release timeline and let an operator see
|
|
27
|
+
* a transient Fly route converge instead of seeing a silent two-minute wait.
|
|
28
|
+
*/
|
|
29
|
+
export function formatGatewayRouteProbeRetry(
|
|
30
|
+
input: GatewayRouteProbeRetry,
|
|
31
|
+
): string {
|
|
32
|
+
return `[gateway-route-probe] ${input.description}: retry ${input.attempt}; next probe in ${input.retryInMs}ms; ${input.remainingMs}ms remaining; cause=${input.error.name}: ${input.error.message}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class GatewayRouteProbeHttpError extends Error {
|
|
36
|
+
readonly status: number;
|
|
37
|
+
readonly actualMachineId: string | null;
|
|
38
|
+
readonly expectedMachineId: string | undefined;
|
|
39
|
+
|
|
40
|
+
constructor(input: {
|
|
41
|
+
status: number;
|
|
42
|
+
body: string;
|
|
43
|
+
actualMachineId: string | null;
|
|
44
|
+
expectedMachineId?: string;
|
|
45
|
+
}) {
|
|
46
|
+
super(
|
|
47
|
+
`Runtime gateway health check failed: HTTP ${input.status}, body=${JSON.stringify(input.body.slice(0, 200))}.`,
|
|
48
|
+
);
|
|
49
|
+
this.name = 'GatewayRouteProbeHttpError';
|
|
50
|
+
this.status = input.status;
|
|
51
|
+
this.actualMachineId = input.actualMachineId;
|
|
52
|
+
this.expectedMachineId = input.expectedMachineId;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isRetryableGatewayRouteProbeError(error: unknown): boolean {
|
|
57
|
+
if (!(error instanceof Error)) return false;
|
|
58
|
+
if (error.name === 'TimeoutError' || error.name === 'AbortError') return true;
|
|
59
|
+
// Fetch surfaces a dropped Fly Proxy connection as an ordinary Error/TypeError
|
|
60
|
+
// before the request timeout can fire. Keep this list narrow: contract,
|
|
61
|
+
// identity, and application failures must still roll back immediately.
|
|
62
|
+
if (
|
|
63
|
+
/^(fetch failed|unable to connect\.|connection (?:reset|refused)|network error$)/i.test(
|
|
64
|
+
error.message,
|
|
65
|
+
)
|
|
66
|
+
) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
if (error instanceof GatewayRouteProbeHttpError) {
|
|
70
|
+
const transientStatus =
|
|
71
|
+
error.status === 408 ||
|
|
72
|
+
error.status === 425 ||
|
|
73
|
+
error.status === 429 ||
|
|
74
|
+
error.status >= 500;
|
|
75
|
+
if (!transientStatus) return false;
|
|
76
|
+
return (
|
|
77
|
+
error.actualMachineId === null ||
|
|
78
|
+
(error.expectedMachineId !== undefined &&
|
|
79
|
+
error.actualMachineId !== error.expectedMachineId)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return (
|
|
83
|
+
/^Replacement Machine .+ did not serve its public route probe/.test(
|
|
84
|
+
error.message,
|
|
85
|
+
) ||
|
|
86
|
+
/^Execution relay Machine .+ failed its exact readiness probe\.$/.test(
|
|
87
|
+
error.message,
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function waitForGatewayRouteProbe(input: {
|
|
93
|
+
description: string;
|
|
94
|
+
probe: () => Promise<void>;
|
|
95
|
+
now?: () => number;
|
|
96
|
+
sleep?: (milliseconds: number) => Promise<void>;
|
|
97
|
+
timeoutMs?: number;
|
|
98
|
+
retryMs?: number;
|
|
99
|
+
onRetry?: (retry: GatewayRouteProbeRetry) => void | Promise<void>;
|
|
100
|
+
}): Promise<void> {
|
|
101
|
+
const now = input.now ?? Date.now;
|
|
102
|
+
const sleep =
|
|
103
|
+
input.sleep ??
|
|
104
|
+
((milliseconds: number) =>
|
|
105
|
+
new Promise<void>((resolve) => setTimeout(resolve, milliseconds)));
|
|
106
|
+
const timeoutMs = input.timeoutMs ?? GATEWAY_ROUTE_PROBE_TIMEOUT_MS;
|
|
107
|
+
const retryMs = input.retryMs ?? GATEWAY_ROUTE_PROBE_RETRY_MS;
|
|
108
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
'Gateway route probe timeout must be a non-negative integer.',
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
if (!Number.isSafeInteger(retryMs) || retryMs <= 0) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
'Gateway route probe retry interval must be a positive integer.',
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const deadline = now() + timeoutMs;
|
|
120
|
+
let lastRetryableError: Error | undefined;
|
|
121
|
+
let attempt = 0;
|
|
122
|
+
for (;;) {
|
|
123
|
+
if (lastRetryableError && now() >= deadline) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`${input.description} did not become routeable within ${timeoutMs} ms: ${lastRetryableError.message}`,
|
|
126
|
+
{ cause: lastRetryableError },
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
await input.probe();
|
|
131
|
+
return;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (
|
|
134
|
+
!isRetryableGatewayRouteProbeError(error) ||
|
|
135
|
+
!(error instanceof Error)
|
|
136
|
+
) {
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
lastRetryableError = error;
|
|
140
|
+
if (now() >= deadline) {
|
|
141
|
+
throw new Error(
|
|
142
|
+
`${input.description} did not become routeable within ${timeoutMs} ms: ${lastRetryableError.message}`,
|
|
143
|
+
{ cause: lastRetryableError },
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
attempt += 1;
|
|
147
|
+
const remainingMs = Math.max(0, deadline - now());
|
|
148
|
+
const retryInMs = Math.min(retryMs, remainingMs);
|
|
149
|
+
// Report the first retry, then sampled checkpoints, and the final
|
|
150
|
+
// budget-consuming retry. This proves active convergence without
|
|
151
|
+
// turning a transient route delay into a wall of log lines.
|
|
152
|
+
if (
|
|
153
|
+
input.onRetry &&
|
|
154
|
+
(attempt === 1 || attempt % 5 === 0 || retryInMs === remainingMs)
|
|
155
|
+
) {
|
|
156
|
+
await input.onRetry({
|
|
157
|
+
description: input.description,
|
|
158
|
+
attempt,
|
|
159
|
+
retryInMs,
|
|
160
|
+
remainingMs,
|
|
161
|
+
error: lastRetryableError,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
await sleep(retryInMs);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function assertGatewayRouteProbe(input: {
|
|
170
|
+
status: number;
|
|
171
|
+
body: string;
|
|
172
|
+
expectedMachineId?: string;
|
|
173
|
+
actualMachineId: string | null;
|
|
174
|
+
allowMissingExpectedIdentity?: boolean;
|
|
175
|
+
}): void {
|
|
176
|
+
if (
|
|
177
|
+
!Number.isInteger(input.status) ||
|
|
178
|
+
input.status < 200 ||
|
|
179
|
+
input.status >= 300 ||
|
|
180
|
+
!/\bok\b/i.test(input.body)
|
|
181
|
+
) {
|
|
182
|
+
throw new GatewayRouteProbeHttpError(input);
|
|
183
|
+
}
|
|
184
|
+
if (!input.expectedMachineId) return;
|
|
185
|
+
if (input.allowMissingExpectedIdentity && input.actualMachineId === null) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (input.actualMachineId !== input.expectedMachineId) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`Replacement Machine ${input.expectedMachineId} did not serve its public route probe (served=${input.actualMachineId ?? '<missing>'}).`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/** Shared Fly Machine identity and provider-confirmed deletion primitives. */
|
|
2
|
+
export type FlyMachine = {
|
|
3
|
+
id?: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
state?: string;
|
|
6
|
+
cordoned?: boolean;
|
|
7
|
+
region?: string;
|
|
8
|
+
/**
|
|
9
|
+
* `name` and `output` are what make a failing check actionable in a release
|
|
10
|
+
* log; Fly always sends them alongside `status`.
|
|
11
|
+
*/
|
|
12
|
+
checks?: Array<{ name?: string; status?: string; output?: string }>;
|
|
13
|
+
events?: Array<{ type?: string; timestamp?: number }>;
|
|
14
|
+
config?: {
|
|
15
|
+
image?: string;
|
|
16
|
+
metadata?: Record<string, string | undefined>;
|
|
17
|
+
guest?: {
|
|
18
|
+
cpu_kind?: string;
|
|
19
|
+
cpus?: number;
|
|
20
|
+
memory_mb?: number;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Fly Machines API expects a Go duration string. Never send bare seconds.
|
|
26
|
+
export const RECEIPT_GATEWAY_DRAIN_TIMEOUT = '300s';
|
|
27
|
+
|
|
28
|
+
export async function stopAndDestroyCordonedMachines(input: {
|
|
29
|
+
machineIds: string[];
|
|
30
|
+
readMachines: () => FlyMachine[] | Promise<FlyMachine[]>;
|
|
31
|
+
listProviderMachineIds: () => Promise<string[]>;
|
|
32
|
+
stopMachine: (machineId: string) => Promise<void>;
|
|
33
|
+
deleteMachine: (machineId: string) => Promise<void>;
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
pollIntervalMs?: number;
|
|
36
|
+
now?: () => number;
|
|
37
|
+
sleep?: (ms: number) => Promise<void>;
|
|
38
|
+
}): Promise<void> {
|
|
39
|
+
const timeoutMs = input.timeoutMs ?? 10 * 60_000;
|
|
40
|
+
const pollIntervalMs = input.pollIntervalMs ?? 5_000;
|
|
41
|
+
const now = input.now ?? Date.now;
|
|
42
|
+
const sleep =
|
|
43
|
+
input.sleep ??
|
|
44
|
+
((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));
|
|
45
|
+
const deadline = now() + timeoutMs;
|
|
46
|
+
const readTargets = async (): Promise<FlyMachine[]> =>
|
|
47
|
+
(await input.readMachines()).filter((machine) =>
|
|
48
|
+
input.machineIds.includes(machine.id ?? ''),
|
|
49
|
+
);
|
|
50
|
+
const assertCordoned = (machines: FlyMachine[]): void => {
|
|
51
|
+
for (const machine of machines) {
|
|
52
|
+
if (machine.cordoned !== true) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Refusing to delete Machine ${machine.id ?? '<missing>'} because it is not cordoned.`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const initial = await readTargets();
|
|
61
|
+
assertCordoned(initial);
|
|
62
|
+
for (const machine of initial) {
|
|
63
|
+
if (!['stopped', 'suspended'].includes(machine.state ?? '')) {
|
|
64
|
+
await input.stopMachine(machine.id!);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let stopped: FlyMachine[] = [];
|
|
69
|
+
while (true) {
|
|
70
|
+
const machines = await readTargets();
|
|
71
|
+
assertCordoned(machines);
|
|
72
|
+
if (
|
|
73
|
+
machines.length === 0 ||
|
|
74
|
+
machines.every((machine) =>
|
|
75
|
+
['stopped', 'suspended'].includes(machine.state ?? ''),
|
|
76
|
+
)
|
|
77
|
+
) {
|
|
78
|
+
stopped = machines;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
if (now() >= deadline) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Cordoned Machines did not stop within ${timeoutMs} ms: ${machines.map((machine) => `${machine.id ?? '<missing>'}=${machine.state ?? 'unknown'}`).join(', ')}.`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
await sleep(Math.min(pollIntervalMs, Math.max(0, deadline - now())));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const machine of stopped) await input.deleteMachine(machine.id!);
|
|
90
|
+
while (true) {
|
|
91
|
+
const providerMachineIds = await input.listProviderMachineIds();
|
|
92
|
+
if (
|
|
93
|
+
input.machineIds.every(
|
|
94
|
+
(machineId) => !providerMachineIds.includes(machineId),
|
|
95
|
+
)
|
|
96
|
+
) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (now() >= deadline) {
|
|
100
|
+
throw new Error('Cordoned Machine deletion is not provider-confirmed.');
|
|
101
|
+
}
|
|
102
|
+
await sleep(Math.min(pollIntervalMs, Math.max(0, deadline - now())));
|
|
103
|
+
}
|
|
104
|
+
}
|