feat: install codex release flows
This commit is contained in:
parent
27979cb621
commit
39e843fb29
12 changed files with 922 additions and 3 deletions
147
apps/patch/test/flow.test.ts
Normal file
147
apps/patch/test/flow.test.ts
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
import { describe, expect, test } from "bun:test";
|
||||
import type { FlowRunView } from "@peezy.tech/flow-runtime/client";
|
||||
import {
|
||||
maintenanceAttemptForWorkspaceDispatch,
|
||||
maintenanceAttemptWithWorkspaceRuns,
|
||||
patchUpstreamReleaseEvent,
|
||||
} from "../src/flow";
|
||||
import type {
|
||||
CandidateRefRecord,
|
||||
FlowDispatchRecord,
|
||||
MaintenanceAttemptRecord,
|
||||
} from "../src/types";
|
||||
|
||||
describe("maintenance attempt sync", () => {
|
||||
test("aggregates multi-run fanout statuses and candidate refs", () => {
|
||||
const attempt = baseAttempt();
|
||||
const next = maintenanceAttemptWithWorkspaceRuns(attempt, [
|
||||
flowRun("run-completed", "completed", [candidate("refs/heads/a", "aaa")]),
|
||||
flowRun("run-changed", "changed", [candidate("refs/heads/b", "bbb")]),
|
||||
], "2026-05-16T00:10:00.000Z");
|
||||
|
||||
expect(next.status).toBe("changed");
|
||||
expect(next.workspaceRunIds).toEqual(["run-completed", "run-changed"]);
|
||||
expect(next.workspaceRunStatuses).toEqual({
|
||||
"run-completed": "completed",
|
||||
"run-changed": "changed",
|
||||
});
|
||||
expect(next.candidateRefs).toMatchObject([
|
||||
{ ref: "refs/heads/a", sha: "aaa" },
|
||||
{ ref: "refs/heads/b", sha: "bbb" },
|
||||
]);
|
||||
expect(next.completedAt).toBe("2026-05-16T00:10:00.000Z");
|
||||
});
|
||||
|
||||
test("uses failure precedence across partial fanout", () => {
|
||||
expect(statusFor(["completed", "skipped"])).toBe("completed");
|
||||
expect(statusFor(["skipped", "skipped"])).toBe("skipped");
|
||||
expect(statusFor(["completed", "changed"])).toBe("changed");
|
||||
expect(statusFor(["changed", "failed"])).toBe("failed");
|
||||
expect(statusFor(["failed", "blocked"])).toBe("blocked");
|
||||
expect(statusFor(["blocked", "needs_intervention"])).toBe("needs_intervention");
|
||||
});
|
||||
|
||||
test("preserves successful candidates when another run fails", () => {
|
||||
const next = maintenanceAttemptWithWorkspaceRuns(baseAttempt(), [
|
||||
flowRun("run-ok", "completed", [candidate("refs/heads/candidate", "abc")]),
|
||||
flowRun("run-failed", "failed", [], "release verification failed"),
|
||||
]);
|
||||
|
||||
expect(next.status).toBe("failed");
|
||||
expect(next.error).toBe("release verification failed");
|
||||
expect(next.candidateRefs).toMatchObject([
|
||||
{ ref: "refs/heads/candidate", sha: "abc" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("records replay attempts with workspace run results", () => {
|
||||
const event = patchUpstreamReleaseEvent({
|
||||
repo: "openai/codex",
|
||||
tag: "rust-v0.130.0",
|
||||
receivedAt: "2026-05-16T00:00:00.000Z",
|
||||
});
|
||||
const record: FlowDispatchRecord = {
|
||||
eventId: event.id,
|
||||
eventType: event.type,
|
||||
operation: "replay",
|
||||
target: "workspace-backend",
|
||||
transport: "workspace-ws",
|
||||
workspaceBackendUrl: "ws://127.0.0.1:3586",
|
||||
status: "dispatched",
|
||||
runIds: ["run-a", "run-b"],
|
||||
matched: 2,
|
||||
createdAt: "2026-05-16T00:05:00.000Z",
|
||||
};
|
||||
|
||||
const attempt = maintenanceAttemptForWorkspaceDispatch(event, record, [
|
||||
flowRun("run-a", "completed"),
|
||||
flowRun("run-b", "changed", [candidate("refs/heads/codex-candidate", "def")]),
|
||||
]);
|
||||
|
||||
expect(attempt.id).toBe(`${event.id}:replay:${record.createdAt}`);
|
||||
expect(attempt.operation).toBe("replay");
|
||||
expect(attempt.status).toBe("changed");
|
||||
expect(attempt.upstreamRepo).toBe("openai/codex");
|
||||
expect(attempt.upstreamTag).toBe("rust-v0.130.0");
|
||||
expect(attempt.workspaceRunIds).toEqual(["run-a", "run-b"]);
|
||||
expect(attempt.candidateRefs).toMatchObject([
|
||||
{ ref: "refs/heads/codex-candidate", sha: "def" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
function statusFor(statuses: string[]): string {
|
||||
return maintenanceAttemptWithWorkspaceRuns(
|
||||
baseAttempt(),
|
||||
statuses.map((status, index) => flowRun(`run-${index}`, status)),
|
||||
).status;
|
||||
}
|
||||
|
||||
function baseAttempt(): MaintenanceAttemptRecord {
|
||||
return {
|
||||
id: "attempt-1",
|
||||
eventId: "event-1",
|
||||
eventType: "upstream.release",
|
||||
operation: "dispatch",
|
||||
status: "started",
|
||||
upstreamRepo: "openai/codex",
|
||||
upstreamTag: "rust-v0.130.0",
|
||||
workspaceRunIds: [],
|
||||
candidateRefs: [],
|
||||
createdAt: "2026-05-16T00:00:00.000Z",
|
||||
updatedAt: "2026-05-16T00:00:00.000Z",
|
||||
};
|
||||
}
|
||||
|
||||
function flowRun(
|
||||
id: string,
|
||||
status: string,
|
||||
candidateRefs: CandidateRefRecord[] = [],
|
||||
message = `${id} ${status}`,
|
||||
): FlowRunView {
|
||||
return {
|
||||
id,
|
||||
eventId: "event-1",
|
||||
flowName: "test-flow",
|
||||
stepName: id,
|
||||
status,
|
||||
effectiveStatus: status,
|
||||
completedAt: "2026-05-16T00:10:00.000Z",
|
||||
resultPayload: {
|
||||
status,
|
||||
message,
|
||||
artifacts: { candidateRefs },
|
||||
},
|
||||
} as FlowRunView;
|
||||
}
|
||||
|
||||
function candidate(ref: string, sha: string): CandidateRefRecord {
|
||||
return {
|
||||
kind: "branch",
|
||||
repo: "peezy-tech/codex",
|
||||
remote: "local",
|
||||
ref,
|
||||
sha,
|
||||
pushed: false,
|
||||
};
|
||||
}
|
||||
|
|
@ -53,6 +53,41 @@ describe("patch.moi harness flow", () => {
|
|||
expect(afterHead).toBe(beforeHead);
|
||||
expect(await git(["status", "--porcelain=v1"])).toBe("");
|
||||
});
|
||||
|
||||
test("matches installed Codex release flows without executing release work", async () => {
|
||||
const flows = await discoverFlows({ cwd: workspaceRoot });
|
||||
const matches = await matchingSteps(flows, {
|
||||
id: "patch:upstream.release:openai/codex:rust-v0.130.0",
|
||||
type: "upstream.release",
|
||||
source: "patch",
|
||||
receivedAt: "2026-05-16T00:00:00.000Z",
|
||||
payload: { repo: "openai/codex", tag: "rust-v0.130.0" },
|
||||
});
|
||||
|
||||
expect(matches.map(({ flow, step }) => `${flow.manifest.name}/${step.name}`)).toEqual([
|
||||
"openai-codex-bindings/regenerate-bindings",
|
||||
"peezy-codex-fork/rebase-patch-stack",
|
||||
]);
|
||||
|
||||
const codeModeMatch = matches.find((entry) => entry.flow.manifest.name === "peezy-codex-fork");
|
||||
expect(codeModeMatch?.step.runner).toBe("code-mode");
|
||||
if (!codeModeMatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
await expect(runFlowStep({
|
||||
flow: codeModeMatch.flow,
|
||||
step: codeModeMatch.step,
|
||||
event: {
|
||||
id: "patch:upstream.release:openai/codex:rust-v0.130.0",
|
||||
type: "upstream.release",
|
||||
source: "patch",
|
||||
receivedAt: "2026-05-16T00:00:00.000Z",
|
||||
payload: { repo: "openai/codex", tag: "rust-v0.130.0" },
|
||||
},
|
||||
env: {},
|
||||
})).rejects.toThrow("requires CODEX_FLOWS_ENABLE_CODE_MODE=1");
|
||||
});
|
||||
});
|
||||
|
||||
async function git(args: string[]): Promise<string> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue