Skip to content

Commit 9928f05

Browse files
committed
fix: get prs logic
1 parent a72f8fb commit 9928f05

File tree

1 file changed

+74
-87
lines changed

1 file changed

+74
-87
lines changed

.github/scripts/release-notes.js

Lines changed: 74 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ if (!token || !repoFull) {
1111

1212
const octokit = new Octokit({ auth: token });
1313

14-
// Section definitions
14+
// Section definitions and prefixes
1515
const SECTIONS = {
1616
"[Task]": "🚀 Tasks",
1717
"[Composite]": "🚀 Tasks",
@@ -43,10 +43,12 @@ PREFIXES.forEach(p => {
4343
PREFIX_ALIASES[`${p}:`] = norm;
4444
});
4545

46+
// Strip prefix from title
4647
function stripPrefix(title) {
4748
return title.replace(/^\[[^\]]+\]\s*/, "").replace(/^[a-z/]+:\s*/i, "").trim();
4849
}
4950

51+
// Extract normalized prefix from title
5052
function getPrefix(title) {
5153
if (!title) return null;
5254
const matchBracket = title.match(/^\[([^\]]+)\]/);
@@ -62,113 +64,100 @@ function getPrefix(title) {
6264
return null;
6365
}
6466

65-
// Fetch all linked issues of a PR via GraphQL
66-
async function getLinkedIssues(prNumber) {
67-
const query = `
68-
query($owner: String!, $repo: String!, $prNumber: Int!) {
69-
repository(owner: $owner, name: $repo) {
70-
pullRequest(number: $prNumber) {
71-
closingIssuesReferences(first: 10) {
72-
nodes {
73-
number
74-
title
75-
labels(first: 10) {
76-
nodes { name }
67+
// Fetch all merged PRs in dev that are not in master
68+
async function fetchPendingPRs(latestTag) {
69+
if (latestTag) {
70+
// Compare commits after last release
71+
const { data: compare } = await octokit.repos.compareCommits({
72+
owner, repo,
73+
base: latestTag,
74+
head: "dev",
75+
});
76+
const devShas = compare.commits.map(c => c.sha);
77+
const pendingPRs = [];
78+
for (const sha of devShas) {
79+
const { data: prs } = await octokit.repos.listPullRequestsAssociatedWithCommit({
80+
owner, repo, commit_sha: sha
81+
});
82+
prs.forEach(pr => {
83+
if (pr.merged_at && !pendingPRs.some(p => p.number === pr.number)) {
84+
pendingPRs.push(pr);
7785
}
78-
}
79-
}
80-
}
86+
});
8187
}
82-
}
83-
`;
84-
const variables = { owner, repo, prNumber };
85-
const result = await octokit.graphql(query, variables);
86-
return result.repository.pullRequest.closingIssuesReferences.nodes;
88+
return pendingPRs;
89+
} else {
90+
// No release → take all merged PRs in dev
91+
const { data: prs } = await octokit.pulls.list({
92+
owner, repo,
93+
state: "closed",
94+
base: "dev",
95+
per_page: 100
96+
});
97+
return prs.filter(p => p.merged_at);
98+
}
8799
}
88100

