Skip to content

Commit b07df76

Browse files
committed
fix: classify prs
1 parent 96bea37 commit b07df76

File tree

1 file changed

+58
-45
lines changed

1 file changed

+58
-45
lines changed

.github/scripts/release-notes.js

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -26,48 +26,23 @@ const graphqlWithAuth = graphql.defaults({
2626
headers: { authorization: `token ${process.env.GITHUB_TOKEN}` },
2727
});
2828

29-
// --- Known prefixes map for classification ---
29+
// --- Prefix map for classification ---
3030
const PREFIX_MAP = {
31-
bug: "🐞 Bug Fixes",
32-
feat: "✨ New Features",
33-
enhancement: "🔧 Enhancements",
34-
refactor: "🛠 Refactoring",
35-
docs: "📚 Documentation",
36-
test: "✅ Tests",
37-
chore: "⚙️ Chores",
38-
task: "🚀 Tasks",
39-
composite: "🚀 Tasks",
31+
"task": "🚀 Tasks",
32+
"composite": "🚀 Tasks",
4033
"ux/ui": "🔧 Enhancements",
41-
proposal: "💡 Ideas & Proposals",
42-
idea: "💡 Ideas & Proposals",
43-
discussion: "💡 Ideas & Proposals",
34+
"enhancement": "🔧 Enhancements",
35+
"bug": "🐞 Bug Fixes",
36+
"feat": "✨ New Features",
37+
"refactor": "🛠 Refactoring",
38+
"docs": "📚 Documentation",
39+
"test": "✅ Tests",
40+
"chore": "⚙️ Chores",
41+
"proposal": "💡 Ideas & Proposals",
42+
"idea": "💡 Ideas & Proposals",
43+
"discussion": "💡 Ideas & Proposals",
4444
};
4545

46-
// --- Normalize title for release notes (keep multi-prefix intact) ---
47-
function normalizeTitleForNotes(title) {
48-
let t = title.trim();
49-
50-
// Convert single-word prefixes like chore:, feat:, 🎨 chore: → [Chore]
51-
t = t.replace(
52-
/^[\s\p{Emoji_Presentation}\p{Extended_Pictographic}]*\s*(bug|feat|enhancement|refactor|docs|test|chore|task|composite|ux\/ui|proposal|idea|discussion)[:\s-]+/i,
53-
(m, p1) => {
54-
const normalized = p1.toLowerCase() === "ux/ui" ? "UX/UI" : p1.charAt(0).toUpperCase() + p1.slice(1).toLowerCase();
55-
return `[${normalized}] `;
56-
}
57-
);
58-
59-
// Leave multi-prefix brackets intact
60-
return t;
61-
}
62-
63-
// --- Classify title for section (by first prefix only) ---
64-
function classifyTitle(title) {
65-
const match = title.match(/\[([^\]]+)\]/);
66-
if (!match) return "Other";
67-
const firstPrefix = match[1].split(',')[0].trim().toLowerCase();
68-
return PREFIX_MAP[firstPrefix] || "Other";
69-
}
70-
7146
// --- Fetch all closed PRs ---
7247
async function getAllPRs({ owner, repo, base }) {
7348
const perPage = 100;
@@ -117,6 +92,42 @@ async function getLinkedIssues(prNumber) {
11792
}
11893
}
11994

