Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
546d2615c7 | ||
|
|
f693d56762 | ||
|
|
a72ea9ad1d | ||
|
|
2cdf68ddff | ||
|
|
81aee2f7fc | ||
|
|
f495ea391f | ||
|
|
d19798dfea | ||
|
|
0812a9be37 | ||
|
|
66649022ca |
@@ -128,6 +128,10 @@ data "coder_parameter" "task_type" {
|
||||
name = "Rework PR"
|
||||
value = "rework-pr"
|
||||
}
|
||||
option {
|
||||
name = "Rework Spec"
|
||||
value = "rework-spec"
|
||||
}
|
||||
option {
|
||||
name = "Release"
|
||||
value = "release"
|
||||
@@ -470,7 +474,7 @@ resource "coder_agent" "main" {
|
||||
git fetch origin develop 2>/dev/null && git checkout develop 2>/dev/null || true
|
||||
|
||||
# Lightweight stages only need the code, not a full build
|
||||
LIGHT_STAGES="analyse architect release maintenance"
|
||||
LIGHT_STAGES="analyse architect rework-spec release maintenance"
|
||||
if echo "$LIGHT_STAGES" | grep -qw "$TASK_TYPE"; then
|
||||
echo "Lightweight stage ($TASK_TYPE) — skipping build"
|
||||
else
|
||||
|
||||
@@ -4,7 +4,9 @@ import type { TaskQueue } from "../queue.js";
|
||||
import type { Config } from "../config.js";
|
||||
|
||||
/**
|
||||
* Issue comment created or edited → re-trigger analyst if still in analysis.
|
||||
* Issue comment created or edited:
|
||||
* - On a PR → re-trigger tester
|
||||
* - On an issue still in analysis → re-trigger analyst
|
||||
*
|
||||
* Replaces: issue-comment-reply.json
|
||||
*/
|
||||
@@ -23,6 +25,29 @@ export function issueComment(config: Config, queue: TaskQueue) {
|
||||
return c.text("ignored: bot comment", 200);
|
||||
}
|
||||
|
||||
const [org, repo] = event.repository.full_name.split("/");
|
||||
const isPullRequest = !!event.issue.pull_request;
|
||||
|
||||
if (isPullRequest) {
|
||||
// Comment on a PR → re-trigger tester
|
||||
const task: TaskRequest = {
|
||||
taskType: "test",
|
||||
issueNumber: event.issue.number,
|
||||
giteaOrg: org,
|
||||
giteaRepo: repo,
|
||||
repoCloneUrl: event.repository.clone_url,
|
||||
giteaToken: config.giteaReviewToken,
|
||||
};
|
||||
|
||||
const queued = await queue.enqueue(task, {
|
||||
useReviewAccount: true,
|
||||
body: "🤖 Re-triggering **test** — workspace queued.",
|
||||
});
|
||||
|
||||
return c.json({ ok: true, queued });
|
||||
}
|
||||
|
||||
// Regular issue comment → re-trigger analyst if still in analysis
|
||||
const labels = event.issue.labels.map((l) => l.name);
|
||||
const pastAnalysis =
|
||||
labels.includes("ready-for-architecture") ||
|
||||
@@ -32,8 +57,6 @@ export function issueComment(config: Config, queue: TaskQueue) {
|
||||
return c.text("ignored: issue past analysis stage", 200);
|
||||
}
|
||||
|
||||
const [org, repo] = event.repository.full_name.split("/");
|
||||
|
||||
const task: TaskRequest = {
|
||||
taskType: "analyse",
|
||||
issueNumber: event.issue.number,
|
||||
|
||||
@@ -22,7 +22,9 @@ export function issueLabel(config: Config, queue: TaskQueue) {
|
||||
return async (c: Context) => {
|
||||
const event = await c.req.json<IssueEvent>();
|
||||
|
||||
if (event.action !== "label_updated" && event.action !== "labeled") {
|
||||
console.log(`[issue-label] action="${event.action}" issue=#${event.issue.number} labels=[${event.issue.labels.map((l) => l.name).join(", ")}]`);
|
||||
|
||||
if (!["labeled", "label_updated", "created"].includes(event.action)) {
|
||||
return c.text("ignored: not a label event", 200);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,15 +16,41 @@ export function prRework(config: Config, queue: TaskQueue) {
|
||||
return c.text("ignored: not a reviewed event", 200);
|
||||
}
|
||||
|
||||
if (!event.review) {
|
||||
console.log(`[pr-rework] ignored: no review object`);
|
||||
return c.text("ignored: no review data", 200);
|
||||
}
|
||||
|
||||
const [org, repo] = event.repository.full_name.split("/");
|
||||
const prNumber = event.pull_request.number;
|
||||
const reviewState = event.review.state.toLowerCase();
|
||||
|
||||
// Gitea webhook payloads use "state" in the API but "type" in webhooks
|
||||
// state: "REQUEST_CHANGES" | "APPROVED" | "COMMENT"
|
||||
// type: "pull_request_review_rejected" | "pull_request_review_approved" | ...
|
||||
let reviewState: string;
|
||||
if (event.review.state) {
|
||||
reviewState = event.review.state.toLowerCase();
|
||||
} else if (event.review.type) {
|
||||
const typeMap: Record<string, string> = {
|
||||
pull_request_review_rejected: "request_changes",
|
||||
pull_request_review_approved: "approved",
|
||||
pull_request_review_comment: "comment",
|
||||
};
|
||||
reviewState = typeMap[event.review.type] || event.review.type;
|
||||
} else {
|
||||
console.log(`[pr-rework] ignored: no review state or type (review: ${JSON.stringify(event.review)})`);
|
||||
return c.text("ignored: no review state", 200);
|
||||
}
|
||||
|
||||
console.log(`[pr-rework] PR #${prNumber} review: ${reviewState}`);
|
||||
|
||||
let taskType: string | null = null;
|
||||
let giteaToken: string;
|
||||
const branch = event.pull_request.head.ref;
|
||||
const isArchitectPR = branch.startsWith("docs/");
|
||||
|
||||
if (reviewState === "request_changes") {
|
||||
taskType = "rework-pr";
|
||||
taskType = isArchitectPR ? "rework-spec" : "rework-pr";
|
||||
giteaToken = config.giteaDevToken;
|
||||
} else if (reviewState === "approved") {
|
||||
taskType = "test";
|
||||
|
||||
+9
-3
@@ -158,9 +158,11 @@ export class TaskQueue {
|
||||
"create-workspace",
|
||||
{ task, comment },
|
||||
{
|
||||
jobId: `create:${key}`,
|
||||
jobId: `create-${key}`,
|
||||
attempts: 3,
|
||||
backoff: { type: "exponential", delay: 5000 },
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
},
|
||||
);
|
||||
} catch (err: unknown) {
|
||||
@@ -198,9 +200,11 @@ export class TaskQueue {
|
||||
"cleanup-workspace",
|
||||
{ workspaceId: entry.workspaceId, workspaceName: entry.workspaceName },
|
||||
{
|
||||
jobId: `cleanup:${workspaceName}`,
|
||||
jobId: `cleanup-${workspaceName}`,
|
||||
attempts: 5,
|
||||
backoff: { type: "exponential", delay: 10000 },
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -308,9 +312,11 @@ export class TaskQueue {
|
||||
"cleanup-workspace",
|
||||
{ workspaceId: ws.id, workspaceName: ws.name },
|
||||
{
|
||||
jobId: `cleanup:stale-${ws.name}-${Date.now()}`,
|
||||
jobId: `cleanup-stale-${ws.name}-${Date.now()}`,
|
||||
attempts: 5,
|
||||
backoff: { type: "exponential", delay: 10000 },
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
|
||||
@@ -109,12 +109,14 @@ export class CoderClient {
|
||||
|
||||
async deleteWorkspace(workspaceId: string): Promise<void> {
|
||||
const res = await fetch(
|
||||
`${this.baseUrl}/api/v2/workspaces/${workspaceId}`,
|
||||
`${this.baseUrl}/api/v2/workspaces/${workspaceId}/builds`,
|
||||
{
|
||||
method: "DELETE",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Coder-Session-Token": this.token,
|
||||
},
|
||||
body: JSON.stringify({ transition: "delete" }),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+3
-1
@@ -30,6 +30,7 @@ export interface GiteaIssue {
|
||||
state: string;
|
||||
user: GiteaUser;
|
||||
labels: GiteaLabel[];
|
||||
pull_request?: { merged: boolean } | null;
|
||||
}
|
||||
|
||||
export interface GiteaPullRequest {
|
||||
@@ -46,7 +47,8 @@ export interface GiteaPullRequest {
|
||||
export interface GiteaReview {
|
||||
id: number;
|
||||
body: string;
|
||||
state: string; // "approved", "request_changes", "comment"
|
||||
state?: string; // "approved", "request_changes", "comment" (API response)
|
||||
type?: string; // "pull_request_review_rejected" | "pull_request_review_approved" (webhook payload)
|
||||
user: GiteaUser;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user