Archived
make webhook
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import {
|
||||
type IssueSnapshot,
|
||||
type Marker,
|
||||
marker,
|
||||
parseMarker,
|
||||
protocolVersion,
|
||||
sha256,
|
||||
} from "../../core/contracts.js";
|
||||
import { parseAgentCommand } from "../../core/webhook.js";
|
||||
import type { GiteaComment, GiteaIssue } from "./types.js";
|
||||
|
||||
export function createIssueSnapshot(
|
||||
issue: GiteaIssue,
|
||||
comments: GiteaComment[],
|
||||
botLogin: string,
|
||||
): IssueSnapshot {
|
||||
const humanComments = comments
|
||||
.filter(
|
||||
(comment) =>
|
||||
comment.user.login.toLowerCase() !== botLogin.toLowerCase(),
|
||||
)
|
||||
.filter((comment) => !parseAgentCommand(comment.body))
|
||||
.map((comment) => ({
|
||||
id: comment.id,
|
||||
author: comment.user.login,
|
||||
createdAt: comment.created_at,
|
||||
body: comment.body,
|
||||
}));
|
||||
const canonical = JSON.stringify({
|
||||
v: protocolVersion,
|
||||
number: issue.number,
|
||||
state: issue.state,
|
||||
title: issue.title,
|
||||
body: issue.body,
|
||||
comments: humanComments.map(({ author, createdAt, body }) => ({
|
||||
author,
|
||||
createdAt,
|
||||
body,
|
||||
})),
|
||||
});
|
||||
return {
|
||||
digest: sha256(canonical),
|
||||
title: issue.title,
|
||||
body: issue.body,
|
||||
comments: humanComments,
|
||||
};
|
||||
}
|
||||
|
||||
export function findAcceptedPlan(
|
||||
comments: GiteaComment[],
|
||||
botLogin: string,
|
||||
issueNumber: number,
|
||||
):
|
||||
| {
|
||||
marker: Marker;
|
||||
markdown: string;
|
||||
comment: GiteaComment;
|
||||
}
|
||||
| undefined {
|
||||
const candidates = comments
|
||||
.filter(
|
||||
(comment) =>
|
||||
comment.user.login.toLowerCase() === botLogin.toLowerCase(),
|
||||
)
|
||||
.map((comment) => ({
|
||||
comment,
|
||||
found: parseMarker(comment.body, "plan"),
|
||||
}))
|
||||
.filter((value): value is { comment: GiteaComment; found: Marker } =>
|
||||
Boolean(value.found),
|
||||
)
|
||||
.filter(
|
||||
(value) =>
|
||||
value.found.issue === issueNumber &&
|
||||
value.found.status === "accepted",
|
||||
)
|
||||
.sort((a, b) =>
|
||||
b.comment.updated_at.localeCompare(a.comment.updated_at),
|
||||
);
|
||||
const selected = candidates[0];
|
||||
if (!selected) return undefined;
|
||||
const header = "## Accepted implementation plan\n\n";
|
||||
const start = selected.comment.body.indexOf(header);
|
||||
const end = selected.comment.body.lastIndexOf(
|
||||
"\n\n<!-- olixero-ci-agent:plan-footer -->",
|
||||
);
|
||||
if (start < 0) return undefined;
|
||||
const markdown = selected.comment.body
|
||||
.slice(start + header.length, end < 0 ? undefined : end)
|
||||
.trim();
|
||||
if (
|
||||
!selected.found.planDigest ||
|
||||
sha256(markdown) !== selected.found.planDigest
|
||||
)
|
||||
return undefined;
|
||||
return { marker: selected.found, markdown, comment: selected.comment };
|
||||
}
|
||||
|
||||
export function renderStatus(input: {
|
||||
marker: Marker;
|
||||
heading: string;
|
||||
detail: string;
|
||||
}): string {
|
||||
return `${marker(input.marker)}\n## ${input.heading}\n\n${input.detail}`;
|
||||
}
|
||||
Reference in New Issue
Block a user