comfy-pr 0.2.26 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +258 -157
- package/bot/IdleWaiter.ts +31 -0
- package/bot/RestartManager.spec.ts +163 -0
- package/bot/RestartManager.ts +190 -0
- package/bot/WorkingTasksManager.spec.ts +154 -0
- package/bot/cli.ts +1171 -0
- package/bot/codesearch-ai-wip.ts +351 -0
- package/bot/error-collector.ts +133 -0
- package/bot/index.ts +14 -0
- package/bot/restart-example.ts +99 -0
- package/bot/slack-bolt.ts +688 -0
- package/bot/slack-bot.ts +1575 -0
- package/bot/state.ts +19 -0
- package/bot/templateLoader.test.ts +84 -0
- package/bot/templateLoader.ts +92 -0
- package/next.config.ts +26 -0
- package/package.json +161 -41
- package/post-summary.ts +44 -0
- package/src/Authors.ts +2 -2
- package/src/CMNodes.ts +3 -3
- package/src/CNRepos.ts +30 -7
- package/src/CRNodes.ts +11 -9
- package/src/CRPulls.ts +3 -0
- package/src/EmailTasks.ts +27 -9
- package/src/FORK_OWNER.ts +3 -1
- package/src/FollowRules.ts +3 -3
- package/src/GithubIssueComments.ts +1 -1
- package/src/Totals.ts +9 -8
- package/src/WorkerInstances.ts +31 -8
- package/src/addCommentAction.ts +19 -11
- package/src/analyzePullsStatus.ts +39 -17
- package/src/analyzeTotals.ts +19 -11
- package/src/bypassRepos.ts +6 -5
- package/src/checkPRsFailures.ts +13 -8
- package/src/cli.test.ts +2 -0
- package/src/cli.ts +1 -1
- package/src/constants.ts +2 -0
- package/src/createComfyRegistryPRsFromCandidates.ts +20 -12
- package/src/createComfyRegistryPullRequests.ts +35 -10
- package/src/createGithubForkForRepo.ts +36 -11
- package/src/createGithubPullRequest.ts +83 -33
- package/src/createIssueComment.ts +4 -4
- package/src/fetchComfyRegistryNodes.ts +34 -4
- package/src/fetchCurrentGeoInfo.ts +3 -1
- package/src/fetchRelatedPullWithComments.ts +1 -1
- package/src/fetchRepoDescriptionMap.ts +1 -4
- package/src/followRuleSchema.ts +10 -4
- package/src/getBranchWorkingDir.ts +3 -7
- package/src/getRepoWorkingDir.ts +2 -3
- package/src/gh.spec.ts +571 -0
- package/src/ghData.ts +13 -0
- package/src/ghPageFlow.ts +33 -0
- package/src/ghSubscriber.ts +76 -0
- package/src/ghUser.ts +28 -4
- package/src/index.ts +13 -4
- package/src/initializeFollowRules.ts +11 -4
- package/src/logger.spec.ts +95 -0
- package/src/logger.ts +46 -0
- package/src/makeGpl3LicenseBranch.ts +66 -0
- package/src/makePublishBranch.ts +21 -15
- package/src/makeTomlBranch.ts +48 -14
- package/src/makeUpdateTomlLicenseBranch.ts +40 -44
- package/src/matchRelatedPulls.ts +9 -4
- package/src/normalizeGithubUrl.spec.ts +131 -0
- package/src/normalizeGithubUrl.ts +64 -0
- package/src/parseIssueUrl.spec.ts +95 -0
- package/src/parseIssueUrl.ts +17 -3
- package/src/parseOwnerRepo.spec.ts +147 -0
- package/src/parseOwnerRepo.ts +8 -5
- package/src/parsePullsState.ts +2 -2
- package/src/parseTitleBodyOfMarkdown.ts +1 -1
- package/src/pickRepoInfo.ts +37 -0
- package/src/postSlackMessage.ts +5 -1
- package/src/preload.ts +30 -27
- package/src/readTemplateTitle.ts +1 -3
- package/src/sendEmailAction.ts +14 -10
- package/src/sendGmail.ts +3 -3
- package/src/updateAuthorsForGithub.ts +53 -35
- package/src/updateAuthorsFromCNRepo.ts +12 -5
- package/src/updateCMNodesDuplicationWarnings.ts +14 -9
- package/src/updateCMRepos.ts +1 -1
- package/src/updateCNRepos.ts +14 -14
- package/src/updateCNReposCRPullsComments.ts +7 -5
- package/src/updateCNReposInfo.ts +49 -40
- package/src/updateCNReposPRCandidate.ts +3 -3
- package/src/updateCNReposPulls.ts +9 -5
- package/src/updateCNReposPullsDashboard.ts +11 -9
- package/src/updateCNReposRelatedPulls.ts +3 -3
- package/src/updateCRNodes.ts +24 -5
- package/src/updateCRRepos.ts +1 -1
- package/src/updateComfyTotals.ts +7 -5
- package/src/updateFollowRuleSet.ts +17 -9
- package/src/updateOutdatedPullsTemplates.ts +61 -48
- package/src/updateSlackMessages.ts +8 -4
- package/tailwind.config.ts +7 -1
- package/next-env.d.ts +0 -5
- package/src/GIT_USEREMAIL.ts +0 -5
- package/src/GIT_USERNAME.ts +0 -9
- package/src/clone_modify_push_Branches.ts +0 -21
- package/src/tomlFillDescription.ts +0 -17
package/src/checkPRsFailures.ts
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { $pipeline } from "@/packages/mongodb-pipeline-ts/$pipeline";
|
|
2
|
-
import { csvFormatBody } from "d3";
|
|
3
2
|
import sflow from "sflow";
|
|
4
3
|
import { CNRepos } from "./CNRepos";
|
|
4
|
+
import { yaml } from "./utils/yaml";
|
|
5
5
|
|
|
6
6
|
if (import.meta.main) {
|
|
7
7
|
const pipeline = $pipeline(CNRepos)
|
|
8
8
|
.match({ "createdPulls.state": "error" })
|
|
9
9
|
.set({ error: "$createdPulls.error" })
|
|
10
|
-
.
|
|
11
|
-
.
|
|
10
|
+
.set({ mtime: "$createdPulls.mtime" })
|
|
11
|
+
.project({ _id: 0, repository: 1, error: 1, mtime: 1 })
|
|
12
|
+
.as<{ repository: string; error: string; mtime: Date }>()
|
|
12
13
|
.aggregate();
|
|
13
|
-
const csv = await sflow(pipeline)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
await Bun.write(".cache/pr-errors.csv", csv);
|
|
14
|
+
// const csv = await sflow(pipeline)
|
|
15
|
+
// .map((e, i) => ((!i && "repository,error\n") || "") + csvFormatBody([e]) + "\n")
|
|
16
|
+
// .log()
|
|
17
|
+
// .text();
|
|
18
|
+
// await Bun.write(".cache/pr-errors.csv", csv);
|
|
19
|
+
// await Bun.$`code .cache/pr-errors.csv`
|
|
20
|
+
const yml = yaml.stringify(await sflow(pipeline).toArray());
|
|
21
|
+
await Bun.write(".cache/pr-errors.yml", yml);
|
|
22
|
+
await Bun.$`code .cache/pr-errors.yml`;
|
|
18
23
|
}
|
package/src/cli.test.ts
CHANGED
package/src/cli.ts
CHANGED
package/src/constants.ts
ADDED
|
@@ -4,18 +4,18 @@ import { match } from "ts-pattern";
|
|
|
4
4
|
import { $OK, TaskError, TaskOK } from "../packages/mongodb-pipeline-ts/Task";
|
|
5
5
|
import { CNRepos } from "./CNRepos";
|
|
6
6
|
import { createComfyRegistryPullRequests } from "./createComfyRegistryPullRequests";
|
|
7
|
-
import { $
|
|
8
|
-
import {
|
|
9
|
-
import { notifySlackLinks } from "
|
|
7
|
+
import { $flatten, $stale } from "./db";
|
|
8
|
+
import { parseGithubRepoUrl, stringifyOwnerRepo } from "./parseOwnerRepo";
|
|
9
|
+
import { notifySlackLinks } from "@/lib/slack/notifySlackLinks";
|
|
10
10
|
import { tLog } from "./utils/tLog";
|
|
11
11
|
if (import.meta.main) {
|
|
12
12
|
await tLog("createComfyRegistryPRsFromCandidates", createComfyRegistryPRsFromCandidates);
|
|
13
13
|
console.log("all done");
|
|
14
14
|
}
|
|
15
15
|
export async function createComfyRegistryPRsFromCandidates() {
|
|
16
|
-
await CNRepos.createIndex($
|
|
16
|
+
await CNRepos.createIndex($flatten({ candidate: { data: 1 } }));
|
|
17
17
|
await CNRepos.createIndex(
|
|
18
|
-
$
|
|
18
|
+
$flatten({
|
|
19
19
|
candidate: { data: 1 },
|
|
20
20
|
createdPulls: { state: 1, mtime: 1 },
|
|
21
21
|
}),
|
|
@@ -23,7 +23,7 @@ export async function createComfyRegistryPRsFromCandidates() {
|
|
|
23
23
|
return await pMap(
|
|
24
24
|
$pipeline(CNRepos)
|
|
25
25
|
.match(
|
|
26
|
-
$
|
|
26
|
+
$flatten({
|
|
27
27
|
candidate: { data: { $eq: true } },
|
|
28
28
|
createdPulls: { state: { $ne: "ok" }, mtime: $stale("5m") },
|
|
29
29
|
}),
|
|
@@ -32,19 +32,27 @@ export async function createComfyRegistryPRsFromCandidates() {
|
|
|
32
32
|
async (repo) => {
|
|
33
33
|
const { repository } = repo;
|
|
34
34
|
console.log("Making PRs for " + repository);
|
|
35
|
-
const createdPulls = await createComfyRegistryPullRequests(repository)
|
|
35
|
+
const createdPulls = await createComfyRegistryPullRequests(repository)
|
|
36
|
+
.then(TaskOK)
|
|
37
|
+
.catch(TaskError);
|
|
36
38
|
match(createdPulls).with($OK, async ({ data }) => {
|
|
37
39
|
const links = data.map((e) => ({
|
|
38
40
|
href: e.html_url,
|
|
39
|
-
name:
|
|
41
|
+
name:
|
|
42
|
+
stringifyOwnerRepo(parseGithubRepoUrl(e.html_url.replace(/\/pull\/.*$/, ""))) +
|
|
43
|
+
" #" +
|
|
44
|
+
e.title,
|
|
40
45
|
}));
|
|
41
|
-
await notifySlackLinks("PR
|
|
46
|
+
await notifySlackLinks("PR Created", links);
|
|
42
47
|
await pMap(data, async (pull) => {
|
|
43
48
|
const { html_url } = pull;
|
|
44
49
|
// also update to crPulls
|
|
45
|
-
await CNRepos.updateOne(
|
|
46
|
-
$
|
|
47
|
-
|
|
50
|
+
await CNRepos.updateOne(
|
|
51
|
+
$flatten({ repository, crPulls: { data: { pull: { html_url } } } }),
|
|
52
|
+
{
|
|
53
|
+
$set: { "crPulls.data.$.pull": pull },
|
|
54
|
+
},
|
|
55
|
+
);
|
|
48
56
|
});
|
|
49
57
|
});
|
|
50
58
|
|
|
@@ -1,26 +1,51 @@
|
|
|
1
|
+
import minimist from "minimist";
|
|
1
2
|
import pMap from "p-map";
|
|
2
|
-
|
|
3
|
-
import { clone_modify_push_Branches } from "./clone_modify_push_Branches";
|
|
4
|
-
import { createGithubForkForRepo } from "./createGithubForkForRepo";
|
|
3
|
+
import { createGithubForkForRepoEx } from "./createGithubForkForRepo";
|
|
5
4
|
import { createGithubPullRequest } from "./createGithubPullRequest";
|
|
6
|
-
import type { GithubPull } from "
|
|
7
|
-
import {
|
|
5
|
+
import type { GithubPull } from "@/lib/github/GithubPull";
|
|
6
|
+
import { makeGpl3LicenseBranch } from "./makeGpl3LicenseBranch";
|
|
7
|
+
import { makePublishcrBranch } from "./makePublishBranch";
|
|
8
|
+
import { makePyprojectBranch } from "./makeTomlBranch";
|
|
8
9
|
import { parsePulls } from "./parsePullsState";
|
|
10
|
+
if (import.meta.main) {
|
|
11
|
+
// test repo
|
|
12
|
+
// const default_test_repo = "https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler";
|
|
13
|
+
const default_test_repo = "https://github.com/snomiao/ComfyNode-Registry-test";
|
|
14
|
+
const test_repo = minimist(process.argv.slice(2)).argv || default_test_repo;
|
|
15
|
+
console.info(await createComfyRegistryPullRequests(test_repo));
|
|
16
|
+
}
|
|
9
17
|
|
|
10
18
|
export async function createComfyRegistryPullRequests(upstreamRepoUrl: string) {
|
|
11
|
-
|
|
19
|
+
console.log("forking " + upstreamRepoUrl);
|
|
20
|
+
const forkedRepo = await createGithubForkForRepoEx(upstreamRepoUrl);
|
|
12
21
|
|
|
22
|
+
console.log("modifing " + forkedRepo.html_url);
|
|
13
23
|
const PR_REQUESTS = await clone_modify_push_Branches(upstreamRepoUrl, forkedRepo.html_url);
|
|
14
|
-
const prs = await pMap(
|
|
24
|
+
const prs = await pMap(
|
|
25
|
+
PR_REQUESTS,
|
|
26
|
+
async ({ type: _type, ...prInfo }) => await createGithubPullRequest({ ...prInfo }),
|
|
27
|
+
);
|
|
15
28
|
|
|
16
29
|
console.log("Registry PRs DONE");
|
|
17
30
|
|
|
18
31
|
return ([...prs] as GithubPull[]).map((e) => parsePulls([e])[0]);
|
|
19
32
|
}
|
|
20
33
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
34
|
+
/**
|
|
35
|
+
* this function will:
|
|
36
|
+
*
|
|
37
|
+
* for each branch (pyproject, publishcr)
|
|
38
|
+
* 1. clone forked srepo
|
|
39
|
+
* 2. modify on forked repo (add pyproject.toml, add .github/workflow/publish.yml)
|
|
40
|
+
* 3. commit changes, push to forked repo
|
|
41
|
+
* 4. check if all branch is ready, create PR to upstream repo
|
|
42
|
+
*/
|
|
43
|
+
export async function clone_modify_push_Branches(upstreamUrl: string, forkUrl: string) {
|
|
44
|
+
const pyprojectBranchInfo = makePyprojectBranch(upstreamUrl, forkUrl);
|
|
45
|
+
const publishcrBranchInfo = makePublishcrBranch(upstreamUrl, forkUrl);
|
|
46
|
+
const gpl3LicenseBranchInfo = makeGpl3LicenseBranch(upstreamUrl, forkUrl);
|
|
47
|
+
return (await Promise.all([pyprojectBranchInfo, publishcrBranchInfo, gpl3LicenseBranchInfo]))
|
|
48
|
+
.filter((info): info is NonNullable<typeof info> => info !== null)
|
|
24
49
|
.map(({ body, branch, title, type }) => ({
|
|
25
50
|
body,
|
|
26
51
|
branch,
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import DIE from "@snomiao/die";
|
|
2
2
|
import md5 from "md5";
|
|
3
3
|
import minimist from "minimist";
|
|
4
|
+
import { db } from "./db";
|
|
4
5
|
import { FORK_OWNER } from "./FORK_OWNER";
|
|
5
6
|
import { FORK_PREFIX } from "./FORK_PREFIX";
|
|
6
|
-
import { createGithubFork } from "
|
|
7
|
+
import { createGithubFork } from "@/lib/github/createGithubFork";
|
|
7
8
|
import { ghUser } from "./ghUser";
|
|
8
|
-
import {
|
|
9
|
+
import { parseGithubRepoUrl } from "./parseOwnerRepo";
|
|
10
|
+
|
|
11
|
+
/** for cache */
|
|
12
|
+
const ForkedRepo = db.collection<{ repo: string; forkedRepo: string; updatedAt: Date }>(
|
|
13
|
+
"ForkedRepo",
|
|
14
|
+
);
|
|
9
15
|
|
|
10
16
|
if (import.meta.main) {
|
|
11
|
-
console.log(
|
|
17
|
+
console.log(
|
|
18
|
+
await createGithubForkForRepoEx("https://github.com/comfyanonymous/ComfyUI_TensorRT"),
|
|
19
|
+
);
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
/**
|
|
@@ -22,10 +30,19 @@ if (import.meta.main) {
|
|
|
22
30
|
* @param upstreamRepoUrl
|
|
23
31
|
* @returns forked repo info
|
|
24
32
|
*/
|
|
25
|
-
export async function
|
|
26
|
-
upstreamRepoUrl
|
|
27
|
-
|
|
28
|
-
) {
|
|
33
|
+
export async function createGithubForkForRepoEx(upstreamRepoUrl: string, { forkUrl = "" } = {}) {
|
|
34
|
+
forkUrl ||= await createGithubForkUrlForRepo(upstreamRepoUrl);
|
|
35
|
+
const alreadyForked = await ForkedRepo.findOne({ repo: upstreamRepoUrl, forkedRepo: forkUrl });
|
|
36
|
+
if (alreadyForked) {
|
|
37
|
+
console.debug(
|
|
38
|
+
`
|
|
39
|
+
Forked ${upstreamRepoUrl}
|
|
40
|
+
into ${forkUrl}
|
|
41
|
+
`.trim(),
|
|
42
|
+
);
|
|
43
|
+
return { html_url: forkUrl };
|
|
44
|
+
}
|
|
45
|
+
|
|
29
46
|
console.debug(
|
|
30
47
|
`
|
|
31
48
|
Forking ${upstreamRepoUrl}
|
|
@@ -39,15 +56,23 @@ Forking ${upstreamRepoUrl}
|
|
|
39
56
|
"forked url not expected, it's likely you already forked this repo in your account before, and now trying to fork it again with different salt. To recovery you could delete that repo forked before by manual. (the repo forked before is listed in FORK OK: .....)",
|
|
40
57
|
),
|
|
41
58
|
);
|
|
59
|
+
await ForkedRepo.updateOne(
|
|
60
|
+
{ repo: upstreamRepoUrl, forkedRepo: forkUrl },
|
|
61
|
+
{ $set: { updatedAt: new Date() } },
|
|
62
|
+
{ upsert: true },
|
|
63
|
+
);
|
|
42
64
|
return forked;
|
|
43
65
|
}
|
|
44
|
-
export function createGithubForkUrlForRepo(upstreamRepoUrl: string) {
|
|
66
|
+
export async function createGithubForkUrlForRepo(upstreamRepoUrl: string) {
|
|
45
67
|
// console.log(`* Change env.SALT=${salt} will fork into a different repo`);
|
|
46
|
-
const upstream =
|
|
68
|
+
const upstream = parseGithubRepoUrl(upstreamRepoUrl);
|
|
47
69
|
const argv = minimist(process.argv.slice(2));
|
|
48
70
|
const salt = argv.salt || process.env.SALT || "m3KMgZ2AeZGWYh7W";
|
|
49
|
-
const repo_hash = md5(
|
|
50
|
-
|
|
71
|
+
const repo_hash = md5(
|
|
72
|
+
`${salt}-${(await ghUser()).name}-${upstream.owner}/${upstream.repo}`,
|
|
73
|
+
).slice(0, 8);
|
|
74
|
+
const forkRepoName =
|
|
75
|
+
(FORK_PREFIX && `${FORK_PREFIX}${upstream.repo}-${repo_hash}`) || upstream.repo;
|
|
51
76
|
const forkDst = `${FORK_OWNER}/${forkRepoName}`;
|
|
52
77
|
const forkUrl = `https://github.com/${forkDst}`;
|
|
53
78
|
return forkUrl;
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import DIE, { catchArgs } from "@snomiao/die";
|
|
2
2
|
import "git-diff";
|
|
3
|
-
import { Octokit } from "octokit";
|
|
4
3
|
import { pickAll } from "rambda";
|
|
5
4
|
import sflow from "sflow";
|
|
6
5
|
import { isRepoBypassed } from "./bypassRepos";
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
6
|
+
import { createOctokit } from "@/lib/github/createOctokit";
|
|
7
|
+
import { gh } from "@/lib/github";
|
|
8
|
+
import type { GithubPull } from "@/lib/github/GithubPull";
|
|
9
|
+
import { parseGithubRepoUrl } from "./parseOwnerRepo";
|
|
10
|
+
import { parseTitleBodyOfMarkdown } from "./parseTitleBodyOfMarkdown";
|
|
10
11
|
if (import.meta.main) {
|
|
11
12
|
const srcUrl = "https://github.com/ComfyNodePRs/PR-ComfyUI-DareMerge-7bcbf6a9";
|
|
12
13
|
const dstUrl = "https://github.com/54rt1n/ComfyUI-DareMerge";
|
|
13
|
-
const src =
|
|
14
|
-
const dst =
|
|
15
|
-
const
|
|
14
|
+
const src = parseGithubRepoUrl(srcUrl);
|
|
15
|
+
const dst = parseGithubRepoUrl(dstUrl);
|
|
16
|
+
const _branch = "licence-update";
|
|
16
17
|
const repo = (await gh.repos.get({ ...dst })).data;
|
|
17
18
|
const head_repo = `${src.owner}/${src.repo}`;
|
|
18
19
|
// const head = `${src.owner}:${branch}`;
|
|
@@ -45,6 +46,21 @@ if (import.meta.main) {
|
|
|
45
46
|
.toLog();
|
|
46
47
|
console.log("all done");
|
|
47
48
|
}
|
|
49
|
+
export async function createPR({
|
|
50
|
+
src: srcUrl,
|
|
51
|
+
dst: dstUrl,
|
|
52
|
+
branch,
|
|
53
|
+
msg,
|
|
54
|
+
}: {
|
|
55
|
+
src: string;
|
|
56
|
+
dst: string;
|
|
57
|
+
branch: string;
|
|
58
|
+
msg: string;
|
|
59
|
+
}) {
|
|
60
|
+
const { title, body } = parseTitleBodyOfMarkdown(msg);
|
|
61
|
+
return (await createGithubPullRequest({ srcUrl, dstUrl, branch, title, body })).html_url;
|
|
62
|
+
}
|
|
63
|
+
|
|
48
64
|
export async function createGithubPullRequest({
|
|
49
65
|
title,
|
|
50
66
|
body,
|
|
@@ -60,42 +76,49 @@ export async function createGithubPullRequest({
|
|
|
60
76
|
dstUrl: string; // upstream
|
|
61
77
|
updateIfNotMatched?: boolean;
|
|
62
78
|
}) {
|
|
79
|
+
// 2025-05-06 this function may create duplicated PR if calling too fast with same args
|
|
80
|
+
// TODO: try grab a lock with reop+branch
|
|
63
81
|
if (isRepoBypassed(dstUrl)) DIE("dst repo is requested to be bypassed");
|
|
64
82
|
|
|
65
|
-
const dst =
|
|
66
|
-
const src =
|
|
83
|
+
const dst = parseGithubRepoUrl(dstUrl);
|
|
84
|
+
const src = parseGithubRepoUrl(srcUrl);
|
|
67
85
|
const repo = (await gh.repos.get({ ...dst })).data;
|
|
68
86
|
|
|
69
|
-
//
|
|
70
|
-
|
|
87
|
+
// 2025-03-13 prevent duplicated PR, if there are PR is closed with same content.
|
|
88
|
+
// reported here
|
|
89
|
+
// https://github.com/Chaoses-Ib/ComfyUI_Ib_CustomNodes/pulls?q=is%3Apr
|
|
90
|
+
//
|
|
91
|
+
const sameContentPRList = (
|
|
71
92
|
await gh.pulls.list({
|
|
72
93
|
// source repo
|
|
73
|
-
state: "
|
|
74
|
-
|
|
75
|
-
head: `${src.owner}:${branch}`,
|
|
94
|
+
// state: "open",
|
|
95
|
+
head: encodeURIComponent(`${src.owner}:${branch}`),
|
|
76
96
|
// pr will merge into
|
|
77
97
|
owner: dst.owner,
|
|
78
98
|
repo: dst.repo,
|
|
79
99
|
base: repo.default_branch,
|
|
80
100
|
})
|
|
81
|
-
).data;
|
|
82
|
-
|
|
101
|
+
).data.filter((e) => e.title === title && e.body === body);
|
|
102
|
+
|
|
103
|
+
sameContentPRList.length <= 1 ||
|
|
83
104
|
DIE(
|
|
84
|
-
new Error(`expect
|
|
85
|
-
cause: {
|
|
105
|
+
new Error(`expect <= 1 same content pr, but got ${sameContentPRList.length}`, {
|
|
106
|
+
cause: {
|
|
107
|
+
sameContentPRList: sameContentPRList.map((e) => ({ url: e.html_url, title: e.title })),
|
|
108
|
+
},
|
|
86
109
|
}),
|
|
87
110
|
);
|
|
88
111
|
|
|
89
112
|
const pr_result =
|
|
90
|
-
existedList[0] ??
|
|
91
|
-
|
|
113
|
+
// existedList[0] ??
|
|
114
|
+
await ghPR()
|
|
92
115
|
.pulls.create({
|
|
93
116
|
// pr info
|
|
94
117
|
title,
|
|
95
118
|
body,
|
|
96
119
|
// source repo
|
|
97
|
-
head_repo: src.owner
|
|
98
|
-
head: src.owner
|
|
120
|
+
head_repo: `${src.owner}/${src.repo}`,
|
|
121
|
+
head: `${src.owner}:${branch}`,
|
|
99
122
|
// pr will merge into
|
|
100
123
|
owner: dst.owner,
|
|
101
124
|
repo: dst.repo,
|
|
@@ -114,8 +137,8 @@ export async function createGithubPullRequest({
|
|
|
114
137
|
await gh.pulls.list({
|
|
115
138
|
// source repo
|
|
116
139
|
state: "open",
|
|
117
|
-
head_repo: `${src.owner}/${src.repo}`,
|
|
118
|
-
head: `${src.owner}:${branch}
|
|
140
|
+
// head_repo: `${src.owner}/${src.repo}`,
|
|
141
|
+
head: encodeURIComponent(`${src.owner}:${branch}`),
|
|
119
142
|
// pr will merge into
|
|
120
143
|
owner: dst.owner,
|
|
121
144
|
repo: dst.repo,
|
|
@@ -123,15 +146,27 @@ export async function createGithubPullRequest({
|
|
|
123
146
|
})
|
|
124
147
|
).data; // .filter(existed => existed.title === title);
|
|
125
148
|
|
|
126
|
-
if (existedList.length !== 1)
|
|
149
|
+
if (existedList.length !== 1) {
|
|
127
150
|
DIE(
|
|
128
151
|
new Error("expect only 1 pr, but got " + existedList.length, {
|
|
129
|
-
cause: {
|
|
152
|
+
cause: {
|
|
153
|
+
existed: existedList.map((e) => ({
|
|
154
|
+
url: e.html_url,
|
|
155
|
+
title: e.title,
|
|
156
|
+
})),
|
|
157
|
+
error: e,
|
|
158
|
+
state: "open",
|
|
159
|
+
head: `${src.owner}:${branch}`,
|
|
160
|
+
owner: dst.owner,
|
|
161
|
+
repo: dst.repo,
|
|
162
|
+
base: repo.default_branch,
|
|
163
|
+
},
|
|
130
164
|
}),
|
|
131
165
|
);
|
|
166
|
+
}
|
|
132
167
|
|
|
133
168
|
return existedList[0];
|
|
134
|
-
})
|
|
169
|
+
});
|
|
135
170
|
|
|
136
171
|
console.log("PR OK", pr_result.html_url);
|
|
137
172
|
const mismatch = pr_result.title !== title || pr_result.body !== body;
|
|
@@ -139,19 +174,34 @@ export async function createGithubPullRequest({
|
|
|
139
174
|
if (!updateIfNotMatched)
|
|
140
175
|
DIE(
|
|
141
176
|
new Error("pr content mismatch", {
|
|
142
|
-
cause: {
|
|
177
|
+
cause: {
|
|
178
|
+
mismatch,
|
|
179
|
+
expected: { title, body },
|
|
180
|
+
actual: pickAll(["title", "body"], pr_result),
|
|
181
|
+
},
|
|
143
182
|
}),
|
|
144
183
|
);
|
|
145
|
-
const { owner, repo } =
|
|
146
|
-
const updated = (
|
|
147
|
-
.
|
|
184
|
+
const { owner, repo } = parseGithubRepoUrl(dstUrl); // upstream repo
|
|
185
|
+
const updated = (
|
|
186
|
+
await catchArgs(ghPR().pulls.update)({
|
|
187
|
+
pull_number: pr_result.number,
|
|
188
|
+
body,
|
|
189
|
+
title,
|
|
190
|
+
owner,
|
|
191
|
+
repo,
|
|
192
|
+
})
|
|
193
|
+
).data!;
|
|
148
194
|
const updatedPRStillMismatch = updated.title !== title || updated.body !== body;
|
|
149
195
|
if (updatedPRStillMismatch) DIE(new Error("updatedPRStillMismatch", { cause: arguments }));
|
|
150
|
-
console.warn(
|
|
196
|
+
console.warn(
|
|
197
|
+
`PR content updated ${owner}/${repo} / \n<< ${pr_result.title}\n>> ${updated.title}`,
|
|
198
|
+
);
|
|
151
199
|
}
|
|
152
200
|
return pr_result as GithubPull;
|
|
153
201
|
}
|
|
154
202
|
|
|
155
203
|
function ghPR() {
|
|
156
|
-
return
|
|
204
|
+
return createOctokit({
|
|
205
|
+
auth: process.env.GH_TOKEN_COMFY_PR || DIE(new Error("Missing env.GH_TOKEN_COMFY_PR")),
|
|
206
|
+
}).rest;
|
|
157
207
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import DIE from "@snomiao/die";
|
|
2
|
-
import { gh } from "
|
|
2
|
+
import { gh } from "@/lib/github";
|
|
3
3
|
import { ghUser } from "./ghUser";
|
|
4
4
|
import { parseIssueUrl } from "./parseIssueUrl";
|
|
5
5
|
|
|
6
6
|
if (import.meta.main) {
|
|
7
7
|
// const url = "https://github.com/snomiao/ComfyNode-Registry-test/pull/1";
|
|
8
8
|
// const body = "Hello World @snomiao";
|
|
9
|
-
// const result = await createIssueComment(url, body, ghUser.login);
|
|
9
|
+
// const result = await createIssueComment(url, body, (await ghUser()).login);
|
|
10
10
|
// console.log(result.comment.html_url);
|
|
11
11
|
// console.log(result.comments.map((e) => e.html_url));
|
|
12
12
|
const url = "https://github.com/snomiao/ComfyNode-Registry-test/pull/28";
|
|
13
13
|
const body = "Hello World @robinjhuang";
|
|
14
|
-
const result = await createIssueComment(url, body, ghUser.login);
|
|
14
|
+
const result = await createIssueComment(url, body, (await ghUser()).login);
|
|
15
15
|
console.log(result.comment.html_url);
|
|
16
16
|
console.log(result.comments.map((e) => e.html_url));
|
|
17
17
|
}
|
|
@@ -25,7 +25,7 @@ export async function createIssueComment(issueUrl: string, body: string, by: str
|
|
|
25
25
|
const result = await (async function () {
|
|
26
26
|
const commentExisted = comments.find((e) => e.body === body);
|
|
27
27
|
if (commentExisted) return { comment: commentExisted, comments };
|
|
28
|
-
if (by !== ghUser.login) DIE("Fails to creating issue: user not match");
|
|
28
|
+
if (by !== (await ghUser()).login) DIE("Fails to creating issue: user not match");
|
|
29
29
|
const comment = (await gh.issues.createComment({ ...parseIssueUrl(issueUrl), body })).data;
|
|
30
30
|
console.log("+ IssueComment " + comment.html_url);
|
|
31
31
|
return { comment, comments: [...comments, comment] };
|
|
@@ -1,11 +1,41 @@
|
|
|
1
|
-
import type { mockPublishedNodes } from "../mocks/mockPublishedNodes";
|
|
2
1
|
import DIE from "@snomiao/die";
|
|
2
|
+
import type { mockPublishedNodes } from "../mocks/mockPublishedNodes";
|
|
3
3
|
import { fetchJson } from "./utils/fetchJson";
|
|
4
4
|
|
|
5
|
-
export async function fetchCRNodes()
|
|
5
|
+
export async function fetchCRNodes(): Promise<
|
|
6
|
+
{
|
|
7
|
+
author: string;
|
|
8
|
+
description: string;
|
|
9
|
+
icon: string;
|
|
10
|
+
id: string;
|
|
11
|
+
latest_version: {
|
|
12
|
+
changelog: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
dependencies: never[];
|
|
15
|
+
deprecated: boolean;
|
|
16
|
+
id: string;
|
|
17
|
+
version: string;
|
|
18
|
+
};
|
|
19
|
+
license: string;
|
|
20
|
+
name: string;
|
|
21
|
+
publisher: {
|
|
22
|
+
createdAt: string;
|
|
23
|
+
description: string;
|
|
24
|
+
id: string;
|
|
25
|
+
logo: string;
|
|
26
|
+
members: never[];
|
|
27
|
+
name: string;
|
|
28
|
+
source_code_repo: string;
|
|
29
|
+
support: string;
|
|
30
|
+
website: string;
|
|
31
|
+
};
|
|
32
|
+
repository: string;
|
|
33
|
+
tags: never[];
|
|
34
|
+
}[]
|
|
35
|
+
> {
|
|
6
36
|
const r = (await fetchJson<typeof mockPublishedNodes>(
|
|
7
|
-
"https://api.comfy.org/nodes?page=1&limit=99999999"
|
|
37
|
+
"https://api.comfy.org/nodes?page=1&limit=99999999",
|
|
8
38
|
)) as typeof mockPublishedNodes;
|
|
9
|
-
r.totalPages
|
|
39
|
+
if (r.totalPages !== 1) DIE("FAIL TO FETCH ALL NODES");
|
|
10
40
|
return r.nodes;
|
|
11
41
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { fetchJson } from "./utils/fetchJson";
|
|
2
2
|
export async function fetchCurrentGeoInfo() {
|
|
3
|
-
const { query, city, iat, lon, countryCode, region, regionName } = (await fetchJson(
|
|
3
|
+
const { query, city, iat, lon, countryCode, region, regionName } = (await fetchJson(
|
|
4
|
+
"http://ip-api.com/json",
|
|
5
|
+
)) as Record<string, unknown>;
|
|
4
6
|
const geo = { ip: query, city, iat, lon, countryCode, region, regionName };
|
|
5
7
|
return geo;
|
|
6
8
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import pMap from "p-map";
|
|
2
|
-
import { fetchIssueComments } from "
|
|
2
|
+
import { fetchIssueComments } from "@/lib/github/fetchIssueComments";
|
|
3
3
|
import { matchRelatedPulls } from "./matchRelatedPulls";
|
|
4
4
|
import type { GithubPullParsed } from "./parsePullsState";
|
|
5
5
|
import { summaryLastPullComment } from "./summaryLastPullComment";
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { fromPairs } from "rambda";
|
|
2
2
|
import { fetchCMNodes } from "./fetchCMNodes";
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
export async function fetchRepoDescriptionMap() {
|
|
6
5
|
const nodeList = await fetchCMNodes();
|
|
7
|
-
const repoDescriptionMap = fromPairs(
|
|
8
|
-
nodeList.map((e) => [e.reference, e.description])
|
|
9
|
-
);
|
|
6
|
+
const repoDescriptionMap = fromPairs(nodeList.map((e) => [e.reference, e.description]));
|
|
10
7
|
repoDescriptionMap["https://github.com/snomiao/ComfyNode-Registry-test"] =
|
|
11
8
|
"ComfyNode-Registry-test-description";
|
|
12
9
|
|
package/src/followRuleSchema.ts
CHANGED
|
@@ -2,7 +2,9 @@ import { $before, $fresh, $stale } from "@/packages/mongodb-pipeline-ts/$fresh";
|
|
|
2
2
|
import { tryCatch } from "rambda";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
|
-
const znot_include = z
|
|
5
|
+
const znot_include = z
|
|
6
|
+
.object({ "not-include": z.string() })
|
|
7
|
+
.transform((x) => ({ $nin: x["not-include"] }));
|
|
6
8
|
const mString = z.string().transform((x) =>
|
|
7
9
|
tryCatch<string, string | RegExp>(
|
|
8
10
|
(x) => new RegExp(x) as RegExp,
|
|
@@ -12,8 +14,12 @@ const mString = z.string().transform((x) =>
|
|
|
12
14
|
const zbefore = z
|
|
13
15
|
.object({ $before: z.coerce.date().or(z.string()).or(z.number()) })
|
|
14
16
|
.transform((x) => $before(x.$before));
|
|
15
|
-
const zfresh = z
|
|
16
|
-
|
|
17
|
+
const zfresh = z
|
|
18
|
+
.object({ $fresh: z.coerce.date().or(z.string()).or(z.number()) })
|
|
19
|
+
.transform((x) => $fresh(x.$fresh));
|
|
20
|
+
const mstale = z
|
|
21
|
+
.object({ $stale: z.coerce.date().or(z.string()).or(z.number()) })
|
|
22
|
+
.transform((x) => $stale(x.$stale));
|
|
17
23
|
const mDate = z.date().or(zbefore).or(zfresh).or(mstale);
|
|
18
24
|
const mAny = z.number().or(znot_include).or(mString).or(zbefore).or(zfresh);
|
|
19
25
|
const mNumber = z
|
|
@@ -70,7 +76,7 @@ const zFollowUpRule = z.object({
|
|
|
70
76
|
tags: mAny,
|
|
71
77
|
})
|
|
72
78
|
.strict(),
|
|
73
|
-
// close: z.
|
|
79
|
+
// close: z.unknown(),
|
|
74
80
|
})
|
|
75
81
|
.strict()
|
|
76
82
|
.partial(),
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { rm } from "fs/promises";
|
|
2
2
|
import { getRepoWorkingDir } from "./getRepoWorkingDir";
|
|
3
|
-
import {
|
|
3
|
+
import { parseGithubRepoUrl } from "./parseOwnerRepo";
|
|
4
4
|
|
|
5
|
-
export async function getBranchWorkingDir(
|
|
6
|
-
upstreamUrl
|
|
7
|
-
forkUrl: string,
|
|
8
|
-
branch: string
|
|
9
|
-
) {
|
|
10
|
-
const src = parseUrlRepoOwner(upstreamUrl);
|
|
5
|
+
export async function getBranchWorkingDir(upstreamUrl: string, forkUrl: string, branch: string) {
|
|
6
|
+
const src = parseGithubRepoUrl(upstreamUrl);
|
|
11
7
|
const dir = getRepoWorkingDir(forkUrl);
|
|
12
8
|
const packageName = src.repo;
|
|
13
9
|
const cwd = `${dir}/${branch}/${packageName}`;
|
package/src/getRepoWorkingDir.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseGithubRepoUrl } from "./parseOwnerRepo";
|
|
2
2
|
|
|
3
3
|
export function getRepoWorkingDir(forkUrl: string) {
|
|
4
|
-
return `prs/${
|
|
4
|
+
return `prs/${parseGithubRepoUrl(forkUrl).repo}`;
|
|
5
5
|
}
|
|
6
|
-
|