Skip to content

Commit

Permalink
collect prompt and images
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuyz0112 committed Dec 21, 2023
1 parent 9785b60 commit 2ca8cb4
Show file tree
Hide file tree
Showing 2 changed files with 254 additions and 11 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/create-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
uses: denoland/setup-deno@v1
with:
deno-version: 1.38.3
- name: Test Deno
run: deno run https://deno.land/[email protected]/examples/welcome.ts
- name: Dump Env
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: deno run -A prompts/ui-gen.ts
261 changes: 252 additions & 9 deletions prompts/ui-gen.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,255 @@
const GITHUB_EVENT_PATH = Deno.env.get("GITHUB_EVENT_PATH");
if (!GITHUB_EVENT_PATH) {
throw new Error("failed to get github event path");
import { Octokit } from "npm:octokit";
import commitPlugin from "npm:octokit-commit-multiple-files";
import { assert } from "https://deno.land/[email protected]/assert/assert.ts";

type IssueEvent = {
issue: {
body: string;
number: number;
labels: string[];
user: { login: string };
};
};

const PatchedOctokit = Octokit.plugin(commitPlugin);
const ghToken = Deno.env.get("GH_TOKEN");
assert(ghToken, "failed to get github token");

const octokit: Octokit = new PatchedOctokit({
auth: ghToken,
});

const whitelist = ["Yuyz0112"];

const vxDevPrefix = `[vx.dev]`;
const uiGenLabel = `ui-gen`;

async function getConnectedPr(
owner: string,
repo: string,
issueNumber: number
) {
const connectedEvent = await octokit.graphql<{
repository: {
issue?: {
timelineItems: {
nodes: {
id: string;
source: { number: number; state: string };
__typename: string;
createdAt: string;
}[];
};
};
};
}>(`{
repository(owner: "${owner}", name: "${repo}") {
issue(number: ${issueNumber}) {
timelineItems(itemTypes: [CROSS_REFERENCED_EVENT], first: 1) {
nodes {
... on CrossReferencedEvent {
id
createdAt
source {
... on PullRequest {
id
number
state
}
}
__typename
}
}
}
}
}
}`);

let { nodes = [] } = connectedEvent.repository.issue?.timelineItems || {};
nodes = nodes
.filter((n) => n.source.state === "OPEN")
.sort(
(a, b) =>
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
);
return nodes[0]?.source.number;
}

async function applyPR(
owner: string,
repo: string,
issueNumber: number,
newBranch: string,
files: Record<string, string>,
commitMsg: string
) {
const baseBranch = "main";

// deno-lint-ignore no-explicit-any
await (octokit as any).createOrUpdateFiles({
owner,
repo,
branch: newBranch,
createBranch: true,
base: baseBranch,
forkFromBaseBranch: true,
changes: [
{
message: commitMsg,
files,
},
],
});

let pr = (
await octokit.rest.search.issuesAndPullRequests({
q: `is:open is:pr base:${baseBranch} head:${newBranch}+repo:${owner}/${repo}`,
})
).data.items[0];
if (!pr) {
pr = // deno-lint-ignore no-explicit-any
(
await octokit.rest.pulls.create({
owner,
repo,
head: newBranch,
base: baseBranch,
title: "created by vx.dev",
body: `${vxDevPrefix} This PR implements #${issueNumber}, created by vx.dev.`,
})
).data as any;
}

octokit.rest.issues.setLabels({
owner,
repo,
issue_number: pr.number,
labels: [uiGenLabel],
});

return pr;
}

async function collectPromptAndImages(
owner: string,
repo: string,
githubEvent: IssueEvent,
pr: { number: number }
) {
const prComments = (
await octokit.rest.issues.listComments({
owner,
repo,
issue_number: pr.number,
})
).data;
const issueComments = (
await octokit.rest.issues.listComments({
owner,
repo,
issue_number: githubEvent.issue.number,
})
).data;

const commentsStr = issueComments
.concat(prComments)
.filter(
(c) =>
!c.body?.includes(vxDevPrefix) &&
whitelist.some((item) => item === c.user?.login)
)
.sort(
(a, b) =>
new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
)
.map((c) => c.body)
.join("\n");

let prompt = `${githubEvent.issue.body || ""}\n${commentsStr}`;
const regex = /!\[.*?\]\((.*?)\)/g;
const imgRegex = /<img .*?src="(.*?)".*?>/g;
const images = [];
let match;
while ((match = regex.exec(prompt)) !== null) {
images.push(match[1]);
}

let imgMatch;
while ((imgMatch = imgRegex.exec(prompt)) !== null) {
images.push(imgMatch[1]);
}
prompt = prompt.replace(regex, "").replace(imgRegex, "");

return {
prompt,
images,
};
}

const githubEvent: { issue: { number: number; labels: string[] } } = (
await import(GITHUB_EVENT_PATH, {
with: { type: "json" },
})
).default;
async function main() {
const githubEventPath = Deno.env.get("GITHUB_EVENT_PATH");
assert(githubEventPath, "failed to get github event path");

const githubEvent: IssueEvent = (
await import(githubEventPath, {
with: { type: "json" },
})
).default;

console.log(githubEvent);

if (githubEvent.issue.labels.some((l) => l === uiGenLabel)) {
return;
}

if (whitelist.every((item) => item !== githubEvent.issue.user.login)) {
return;
}

const owner = Deno.env.get("GITHUB_REPOSITORY_OWNER");
assert(owner, "failed to get repo owner");

let repo = Deno.env.get("GITHUB_REPOSITORY");
assert(repo, "failed to get repo name");
repo = repo.replace(`${owner}/`, "");

const branch = `ui-gen-issue-${githubEvent.issue.number}`;

const connectedPrNumber = await getConnectedPr(
owner,
repo,
githubEvent.issue.number
);

const pr = connectedPrNumber
? (
await octokit.rest.pulls.get({
owner,
repo,
pull_number: connectedPrNumber,
})
).data
: await applyPR(
owner,
repo,
githubEvent.issue.number,
branch,
{
"vx.dev.txt": "placeholder",
},
"vx.dev: init the PR"
);

const { prompt, images } = await collectPromptAndImages(
owner,
repo,
githubEvent,
pr
);
console.log({
prompt,
images,
});
}

console.log(githubEvent.issue);
main();

0 comments on commit 2ca8cb4

Please sign in to comment.