95+
// --- Classify title by first prefix ---
96+
function classifyTitle(title) {
97+
const t = title.trim();
98+
99+
// 1️⃣ Bracket prefix [Feat], [Feat, UX/UI], etc.
100+
let match = t.match(/\[([^\]]+)\]/);
101+
if (match) {
102+
const firstPrefix = match[1].split(',')[0].trim().toLowerCase();
103+
return PREFIX_MAP[firstPrefix] || "Other";
104+
}
105+
106+
// 2️⃣ Single-word prefix like chore:, feat:, 🎨 chore:
107+
match = t.match(/^[\s\p{Emoji_Presentation}\p{Extended_Pictographic}]*\s*(bug|feat|enhancement|refactor|docs|test|chore|task|composite|ux\/ui|proposal|idea|discussion)[:\s-]+/i);
108+
if (match) {
109+
const prefix = match[1].toLowerCase();
110+
return PREFIX_MAP[prefix] || "Other";
111+
}
112+
113+
// 3️⃣ Fallback
114+
return "Other";
115+
}
116+
117+
// --- Normalize title prefixes (for display) ---
118+
function normalizeTitleForNotes(title) {
119+
let t = title.trim();
120+
121+
// Convert single-word prefixes to [Title] style, keep bracketed titles as-is
122+
const match = t.match(/^[\s\p{Emoji_Presentation}\p{Extended_Pictographic}]*\s*(bug|feat|enhancement|refactor|docs|test|chore|task|composite|ux\/ui|proposal|idea|discussion)[:\s-]+/i);
123+
if (match) {
124+
const prefix = match[1];
125+
t = t.replace(match[0], `[${prefix.charAt(0).toUpperCase() + prefix.slice(1).toLowerCase()}] `);
126+
}
127+
128+
return t;
129+
}
130+
120131
// --- Semantic versioning ---
121132
function nextVersion(lastTag) {
122133
if (!lastTag) return "v0.1.0";
@@ -133,15 +144,15 @@ async function main() {
133144
let lastRelease = null;
134145
try {
135146
const { data } = await octokit.repos.listReleases({ owner: OWNER, repo: REPO, per_page: 20 });
136-
const publishedReleases = data.filter(r => !r.draft);
137-
lastRelease = publishedReleases.length ? publishedReleases[0] : null;
147+
const published = data.filter(r => !r.draft);
148+
lastRelease = published.length ? published[0] : null;
138149
} catch {}
139150

140151
const since = lastRelease ? new Date(lastRelease.created_at) : null;
141152
const lastTag = lastRelease?.tag_name || null;
142153
const newTag = nextVersion(lastTag);
143154

144-
// 2️⃣ Target branch
155+
// 2️⃣ Determine target branch
145156
const branches = await octokit.repos.listBranches({ owner: OWNER, repo: REPO });
146157
const branchNames = branches.data.map(b => b.name);
147158
let targetBranch = MASTER_BRANCH;
@@ -177,7 +188,7 @@ async function main() {
177188
}
178189
}
179190

180-
// 5️⃣ Classify and organize sections
191+
// 5️⃣ Build sections
181192
const sections = {
182193
"🚀 Tasks": [],
183194
"🔧 Enhancements": [],
@@ -191,16 +202,18 @@ async function main() {
191202
Other: [],
192203
};
193204

205+
// PRs linked to issues
194206
for (const [num, info] of Object.entries(issueMap)) {
195207
const section = classifyTitle(info.title);
196-
const title = normalizeTitleForNotes(info.title);
208+
const title = info.title; // keep original title with all prefixes
197209
const prsText = info.prs.sort((a, b) => a - b).map(n => `#${n}`).join(", ");
198210
sections[section].push(`#${num} ${title}\n↳ PRs: ${prsText}`);
199211
}
200212

213+
// PRs without issues
201214
for (const pr of prsWithoutIssue) {
202-
const section = classifyTitle(pr.title);
203215
const title = normalizeTitleForNotes(pr.title);
216+
const section = classifyTitle(title);
204217
sections[section].push(`#${pr.number} ${title}`);
205218
}
206219

@@ -216,7 +229,7 @@ async function main() {
216229

217230
console.log(releaseNotesText);
218231

219-
// 7️⃣ Update or create draft release
232+
// 7️⃣ Find or create draft release
220233
let draftRelease = null;
221234
try {
222235
const { data: releases } = await octokit.repos.listReleases({ owner: OWNER, repo: REPO, per_page: 10 });

0 commit comments

Comments
 (0)