Add Co-authored-by trailers to Merge's squash commit
This commit is contained in:
+50
-1
@@ -160,6 +160,25 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/tasks/{taskId}/co-authors": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
taskId: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
/** @description The humans other than the caller with a message in the task's transcript (sent its agent a message, or had an inbox answer delivered under their name), in order of their first message. A merge by the caller gives each one with a git identity a Co-authored-by trailer. */
|
||||
get: operations["getCoAuthors"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/tasks/{taskId}/merge": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -171,7 +190,7 @@ export interface paths {
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** @description Starts the same agent turn as merge-in. When that turn ends normally, byre squashes the task's worktree into one commit titled with the task's merge_title on the project's default branch, fast-forwards the project's checkout to it, then removes the worktree and branch and marks the task merged. Nothing is pushed. Refused while a turn is running or queued. */
|
||||
/** @description Starts the same agent turn as merge-in. When that turn ends normally, byre squashes the task's worktree into one commit titled with the task's merge_title on the project's default branch, authored by the caller with a Co-authored-by trailer for each of the task's co-authors, pushes it to origin, fast-forwards the project's checkout to it, then removes the worktree and branch and marks the task merged. Refused while a turn is running or queued. */
|
||||
post: operations["mergeTask"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
@@ -595,6 +614,13 @@ export interface components {
|
||||
/** @description Paths with uncommitted changes in the worktree. */
|
||||
uncommitted: number;
|
||||
};
|
||||
Participant: {
|
||||
login: string;
|
||||
display_name: string;
|
||||
/** @description Empty when the user hasn't set a git identity; they then get no trailer. */
|
||||
git_name: string;
|
||||
git_email: string;
|
||||
};
|
||||
TaskCreate: {
|
||||
title: string;
|
||||
};
|
||||
@@ -1163,6 +1189,29 @@ export interface operations {
|
||||
409: components["responses"]["Error"];
|
||||
};
|
||||
};
|
||||
getCoAuthors: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
taskId: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description The co-authors. */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["Participant"][];
|
||||
};
|
||||
};
|
||||
404: components["responses"]["Error"];
|
||||
};
|
||||
};
|
||||
mergeTask: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
||||
@@ -17,6 +17,7 @@ export type AgentProcess = Schemas["AgentProcess"];
|
||||
export type Zombie = Schemas["Zombie"];
|
||||
export type ProtocolErrors = Schemas["ProtocolErrors"];
|
||||
export type Snapshot = Schemas["Snapshot"];
|
||||
export type Participant = Schemas["Participant"];
|
||||
|
||||
type Method = "get" | "post" | "put" | "delete";
|
||||
|
||||
|
||||
+24
-3
@@ -1,4 +1,4 @@
|
||||
import { get, post, type ByreEvent, type Snapshot } from "./api.js";
|
||||
import { get, post, type ByreEvent, type Participant, type Snapshot } from "./api.js";
|
||||
import { ago, errorText, h, replace, showAlert, showConfirm } from "./dom.js";
|
||||
import { markdown } from "./markdown.js";
|
||||
import { openChanges } from "./panes.js";
|
||||
@@ -161,10 +161,18 @@ export function mountTranscript(root: HTMLElement): () => void {
|
||||
{ disabled: busy || unmerged?.taskId !== task.id || !unmerged.has, title: `The agent merges ${target} in, then byre squashes into one commit on ${target} and pushes it to origin` },
|
||||
"Merge",
|
||||
);
|
||||
merge.onclick = () =>
|
||||
confirmThen(`Merge "${task.title}" into ${target}?`, "Merge", false, async () => {
|
||||
merge.onclick = async () => {
|
||||
let coAuthors;
|
||||
try {
|
||||
coAuthors = await get("/api/tasks/{taskId}/co-authors", { path: { taskId: task.id } });
|
||||
} catch (err) {
|
||||
void showAlert(errorText(err));
|
||||
return;
|
||||
}
|
||||
await confirmThen(mergeQuestion(task.merge_title, target, coAuthors), "Merge", false, async () => {
|
||||
await post("/api/tasks/{taskId}/merge", { path: { taskId: task.id } });
|
||||
});
|
||||
};
|
||||
const mergeIn = h("button", { disabled: busy, title: `Ask the agent to bring ${target} into this branch and resolve any conflicts` }, `Sync from ${target}`);
|
||||
mergeIn.onclick = async () => {
|
||||
mergeIn.disabled = true;
|
||||
@@ -881,6 +889,19 @@ function messageImages(ids: unknown): HTMLElement | null {
|
||||
);
|
||||
}
|
||||
|
||||
function mergeQuestion(title: string, target: string, coAuthors: Participant[]): string {
|
||||
const lines = [`Merge "${title}" into ${target}?`];
|
||||
const credited = coAuthors.filter((p) => p.git_name && p.git_email);
|
||||
const missing = coAuthors.filter((p) => !p.git_name || !p.git_email);
|
||||
if (credited.length > 0) {
|
||||
lines.push("", "Co-authored-by:", ...credited.map((p) => ` ${p.git_name} <${p.git_email}>`));
|
||||
}
|
||||
if (missing.length > 0) {
|
||||
lines.push("", "No git identity, so no trailer:", ...missing.map((p) => ` ${p.display_name} (${p.login})`));
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
async function confirmThen(question: string, action: string, danger: boolean, act: () => Promise<void>) {
|
||||
if (!(await showConfirm(question, action, danger))) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user