89101
async function main() {
90-
// 1. Get latest release (tag) on master
102+
// 1. Get latest release tag
91103
const { data: releases } = await octokit.repos.listReleases({ owner, repo });
92104
const latestRelease = releases.find(r => !r.draft);
93-
const latestTag = latestRelease?.tag_name || null;
94-
95-
// 2. Compare dev vs master (or master vs master if no release)
96-
const baseRef = latestTag || "master";
97-
const { data: compare } = await octokit.repos.compareCommits({
98-
owner,
99-
repo,
100-
base: baseRef,
101-
head: "dev",
102-
});
103-
104-
const devShas = compare.commits.map(c => c.sha);
105-
106-
// 3. Collect merged PRs in dev after last release
107-
const pendingPRs = [];
108-
for (const sha of devShas) {
109-
const { data: prs } = await octokit.repos.listPullRequestsAssociatedWithCommit({
110-
owner,
111-
repo,
112-
commit_sha: sha,
113-
});
114-
prs.forEach(pr => {
115-
if (pr.merged_at && !pendingPRs.some(p => p.number === pr.number)) {
116-
pendingPRs.push(pr);
117-
}
118-
});
119-
}
105+
const latestTag = latestRelease?.tag_name;
106+
107+
// 2. Fetch all pending PRs from dev
108+
const pendingPRs = await fetchPendingPRs(latestTag);
120109

121110
if (!pendingPRs.length) {
122111
console.log("No merged PRs in dev after last release found.");
123112
return;
124113
}
125114

126-
// 4. Group by linked issue
127-
const issueMap = new Map(); // key: issue number, value: {title, prefix, prs: []}
128-
const standalonePRs = [];
115+
// 3. Map issues and attach PRs
116+
const issueMap = new Map();
129117

130118
for (const pr of pendingPRs) {
131-
const linkedIssues = await getLinkedIssues(pr.number);
119+
// Fetch linked issues via the API
120+
const { data: linkedIssues } = await octokit.pulls.listLinkedIssues({
121+
owner, repo, pull_number: pr.number
122+
}).catch(() => ({ data: [] }));
123+
132124
if (linkedIssues.length) {
133-
for (const issue of linkedIssues) {
125+
linkedIssues.forEach(issue => {
134126
if (!issueMap.has(issue.number)) {
135-
const prefix = getPrefix(issue.title) || "Other";
136-
issueMap.set(issue.number, {
137-
title: issue.title,
138-
prefix,
139-
prs: [],
140-
});
127+
issueMap.set(issue.number, { issue, prs: [] });
141128
}
142-
issueMap.get(issue.number).prs.push({ number: pr.number, title: pr.title, user: pr.user.login });
143-
}
129+
issueMap.get(issue.number).prs.push(pr);
130+
});
144131
} else {
145-
// PR without linked issue
146-
standalonePRs.push({ number: pr.number, title: pr.title, user: pr.user.login, prefix: getPrefix(pr.title) || "Other" });
132+
// PR without issue → special case
133+
issueMap.set(`pr-${pr.number}`, { prs: [pr], isStandalone: true });
147134
}
148135
}
149136

150-
// 5. Prepare sections
137+
// 4. Group by sections
151138
const sectionGroups = {};
152139
Object.keys(SECTIONS).forEach(k => sectionGroups[k] = []);
153140

154-
// Add issues
155-
for (const [issueNum, issueData] of issueMap.entries()) {
156-
const section = SECTIONS[issueData.prefix] ? issueData.prefix : "Other";
157-
const prList = issueData.prs.map(pr => `#${pr.number} by @${pr.user}`).join(", ");
158-
const line = issueData.prs.length > 0
159-
? `• ${issueData.prefix} ${issueData.title} (#${issueNum})\n ↳ PRs: ${prList}`
160-
: `• ${issueData.prefix} ${issueData.title} (#${issueNum})`;
161-
sectionGroups[section].push(line);
162-
}
163-
164-
// Add standalone PRs
165-
for (const pr of standalonePRs) {
166-
const section = SECTIONS[pr.prefix] ? pr.prefix : "Other";
167-
const line = `• ${pr.prefix} ${pr.title} (#${pr.number}) by @${pr.user}`;
168-
sectionGroups[section].push(line);
141+
for (const entry of issueMap.values()) {
142+
if (entry.isStandalone) {
143+
const pr = entry.prs[0];
144+
const prefix = getPrefix(pr.title) || "Other";
145+
const section = SECTIONS[prefix] ? prefix : "Other";
146+
const line = section === "Other"
147+
? `• ${pr.title} (#${pr.number}) by @${pr.user.login}`
148+
: `• ${prefix} ${stripPrefix(pr.title)} (#${pr.number}) by @${pr.user.login}`;
149+
sectionGroups[section].push(line);
150+
} else {
151+
const issueTitle = entry.issue.title;
152+
const prefix = getPrefix(issueTitle) || "Other";
153+
const section = SECTIONS[prefix] ? prefix : "Other";
154+
const prRefs = entry.prs.map(p => `#${p.number} by @${p.user.login}`).join(", ");
155+
const line = `• ${prefix} ${stripPrefix(issueTitle)} (#${entry.issue.number})\n ↳ PRs: ${prRefs}`;
156+
sectionGroups[section].push(line);
157+
}
169158
}
170159

171-
// 6. Order sections: Tasks first, Other last
160+
// 5. Order sections
172161
const orderedSections = ["[Task]", ...Object.keys(SECTIONS).filter(k => k !== "[Task]" && k !== "Other"), "Other"];
173162

174163
let body = "# 🚀 Release Notes\n\n";
@@ -179,7 +168,7 @@ async function main() {
179168
}
180169
}
181170

182-
// 7. Determine next patch version
171+
// 6. Determine next version
183172
let nextVersion = "v0.1.0";
184173
const latestNonDraft = releases.find(r => !r.draft) || releases[0];
185174
if (latestNonDraft) {
@@ -190,22 +179,20 @@ async function main() {
190179
}
191180
}
192181

193-
// 8. Create or update draft release
182+
// 7. Create/update draft release
194183
const draft = releases.find(r => r.draft);
195184
if (draft) {
196185
console.log("Updating existing draft release:", draft.tag_name);
197186
await octokit.repos.updateRelease({
198-
owner,
199-
repo,
187+
owner, repo,
200188
release_id: draft.id,
201189
name: nextVersion,
202190
body,
203191
});
204192
} else {
205193
console.log("Creating new draft release");
206194
await octokit.repos.createRelease({
207-
owner,
208-
repo,
195+
owner, repo,
209196
tag_name: nextVersion,
210197
name: nextVersion,
211198
body,

0 commit comments

Comments
 (0)