comfy-pr 0.2.24 → 0.2.26
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 +247 -225
- package/index.ts +6 -0
- package/next-env.d.ts +5 -5
- package/package.json +52 -25
- package/src/Authors.ts +42 -48
- package/src/CMNodes.ts +60 -60
- package/src/CNRepos.ts +78 -74
- package/src/CRNodes.ts +27 -27
- package/src/CRPulls.ts +4 -4
- package/src/EmailTasks.ts +101 -0
- package/src/FORK_OWNER.ts +5 -5
- package/src/FORK_PREFIX.ts +5 -5
- package/src/FollowRules.ts +25 -25
- package/src/GIT_USEREMAIL.ts +5 -5
- package/src/GIT_USERNAME.ts +9 -9
- package/src/GithubIssueComments.ts +3 -3
- package/src/PushedBranch.ts +3 -3
- package/src/Totals.ts +24 -9
- package/src/TrackingPRs.ts +12 -12
- package/src/WorkerInstances.ts +89 -89
- package/src/addCommentAction.ts +73 -65
- package/src/analyzePullsStatus.ts +219 -170
- package/src/analyzeTotals.test.ts +5 -5
- package/src/analyzeTotals.ts +118 -118
- package/src/bypassRepos.spec.ts +10 -0
- package/src/bypassRepos.ts +8 -0
- package/src/checkComfyActivated.ts +39 -39
- package/src/checkPRsFailures.ts +18 -0
- package/src/cli.ts +40 -40
- package/src/clone_modify_push_Branches.ts +21 -21
- package/src/createComfyRegistryPRsFromCandidates.ts +55 -55
- package/src/createComfyRegistryPullRequests.ts +32 -22
- package/src/createGithubForkForRepo.ts +54 -37
- package/src/createGithubPullRequest.ts +157 -94
- package/src/createIssueComment.ts +34 -29
- package/src/fetchCMNodes.ts +22 -22
- package/src/fetchComfyRegistryNodes.ts +11 -11
- package/src/fetchCurrentGeoInfo.ts +6 -6
- package/src/fetchRelatedPullWithComments.ts +15 -15
- package/src/fetchRepoDescriptionMap.ts +15 -15
- package/src/followRuleSchema.ts +79 -76
- package/src/getActivatedShell.ts +18 -18
- package/src/getBranchWorkingDir.ts +16 -16
- package/src/getRepoWorkingDir.ts +5 -5
- package/src/ghUser.ts +9 -6
- package/src/index.ts +26 -22
- package/src/initializeFollowRules.ts +26 -26
- package/src/makePublishBranch.ts +58 -58
- package/src/makeTomlBranch.ts +53 -53
- package/src/makeUpdateTomlLicenseBranch.ts +245 -0
- package/src/matchRelatedPulls.ts +47 -60
- package/src/muteInstances.ts +16 -16
- package/src/parseIssueUrl.ts +6 -6
- package/src/parseOwnerRepo.ts +33 -33
- package/src/parsePullUrl.ts +6 -6
- package/src/parsePullsState.ts +6 -6
- package/src/parseTitleBodyOfMarkdown.ts +8 -8
- package/src/postSlackMessage.ts +21 -21
- package/src/preload.ts +30 -30
- package/src/pullStatusFollowSchema.ts +12 -12
- package/src/readTemplateTitle.ts +11 -11
- package/src/sendEmailAction.ts +56 -0
- package/src/sendGmail.ts +105 -0
- package/src/showFollowRuleSet.ts +12 -12
- package/src/summaryLastPullComment.ts +8 -8
- package/src/tomlFillDescription.ts +17 -17
- package/src/updateAuthors.ts +7 -7
- package/src/updateAuthorsForGithub.ts +54 -50
- package/src/updateAuthorsFromCNRepo.ts +30 -30
- package/src/updateCMNodesDuplicationWarnings.ts +56 -56
- package/src/updateCMRepos.ts +27 -27
- package/src/updateCNRepos.ts +58 -58
- package/src/updateCNReposCRPullsComments.ts +40 -40
- package/src/updateCNReposInfo.ts +58 -57
- package/src/updateCNReposPRCandidate.ts +85 -85
- package/src/updateCNReposPulls.ts +31 -28
- package/src/updateCNReposPullsDashboard.ts +49 -49
- package/src/updateCNReposRelatedPulls.ts +25 -25
- package/src/updateCRNodes.ts +50 -50
- package/src/updateCRRepos.ts +26 -26
- package/src/updateComfyTotals.ts +43 -43
- package/src/updateFollowRuleSet.ts +136 -127
- package/src/updateOutdatedPullsTemplates.ts +165 -160
- package/src/updateSlackMessages.ts +43 -43
- package/tailwind.config.ts +75 -20
package/src/Authors.ts
CHANGED
|
@@ -1,48 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (import.meta.main) {
|
|
44
|
-
// collect github id from cn repos
|
|
45
|
-
await updateAuthors();
|
|
46
|
-
console.log("done");
|
|
47
|
-
}
|
|
48
|
-
|
|
1
|
+
import { db } from "./db";
|
|
2
|
+
import type { GHUser } from "./ghUser";
|
|
3
|
+
import { updateAuthors } from "./updateAuthors";
|
|
4
|
+
export type Author = {
|
|
5
|
+
email?: string;
|
|
6
|
+
// cold down
|
|
7
|
+
githubMtime?: Date;
|
|
8
|
+
instagramMtime?: Date;
|
|
9
|
+
discordMtime?: Date;
|
|
10
|
+
twitterMtime?: Date;
|
|
11
|
+
|
|
12
|
+
// id
|
|
13
|
+
githubId?: string;
|
|
14
|
+
instagramId?: string;
|
|
15
|
+
discordId?: string;
|
|
16
|
+
twitterId?: string;
|
|
17
|
+
|
|
18
|
+
// repos on cm and cr
|
|
19
|
+
cm: number; // comfy node manager
|
|
20
|
+
cr: number; // comfy registry
|
|
21
|
+
|
|
22
|
+
// common info
|
|
23
|
+
nicknames?: string[];
|
|
24
|
+
bios?: string[];
|
|
25
|
+
links?: string[]; // one could be url or markdown style link: [name](link)
|
|
26
|
+
blogs?: string[]; // one could be url or markdown style link: [name](link)
|
|
27
|
+
companies?: string[];
|
|
28
|
+
locations?: string[];
|
|
29
|
+
avatars?: string[];
|
|
30
|
+
hireable?: boolean; // github
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const Authors = db.collection<Author>("Authors");
|
|
34
|
+
await Authors.createIndex("githubId");
|
|
35
|
+
await Authors.createIndex("email");
|
|
36
|
+
export const GithubUsers = db.collection<{ username: string } & GHUser>("GithubUsers");
|
|
37
|
+
|
|
38
|
+
if (import.meta.main) {
|
|
39
|
+
// collect github id from cn repos
|
|
40
|
+
await updateAuthors();
|
|
41
|
+
console.log("done");
|
|
42
|
+
}
|
package/src/CMNodes.ts
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import jsonStableStringify from "json-stable-stringify";
|
|
2
|
-
import md5 from "md5";
|
|
3
|
-
import type { ObjectId } from "mongodb";
|
|
4
|
-
import { db } from "./db";
|
|
5
|
-
import { fetchCMNodes } from "./fetchCMNodes";
|
|
6
|
-
import { type SlackMsg } from "./slack/SlackMsgs";
|
|
7
|
-
import { updateCMNodesDuplicationWarnings } from "./updateCMNodesDuplicationWarnings";
|
|
8
|
-
|
|
9
|
-
// Raw version maybe duplicated with id or reference
|
|
10
|
-
type CMNodeRaw = Awaited<ReturnType<typeof fetchCMNodesWithHash>>[number];
|
|
11
|
-
export type CMNode = CMNodeRaw & {
|
|
12
|
-
repo_id?: ObjectId;
|
|
13
|
-
duplicated?: {
|
|
14
|
-
[k: string]: { hashes: string[]; slackNotification: SlackMsg };
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
export const CMNodes = db.collection<CMNode>("CMNodes");
|
|
18
|
-
await CMNodes.createIndex({ mtime: -1 });
|
|
19
|
-
await CMNodes.createIndex({ hash: 1 }, { unique: true }).catch(() => {});
|
|
20
|
-
|
|
21
|
-
if (import.meta.main) {
|
|
22
|
-
console.time("CMNodes");
|
|
23
|
-
console.log(await updateCMNodes());
|
|
24
|
-
// check dups
|
|
25
|
-
console.timeLog("CMNodes");
|
|
26
|
-
const all = await CMNodes.countDocuments({});
|
|
27
|
-
console.timeLog("CMNodes");
|
|
28
|
-
const dups = await CMNodes.countDocuments({ duplicated: { $exists: true } });
|
|
29
|
-
console.timeEnd("CMNodes");
|
|
30
|
-
console.log("CMNodes updated, duplicates:", dups, "all:", all);
|
|
31
|
-
}
|
|
32
|
-
export async function updateCMNodes() {
|
|
33
|
-
const nodes = await fetchCMNodesWithHash();
|
|
34
|
-
console.log("CMNodes updating (" + nodes.length + " nodes)");
|
|
35
|
-
|
|
36
|
-
// updating nodes
|
|
37
|
-
const updateResult = await CMNodes.bulkWrite(
|
|
38
|
-
nodes.flatMap((node) => ({
|
|
39
|
-
updateOne: {
|
|
40
|
-
filter: { hash: node.hash },
|
|
41
|
-
update: { $set: node },
|
|
42
|
-
upsert: true,
|
|
43
|
-
},
|
|
44
|
-
})),
|
|
45
|
-
{ ordered: false },
|
|
46
|
-
);
|
|
47
|
-
// delete outdated
|
|
48
|
-
// await CMNodes.deleteMany({ hash: { $nin: nodes.map(prop("hash")) } });
|
|
49
|
-
|
|
50
|
-
await updateCMNodesDuplicationWarnings(nodes);
|
|
51
|
-
|
|
52
|
-
return [updateResult];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function fetchCMNodesWithHash() {
|
|
56
|
-
return (await fetchCMNodes()).map((e) => ({
|
|
57
|
-
...e,
|
|
58
|
-
hash: md5("SALT=Bvxmh8mGYh6qzLGE " + jsonStableStringify(e)),
|
|
59
|
-
}));
|
|
60
|
-
}
|
|
1
|
+
import jsonStableStringify from "json-stable-stringify";
|
|
2
|
+
import md5 from "md5";
|
|
3
|
+
import type { ObjectId } from "mongodb";
|
|
4
|
+
import { db } from "./db";
|
|
5
|
+
import { fetchCMNodes } from "./fetchCMNodes";
|
|
6
|
+
import { type SlackMsg } from "./slack/SlackMsgs";
|
|
7
|
+
import { updateCMNodesDuplicationWarnings } from "./updateCMNodesDuplicationWarnings";
|
|
8
|
+
|
|
9
|
+
// Raw version maybe duplicated with id or reference
|
|
10
|
+
type CMNodeRaw = Awaited<ReturnType<typeof fetchCMNodesWithHash>>[number];
|
|
11
|
+
export type CMNode = CMNodeRaw & {
|
|
12
|
+
repo_id?: ObjectId;
|
|
13
|
+
duplicated?: {
|
|
14
|
+
[k: string]: { hashes: string[]; slackNotification: SlackMsg };
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export const CMNodes = db.collection<CMNode>("CMNodes");
|
|
18
|
+
await CMNodes.createIndex({ mtime: -1 });
|
|
19
|
+
await CMNodes.createIndex({ hash: 1 }, { unique: true }).catch(() => {});
|
|
20
|
+
|
|
21
|
+
if (import.meta.main) {
|
|
22
|
+
console.time("CMNodes");
|
|
23
|
+
console.log(await updateCMNodes());
|
|
24
|
+
// check dups
|
|
25
|
+
console.timeLog("CMNodes");
|
|
26
|
+
const all = await CMNodes.countDocuments({});
|
|
27
|
+
console.timeLog("CMNodes");
|
|
28
|
+
const dups = await CMNodes.countDocuments({ duplicated: { $exists: true } });
|
|
29
|
+
console.timeEnd("CMNodes");
|
|
30
|
+
console.log("CMNodes updated, duplicates:", dups, "all:", all);
|
|
31
|
+
}
|
|
32
|
+
export async function updateCMNodes() {
|
|
33
|
+
const nodes = await fetchCMNodesWithHash();
|
|
34
|
+
console.log("CMNodes updating (" + nodes.length + " nodes)");
|
|
35
|
+
|
|
36
|
+
// updating nodes
|
|
37
|
+
const updateResult = await CMNodes.bulkWrite(
|
|
38
|
+
nodes.flatMap((node) => ({
|
|
39
|
+
updateOne: {
|
|
40
|
+
filter: { hash: node.hash },
|
|
41
|
+
update: { $set: node },
|
|
42
|
+
upsert: true,
|
|
43
|
+
},
|
|
44
|
+
})),
|
|
45
|
+
{ ordered: false },
|
|
46
|
+
);
|
|
47
|
+
// delete outdated
|
|
48
|
+
// await CMNodes.deleteMany({ hash: { $nin: nodes.map(prop("hash")) } });
|
|
49
|
+
|
|
50
|
+
await updateCMNodesDuplicationWarnings(nodes);
|
|
51
|
+
|
|
52
|
+
return [updateResult];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function fetchCMNodesWithHash() {
|
|
56
|
+
return (await fetchCMNodes()).map((e) => ({
|
|
57
|
+
...e,
|
|
58
|
+
hash: md5("SALT=Bvxmh8mGYh6qzLGE " + jsonStableStringify(e)),
|
|
59
|
+
}));
|
|
60
|
+
}
|
package/src/CNRepos.ts
CHANGED
|
@@ -1,74 +1,78 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/*
|
|
3
|
-
bun index.ts
|
|
4
|
-
*/
|
|
5
|
-
import type { ObjectId, WithId } from "mongodb";
|
|
6
|
-
import "react-hook-form";
|
|
7
|
-
import {
|
|
8
|
-
import { type
|
|
9
|
-
import { type
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import type
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
type
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
cr_ids
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/*
|
|
3
|
+
bun index.ts
|
|
4
|
+
*/
|
|
5
|
+
import type { ObjectId, WithId } from "mongodb";
|
|
6
|
+
import "react-hook-form";
|
|
7
|
+
import { nil } from "sflow";
|
|
8
|
+
import { type Task } from "../packages/mongodb-pipeline-ts/Task";
|
|
9
|
+
import { type CMNode } from "./CMNodes";
|
|
10
|
+
import { type CRNode } from "./CRNodes";
|
|
11
|
+
import { db } from "./db";
|
|
12
|
+
import { gh } from "./gh";
|
|
13
|
+
import { type RelatedPull } from "./matchRelatedPulls";
|
|
14
|
+
import type { GithubPullParsed } from "./parsePullsState";
|
|
15
|
+
import { type SlackMsg } from "./slack/SlackMsgs";
|
|
16
|
+
|
|
17
|
+
type Email = {
|
|
18
|
+
from?: string;
|
|
19
|
+
title: string;
|
|
20
|
+
body: string;
|
|
21
|
+
to: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type Sent = {
|
|
25
|
+
slack?: SlackMsg;
|
|
26
|
+
emails?: Email[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// TODO: refactor into interface to improve performance
|
|
30
|
+
type GithubIssueComment = Awaited<ReturnType<typeof gh.issues.getComment>>["data"];
|
|
31
|
+
type GithubIssue = Awaited<ReturnType<typeof gh.issues.get>>["data"];
|
|
32
|
+
type GithubRepo = Awaited<ReturnType<typeof gh.repos.get>>["data"];
|
|
33
|
+
export type CRType = "pyproject" | "publichcr";
|
|
34
|
+
export type CRPull = RelatedPull & {
|
|
35
|
+
edited?: Task<boolean>;
|
|
36
|
+
comments?: Task<Pick<GithubIssueComment, "body">[]>;
|
|
37
|
+
issue?: Task<Pick<GithubIssue, "number" | "html_url" | "body" | "updated_at">>;
|
|
38
|
+
emailTask_id?: ObjectId;
|
|
39
|
+
};
|
|
40
|
+
// TODO: refactor into interface to improve performance
|
|
41
|
+
export type CustomNodeRepo = {
|
|
42
|
+
repository: string;
|
|
43
|
+
info?: Task<Pick<GithubRepo, "html_url" | "archived" | "default_branch" | "private">>;
|
|
44
|
+
|
|
45
|
+
// author?: Author;
|
|
46
|
+
|
|
47
|
+
/** @deprecated use cr_ids or on_registry */
|
|
48
|
+
cr?: Pick<WithId<CRNode>, "_id" | "id" | "name">;
|
|
49
|
+
|
|
50
|
+
cr_ids?: ObjectId[];
|
|
51
|
+
on_registry?: Task<boolean>; // check if cr_ids is not empty
|
|
52
|
+
|
|
53
|
+
/** @deprecated use cm_ids */
|
|
54
|
+
cm?: Pick<WithId<CMNode>, "_id" | "id" | "title">;
|
|
55
|
+
cm_ids?: ObjectId[];
|
|
56
|
+
|
|
57
|
+
// cache
|
|
58
|
+
pulls?: Task<GithubPullParsed[]>;
|
|
59
|
+
|
|
60
|
+
crPulls?: Task<CRPull[]>;
|
|
61
|
+
|
|
62
|
+
/** @deprecated TODO: NOT IMPLEMENTD */
|
|
63
|
+
crPull_ids: ObjectId[];
|
|
64
|
+
|
|
65
|
+
candidate?: Task<boolean>;
|
|
66
|
+
// createFork?: Task<GithubRepo>;
|
|
67
|
+
// createBranches?: Task<{ type: CRType; assigned: Worker } & PushedBranch>[];
|
|
68
|
+
|
|
69
|
+
/** @deprecated use CRPulls.pull */
|
|
70
|
+
createdPulls?: Task<GithubPullParsed[]>;
|
|
71
|
+
};
|
|
72
|
+
export type CNRepo = CustomNodeRepo;
|
|
73
|
+
export const CNRepos = db.collection<CNRepo>("CNRepos");
|
|
74
|
+
|
|
75
|
+
CNRepos.createIndex({ repository: 1 }, { unique: true }).catch(nil);
|
|
76
|
+
|
|
77
|
+
// fix cr null, it should be not exists
|
|
78
|
+
// await CNRepos.updateMany({ cr: null as unknown as WithId<CRNode> }, { $unset: { cr: 1 } });
|
package/src/CRNodes.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { $pipeline } from "@/packages/mongodb-pipeline-ts/$pipeline";
|
|
2
|
-
import type { ObjectId } from "mongodb";
|
|
3
|
-
import pMap from "p-map";
|
|
4
|
-
import { peekYaml } from "peek-log";
|
|
5
|
-
import { CNRepos } from "./CNRepos";
|
|
6
|
-
import { db } from "./db";
|
|
7
|
-
import { fetchCRNodes } from "./fetchComfyRegistryNodes";
|
|
8
|
-
import { type SlackMsg } from "./slack/SlackMsgs";
|
|
9
|
-
|
|
10
|
-
export type CRNode = Awaited<ReturnType<typeof fetchCRNodes>>[number] & {
|
|
11
|
-
sent?: { slack?: SlackMsg };
|
|
12
|
-
repo_id?: ObjectId;
|
|
13
|
-
};
|
|
14
|
-
export const CRNodes = db.collection<CRNode>("CRNodes");
|
|
15
|
-
await CRNodes.createIndex({ id: 1 }, { unique: true });
|
|
16
|
-
await CRNodes.createIndex({ repository: 1 }, { unique: false }); // WARN: duplicate is allowed
|
|
17
|
-
|
|
18
|
-
if (import.meta.main) {
|
|
19
|
-
const r = await pMap(
|
|
20
|
-
$pipeline(CNRepos)
|
|
21
|
-
.match({ cr: { $exists: true } })
|
|
22
|
-
.replaceRoot({ newRoot: "$cr" })
|
|
23
|
-
.aggregate(),
|
|
24
|
-
(e) => e,
|
|
25
|
-
);
|
|
26
|
-
peekYaml({ r, len: r.length });
|
|
27
|
-
}
|
|
1
|
+
import { $pipeline } from "@/packages/mongodb-pipeline-ts/$pipeline";
|
|
2
|
+
import type { ObjectId } from "mongodb";
|
|
3
|
+
import pMap from "p-map";
|
|
4
|
+
import { peekYaml } from "peek-log";
|
|
5
|
+
import { CNRepos } from "./CNRepos";
|
|
6
|
+
import { db } from "./db";
|
|
7
|
+
import { fetchCRNodes } from "./fetchComfyRegistryNodes";
|
|
8
|
+
import { type SlackMsg } from "./slack/SlackMsgs";
|
|
9
|
+
|
|
10
|
+
export type CRNode = Awaited<ReturnType<typeof fetchCRNodes>>[number] & {
|
|
11
|
+
sent?: { slack?: SlackMsg };
|
|
12
|
+
repo_id?: ObjectId;
|
|
13
|
+
};
|
|
14
|
+
export const CRNodes = db.collection<CRNode>("CRNodes");
|
|
15
|
+
await CRNodes.createIndex({ id: 1 }, { unique: true });
|
|
16
|
+
await CRNodes.createIndex({ repository: 1 }, { unique: false }); // WARN: duplicate is allowed
|
|
17
|
+
|
|
18
|
+
if (import.meta.main) {
|
|
19
|
+
const r = await pMap(
|
|
20
|
+
$pipeline(CNRepos)
|
|
21
|
+
.match({ cr: { $exists: true } })
|
|
22
|
+
.replaceRoot({ newRoot: "$cr" })
|
|
23
|
+
.aggregate(),
|
|
24
|
+
(e) => e,
|
|
25
|
+
);
|
|
26
|
+
peekYaml({ r, len: r.length });
|
|
27
|
+
}
|
package/src/CRPulls.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CRPull } from "./CNRepos";
|
|
2
|
-
import { db } from "./db";
|
|
3
|
-
|
|
4
|
-
export const CRPulls = db.collection<CRPull>("CRPulls");
|
|
1
|
+
import type { CRPull } from "./CNRepos";
|
|
2
|
+
import { db } from "./db";
|
|
3
|
+
|
|
4
|
+
export const CRPulls = db.collection<CRPull>("CRPulls");
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import DIE from "@snomiao/die";
|
|
2
|
+
import markdownIt from "markdown-it";
|
|
3
|
+
import type { WithId } from "mongodb";
|
|
4
|
+
import { sf } from "snoflow";
|
|
5
|
+
import type { z } from "zod";
|
|
6
|
+
import { $stale, db } from "./db";
|
|
7
|
+
import type { zSendEmailAction } from "./followRuleSchema";
|
|
8
|
+
import { getGCloudOAuth2Client } from "./gcloud/GCloudOAuth2Credentials";
|
|
9
|
+
import { sendGmail } from "./sendGmail";
|
|
10
|
+
|
|
11
|
+
export type EmailTask = {
|
|
12
|
+
mtime: Date;
|
|
13
|
+
state: "waiting" | "sending" | "sent" | "error";
|
|
14
|
+
error?: string;
|
|
15
|
+
provider: "google";
|
|
16
|
+
name: string;
|
|
17
|
+
from: string;
|
|
18
|
+
to: string;
|
|
19
|
+
subject: string;
|
|
20
|
+
/** body is markdown */
|
|
21
|
+
body: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const EmailTasks = db.collection<EmailTask>("EmailTasks");
|
|
25
|
+
await EmailTasks.createIndex({ from: 1, to: 1, subject: 1 });
|
|
26
|
+
|
|
27
|
+
if (import.meta.main) {
|
|
28
|
+
sf(EmailTasks.find({}))
|
|
29
|
+
.map((e) => e.state)
|
|
30
|
+
.toLog();
|
|
31
|
+
// sf(EmailTasks.watch([], { fullDocument: "whenAvailable" }))
|
|
32
|
+
// // .filter((c) => c.operationType === "modify")
|
|
33
|
+
// .toLog()
|
|
34
|
+
// .then(() => console.log("all done"));
|
|
35
|
+
// const saved = await enqueueEmailTask({
|
|
36
|
+
// provider: "google",
|
|
37
|
+
// name: "Snowstar Miao",
|
|
38
|
+
// from: "snomiao@gmail.com",
|
|
39
|
+
// to: "snomiao@gmail.com",
|
|
40
|
+
// subject: "hello again from email queue 2",
|
|
41
|
+
// body: `
|
|
42
|
+
// # hello from sno
|
|
43
|
+
|
|
44
|
+
// You've <b>just</b> received an *email* from snomiao.
|
|
45
|
+
|
|
46
|
+
// Thank you for receiving this email!!!!
|
|
47
|
+
// `,
|
|
48
|
+
// });
|
|
49
|
+
// console.log({ saved });
|
|
50
|
+
// await updateEmailTasks();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* deduplicated by [from, to, subject].join(' ')
|
|
55
|
+
*/
|
|
56
|
+
export async function enqueueEmailTask(task: z.infer<typeof zSendEmailAction>) {
|
|
57
|
+
const { name, from, to, subject, body, provider } = task;
|
|
58
|
+
return (
|
|
59
|
+
(await EmailTasks.findOneAndUpdate(
|
|
60
|
+
{ from, to, subject },
|
|
61
|
+
{ $set: task, $setOnInsert: { mtime: new Date(), state: "waiting" } },
|
|
62
|
+
{ upsert: true, returnDocument: "after" },
|
|
63
|
+
)) ?? DIE(new Error("fail to enqueue email task"))
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
export async function updateEmailTasks() {
|
|
67
|
+
const count = await sf(EmailTasks.find({ state: "waiting" }))
|
|
68
|
+
.join(sf(EmailTasks.find({ state: "sending", mtime: $stale("1m") })))
|
|
69
|
+
.map(async (e) => {
|
|
70
|
+
const { _id, state } = e;
|
|
71
|
+
if (state === "waiting") {
|
|
72
|
+
await EmailTasks.updateOne({ _id }, { $set: { mtime: new Date(), state: "sending" } });
|
|
73
|
+
return await sendEmailTask(e)
|
|
74
|
+
.then(() => EmailTasks.updateOne({ _id }, { $set: { mtime: new Date(), state: "sent" } }))
|
|
75
|
+
.catch((err) =>
|
|
76
|
+
EmailTasks.updateOne({ _id }, { $set: { mtime: new Date(), state: "error", error: String(err) } }),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
.toCount();
|
|
81
|
+
console.log(count, "email tasks processed");
|
|
82
|
+
}
|
|
83
|
+
export async function sendEmailTask({ _id, state, name, from, to, subject, body, provider }: WithId<EmailTask>) {
|
|
84
|
+
// prerequisites
|
|
85
|
+
if (provider !== "google") DIE("providers other than google is implemented yet");
|
|
86
|
+
const auth = await getGCloudOAuth2Client({
|
|
87
|
+
email: from,
|
|
88
|
+
scope: ["https://www.googleapis.com/auth/gmail.compose"],
|
|
89
|
+
authorize: async (url) =>
|
|
90
|
+
DIE("ERROR: Trying send email from " + from + " but it's not authorized, plz grant permission in " + url),
|
|
91
|
+
});
|
|
92
|
+
// do send
|
|
93
|
+
return sendGmail({
|
|
94
|
+
auth,
|
|
95
|
+
from,
|
|
96
|
+
name,
|
|
97
|
+
to,
|
|
98
|
+
subject,
|
|
99
|
+
html: markdownIt().render(body),
|
|
100
|
+
});
|
|
101
|
+
}
|
package/src/FORK_OWNER.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import DIE from "@snomiao/die";
|
|
2
|
-
import { ghUser } from "./ghUser";
|
|
3
|
-
|
|
4
|
-
export const FORK_OWNER =
|
|
5
|
-
process.env.FORK_OWNER?.replace(/"/g, "")?.trim() || ghUser.login || DIE("Missing env.FORK_OWNER");
|
|
1
|
+
import DIE from "@snomiao/die";
|
|
2
|
+
import { ghUser } from "./ghUser";
|
|
3
|
+
|
|
4
|
+
export const FORK_OWNER =
|
|
5
|
+
process.env.FORK_OWNER?.replace(/"/g, "")?.trim() || ghUser.login || DIE("Missing env.FORK_OWNER");
|
package/src/FORK_PREFIX.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import DIE from "@snomiao/die";
|
|
2
|
-
|
|
3
|
-
export const FORK_PREFIX =
|
|
4
|
-
process.env.FORK_PREFIX?.replace(/"/g, "")?.trim() ||
|
|
5
|
-
DIE('Missing env.FORK_PREFIX, if you want empty maybe try FORK_PREFIX=""');
|
|
1
|
+
import DIE from "@snomiao/die";
|
|
2
|
+
|
|
3
|
+
export const FORK_PREFIX =
|
|
4
|
+
process.env.FORK_PREFIX?.replace(/"/g, "")?.trim() ||
|
|
5
|
+
DIE('Missing env.FORK_PREFIX, if you want empty maybe try FORK_PREFIX=""');
|
package/src/FollowRules.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { type Task } from "../packages/mongodb-pipeline-ts/Task";
|
|
3
|
-
import type { PullsStatus } from "./analyzePullsStatus";
|
|
4
|
-
import { db } from "./db";
|
|
5
|
-
import type { zFollowUpRules } from "./followRuleSchema";
|
|
6
|
-
|
|
7
|
-
// migrate data
|
|
8
|
-
// await db.renameCollection("FollowRules", "FollowRuleSets").catch(() => null);
|
|
9
|
-
export type FollowRule = z.infer<typeof zFollowUpRules>[number];
|
|
10
|
-
export type FollowRuleSet = {
|
|
11
|
-
// rules?: Task<FollowRule[]>;
|
|
12
|
-
name: string;
|
|
13
|
-
yaml: string;
|
|
14
|
-
rules?: FollowRule[];
|
|
15
|
-
matched?: Task<PullsStatus>;
|
|
16
|
-
action_results?: {
|
|
17
|
-
name: string;
|
|
18
|
-
action: any;
|
|
19
|
-
result: Task<any>;
|
|
20
|
-
}[];
|
|
21
|
-
enabled?: boolean;
|
|
22
|
-
yamlWhenEnabled?: string;
|
|
23
|
-
};
|
|
24
|
-
export const FollowRuleSets = db.collection<FollowRuleSet>("FollowRuleSets");
|
|
25
|
-
await FollowRuleSets.createIndex("name", { unique: true });
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type Task } from "../packages/mongodb-pipeline-ts/Task";
|
|
3
|
+
import type { PullsStatus } from "./analyzePullsStatus";
|
|
4
|
+
import { db } from "./db";
|
|
5
|
+
import type { zFollowUpRules } from "./followRuleSchema";
|
|
6
|
+
|
|
7
|
+
// migrate data
|
|
8
|
+
// await db.renameCollection("FollowRules", "FollowRuleSets").catch(() => null);
|
|
9
|
+
export type FollowRule = z.infer<typeof zFollowUpRules>[number];
|
|
10
|
+
export type FollowRuleSet = {
|
|
11
|
+
// rules?: Task<FollowRule[]>;
|
|
12
|
+
name: string;
|
|
13
|
+
yaml: string;
|
|
14
|
+
rules?: FollowRule[];
|
|
15
|
+
matched?: Task<PullsStatus>;
|
|
16
|
+
action_results?: {
|
|
17
|
+
name: string;
|
|
18
|
+
action: any;
|
|
19
|
+
result: Task<any>;
|
|
20
|
+
}[];
|
|
21
|
+
enabled?: boolean;
|
|
22
|
+
yamlWhenEnabled?: string;
|
|
23
|
+
};
|
|
24
|
+
export const FollowRuleSets = db.collection<FollowRuleSet>("FollowRuleSets");
|
|
25
|
+
await FollowRuleSets.createIndex("name", { unique: true });
|
package/src/GIT_USEREMAIL.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import DIE from "@snomiao/die";
|
|
2
|
-
import { ghUser } from "./ghUser";
|
|
3
|
-
|
|
4
|
-
export const GIT_USEREMAIL =
|
|
5
|
-
process.env.GIT_USEREMAIL || (ghUser.email && ghUser.email) || DIE("Missing env.GIT_USEREMAIL");
|
|
1
|
+
import DIE from "@snomiao/die";
|
|
2
|
+
import { ghUser } from "./ghUser";
|
|
3
|
+
|
|
4
|
+
export const GIT_USEREMAIL =
|
|
5
|
+
process.env.GIT_USEREMAIL || (ghUser.email && ghUser.email) || DIE("Missing env.GIT_USEREMAIL");
|
package/src/GIT_USERNAME.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import DIE from "@snomiao/die";
|
|
2
|
-
import { GIT_USEREMAIL } from "./GIT_USEREMAIL";
|
|
3
|
-
import { ghUser } from "./ghUser";
|
|
4
|
-
|
|
5
|
-
export const GIT_USERNAME =
|
|
6
|
-
process.env.GIT_USERNAME || (ghUser.email && ghUser.name) || DIE("Missing env.GIT_USERNAME");
|
|
7
|
-
|
|
8
|
-
// read env/parameters
|
|
9
|
-
console.log(`GIT COMMIT USER: ${GIT_USERNAME} <${GIT_USEREMAIL}>`);
|
|
1
|
+
import DIE from "@snomiao/die";
|
|
2
|
+
import { GIT_USEREMAIL } from "./GIT_USEREMAIL";
|
|
3
|
+
import { ghUser } from "./ghUser";
|
|
4
|
+
|
|
5
|
+
export const GIT_USERNAME =
|
|
6
|
+
process.env.GIT_USERNAME || (ghUser.email && ghUser.name) || DIE("Missing env.GIT_USERNAME");
|
|
7
|
+
|
|
8
|
+
// read env/parameters
|
|
9
|
+
console.log(`GIT COMMIT USER: ${GIT_USERNAME} <${GIT_USEREMAIL}>`);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { fetchIssueComments } from "./gh/fetchIssueComments";
|
|
2
|
-
|
|
3
|
-
export type GithubIssueComment = Awaited<ReturnType<typeof fetchIssueComments>>[number];
|
|
1
|
+
import { fetchIssueComments } from "./gh/fetchIssueComments";
|
|
2
|
+
|
|
3
|
+
export type GithubIssueComment = Awaited<ReturnType<typeof fetchIssueComments>>[number];
|