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.
Files changed (100) hide show
  1. package/README.md +258 -157
  2. package/bot/IdleWaiter.ts +31 -0
  3. package/bot/RestartManager.spec.ts +163 -0
  4. package/bot/RestartManager.ts +190 -0
  5. package/bot/WorkingTasksManager.spec.ts +154 -0
  6. package/bot/cli.ts +1171 -0
  7. package/bot/codesearch-ai-wip.ts +351 -0
  8. package/bot/error-collector.ts +133 -0
  9. package/bot/index.ts +14 -0
  10. package/bot/restart-example.ts +99 -0
  11. package/bot/slack-bolt.ts +688 -0
  12. package/bot/slack-bot.ts +1575 -0
  13. package/bot/state.ts +19 -0
  14. package/bot/templateLoader.test.ts +84 -0
  15. package/bot/templateLoader.ts +92 -0
  16. package/next.config.ts +26 -0
  17. package/package.json +161 -41
  18. package/post-summary.ts +44 -0
  19. package/src/Authors.ts +2 -2
  20. package/src/CMNodes.ts +3 -3
  21. package/src/CNRepos.ts +30 -7
  22. package/src/CRNodes.ts +11 -9
  23. package/src/CRPulls.ts +3 -0
  24. package/src/EmailTasks.ts +27 -9
  25. package/src/FORK_OWNER.ts +3 -1
  26. package/src/FollowRules.ts +3 -3
  27. package/src/GithubIssueComments.ts +1 -1
  28. package/src/Totals.ts +9 -8
  29. package/src/WorkerInstances.ts +31 -8
  30. package/src/addCommentAction.ts +19 -11
  31. package/src/analyzePullsStatus.ts +39 -17
  32. package/src/analyzeTotals.ts +19 -11
  33. package/src/bypassRepos.ts +6 -5
  34. package/src/checkPRsFailures.ts +13 -8
  35. package/src/cli.test.ts +2 -0
  36. package/src/cli.ts +1 -1
  37. package/src/constants.ts +2 -0
  38. package/src/createComfyRegistryPRsFromCandidates.ts +20 -12
  39. package/src/createComfyRegistryPullRequests.ts +35 -10
  40. package/src/createGithubForkForRepo.ts +36 -11
  41. package/src/createGithubPullRequest.ts +83 -33
  42. package/src/createIssueComment.ts +4 -4
  43. package/src/fetchComfyRegistryNodes.ts +34 -4
  44. package/src/fetchCurrentGeoInfo.ts +3 -1
  45. package/src/fetchRelatedPullWithComments.ts +1 -1
  46. package/src/fetchRepoDescriptionMap.ts +1 -4
  47. package/src/followRuleSchema.ts +10 -4
  48. package/src/getBranchWorkingDir.ts +3 -7
  49. package/src/getRepoWorkingDir.ts +2 -3
  50. package/src/gh.spec.ts +571 -0
  51. package/src/ghData.ts +13 -0
  52. package/src/ghPageFlow.ts +33 -0
  53. package/src/ghSubscriber.ts +76 -0
  54. package/src/ghUser.ts +28 -4
  55. package/src/index.ts +13 -4
  56. package/src/initializeFollowRules.ts +11 -4
  57. package/src/logger.spec.ts +95 -0
  58. package/src/logger.ts +46 -0
  59. package/src/makeGpl3LicenseBranch.ts +66 -0
  60. package/src/makePublishBranch.ts +21 -15
  61. package/src/makeTomlBranch.ts +48 -14
  62. package/src/makeUpdateTomlLicenseBranch.ts +40 -44
  63. package/src/matchRelatedPulls.ts +9 -4
  64. package/src/normalizeGithubUrl.spec.ts +131 -0
  65. package/src/normalizeGithubUrl.ts +64 -0
  66. package/src/parseIssueUrl.spec.ts +95 -0
  67. package/src/parseIssueUrl.ts +17 -3
  68. package/src/parseOwnerRepo.spec.ts +147 -0
  69. package/src/parseOwnerRepo.ts +8 -5
  70. package/src/parsePullsState.ts +2 -2
  71. package/src/parseTitleBodyOfMarkdown.ts +1 -1
  72. package/src/pickRepoInfo.ts +37 -0
  73. package/src/postSlackMessage.ts +5 -1
  74. package/src/preload.ts +30 -27
  75. package/src/readTemplateTitle.ts +1 -3
  76. package/src/sendEmailAction.ts +14 -10
  77. package/src/sendGmail.ts +3 -3
  78. package/src/updateAuthorsForGithub.ts +53 -35
  79. package/src/updateAuthorsFromCNRepo.ts +12 -5
  80. package/src/updateCMNodesDuplicationWarnings.ts +14 -9
  81. package/src/updateCMRepos.ts +1 -1
  82. package/src/updateCNRepos.ts +14 -14
  83. package/src/updateCNReposCRPullsComments.ts +7 -5
  84. package/src/updateCNReposInfo.ts +49 -40
  85. package/src/updateCNReposPRCandidate.ts +3 -3
  86. package/src/updateCNReposPulls.ts +9 -5
  87. package/src/updateCNReposPullsDashboard.ts +11 -9
  88. package/src/updateCNReposRelatedPulls.ts +3 -3
  89. package/src/updateCRNodes.ts +24 -5
  90. package/src/updateCRRepos.ts +1 -1
  91. package/src/updateComfyTotals.ts +7 -5
  92. package/src/updateFollowRuleSet.ts +17 -9
  93. package/src/updateOutdatedPullsTemplates.ts +61 -48
  94. package/src/updateSlackMessages.ts +8 -4
  95. package/tailwind.config.ts +7 -1
  96. package/next-env.d.ts +0 -5
  97. package/src/GIT_USEREMAIL.ts +0 -5
  98. package/src/GIT_USERNAME.ts +0 -9
  99. package/src/clone_modify_push_Branches.ts +0 -21
  100. package/src/tomlFillDescription.ts +0 -17
