Skip to content
File

Blob: src/worker/dispatch/shared/run-execution-context/types.ts

typescript143 lines
1import type { ExecutionSession, Process, Sandbox } from "@cloudflare/sandbox";
2 
3import { type ProjectId, type RunId, type UnixTimestampMs } from "@/contracts";
4import {
5 type AcceptedRunSnapshot,
6 type AppendRunLogsInput,
7 type ExecuteRunWork,
8 type FinalizeRunExecutionResult,
9 type PositiveInteger,
10 type ProjectRunStatus,
11 type RecordRunResolvedCommitInput,
12 type RecordRunResolvedCommitResult,
13 type ReplaceRunStepsInput,
14 type RepoConfig,
15 type RunHeartbeatResult,
16 type RunMetaState,
17 type UpdateRunStateInput,
18 type UpdateRunStepStateInput,
19} from "@/worker/contracts";
20import type { ProjectDO, RunDO } from "@/worker/durable";
21import type { ProjectExecutionMaterial } from "@/worker/durable/project-do/types";
22import type { TryUpdateRunStateResult } from "@/worker/durable/run-do/repo/core";
23 
24export type RunExecutionPhase =
25 | "booting"
26 | "checking_out"
27 | "running_step"
28 | "canceling"
29 | "cleaning_up"
30 | "finalizing";
31 
32export type RunExecutionOutcome =
33 | { kind: "passed"; exitCode: number | null }
34 | { kind: "failed"; exitCode: number | null; errorMessage: string }
35 | { kind: "canceled" }
36 | { kind: "ownership_lost"; observedStatus: ProjectRunStatus | null };
37 
38export interface PreparedExecutionEnvironment {
39 readonly repoConfig: RepoConfig;
40 readonly workingDirectory: string;
41}
42 
43export interface RunExecutionLogContext {
44 readonly projectId: ProjectId;
45 readonly runId: RunId;
46}
47 
48export interface RunExecutionScope {
49 readonly env: Env;
50 readonly executionMaterial: ProjectExecutionMaterial;
51 readonly claim: ExecuteRunWork;
52 readonly snapshot: AcceptedRunSnapshot;
53 readonly projectId: ProjectId;
54 readonly runId: RunId;
55 readonly repoRoot: string;
56 readonly startedAt: UnixTimestampMs;
57 readonly logContext: RunExecutionLogContext;
58}
59 
60export interface RunExecutionContextState {
61 phase: RunExecutionPhase;
62 session: ExecutionSession | null;
63 currentProcess: Process | null;
64 currentStepPosition: PositiveInteger | null;
65 cancelRequestedAt: UnixTimestampMs | null;
66 preservedTerminalStatus: Extract<RunMetaState["status"], "passed" | "failed"> | null;
67 ownershipLost: boolean;
68 ownershipLossStatus: ProjectRunStatus | null;
69 softCancelIssued: boolean;
70 hardCancelIssued: boolean;
71 redactionSecrets: string[];
72}
73 
74export type RunStateUpdateOverrides = Partial<
75 Pick<RunMetaState, "currentStep" | "startedAt" | "finishedAt" | "exitCode" | "errorMessage">
76>;
77 
78export interface RunStore {
79 getFreshStub(): DurableObjectStub<RunDO>;
80 getMeta(): Promise<RunMetaState>;
81 updateState(input: Omit<UpdateRunStateInput, "runId">): Promise<void>;
82 tryUpdateState(input: Omit<UpdateRunStateInput, "runId">): Promise<TryUpdateRunStateResult>;
83 repairTerminalState(input: Omit<UpdateRunStateInput, "runId">): Promise<void>;
84 replaceSteps(input: Omit<ReplaceRunStepsInput, "runId">): Promise<void>;
85 updateStepState(input: Omit<UpdateRunStepStateInput, "runId">): Promise<void>;
86 appendLogs(events: AppendRunLogsInput["events"]): Promise<void>;
87}
88 
89export interface ProjectControl {
90 getFreshStub(): DurableObjectStub<ProjectDO>;
91 recordHeartbeat(): Promise<RunHeartbeatResult>;
92 recordResolvedCommit(commitSha: RecordRunResolvedCommitInput["commitSha"]): Promise<RecordRunResolvedCommitResult>;
93 finalizeRunExecution(
94 terminalStatus: Extract<RunMetaState["status"], "passed" | "failed" | "canceled">,
95 lastError: string | null,
96 sandboxDestroyed: boolean,
97 ): Promise<FinalizeRunExecutionResult>;
98 kickReconciliation(trigger: string): Promise<void>;
99}
100 
101export interface RunLogs {
102 appendSystemLog(message: string): Promise<void>;
103 redactMessage(message: string): string;
104}
105 
106export interface RunRuntime {
107 readonly sandbox: Sandbox;
108 getSession(sessionId: string): Promise<ExecutionSession>;
109 deleteSession(sessionId: string): Promise<void>;
110 disposeSession(session: ExecutionSession | null): void;
111 getLiveCurrentProcess(): Process | null;
112 isProcessTreeAlive(session: ExecutionSession): Promise<boolean>;
113 softCancelProcessTree(session: ExecutionSession, process: Process | null): Promise<void>;
114 hardCancelProcessTree(session: ExecutionSession, process: Process | null): Promise<void>;
115 waitForProcessTreeToStop(session: ExecutionSession, timeoutMs: number, pollIntervalMs?: number): Promise<boolean>;
116 waitForProcessTreeToStopSafely(session: ExecutionSession, timeoutMs: number, cleanupPhase: string): Promise<boolean>;
117 destroySandbox(): Promise<boolean>;
118 dispose(): void;
119}
120 
121export interface RunControl {
122 getRunMeta(): Promise<RunMetaState>;
123 updateRunFromCurrent(
124 current: RunMetaState,
125 status: UpdateRunStateInput["status"],
126 overrides?: RunStateUpdateOverrides,
127 ): Promise<RunMetaState>;
128 preserveTerminalOutcome(outcome: Exclude<RunExecutionOutcome, { kind: "ownership_lost" | "canceled" }>): void;
129 ensureRunCancelRequested(): Promise<RunMetaState | null>;
130 ensureRunCanceling(): Promise<RunMetaState | null>;
131 markOwnershipLost(status: ProjectRunStatus | null): void;
132}
133 
134export interface RunExecutionContext {
135 readonly scope: RunExecutionScope;
136 readonly state: RunExecutionContextState;
137 readonly runStore: RunStore;
138 readonly projectControl: ProjectControl;
139 readonly runtime: RunRuntime;
140 readonly logs: RunLogs;
141 readonly control: RunControl;
142}