Compare commits

..
3 Commits
Author SHA1 Message Date
Daniel SamsonandClaude Opus 4.6 f693d56762 Handle Gitea review webhook type field alongside state
Publish Image / publish (push) Successful in 23s
Gitea webhook payloads use "type" (e.g. "pull_request_review_rejected")
instead of "state" (e.g. "REQUEST_CHANGES"). Map both formats so
the pr-rework handler works with all Gitea webhook variations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 18:13:51 +01:00
Daniel SamsonandClaude Opus 4.6 a72ea9ad1d Fix BullMQ job dedup — remove completed/failed jobs
Publish Image / publish (push) Successful in 21s
Completed jobs with the same jobId block new jobs from being added.
Add removeOnComplete and removeOnFail to all job options so the same
task can be re-triggered after completion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 16:37:52 +01:00
Daniel SamsonandClaude Opus 4.6 2cdf68ddff Fix pr-rework crash — guard against missing review.state
Publish Image / publish (push) Successful in 23s
Gitea sends review objects without a state field in some cases.
Use optional chaining and log the review payload for debugging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 16:26:58 +01:00
3 changed files with 29 additions and 2 deletions
+21 -1
View File
@@ -17,12 +17,32 @@ export function prRework(config: Config, queue: TaskQueue) {
}
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;
+6
View File
@@ -161,6 +161,8 @@ export class TaskQueue {
jobId: `create-${key}`,
attempts: 3,
backoff: { type: "exponential", delay: 5000 },
removeOnComplete: true,
removeOnFail: true,
},
);
} catch (err: unknown) {
@@ -201,6 +203,8 @@ export class TaskQueue {
jobId: `cleanup-${workspaceName}`,
attempts: 5,
backoff: { type: "exponential", delay: 10000 },
removeOnComplete: true,
removeOnFail: true,
},
);
@@ -311,6 +315,8 @@ export class TaskQueue {
jobId: `cleanup-stale-${ws.name}-${Date.now()}`,
attempts: 5,
backoff: { type: "exponential", delay: 10000 },
removeOnComplete: true,
removeOnFail: true,
},
);
} catch (err) {
+2 -1
View File
@@ -46,7 +46,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;
}