@@ -3,11 +3,11 @@ import { $elemMatch } from "@/packages/mongodb-pipeline-ts/$elemMatch";
3
3
  import { TaskDataOrNull, type Task } from "@/packages/mongodb-pipeline-ts/Task";
4
4
  import DIE from "@snomiao/die";
5
5
  import pMap from "p-map";
6
- import { snoflow } from "snoflow";
6
+ import { sflow } from "sflow";
7
7
  import type { z } from "zod";
8
8
  import type { PullStatusShown } from "./analyzePullsStatus";
9
9
  import { CNRepos } from "./CNRepos";
10
- import { $filaten } from "./db";
10
+ import { $flatten } from "./db";
11
11
  import { enqueueEmailTask } from "./EmailTasks";
12
12
  import { zSendEmailAction } from "./followRuleSchema";
13
13
  import { yaml } from "./utils/yaml";
@@ -27,14 +27,15 @@ export async function sendEmailAction({
27
27
  TaskDataOrNull(matched) ?? DIE("NO-PAYLOAD-AVAILABLE"),
28
28
  async (payload) => {
29
29
  if (action.provider !== "google") DIE("Currently we only support gmail sender");
30
- const loadedAction = await snoflow([action])
30
+ const loadedAction = await sflow([action])
31
31
  .map((e) => yaml.stringify(e))
32
32
  .map((e) =>
33
33
  // replace {{var}} s
34
- e.replace(
35
- /{{\$([_A-Za-z0-9]+)}}/g,
36
- (_, key: string) =>
37
- (payload as any)[key] || DIE("Missing key: " + key + " in payload: " + JSON.stringify(payload)),
34
+ e.replace(/{{\$([_A-Za-z0-9]+)}}/g, (_, key: string) =>
35
+ String(
36
+ (payload as Record<string, unknown>)[key] ??
37
+ DIE("Missing key: " + key + " in payload: " + JSON.stringify(payload)),
38
+ ),
38
39
  ),
39
40
  )
40
41
  .map((e) => yaml.parse(e))
@@ -45,9 +46,12 @@ export async function sendEmailAction({
45
46
  if (runAction) {
46
47
  const task = await enqueueEmailTask(loadedAction);
47
48
  console.log(rule.name + " email enqueued :" + yaml.stringify(loadedAction));
48
- await CNRepos.updateOne($filaten({ crPulls: { data: $elemMatch({ pull: { html_url: payload.url } }) } }), {
49
- $set: { "crPulls.data.$.emailTask_id": task._id },
50
- });
49
+ await CNRepos.updateOne(
50
+ $flatten({ crPulls: { data: $elemMatch({ pull: { html_url: payload.url } }) } }),
51
+ {
52
+ $set: { "crPulls.data.$.emailTask_id": task._id },
53
+ },
54
+ );
51
55
  }
52
56
  return loadedAction;
53
57
  },
package/src/sendGmail.ts CHANGED
@@ -91,9 +91,9 @@ export async function sendGmail({
91
91
  msg.setSender({ name: name, addr: from });
92
92
  msg.setRecipient(to);
93
93
  msg.setSubject(subject);
94
- text && msg.addMessage({ contentType: "text/plain", data: text });
95
- html && msg.addMessage({ contentType: "text/html", data: html });
96
- text || html || DIE("Missing msg body");
94
+ if (text) msg.addMessage({ contentType: "text/plain", data: text });
95
+ if (html) msg.addMessage({ contentType: "text/html", data: html });
96
+ if (!text && !html) DIE("Missing msg body");
97
97
 
98
98
  const result = await new GoogleApis().gmail("v1").users.messages.send({
99
99
  auth,
@@ -2,53 +2,71 @@ import { $OK, TaskDataOrNull, TaskError, TaskOK } from "@/packages/mongodb-pipel
2
2
  import DIE from "@snomiao/die";
3
3
  import console from "console";
4
4
  import { peekYaml } from "peek-log";
5
- import { snoflow } from "snoflow";
5
+ import { sflow } from "sflow";
6
6
  import { Authors, GithubUsers } from "./Authors";
7
7
  import { $stale } from "./db";
8
- import { gh } from "./gh";
8
+ import { gh } from "@/lib/github";
9
9
 
10
10
  if (import.meta.main) {
11
11
  await updateAuthorsForGithub();
12
12
  }
13
13
  export async function updateAuthorsForGithub() {
14
- await snoflow(Authors.find({ githubMtime: $stale("7d") }))
14
+ await sflow(Authors.find({ githubMtime: $stale("7d") }))
15
15
  .map((e) => e.githubId)
16
16
  .filter()
17
- .pMap(2, async (username) => {
18
- const cached = await GithubUsers.findOne({ username, mtime: $stale("1d"), ...$OK });
19
- if (cached) return cached;
20
- console.log("Fetching github user " + username);
21
- const result = await gh.users
22
- .getByUsername({ username })
23
- .then((e) => e.data)
24
- .then(TaskOK)
25
- .catch(TaskError);
26
- return (
27
- (await GithubUsers.findOneAndUpdate(
28
- { username },
29
- { $set: result },
30
- { returnDocument: "after", upsert: true },
31
- )) ?? DIE(`fail to udpate gh users`)
32
- );
33
- })
17
+ .pMap(
18
+ async (username) => {
19
+ const cached = await GithubUsers.findOne({ username, mtime: $stale("1d"), ...$OK });
20
+ if (cached) return cached;
21
+ console.log("Fetching github user " + username);
22
+ const result = await gh.users
23
+ .getByUsername({ username })
24
+ .then((e) => e.data)
25
+ .then(TaskOK)
26
+ .catch(TaskError);
27
+ return (
28
+ (await GithubUsers.findOneAndUpdate(
29
+ { username },
30
+ { $set: result },
31
+ { returnDocument: "after", upsert: true },
32
+ )) ?? DIE(`fail to udpate gh users`)
33
+ );
34
+ },
35
+ { concurrency: 2 },
36
+ )
34
37
  .map((e) => TaskDataOrNull(e))
35
38
  .filter()
36
- .map(({ email, avatar_url, blog, updated_at, location, company, hireable, bio, login }) =>
37
- Authors.findOneAndUpdate(
38
- { githubId: login },
39
- {
40
- $set: { githubMtime: new Date(), ...(email && { email }), ...(null != hireable && { hireable }) },
41
- $addToSet: {
42
- ...(bio && { bios: bio }),
43
- avatars: avatar_url,
44
- ...(location && { locations: location }),
45
- ...(blog && { blogs: blog }),
46
- ...(company && { companies: company }),
39
+ .map(
40
+ ({
41
+ email,
42
+ avatar_url,
43
+ blog,
44
+ updated_at: _updated_at,
45
+ location,
46
+ company,
47
+ hireable,
48
+ bio,
49
+ login,
50
+ }) =>
51
+ Authors.findOneAndUpdate(
52
+ { githubId: login },
53
+ {
54
+ $set: {
55
+ githubMtime: new Date(),
56
+ ...(email && { email }),
57
+ ...(null != hireable && { hireable }),
58
+ },
59
+ $addToSet: {
60
+ ...(bio && { bios: bio }),
61
+ avatars: avatar_url,
62
+ ...(location && { locations: location }),
63
+ ...(blog && { blogs: blog }),
64
+ ...(company && { companies: company }),
65
+ },
47
66
  },
48
- },
49
- { upsert: true, returnDocument: "after" },
50
- ),
67
+ { upsert: true, returnDocument: "after" },
68
+ ),
51
69
  )
52
- .peek(peekYaml)
70
+ .forEach(peekYaml)
53
71
  .done();
54
72
  }
@@ -1,14 +1,14 @@
1
1
  import { $pipeline } from "@/packages/mongodb-pipeline-ts/$pipeline";
2
- import { snoflow } from "snoflow";
2
+ import { sflow } from "sflow";
3
3
  import { Authors } from "./Authors";
4
4
  import { CNRepos } from "./CNRepos";
5
- import { $filaten } from "./db";
5
+ import { $flatten } from "./db";
6
6
 
7
7
  /** Update authors for gh users, collecting emails/username/hireable */
8
8
  export async function updateAuthorsFromCNRepo() {
9
- return await snoflow(
9
+ return await sflow(
10
10
  $pipeline(CNRepos)
11
- .match($filaten({ info: { data: { owner: { login: { $exists: true } } } } }))
11
+ .match($flatten({ info: { data: { owner: { login: { $exists: true } } } } }))
12
12
  .group({
13
13
  _id: "$info.data.owner.login",
14
14
  // author: "$info.data.owner.login",
@@ -25,6 +25,13 @@ export async function updateAuthorsFromCNRepo() {
25
25
  .project({ _id: 0 })
26
26
  .aggregate(),
27
27
  )
28
- .map(({ githubId, ...$set }) => Authors.updateOne({ githubId }, { $set }, { upsert: true }))
28
+ .map((doc) => {
29
+ const { githubId, ...$set } = doc as Record<string, unknown>;
30
+ return Authors.updateOne(
31
+ { githubId: githubId as string | undefined },
32
+ { $set: $set as Record<string, unknown> } as Parameters<typeof Authors.updateOne>[1],
33
+ { upsert: true },
34
+ );
35
+ })
29
36
  .done();
30
37
  }
@@ -1,19 +1,21 @@
1
- import { unary } from "lodash-es";
2
1
  import pMap from "p-map";
3
- import { dissoc, filter, groupBy, map, prop, toPairs } from "rambda";
2
+ import { filter, groupBy, map, prop, toPairs } from "rambda";
4
3
  import YAML from "yaml";
5
4
  import { CMNodes, type CMNode } from "./CMNodes";
6
- import { notifySlack } from "./slack/notifySlack";
5
+ import { notifySlack } from "@/lib/slack/notifySlack";
7
6
 
8
7
  export async function updateCMNodesDuplicationWarnings(nodes: CMNode[]) {
9
8
  console.log("CMNodes checking duplicates");
9
+ const idGroups = groupBy((e) => e.id, nodes);
10
+ const titleGroups = groupBy((e) => e.title, nodes);
11
+ const referenceGroups = groupBy((e) => e.reference, nodes);
10
12
  // prettier-ignore
11
13
  const dups = {
12
- ID: filter((e: typeof nodes) => e.length > 1, groupBy((e) => e.id, nodes)),
13
- TITLE: filter((e: typeof nodes) => e.length > 1, groupBy((e) => e.title, nodes)),
14
- REFERENCE: filter((e: typeof nodes) => e.length > 1, groupBy((e) => e.reference, nodes)),
14
+ ID: filter((e?: CMNode[]) => (e?.length??0) > 1, idGroups),
15
+ TITLE: filter((e?: CMNode[]) => (e?.length??0) > 1, titleGroups),
16
+ REFERENCE: filter((e?: CMNode[]) => (e?.length??0) > 1, referenceGroups),
15
17
  };
16
- const dupsSummary = JSON.stringify(map((x) => map((x) => x.length, x), dups));
18
+ const dupsSummary = JSON.stringify(map((x) => map((x) => x?.length ?? 0, x), dups));
17
19
  await notifySlack(
18
20
  `[WARN] CMNodes duplicates: ${dupsSummary}\nSolve them in https://github.com/ltdrdata/ComfyUI-Manager/blob/main/custom-node-list.json`,
19
21
  );
@@ -24,8 +26,11 @@ export async function updateCMNodesDuplicationWarnings(nodes: CMNode[]) {
24
26
  await pMap(
25
27
  toPairs(nodes),
26
28
  async ([key, nodesRaw]) => {
27
- const nodes = nodesRaw.map(unary(dissoc("hash")));
28
- const hashes = nodesRaw.map(prop("hash"));
29
+ const nodes = nodesRaw?.map((nodeRaw) => {
30
+ const { hash: _hash, ...node } = { ...nodeRaw };
31
+ return node;
32
+ });
33
+ const hashes = nodesRaw?.map(prop("hash"));
29
34
  // check sent
30
35
  const someDuplicateSent = await CMNodes.findOne({
31
36
  hash: { $in: hashes },
@@ -8,7 +8,7 @@ if (import.meta.main) {
8
8
  }
9
9
  export async function updateCMRepos() {
10
10
  await CMNodes.createIndex({ repo_id: 1 });
11
- return await pMap(CMNodes.find({ repo_id: { $exists: false } }), async (cm, i) => {
11
+ return await pMap(CMNodes.find({ repo_id: { $exists: false } }), async (cm, _i) => {
12
12
  const { reference: repository, _id } = cm;
13
13
  return await CNRepos.updateOne(
14
14
  { repository },
@@ -1,7 +1,7 @@
1
1
  import { updateCMNodes } from "./CMNodes";
2
2
  import { getWorkerInstance } from "./WorkerInstances";
3
3
  import { createComfyRegistryPRsFromCandidates } from "./createComfyRegistryPRsFromCandidates";
4
- import { notifySlack } from "./slack/notifySlack";
4
+ import { notifySlack } from "@/lib/slack/notifySlack";
5
5
  import { updateCMRepos } from "./updateCMRepos";
6
6
  import { updateCNReposCRPullsComments } from "./updateCNReposCRPullsComments";
7
7
  import { updateCNReposInfo } from "./updateCNReposInfo";
@@ -34,24 +34,24 @@ export async function updateCNRepos() {
34
34
  return [await notifySlack(msg, { unique: true, silent: true })];
35
35
  }),
36
36
  // stage 1: get repos
37
- await tLog("Update Nodes from ComfyUI Manager", updateCMNodes),
38
- await tLog("Update Repos from ComfyUI Manager", updateCMRepos),
39
- await tLog("Update Nodes from ComfyRegistry", updateCRNodes),
40
- await tLog("Update Repos from ComfyRegistry", updateCRRepos),
37
+ tLog("Update Nodes from ComfyUI Manager", updateCMNodes),
38
+ tLog("Update Repos from ComfyUI Manager", updateCMRepos),
39
+ tLog("Update Nodes from ComfyRegistry", updateCRNodes),
40
+ tLog("Update Repos from ComfyRegistry", updateCRRepos),
41
41
  // stage 2: update repo info & pulls
42
- await tLog("Update CNRepos for Repo Infos", updateCNReposInfo),
43
- await tLog("Update CNRepos for Github Pulls", updateCNReposPulls),
44
- await tLog("Update Pulls Dashboard", updateCNRepoPullsDashboard),
42
+ tLog("Update CNRepos for Repo Infos", updateCNReposInfo),
43
+ tLog("Update CNRepos for Github Pulls", updateCNReposPulls),
44
+ tLog("Update Pulls Dashboard", updateCNRepoPullsDashboard),
45
45
  // stage 3: update related pulls and comments
46
- await tLog("Update CNRepos for Related Pulls", updateCNReposRelatedPulls),
47
- await tLog("Update Outdated Pulls Templates", updateOutdatedPullsTemplates),
46
+ tLog("Update CNRepos for Related Pulls", updateCNReposRelatedPulls),
47
+ tLog("Update Outdated Pulls Templates", updateOutdatedPullsTemplates),
48
48
  // stage 4: update related comments
49
- await tLog("Update CNRepos for Related Comments", updateCNReposCRPullsComments),
49
+ tLog("Update CNRepos for Related Comments", updateCNReposCRPullsComments),
50
50
  // stage 5: mark and create PRs
51
- await tLog("Update CNRepos PR Candidates", updateCNReposPRCandidate),
52
- await tLog("Create ComfyRegistry PRs", createComfyRegistryPRsFromCandidates),
51
+ tLog("Update CNRepos PR Candidates", updateCNReposPRCandidate),
52
+ tLog("Create ComfyRegistry PRs", createComfyRegistryPRsFromCandidates),
53
53
  // final
54
- await tLog("Update Comfy Totals", updateComfyTotals),
54
+ tLog("Update Comfy Totals", updateComfyTotals),
55
55
  ]);
56
56
 
57
57
  console.log("All repo updated");
@@ -3,11 +3,11 @@ import { CNRepos, type CRPull } from "./CNRepos";
3
3
  import { $fresh, $stale } from "./db";
4
4
  // import { $filaten } from "./db";
5
5
  // import { $pipeline } from "./db/$pipeline";
6
- import { $filaten } from "@/packages/mongodb-pipeline-ts/$filaten";
6
+ import { $flatten } from "@/packages/mongodb-pipeline-ts/$flatten";
7
7
  import { $pipeline } from "@/packages/mongodb-pipeline-ts/$pipeline";
8
8
  import { TaskError, TaskOK } from "../packages/mongodb-pipeline-ts/Task";
9
9
  import { getWorkerInstance } from "./WorkerInstances";
10
- import { fetchIssueComments } from "./gh/fetchIssueComments";
10
+ import { fetchIssueComments } from "@/lib/github/fetchIssueComments";
11
11
 
12
12
  if (import.meta.main) {
13
13
  await getWorkerInstance("updateCNReposCRPullsComments");
@@ -19,8 +19,10 @@ export async function updateCNReposCRPullsComments() {
19
19
  return await pMap(
20
20
  $pipeline(CNRepos)
21
21
  .unwind("$crPulls.data")
22
- .match($filaten({ crPulls: { mtime: $fresh("7d"), data: { comments: { mtime: $stale("1d") } } } }))
23
- .set($filaten({ "crPulls.data": { repository: "$repository" } }))
22
+ .match(
23
+ $flatten({ crPulls: { mtime: $fresh("7d"), data: { comments: { mtime: $stale("1d") } } } }),
24
+ )
25
+ .set($flatten({ "crPulls.data": { repository: "$repository" } }))
24
26
  .replaceRoot({ newRoot: "$crPulls.data" })
25
27
  .aggregate(),
26
28
  async (data) => {
@@ -29,7 +31,7 @@ export async function updateCNReposCRPullsComments() {
29
31
  const comments = await fetchIssueComments(repository, pull).then(TaskOK).catch(TaskError);
30
32
  return [
31
33
  await CNRepos.findOneAndUpdate(
32
- $filaten({ repository, crPulls: { data: { pull: { html_url } } } }),
34
+ $flatten({ repository, crPulls: { data: { pull: { html_url } } } }),
33
35
  { $set: { "crPulls.data.$.comments": comments } },
34
36
  { upsert: true, returnDocument: "after" },
35
37
  ),
@@ -4,10 +4,11 @@ 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 { getWorkerInstance } from "./WorkerInstances";
7
- import { $filaten, $stale } from "./db";
8
- import { gh } from "./gh";
9
- import { parseUrlRepoOwner } from "./parseOwnerRepo";
7
+ import { $flatten, $stale } from "./db";
8
+ import { gh } from "@/lib/github";
9
+ import { parseGithubRepoUrl } from "./parseOwnerRepo";
10
10
  import { tLog } from "./utils/tLog";
11
+ import { pickRepoInfo } from "./pickRepoInfo";
11
12
 
12
13
  if (import.meta.main) {
13
14
  await getWorkerInstance("updateCNReposInfo");
@@ -15,44 +16,52 @@ if (import.meta.main) {
15
16
  }
16
17
 
17
18
  export async function updateCNReposInfo() {
18
- await CNRepos.createIndex($filaten({ info: { mtime: 1 } }));
19
- return await sflow(
20
- CNRepos.find($filaten({ info: { mtime: $stale("1d") } }))
21
- ).pMap(
22
- async (repo) => {
23
- const { repository } = repo;
24
- console.log("[INFO] Fetching meta info from " + repository);
25
- const _info = await gh.repos
26
- .get({ ...parseUrlRepoOwner(repository) })
27
- .then(({ data }) => data)
28
- .then(TaskOK)
29
- .catch(TaskError);
30
- // Handle renamed repos
31
- const { info, url } = await match(_info)
32
- .with($OK, async (info) => {
33
- const url = info.data.html_url;
34
- if (url === repository) return { info, url: repository };
19
+ await CNRepos.createIndex($flatten({ info: { mtime: 1 } }));
20
+ return await sflow(CNRepos.find($flatten({ info: { mtime: $stale("1d") } })))
21
+ .filter((repo) => {
22
+ // Skip repos with invalid repository values
23
+ if (!repo.repository || typeof repo.repository !== "string") {
24
+ console.log("[WARN] Skipping repo with invalid repository field:", repo);
25
+ return false;
26
+ }
27
+ return true;
28
+ })
29
+ .pMap(
30
+ async (repo) => {
31
+ const { repository } = repo;
32
+ console.log("[INFO] Fetching meta info from " + repository);
33
+ const _info = await gh.repos
34
+ .get({ ...parseGithubRepoUrl(repository) })
35
+ .then(({ data }) => pickRepoInfo(data))
36
+ .then(TaskOK)
37
+ .catch(TaskError);
38
+ // Handle renamed repos
39
+ const { info, url } = await match(_info)
40
+ .with($OK, async (info) => {
41
+ const url = info.data.html_url;
42
+ if (url === repository) return { info, url: repository };
35
43
 
36
- // console.log(info.data.use_squash_pr_title_as_default)
37
- console.log("[INFO] Migrating renamed repo: \nfrom: ", repository + "\n to: " + url);
38
- // migrate data into new CNRepo
39
- await CNRepos.updateOne(
40
- { repository: url },
41
- {
42
- $set: {
43
- ...dissoc("_id", { ...(await CNRepos.findOneAndDelete({ repository })) }),
44
- repository: url,
45
- oldUrls: { $addToSet: repository },
44
+ // console.log(info.data.use_squash_pr_title_as_default)
45
+ console.log("[INFO] Migrating renamed repo: \nfrom: ", repository + "\n to: " + url);
46
+ // migrate data into new CNRepo
47
+ await CNRepos.updateOne(
48
+ { repository: url },
49
+ {
50
+ $set: {
51
+ ...dissoc("_id", { ...(await CNRepos.findOneAndDelete({ repository })) }),
52
+ repository: url,
53
+ oldUrls: { $addToSet: repository },
54
+ },
46
55
  },
47
- },
48
- { upsert: true },
49
- );
50
- return { info, url };
51
- })
52
- .otherwise((info) => ({ info, url: repository }));
56
+ { upsert: true },
57
+ );
58
+ return { info, url };
59
+ })
60
+ .otherwise((info) => ({ info, url: repository }));
53
61
 
54
- return await CNRepos.updateOne({ repository: url }, { $set: { info } }, { upsert: true });
55
- },
56
- { concurrency: 2 },
57
- ).toArray();
62
+ return await CNRepos.updateOne({ repository: url }, { $set: { info } }, { upsert: true });
63
+ },
64
+ { concurrency: 2 },
65
+ )
66
+ .toArray();
58
67
  }
@@ -4,7 +4,7 @@ import { peekYaml } from "peek-log";
4
4
  import { match } from "ts-pattern";
5
5
  import { $OK, TaskOK } from "../packages/mongodb-pipeline-ts/Task";
6
6
  import { CNRepos } from "./CNRepos";
7
- import { $filaten, $fresh, $stale } from "./db";
7
+ import { $flatten, $fresh, $stale } from "./db";
8
8
  import { updateCNReposPulls } from "./updateCNReposPulls";
9
9
  import { updateCNReposRelatedPulls } from "./updateCNReposRelatedPulls";
10
10
  import { tLog } from "./utils/tLog";
@@ -31,7 +31,7 @@ if (import.meta.main) {
31
31
  peekYaml(
32
32
  await $pipeline(CNRepos)
33
33
  .match(
34
- $filaten({
34
+ $flatten({
35
35
  crPulls: { mtime: $stale("7d") },
36
36
  // info: { mtime: $fresh("7d"), data: { private: false, archived: false } },
37
37
  // createdPulls: { mtime: $stale("7d"), data: { $exists: false } },
@@ -55,7 +55,7 @@ export async function updateCNReposPRCandidate() {
55
55
  return await pMap(
56
56
  $pipeline(CNRepos)
57
57
  .match(
58
- $filaten({
58
+ $flatten({
59
59
  crPulls: { mtime: $fresh("1d"), ...$OK },
60
60
  // info: { mtime: $fresh("7d"), ...$OK, data: { private: false, archived: false } },
61
61
  info: { mtime: $fresh("7d"), ...$OK },
@@ -3,8 +3,8 @@ import sflow from "sflow";
3
3
  import { match } from "ts-pattern";
4
4
  import { $OK, TaskError, TaskOK } from "../packages/mongodb-pipeline-ts/Task";
5
5
  import { CNRepos } from "./CNRepos";
6
- import { $filaten, $stale } from "./db";
7
- import { fetchGithubPulls } from "./gh/fetchGithubPulls";
6
+ import { $flatten, $stale } from "./db";
7
+ import { fetchGithubPulls } from "@/lib/github/fetchGithubPulls";
8
8
  import { tLog } from "./utils/tLog";
9
9
  if (import.meta.main) {
10
10
  console.log(await tLog("Update CNRepos for Github Pulls", updateCNReposPulls));
@@ -13,15 +13,19 @@ export async function updateCNReposPulls() {
13
13
  await CNRepos.createIndex("pulls.mtime");
14
14
  return await sflow(
15
15
  $pipeline(CNRepos)
16
- .match($filaten({ pulls: { mtime: $stale("1d") } }))
16
+ .match($flatten({ pulls: { mtime: $stale("1d") } }))
17
17
  .project({ repository: 1 })
18
18
  .aggregate(),
19
19
  )
20
20
  .pMap(
21
21
  async ({ repository }) => {
22
- const pulls = await fetchGithubPulls(repository).then(TaskOK).catch(TaskError);
22
+ const pulls = await fetchGithubPulls(repository as string)
23
+ .then(TaskOK)
24
+ .catch(TaskError);
23
25
  match(pulls)
24
- .with($OK, ({ data }) => console.debug(`[DEBUG] fetched ${data.length} Pulls from ${repository}`))
26
+ .with($OK, ({ data }) =>
27
+ console.debug(`[DEBUG] fetched ${data.length} Pulls from ${repository}`),
28
+ )
25
29
  .otherwise(() => {});
26
30
  return await CNRepos.updateOne({ repository }, { $set: { pulls } });
27
31
  },
@@ -2,24 +2,26 @@ import DIE from "@snomiao/die";
2
2
  import { match } from "ts-pattern";
3
3
  import { $OK } from "../packages/mongodb-pipeline-ts/Task";
4
4
  import { CNRepos } from "./CNRepos";
5
- import { $filaten, $fresh } from "./db";
6
- import { gh } from "./gh";
5
+ import { $flatten, $fresh } from "./db";
6
+ import { gh } from "@/lib/github";
7
7
  import { ghUser } from "./ghUser";
8
- import { parseUrlRepoOwner, stringifyOwnerRepo } from "./parseOwnerRepo";
8
+ import { parseGithubRepoUrl, stringifyOwnerRepo } from "./parseOwnerRepo";
9
9
 
10
10
  export async function updateCNRepoPullsDashboard() {
11
- if (ghUser.login !== "snomiao") return [];
11
+ if ((await ghUser()).login !== "snomiao") return [];
12
12
  const dashBoardIssue = process.env.DASHBOARD_ISSUE_URL || DIE("DASHBOARD_ISSUE_URL not found");
13
13
  const dashBoardRepo = dashBoardIssue.replace(/\/issues\/\d+$/, "");
14
- const dashBoardIssueNumber = Number(dashBoardIssue.match(/\/issues\/(\d+)$/)?.[1] || DIE("Issue number not found"));
14
+ const dashBoardIssueNumber = Number(
15
+ dashBoardIssue.match(/\/issues\/(\d+)$/)?.[1] || DIE("Issue number not found"),
16
+ );
15
17
  // update dashboard issue if run by @snomiao
16
- const repos = await CNRepos.find($filaten({ crPulls: { mtime: $fresh("1d") } })).toArray();
18
+ const repos = await CNRepos.find($flatten({ crPulls: { mtime: $fresh("1d") } })).toArray();
17
19
  const result = repos
18
20
  .map((repo) => {
19
21
  const crPulls = match(repo.crPulls)
20
22
  .with($OK, ({ data }) => data)
21
23
  .otherwise(() => DIE("CR Pulls not found"));
22
- const repoName = stringifyOwnerRepo(parseUrlRepoOwner(repo.repository));
24
+ const repoName = stringifyOwnerRepo(parseGithubRepoUrl(repo.repository));
23
25
  const body = crPulls
24
26
  .filter((e) => e.pull.prState !== "closed")
25
27
  .map((e) => {
@@ -31,7 +33,7 @@ export async function updateCNRepoPullsDashboard() {
31
33
  };
32
34
  })
33
35
  .map(({ href, name }) => `- [${name}](${href})`)
34
- .toSorted()
36
+ .sort()
35
37
  .join("\n");
36
38
  return body;
37
39
  })
@@ -41,7 +43,7 @@ export async function updateCNRepoPullsDashboard() {
41
43
 
42
44
  return [
43
45
  await gh.issues.update({
44
- ...parseUrlRepoOwner(dashBoardRepo),
46
+ ...parseGithubRepoUrl(dashBoardRepo),
45
47
  issue_number: dashBoardIssueNumber,
46
48
  body,
47
49
  }),
@@ -3,7 +3,7 @@ import pMap from "p-map";
3
3
  import { match } from "ts-pattern";
4
4
  import { $OK, TaskError, TaskOK } from "../packages/mongodb-pipeline-ts/Task";
5
5
  import { CNRepos } from "./CNRepos";
6
- import { $filaten, $stale } from "./db";
6
+ import { $flatten, $stale } from "./db";
7
7
  import { matchRelatedPulls } from "./matchRelatedPulls";
8
8
  import { tLog } from "./utils/tLog";
9
9
  if (import.meta.main) {
@@ -12,8 +12,8 @@ if (import.meta.main) {
12
12
  export async function updateCNReposRelatedPulls() {
13
13
  await CNRepos.createIndex({ "pulls.state": 1, "crPulls.mtime": 1 });
14
14
  return await pMap(
15
- CNRepos.find($filaten({ pulls: { state: "ok" }, crPulls: { mtime: $stale("1d") } })),
16
- async (repo, i) => {
15
+ CNRepos.find($flatten({ pulls: { state: "ok" }, crPulls: { mtime: $stale("1d") } })),
16
+ async (repo, _i) => {
17
17
  const { repository } = repo;
18
18
  const pulls = match(repo.pulls)
19
19
  .with($OK, (e) => e.data)
@@ -4,9 +4,10 @@ import { filter, groupBy, values } from "rambda";
4
4
  import YAML from "yaml";
5
5
  import { TaskOK } from "../packages/mongodb-pipeline-ts/Task";
6
6
  import { CNRepos } from "./CNRepos";
7
+ import { UNCLAIMED_ADMIN_PUBLISHER_ID } from "./constants";
7
8
  import { CRNodes } from "./CRNodes";
8
9
  import { fetchCRNodes } from "./fetchComfyRegistryNodes";
9
- import { notifySlack } from "./slack/notifySlack";
10
+ import { notifySlack } from "@/lib/slack/notifySlack";
10
11
  import { tLog } from "./utils/tLog";
11
12
 
12
13
  if (import.meta.main) {
@@ -14,7 +15,11 @@ if (import.meta.main) {
14
15
  console.log("CRNodes updated");
15
16
 
16
17
  peekYaml(
17
- await $pipeline(CNRepos).project({ repository: 1, on_registry: 1 }).sample({ size: 8 }).aggregate().toArray(),
18
+ await $pipeline(CNRepos)
19
+ .project({ repository: 1, on_registry: 1 })
20
+ .sample({ size: 8 })
21
+ .aggregate()
22
+ .toArray(),
18
23
  );
19
24
  }
20
25
  export async function updateCRNodes() {
@@ -22,17 +27,31 @@ export async function updateCRNodes() {
22
27
 
23
28
  // check src duplicated
24
29
  const group = groupBy((e) => e.repository, nodes);
25
- const duplicates = filter((e) => e.length > 1, group);
30
+ const duplicates = filter((e) => (e?.length ?? 0) > 1, group);
26
31
  if (values(duplicates).length) {
27
32
  const msg =
28
- "[WARN] Same repo but different ids found in comfyregistry:\n" + "```\n" + YAML.stringify(duplicates) + "\n```";
33
+ "[WARN] Same repo but different ids found in comfyregistry:\n" +
34
+ "```\n" +
35
+ YAML.stringify(duplicates) +
36
+ "\n```";
29
37
  await notifySlack(msg, { unique: true });
30
38
  }
31
39
 
40
+ const CRNodesRepo = nodes.map(({ repository }) => repository).filter(Boolean);
41
+ const CRNodesRepoExcludeUnclaimed = nodes
42
+ .filter((e) => e.publisher.id !== UNCLAIMED_ADMIN_PUBLISHER_ID)
43
+ .map(({ repository }) => repository)
44
+ .filter(Boolean);
32
45
  return [
33
46
  await $pipeline(CNRepos)
34
47
  .project({ repository: 1 })
35
- .set({ on_registry: TaskOK({ $in: ["$repository", nodes.map(({ repository }) => repository).filter(Boolean)] }) })
48
+ .match({
49
+ repository: { $exists: true, $ne: null as unknown as string, $type: "string" },
50
+ })
51
+ .set({
52
+ on_registry_all: TaskOK({ $in: ["$repository", CRNodesRepo] }),
53
+ on_registry: TaskOK({ $in: ["$repository", CRNodesRepoExcludeUnclaimed] }),
54
+ })
36
55
  .merge({ into: "CNRepos", on: "repository", whenMatched: "merge", whenNotMatched: "insert" })
37
56
  .aggregate()
38
57
  .next(),