The forge's announcer announces each new head of an open pull request as pull.updated, once, and marks the head pending; the controller asks the build seat to check it against every machine of the mesh's facts, and says the verdict as checked, which the forge's holder sets as the head commit's status mesh/merge-gate - an error never as a success - with the check's own account as a comment when it is not a pass. merge-check.sh is the catalogue's check: every manifest through the running controller's module check and merge gate, and the Go tests of each module the change touches, a module whose dependencies cannot be fetched said as not tested.
57 lines
2.9 KiB
TypeScript
57 lines
2.9 KiB
TypeScript
// A pull request's merge check (novox/hq to-be 45 §9): what the forge's announcer says when a pull
|
|
// request's head moves, and what it sets as the pull request's status when the mesh says the verdict.
|
|
//
|
|
// **Before merge, never after.** Every check the mesh had ran after a merge, on a machine: a manifest the
|
|
// node-engine refuses (issue 236), an identity a real machine's name made too long (263). So each new head
|
|
// of an open pull request is announced as `pull.updated`; the controller asks the build seat to check it
|
|
// against every machine of the mesh's facts; and the verdict comes back as the controller's `checked`,
|
|
// which this sets as the head commit's status — and, when it is not a pass, as a comment saying why.
|
|
//
|
|
// Pure functions here, so they are tested without a forge; index.ts does the asking and the setting.
|
|
|
|
import type { CommitStatus, GiteaPull } from "./client.js";
|
|
|
|
/** The status context a merge check is kept under: one per commit, the newest replacing the last. */
|
|
export const CHECK_CONTEXT = "mesh/merge-gate";
|
|
|
|
/** What the controller says as `checked` (mesh-controller internal/link, Checked). */
|
|
export interface Checked {
|
|
owner: string;
|
|
repo: string;
|
|
number?: number;
|
|
commit: string;
|
|
verdict: string;
|
|
summary: string;
|
|
report?: string;
|
|
id: string;
|
|
on?: string;
|
|
}
|
|
|
|
/** The heads already announced, keyed `owner/repo#number`, so a restart announces nothing twice. */
|
|
export type Announced = Record<string, string>;
|
|
|
|
/** Which open pull requests have a head not yet announced. A pull request merged or closed is not
|
|
* open and is never asked about. */
|
|
export function headsToAnnounce(full: string, pulls: GiteaPull[], announced: Announced): GiteaPull[] {
|
|
return pulls.filter((p) => p.state === "open" && !!p.head_sha && announced[`${full}#${p.number}`] !== p.head_sha);
|
|
}
|
|
|
|
/** The forge's status for a verdict: an error is the forge's `error`, never a success. */
|
|
export function statusFor(c: Checked): CommitStatus {
|
|
const state: CommitStatus["state"] =
|
|
c.verdict === "pass" ? "success" : c.verdict === "warning" ? "warning" : c.verdict === "fail" ? "failure" : "error";
|
|
// The forge keeps a short description; the rest is the comment's.
|
|
let description = `${c.verdict}: ${c.summary}`.replace(/\s+/g, " ").trim();
|
|
if (description.length > 140) description = description.slice(0, 139) + "…";
|
|
return { state, context: CHECK_CONTEXT, description };
|
|
}
|
|
|
|
/** The comment a verdict that is not a pass leaves on its pull request: the check's own account. */
|
|
export function commentFor(c: Checked): string | null {
|
|
if (c.verdict === "pass") return null;
|
|
const head = `**Merge check: ${c.verdict.toUpperCase()}** at \`${c.commit.slice(0, 8)}\` — ${c.summary}`;
|
|
const ran = c.on ? `\n\nRun by the build seat on ${c.on} as \`${c.id}\` (\`builds --log ${c.id}\`).` : "";
|
|
const report = c.report ? `\n\n\`\`\`\n${c.report.replace(/```/g, "'''")}\n\`\`\`` : "";
|
|
return head + ran + report;
|
|
}
|