GIT5: the forge's push webhook of a branch says branch-moved with the new head on the seat
Builds: design 47 G §11, §111, §331 branch-moved Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -10,9 +10,9 @@
|
||||
import { seatPublish } from "@novox/mesh-sdk/runtime";
|
||||
import {
|
||||
forgeDeleteSubject, forgePullSubject, forgeStatusSubject, pullClosedSubject, pullMergedSubject, pullUpdatedSubject,
|
||||
repoWritableSubject,
|
||||
repoWritableSubject, branchMovedSubject,
|
||||
type Change, type ForgeDeleteEvent, type ForgePullEvent, type ForgeStatusEvent, type PullClosedEvent, type PullMergedEvent,
|
||||
type PullUpdatedEvent, type RepoWritableEvent,
|
||||
type PullUpdatedEvent, type RepoWritableEvent, type BranchMovedEvent,
|
||||
} from "@novox/mesh-delivery-contracts-git";
|
||||
|
||||
/** The forge's pull request actions the seat's forge.pull says (the contract's word list). */
|
||||
@@ -36,8 +36,18 @@ function changeOf(b: Body): Change | undefined {
|
||||
}
|
||||
|
||||
/** The seat's subject and body for one of the module's announcements, or undefined when the seat says no such event. */
|
||||
export function seatEventOf(type: string, b: Body): { subject: string; body: unknown } | undefined {
|
||||
export function seatEventOf(type: string, b: Body, now: () => Date = () => new Date()): { subject: string; body: unknown; id?: string } | undefined {
|
||||
switch (type) {
|
||||
case "forge.push": {
|
||||
// A branch moved, by a merge or an admitted push alike (design 47 §11): the forge's push webhook of a branch with a
|
||||
// head after it. A tag's push, and a branch's deletion (the head is the zero hash), move no branch.
|
||||
const ref = s(b.ref) ?? "";
|
||||
const head = s(b.after) ?? "";
|
||||
if (!ref.startsWith("refs/heads/") || !/^[0-9a-f]{40,64}$/.test(head) || /^0+$/.test(head)) return undefined;
|
||||
const branch = ref.slice("refs/heads/".length);
|
||||
const body: BranchMovedEvent = { repository: repositoryOf(b), branch, head, at: now().toISOString() };
|
||||
return { subject: branchMovedSubject, body, id: `moved:${body.repository}:${branch}@${head}` };
|
||||
}
|
||||
case "pull.updated": {
|
||||
const body: PullUpdatedEvent = { repository: repositoryOf(b), number: Number(b.number), base: String(b.base ?? ""),
|
||||
branch: String(b.head ?? ""), head: String(b.head_sha ?? ""), ...opt("base-head", s(b.base_sha)),
|
||||
@@ -82,7 +92,8 @@ export function seatEventOf(type: string, b: Body): { subject: string; body: unk
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** The module's own events the seat says instead: never said as the module's (its `emits` names none of them). */
|
||||
/** The module's own events the seat says instead: never said as the module's (its `emits` names none of them). `forge.push`
|
||||
* stays the module's own too: the seat's branch-moved is said beside it. */
|
||||
export const SEAT_ONLY = new Set(["pull.updated", "pull.merged", "pull.closed", "forge.status", "forge.delete", "repo.writable"]);
|
||||
|
||||
export type Say = (subject: string, body: unknown, id?: string) => Promise<void>;
|
||||
@@ -91,6 +102,6 @@ export type Say = (subject: string, body: unknown, id?: string) => Promise<void>
|
||||
export async function sayOnSeat(type: string, body: Body, id = "", say: Say = seatPublish): Promise<boolean> {
|
||||
const e = seatEventOf(type, body);
|
||||
if (!e) return false;
|
||||
await say(e.subject, e.body, id);
|
||||
await say(e.subject, e.body, id || e.id);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -45,13 +45,19 @@ test("an announcement is said on the seat git with its id, in the contract's bod
|
||||
["repo.writable", { owner: "novox", repo: "app", account: "mesh-tools" }, "mesh.seat.git.event.repo-writable"],
|
||||
["forge.pull-request.synchronized", { full_name: "novox/app", number: 7, head_sha: "aaa111" }, "mesh.seat.git.event.forge-pull"],
|
||||
["forge.pull-request.labeled", { full_name: "novox/app", number: 7 }, undefined],
|
||||
["forge.push", { full_name: "novox/app" }, undefined],
|
||||
["forge.push", { full_name: "novox/app", ref: "refs/tags/v1", after: "a".repeat(40) }, undefined],
|
||||
["forge.push", { full_name: "novox/app", ref: "refs/heads/gone", after: "0".repeat(40) }, undefined],
|
||||
["forge.push", { full_name: "novox/app", ref: "refs/heads/feat/x", after: "a".repeat(40) }, "mesh.seat.git.event.branch-moved"],
|
||||
];
|
||||
for (const [type, body, subject] of rows) assert.equal(seatEventOf(type, body)?.subject, subject, type);
|
||||
assert.deepEqual(seatEventOf("pull.updated", updated)?.body, { repository: "novox/app", number: 7, base: "main", branch: "",
|
||||
head: "aaa111", "base-head": "ttt333" });
|
||||
assert.equal(seatEventOf("forge.pull-request.edited", { full_name: "novox/app", number: 7, body: "x" })?.body &&
|
||||
"body" in (seatEventOf("forge.pull-request.edited", { full_name: "novox/app", number: 7, body: "x" })!.body as object), false);
|
||||
// A push of a branch (a merge and an admitted push alike) says branch-moved with the new head, one id per move.
|
||||
const moved = seatEventOf("forge.push", { full_name: "novox/app", ref: "refs/heads/main", after: "b".repeat(40) }, () => new Date("2026-11-11T10:00:00Z"));
|
||||
assert.deepEqual(moved?.body, { repository: "novox/app", branch: "main", head: "b".repeat(40), at: "2026-11-11T10:00:00.000Z" });
|
||||
assert.equal(moved?.id, `moved:novox/app:main@${"b".repeat(40)}`);
|
||||
assert.deepEqual([...SEAT_ONLY].sort(), ["forge.delete", "forge.status", "pull.closed", "pull.merged", "pull.updated", "repo.writable"]);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user