Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ For repositories with many packages, comments might get too long. In that case,

pkg.pr.new uses `npm pack --json` under the hood, in case you face issues, you can also use the `--pnpm`, `--yarn`, or `--bun` flag so it starts using `pnpm pack`, `yarn pack`, or `bun pm pack`. This is not necessary in most cases.

If you want to add comments to the associated issue simultaneously, you can set `--syncCommentWithIssue`.

<img width="100%" src="https://github.com/stackblitz-labs/pkg.pr.new/assets/37929992/2fc03b94-ebae-4c47-a271-03a4ad5d2449" />

pkg.pr.new is not available in your local environment and it only works in workflows.
Expand Down
155 changes: 128 additions & 27 deletions packages/app/server/routes/publish.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default eventHandler(async (event) => {
"sb-only-templates": onlyTemplatesHeader,
"sb-comment-with-sha": commentWithShaHeader,
"sb-comment-with-dev": commentWithDevHeader,
"sb-sync-comment-with-issue": syncCommentWithIssueHeader,
} = getHeaders(event);
const compact = compactHeader === "true";
const onlyTemplates = onlyTemplatesHeader === "true";
Expand All @@ -32,6 +33,7 @@ export default eventHandler(async (event) => {
(packageManagerHeader as PackageManager) || "npm";
const commentWithSha = commentWithShaHeader === "true";
const commentWithDev = commentWithDevHeader === "true";
const syncCommentWithIssue = syncCommentWithIssueHeader === "true";

if (!key || !runIdHeader || !shasumsHeader) {
throw createError({
Expand Down Expand Up @@ -246,7 +248,17 @@ export default eventHandler(async (event) => {
isPullRequest(workflowData.ref) &&
(await getPullRequestState(installation, workflowData)) === "open"
) {
let prevComment: OctokitComponents["schemas"]["issue-comment"];
let prevComment:
| OctokitComponents["schemas"]["issue-comment"]
| undefined;
const prevIssueComments: OctokitComponents["schemas"]["issue-comment"][] =
[];
const relatedIssueNumbers: number[] = [];
const matchIssueNumber = /(fix(es)?|closes?|resolves?)\s*(\d+)/gi;
const fullAddressMatchIssueNumber = new RegExp(
`(fix(es)|closes?|resolves?)\\s*https://github.com/${workflowData.owner}/${workflowData.repo}/issues/(\\d+)`,
"gi",
);

await installation.paginate(
"GET /repos/{owner}/{repo}/issues/{issue_number}/comments",
Expand All @@ -261,12 +273,65 @@ export default eventHandler(async (event) => {
prevComment = c;
done();
break;
} else {
const body = c.body || "";
let match;
matchIssueNumber.lastIndex = 0;
while ((match = matchIssueNumber.exec(body)) !== null) {
const issueNumber = Number(match[2]);
if (!isNaN(issueNumber)) {
relatedIssueNumbers.push(issueNumber);
}
}
if (!relatedIssueNumbers.length) {
fullAddressMatchIssueNumber.lastIndex = 0;
while (
(match = fullAddressMatchIssueNumber.exec(body)) !== null
) {
const issueNumber = Number(match[2]);
if (!isNaN(issueNumber)) {
relatedIssueNumbers.push(issueNumber);
}
}
}
}
if (prevComment) {
if (!syncCommentWithIssue) {
done();
break;
} else if (relatedIssueNumbers.length) {
done();
break;
}
}
}
return [];
},
);

if (syncCommentWithIssue && relatedIssueNumbers.length) {
for (const issueNumber of relatedIssueNumbers) {
await installation.paginate(
"GET /repos/{owner}/{repo}/issues/{issue_number}/comments",
{
owner: workflowData.owner,
repo: workflowData.repo,
issue_number: issueNumber,
},
({ data }, done) => {
for (const c of data) {
if (c.performed_via_github_app?.id === Number(appId)) {
prevIssueComments.push(c);
done();
break;
}
}
return [];
},
);
}
}

if (comment !== "off") {
const {
data: { permissions },
Expand All @@ -280,49 +345,85 @@ export default eventHandler(async (event) => {

try {
if (comment === "update" && prevComment!) {
const commentBody = generatePullRequestPublishMessage(
origin,
templatesHtmlMap,
packagesWithoutPrefix,
workflowData,
compact,
onlyTemplates,
checkRunUrl,
packageManager,
commentWithSha ? "sha" : "ref",
bin,
commentWithDev,
);

await installation.request(
"PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}",
{
owner: workflowData.owner,
repo: workflowData.repo,
comment_id: prevComment.id,
body: generatePullRequestPublishMessage(
origin,
templatesHtmlMap,
packagesWithoutPrefix,
workflowData,
compact,
onlyTemplates,
checkRunUrl,
packageManager,
commentWithSha ? "sha" : "ref",
bin,
commentWithDev,
),
body: commentBody,
},
);
if (
syncCommentWithIssue &&
relatedIssueNumbers.length &&
prevIssueComments.length
) {
for (let i = 0; i < relatedIssueNumbers.length; i++) {
const prevIssueComment = prevIssueComments[i];
if (!prevIssueComment) continue;

await installation.request(
"PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}",
{
owner: workflowData.owner,
repo: workflowData.repo,
comment_id: prevIssueComment.id,
body: commentBody,
},
);
}
}
} else {
const commentBody = generatePullRequestPublishMessage(
origin,
templatesHtmlMap,
packagesWithoutPrefix,
workflowData,
compact,
onlyTemplates,
checkRunUrl,
packageManager,
comment === "update" ? "ref" : "sha",
bin,
commentWithDev,
);
await installation.request(
"POST /repos/{owner}/{repo}/issues/{issue_number}/comments",
{
owner: workflowData.owner,
repo: workflowData.repo,
issue_number: Number(workflowData.ref),
body: generatePullRequestPublishMessage(
origin,
templatesHtmlMap,
packagesWithoutPrefix,
workflowData,
compact,
onlyTemplates,
checkRunUrl,
packageManager,
comment === "update" ? "ref" : "sha",
bin,
commentWithDev,
),
body: commentBody,
},
);
if (syncCommentWithIssue && relatedIssueNumbers.length) {
for (const relatedIssueNumber of relatedIssueNumbers) {
await installation.request(
"POST /repos/{owner}/{repo}/issues/{issue_number}/comments",
{
owner: workflowData.owner,
repo: workflowData.repo,
issue_number: relatedIssueNumber,
body: commentBody,
},
);
}
}
}
} catch (error) {
console.error("failed to create/update comment", error, permissions);
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ const main = defineCommand({
"should install the packages with the 'dev' tag in the comment links",
default: false,
},
syncCommentWithIssue: {
type: "boolean",
description:
"sync the comment with the related issue if any (issue number is extracted from the comment body)",
default: false,
},
"only-templates": {
type: "boolean",
description: `generate only stackblitz templates`,
Expand Down Expand Up @@ -156,6 +162,7 @@ const main = defineCommand({
const isBinaryApplication = !!args.bin;
const isCommentWithSha = !!args.commentWithSha;
const isCommentWithDev = !!args.commentWithDev;
const isSyncCommentWithIssue = !!args.syncCommentWithIssue;
const comment: Comment = args.comment as Comment;
const selectedPackageManager = [
...new Set(
Expand Down Expand Up @@ -559,6 +566,7 @@ const main = defineCommand({
"sb-only-templates": `${isOnlyTemplates}`,
"sb-comment-with-sha": `${isCommentWithSha}`,
"sb-comment-with-dev": `${isCommentWithDev}`,
"sb-sync-comment-with-issue": `${isSyncCommentWithIssue}`,
},
body: formData,
});
Expand Down
Loading