Skip to content

Commit 9260512

Browse files
authored
Merge branch 'main' into sno-better-auth
2 parents 8d121b0 + d493c3d commit 9260512

File tree

10 files changed

+1131
-21
lines changed

10 files changed

+1131
-21
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
#!/usr/bin/env bun
2+
import { db } from "@/src/db";
3+
import { gh } from "@/src/gh";
4+
import { parseGithubRepoUrl } from "@/src/parseOwnerRepo";
5+
import DIE from "@snomiao/die";
6+
import { $ } from "bun";
7+
import isCI from "is-ci";
8+
import { pageFlow } from "sflow";
9+
10+
/**
11+
* GitHub ComfyUI to Desktop Issue Transfer Task
12+
*
13+
* Workflow:
14+
* 1. Fetch new/unseen issues from comfyanonymous/ComfyUI with label "desktop"
15+
* 2. For each issue:
16+
* 1. Create corresponding issues in Comfy-Org/desktop, copying title, body (+meta and backlinks), labels, assignees
17+
* 2. Comment on original issue that it's been transferred
18+
* 3. Close original issue in comfyanonymous/ComfyUI
19+
* 4. Track transferred issues to avoid duplicates
20+
*/
21+
22+
const config = {
23+
srcRepoUrl: "https://github.com/comfyanonymous/ComfyUI",
24+
dstRepoUrl: "https://github.com/Comfy-Org/desktop",
25+
desktopLabel: "desktop",
26+
transferComment: (newIssueUrl: string) =>
27+
`This issue has been transferred to the desktop repository: ${newIssueUrl}\n\nPlease continue the discussion there.`,
28+
};
29+
30+
export type GithubComfyUIToDesktopIssueTransferTask = {
31+
sourceIssueNumber: number;
32+
sourceIssueUrl: string;
33+
targetIssueNumber?: number;
34+
targetIssueUrl?: string;
35+
transferredAt?: Date;
36+
commentPosted?: boolean;
37+
commentUrl?: string;
38+
error?: string;
39+
};
40+
41+
export const GithubComfyUIToDesktopIssueTransferTask = db.collection<GithubComfyUIToDesktopIssueTransferTask>(
42+
"GithubComfyUIToDesktopIssueTransferTask",
43+
);
44+
45+
await GithubComfyUIToDesktopIssueTransferTask.createIndex({ sourceIssueNumber: 1 }, { unique: true });
46+
47+
const save = async (task: { sourceIssueNumber: number } & Partial<GithubComfyUIToDesktopIssueTransferTask>) =>
48+
(await GithubComfyUIToDesktopIssueTransferTask.findOneAndUpdate(
49+
{ sourceIssueNumber: task.sourceIssueNumber },
50+
{ $set: task },
51+
{ upsert: true, returnDocument: "after" },
52+
)) || DIE("never");
53+
54+
if (import.meta.main) {
55+
await runGithubComfyUIToDesktopIssueTransferTask();
56+
if (isCI) {
57+
await db.close();
58+
process.exit(0);
59+
}
60+
}
61+
62+
async function runGithubComfyUIToDesktopIssueTransferTask() {
63+
const sourceRepo = parseGithubRepoUrl(config.srcRepoUrl);
64+
const targetRepo = parseGithubRepoUrl(config.dstRepoUrl);
65+
66+
// Fetch all open issues with "desktop" label from source repo using pagination
67+
await pageFlow(1, async (page) => {
68+
const per_page = 100;
69+
const sourceIssues = await gh.issues.listForRepo({
70+
owner: sourceRepo.owner,
71+
repo: sourceRepo.repo,
72+
labels: config.desktopLabel,
73+
state: "open",
74+
page,
75+
per_page,
76+
});
77+
78+
console.log(`Found ${sourceIssues.data.length} open desktop issues (page ${page}) in ${config.srcRepoUrl}`);
79+
80+
return {
81+
data: sourceIssues.data,
82+
next: sourceIssues.data.length >= per_page ? page + 1 : undefined,
83+
};
84+
})
85+
.flat()
86+
.map(async (issue) => {
87+
// Skip pull requests (they come through the issues API too)
88+
if (issue.pull_request) {
89+
return null;
90+
}
91+
92+
const existingTask = await GithubComfyUIToDesktopIssueTransferTask.findOne({
93+
sourceIssueNumber: issue.number,
94+
});
95+
96+
// Skip if already transferred
97+
if (existingTask?.targetIssueUrl) {
98+
console.log(`Issue #${issue.number} already transferred to ${existingTask.targetIssueUrl}`);
99+
return existingTask;
100+
}
101+
102+
console.log(issue.html_url);
103+
let task = await save({
104+
sourceIssueNumber: issue.number,
105+
sourceIssueUrl: issue.html_url,
106+
});
107+
108+
try {
109+
const comments = await pageFlow(1, async (page) => {
110+
const per_page = 100;
111+
const comments = await gh.issues.listComments({
112+
owner: sourceRepo.owner,
113+
repo: sourceRepo.repo,
114+
issue_number: issue.number,
115+
page,
116+
per_page,
117+
});
118+
119+
return {
120+
data: comments.data,
121+
next: comments.data.length >= per_page ? page + 1 : undefined,
122+
};
123+
})
124+
.flat()
125+
.map((comment) => `@${comment.user?.login}: <pre>${comment.body}</pre>`)
126+
.toArray();
127+
// Create new issue in target repo
128+
const body = `
129+
${issue.body || ""}
130+
131+
---
132+
133+
*This issue is transferred from: ${issue.html_url}*
134+
135+
Original issue was created ${issue.user?.login?.replace(/^/, "by @")} at ${new Date(issue.created_at).toISOString()}
136+
137+
${comments.length ? `\n\n**Original Comments:**\n\n${comments.join("\n\n")}` : ""}
138+
`;
139+
140+
const newIssue = await gh.issues.create({
141+
owner: targetRepo.owner,
142+
repo: targetRepo.repo,
143+
title: issue.title,
144+
body,
145+
labels: issue.labels
146+
.map((label) => (typeof label === "string" ? label : label.name))
147+
.filter((name): name is string => !!name)
148+
.filter((name) => name.toLowerCase() !== "desktop"),
149+
assignees: issue.assignees?.map((assignee) => assignee.login).filter((login): login is string => !!login),
150+
});
151+
152+
console.log(`Created issue #${newIssue.data.number} in ${targetRepo.owner}/${targetRepo.repo}`);
153+
if (!isCI && process.platform === "darwin") {
154+
await $`open ${newIssue.data.html_url}`;
155+
}
156+
task = await save({
157+
sourceIssueNumber: issue.number,
158+
targetIssueNumber: newIssue.data.number,
159+
targetIssueUrl: newIssue.data.html_url,
160+
transferredAt: new Date(),
161+
});
162+
163+
// Comment on original issue
164+
try {
165+
const comment = await gh.issues.createComment({
166+
owner: sourceRepo.owner,
167+
repo: sourceRepo.repo,
168+
issue_number: issue.number,
169+
body: config.transferComment(newIssue.data.html_url),
170+
});
171+
// close original issue
172+
await gh.issues.update({
173+
owner: sourceRepo.owner,
174+
repo: sourceRepo.repo,
175+
issue_number: issue.number,
176+
state: "closed",
177+
});
178+
179+
console.log(`Posted comment on original issue #${issue.number}`);
180+
181+
task = await save({
182+
sourceIssueNumber: issue.number,
183+
commentPosted: true,
184+
commentUrl: comment.data.html_url,
185+
});
186+
} catch (commentError) {
187+
console.error(`Failed to post comment on issue #${issue.number}:`, commentError);
188+
task = await save({
189+
sourceIssueNumber: issue.number,
190+
commentPosted: false,
191+
error: String(commentError),
192+
});
193+
}
194+
195+
return task;
196+
} catch (error) {
197+
console.error(`Failed to transfer issue #${issue.number}:`, error);
198+
task = await save({
199+
sourceIssueNumber: issue.number,
200+
error: String(error),
201+
});
202+
return task;
203+
}
204+
})
205+
.filter((task) => task !== null)
206+
.log()
207+
.run();
208+
}
209+
210+
export default runGithubComfyUIToDesktopIssueTransferTask;

app/tasks/gh-frontend-issue-transfer/index.spec.ts renamed to app/tasks/gh-issue-transfer-comfyui-to-frontend/index.spec.ts

File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)