gitea: give the controller what it maps a pull request onto the graph with; set both check statuses; protect a branch by tool (hq ADR 0237)
The controller now decides what a pull request's check runs from the mesh's module graph, so the announcement carries the changed directories that hold a module at the head and whether the head has a merge-check.sh. A verdict sets mesh/merge-gate (the gate, with the modules it judged) and mesh/repo-check (the repository's own tests). gitea_branch_protection_get/set let the operator's agent make those statuses required. The catalogue's merge-check.sh leaves the gate to the build seat and keeps its own layer: every manifest through module check, and the touched Go modules' tests.
This commit is contained in:
@@ -10,7 +10,18 @@
|
||||
|
||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||
import { emit } from "@novox/mesh-sdk/events";
|
||||
import { GiteaClient } from "../client.js";
|
||||
import { GiteaClient, type BranchProtection } from "../client.js";
|
||||
|
||||
/** A protection rule as a person reads it: what it guards, not every field the forge keeps. */
|
||||
export function summarised(p: BranchProtection) {
|
||||
return {
|
||||
rule: p.rule_name ?? p.branch_name,
|
||||
push: p.enable_push ?? false,
|
||||
required_statuses: p.enable_status_check ? (p.status_check_contexts ?? []) : [],
|
||||
required_approvals: p.required_approvals ?? 0,
|
||||
admin_may_override: !(p.block_admin_merge_override ?? false),
|
||||
};
|
||||
}
|
||||
|
||||
/** Coerce a comma-separated label string into names; empty/absent yields none. */
|
||||
function parseLabels(raw: unknown): string[] {
|
||||
@@ -361,6 +372,46 @@ export function getGiteaTools(gitea: GiteaClient): ToolDefinition[] {
|
||||
},
|
||||
},
|
||||
|
||||
// ---- Branch protection (novox/hq ADR 0237) ----
|
||||
{
|
||||
name: "gitea_branch_protection_get",
|
||||
description: "A repository's branch protection: the rule for one branch (null when it has none) or every rule — whether direct pushes are refused, which commit statuses a pull request must have succeeded to merge (e.g. mesh/merge-gate), approvals, and whether an administrator may merge past them.",
|
||||
input: {
|
||||
owner: { type: "string", description: "the repository owner" },
|
||||
repo: { type: "string", description: "the repository name" },
|
||||
branch: { type: "string", description: "the branch (rule name); every rule when not given" },
|
||||
},
|
||||
run: async (args) => {
|
||||
const owner = String(args.owner), repo = String(args.repo);
|
||||
if (!args.branch) return { rules: (await gitea.branchProtections(owner, repo)).map(summarised) };
|
||||
const rule = await gitea.branchProtection(owner, repo, String(args.branch));
|
||||
return { branch: String(args.branch), rule: rule ? summarised(rule) : null };
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "gitea_branch_protection_set",
|
||||
description: "Make a branch require commit statuses before a pull request merges into it — the mesh's merge check sets `mesh/merge-gate` (the module graph's gate) and `mesh/repo-check` (the repository's own tests). Edits the branch's rule (its required statuses replaced by these, everything else kept unless given) or creates one, which refuses direct pushes unless push=true. Answers the rule before and after.",
|
||||
input: {
|
||||
owner: { type: "string", description: "the repository owner" },
|
||||
repo: { type: "string", description: "the repository name" },
|
||||
branch: { type: "string", description: "the branch to protect, e.g. main" },
|
||||
status_checks: { type: "string", description: "comma-separated statuses required to merge, e.g. mesh/merge-gate,mesh/repo-check; empty requires none" },
|
||||
push: { type: "boolean", description: "whether a person may push to the branch directly (a new rule refuses it when not given)" },
|
||||
block_admin_override: { type: "boolean", description: "stop an administrator merging past a status that has not succeeded (left as it is when not given)" },
|
||||
},
|
||||
run: async (args) => {
|
||||
const owner = String(args.owner), repo = String(args.repo), branch = String(args.branch ?? "").trim();
|
||||
if (!branch) throw new Error("name the branch to protect");
|
||||
const before = await gitea.branchProtection(owner, repo, branch);
|
||||
const { created, rule } = await gitea.setBranchProtection(owner, repo, branch, {
|
||||
statusChecks: String(args.status_checks ?? "").split(","),
|
||||
push: args.push === undefined ? undefined : Boolean(args.push),
|
||||
blockAdminOverride: args.block_admin_override === undefined ? undefined : Boolean(args.block_admin_override),
|
||||
});
|
||||
return { branch, created, before: before ? summarised(before) : null, after: summarised(rule) };
|
||||
},
|
||||
},
|
||||
|
||||
// ---- Labels ----
|
||||
{
|
||||
name: "gitea_list_labels",
|
||||
|
||||
Reference in New Issue
Block a user