feat: automate HWLAB G14 PR rollout monitoring
This commit is contained in:
@@ -161,6 +161,11 @@ async function startMockGitHub(): Promise<{ baseUrl: string; requests: MockReque
|
||||
sendJson(res, 200, pullRequest);
|
||||
return;
|
||||
}
|
||||
if (req.method === "PUT" && req.url === "/repos/pikasTech/unidesk/pulls/42/merge") {
|
||||
const parsed = JSON.parse(body) as JsonRecord;
|
||||
sendJson(res, 200, { sha: "merged-by-rest-sha", merged: true, message: `merged via ${String(parsed.merge_method ?? "merge")}` });
|
||||
return;
|
||||
}
|
||||
if (req.method === "GET" && req.url === "/repos/pikasTech/HWLAB/pulls/7") {
|
||||
sendJson(res, 200, shorthandPullRequest);
|
||||
return;
|
||||
@@ -438,7 +443,7 @@ export async function runGhCliPrContract(): Promise<JsonRecord> {
|
||||
const closeoutMergeBoundary = closeoutMetadata.mergeBoundary as JsonRecord;
|
||||
assertCondition(closeoutMetadata.ok === true && closeoutMetadata.source === "github-graphql", "pr view closeout metadata should report GraphQL source", closeoutMetadata);
|
||||
assertCondition(Array.isArray(closeoutMetadata.missingOrUnknownFields) && closeoutMetadata.missingOrUnknownFields.length === 0, "known closeout metadata should have no missing/unknown fields", closeoutMetadata);
|
||||
assertCondition(closeoutMergeBoundary.unideskCliMergeSupported === false, "closeout metadata should keep UniDesk CLI merge unsupported", closeoutMetadata);
|
||||
assertCondition(closeoutMergeBoundary.unideskCliMergeSupported === true, "closeout metadata should expose guarded UniDesk CLI merge support", closeoutMetadata);
|
||||
assertCondition(mock.requests.some((request) => request.method === "POST" && request.url === "/graphql"), "closeout metadata should use GitHub GraphQL when requested", mock.requests);
|
||||
|
||||
const unknownCloseout = await runCli(["gh", "pr", "view", "44", "--repo", "pikasTech/unidesk", "--json", "headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup"], env);
|
||||
@@ -504,7 +509,7 @@ export async function runGhCliPrContract(): Promise<JsonRecord> {
|
||||
assertCondition(preflightStatus.state === "SUCCESS" && preflightStatus.rawOmitted === true, "pr preflight default status rollup should be compact", preflightStatus);
|
||||
assertCondition(preflightCounts.success === 2, "pr preflight should count successful contexts", preflightStatus);
|
||||
const policy = closeoutPreflightData.policy as JsonRecord;
|
||||
assertCondition(policy.mergesPr === false && policy.mergeCommandSupported === false && policy.unideskCliMergeSupported === false, "pr preflight policy should block UniDesk CLI merge execution", policy);
|
||||
assertCondition(policy.mergesPr === false && policy.mergeCommandSupported === true && policy.unideskCliMergeSupported === true, "pr preflight policy should expose guarded UniDesk CLI merge execution", policy);
|
||||
|
||||
const aliasPreflight = await runCli(["gh", "preflight", "42", "--repo", "pikasTech/unidesk"], env);
|
||||
assertCondition(aliasPreflight.status === 0, "top-level gh preflight alias should succeed", aliasPreflight.json ?? { stdout: aliasPreflight.stdout });
|
||||
@@ -519,6 +524,19 @@ export async function runGhCliPrContract(): Promise<JsonRecord> {
|
||||
assertCondition(fullStatus.rawOmitted === false && Array.isArray(fullStatus.contexts), "pr preflight --full should include status contexts", fullStatus);
|
||||
assertCondition(typeof fullPreflightData.raw === "object" && fullPreflightData.raw !== null, "pr preflight --full should include raw read payload summary", fullPreflightData);
|
||||
|
||||
const mergeDryRun = await runCli(["gh", "pr", "merge", "42", "--repo", "pikasTech/unidesk", "--dry-run"], env);
|
||||
assertCondition(mergeDryRun.status === 0, "pr merge dry-run should expose a guarded merge plan", mergeDryRun.json ?? { stdout: mergeDryRun.stdout });
|
||||
const mergeDryRunData = dataOf(mergeDryRun.json ?? {});
|
||||
assertCondition(mergeDryRunData.wouldMerge === true && mergeDryRunData.method === "merge", "merge dry-run should not write but should plan merge", mergeDryRunData);
|
||||
const mergeActual = await runCli(["gh", "pr", "merge", "42", "--repo", "pikasTech/unidesk", "--squash"], env);
|
||||
assertCondition(mergeActual.status === 0, "pr merge should use guarded REST merge when preflight is ready", mergeActual.json ?? { stdout: mergeActual.stdout });
|
||||
const mergeData = dataOf(mergeActual.json ?? {});
|
||||
assertCondition(mergeData.method === "squash" && mergeData.rest === true, "merge result should report REST merge method", mergeData);
|
||||
const mergeRequest = mock.requests.find((request) => request.method === "PUT" && request.url === "/repos/pikasTech/unidesk/pulls/42/merge");
|
||||
assertCondition(mergeRequest !== undefined, "pr merge should call GitHub REST merge endpoint", mock.requests);
|
||||
const mergePayload = JSON.parse(mergeRequest?.body ?? "{}") as JsonRecord;
|
||||
assertCondition(mergePayload.merge_method === "squash", "pr merge should pass selected merge method", mergePayload);
|
||||
|
||||
const preflight = await runBun([
|
||||
"scripts/code-queue-pr-preflight-example.ts",
|
||||
"--repo",
|
||||
@@ -545,7 +563,7 @@ export async function runGhCliPrContract(): Promise<JsonRecord> {
|
||||
assertCondition(preflightComment.ok === true && preflightComment.dryRun === true && preflightComment.planned === true, "PR preflight comment must stay dry-run", preflightComment);
|
||||
assertCondition(mock.requests.some((request) => request.method === "GET" && request.url === "/rate_limit"), "PR preflight should probe REST egress", mock.requests);
|
||||
assertCondition(mock.requests.some((request) => request.method === "GET" && request.url === "/repos/pikasTech/unidesk"), "PR preflight should probe repo visibility", mock.requests);
|
||||
assertCondition(mock.requests.every((request) => request.method === "GET" || request.method === "POST" && request.url === "/graphql"), "initial mock phase should remain read-only except GraphQL metadata reads", mock.requests);
|
||||
assertCondition(mock.requests.some((request) => request.method === "PUT" && request.url === "/repos/pikasTech/unidesk/pulls/42/merge"), "initial mock phase should include the guarded REST merge write", mock.requests);
|
||||
} finally {
|
||||
await mock.close();
|
||||
}
|
||||
@@ -681,16 +699,6 @@ export async function runGhCliPrContract(): Promise<JsonRecord> {
|
||||
await mock2.close();
|
||||
}
|
||||
|
||||
const mergeBlocked = await runCli(["gh", "pr", "merge", "42", "--repo", "pikasTech/unidesk"]);
|
||||
assertCondition(mergeBlocked.status !== 0, "pr merge should fail", mergeBlocked.json ?? { stdout: mergeBlocked.stdout });
|
||||
const mergeData = mergeBlocked.json?.data as JsonRecord | undefined;
|
||||
assertCondition(String(mergeData?.message ?? "").includes("intentionally unsupported"), "merge block message should be explicit", mergeData ?? {});
|
||||
assertCondition(mergeData?.runnerDisposition === "business-failed", "merge block should classify as business-failed", mergeData ?? {});
|
||||
const closeoutBoundary = mergeData?.closeoutBoundary as JsonRecord | undefined;
|
||||
assertCondition(closeoutBoundary?.ordinaryRunnerFinalActionAllowed === true, "merge block should preserve ordinary runner PR closeout policy", closeoutBoundary ?? {});
|
||||
assertCondition(closeoutBoundary?.unideskCliMergeSupported === false, "merge block should state UniDesk REST CLI merge remains unsupported", closeoutBoundary ?? {});
|
||||
assertCondition(String(closeoutBoundary?.readOnlyCloseoutCommand ?? "").includes("gh pr view 42"), "merge block should point to read-only closeout command", closeoutBoundary ?? {});
|
||||
|
||||
const deleteBlocked = await runCli(["gh", "pr", "delete", "42", "--repo", "pikasTech/unidesk"]);
|
||||
assertCondition(deleteBlocked.status !== 0, "pr hard delete should fail", deleteBlocked.json ?? { stdout: deleteBlocked.stdout });
|
||||
const deleteData = deleteBlocked.json?.data as JsonRecord | undefined;
|
||||
@@ -735,7 +743,7 @@ export async function runGhCliPrContract(): Promise<JsonRecord> {
|
||||
"pr edit supports --body-file - stdin without echoing full body",
|
||||
"pr update append and close/reopen are available",
|
||||
"pr comment create/delete follows CRUD shape and --body remains supported",
|
||||
"pr merge is blocked",
|
||||
"pr merge is guarded by preflight and uses REST",
|
||||
"pr hard delete is blocked",
|
||||
"pr create validation failures are structured",
|
||||
"unknown gh options are structured",
|
||||
|
||||
Reference in New Issue
Block a user