Skip to content

Commit 3e5f996

Browse files
committed
fix: get prefixes
1 parent 33c995e commit 3e5f996

File tree

1 file changed

+78
-57
lines changed

1 file changed

+78
-57
lines changed

.github/scripts/release-notes.js

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

29+
// --- Known prefixes map ---
30+
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",
40+
"ux/ui": "🔧 Enhancements",
41+
proposal: "💡 Ideas & Proposals",
42+
idea: "💡 Ideas & Proposals",
43+
discussion: "💡 Ideas & Proposals",
44+
};
45+
46+
// --- Helper to capitalize prefix cleanly ---
47+
function capitalizePrefix(prefix) {
48+
return prefix
49+
.toLowerCase()
50+
.split("/")
51+
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
52+
.join("/");
53+
}
54+
55+
// --- Extract and normalize all prefixes in title ---
56+
function extractPrefixes(title) {
57+
const bracketMatch = title.match(/^\s*\[([^\]]+)\]/);
58+
if (bracketMatch) {
59+
const raw = bracketMatch[1]
60+
.split(/[,]+/)
61+
.map((p) => p.trim())
62+
.filter(Boolean);
63+
return raw.map((p) => `[${capitalizePrefix(p)}]`);
64+
}
65+
66+
const match = title.match(
67+
/^([^\w]*)(bug|feat|enhancement|refactor|docs|test|chore|task|composite|ux\/ui|proposal|idea|discussion)[:\-\s]/i
68+
);
69+
if (match) {
70+
return [`[${capitalizePrefix(match[2])}]`];
71+
}
72+
73+
return [];
74+
}
75+
76+
// --- Normalize title: make sure prefix becomes [Prefix] ---
77+
function normalizeTitlePrefixes(title) {
78+
const prefixes = extractPrefixes(title);
79+
if (!prefixes.length) return title.trim();
80+
81+
let clean = title
82+
.replace(/^\s*(?:\[([^\]]+)\]|([^\s:]+))\s*:?\s*/i, "")
83+
.trim();
84+
return `${prefixes.join(", ")} ${clean}`;
85+
}
86+
87+
// --- Classify title ---
88+
function classifyTitle(title) {
89+
const prefixes = extractPrefixes(title);
90+
if (!prefixes.length) return "Other";
91+
92+
for (const prefix of prefixes) {
93+
const clean = prefix.replace(/[\[\]]/g, "").toLowerCase();
94+
if (PREFIX_MAP[clean]) return PREFIX_MAP[clean];
95+
}
96+
return "Other";
97+
}
98+
2999
// --- Fetch all closed PRs ---
30100
async function getAllPRs({ owner, repo, base }) {
31101
const perPage = 100;
@@ -75,52 +145,6 @@ async function getLinkedIssues(prNumber) {
75145
}
76146
}
77147

78-
// --- Determine section from prefix ---
79-
function classifyTitle(title) {
80-
const cleaned = title.replace(/^[\s\p{Emoji_Presentation}\p{Extended_Pictographic}]+/u, "").trim();
81-
const match = cleaned.match(/^\s*\[([^\]]+)\]|^\s*([^\s:]+)\s*:?\s*/i);
82-
if (!match) return "Other";
83-
84-
const rawPrefix = (match[1] || match[2] || "").split(",")[0].trim().toLowerCase();
85-
86-
const map = {
87-
"task": "🚀 Tasks",
88-
"composite": "🚀 Tasks",
89-
"ux/ui": "🔧 Enhancements",
90-
"enhancement": "🔧 Enhancements",
91-
"bug": "🐞 Bug Fixes",
92-
"feat": "✨ New Features",
93-
"refactor": "🛠 Refactoring",
94-
"docs": "📚 Documentation",
95-
"test": "✅ Tests",
96-
"chore": "⚙️ Chores",
97-
"proposal": "💡 Ideas & Proposals",
98-
"idea": "💡 Ideas & Proposals",
99-
"discussion": "💡 Ideas & Proposals",
100-
};
101-
102-
return map[rawPrefix] || "Other";
103-
}
104-
105-
// --- Normalize title, preserving multiple prefixes ---
106-
function normalizeTitlePrefixes(title) {
107-
let cleaned = title.trim();
108-
109-
// Extract prefix part if exists
110-
const match = cleaned.match(/^\s*(?:\[([^\]]+)\]|([^\s:]+))\s*:?\s*/i);
111-
if (match) {
112-
let prefixText = match[1] || match[2] || "";
113-
// Keep multiple prefixes intact (e.g. "Feat, UX/UI")
114-
const formatted = `[${prefixText
115-
.split(",")
116-
.map(p => p.trim().replace(/^[\[\]]+/g, "").replace(/^([a-z])/, (_, c) => c.toUpperCase()))
117-
.join(", ")}]`;
118-
cleaned = cleaned.replace(match[0], `${formatted} `);
119-
}
120-
121-
return cleaned;
122-
}
123-
124148
// --- Semantic versioning ---
125149
function nextVersion(lastTag) {
126150
if (!lastTag) return "v0.1.0";
@@ -131,14 +155,13 @@ function nextVersion(lastTag) {
131155
return `v${major}.${minor}.${patch}`;
132156
}
133157

134-
// --- Main ---
158+
// --- Main function ---
135159
async function main() {
136-
// 1️⃣ Get last release
137160
let lastRelease = null;
138161
try {
139162
const { data } = await octokit.repos.listReleases({ owner: OWNER, repo: REPO, per_page: 20 });
140-
const published = data.filter(r => !r.draft);
141-
lastRelease = published.length ? published[0] : null;
163+
const publishedReleases = data.filter(r => !r.draft);
164+
lastRelease = publishedReleases.length ? publishedReleases[0] : null;
142165
} catch {}
143166

144167
const since = lastRelease ? new Date(lastRelease.created_at) : null;
@@ -161,11 +184,10 @@ async function main() {
161184
} catch {}
162185
}
163186

164-
// 3️⃣ Merged PRs since last release
165187
const prs = await getAllPRs({ owner: OWNER, repo: REPO, base: targetBranch });
166188
const mergedPRs = prs.filter(pr => pr.merged_at && (!since || new Date(pr.merged_at) > since));
167189

168-
// 4️⃣ Build issue → PR map
190+
// Build issue → PR map
169191
const issueMap = {};
170192
const prsWithoutIssue = [];
171193

@@ -181,7 +203,7 @@ async function main() {
181203
}
182204
}
183205

184-
// 5️⃣ Group by section
206+
// Sections
185207
const sections = {
186208
"🚀 Tasks": [],
187209
"🔧 Enhancements": [],
@@ -208,19 +230,17 @@ async function main() {
208230
sections[section].push(`#${pr.number} ${title}`);
209231
}
210232

211-
// 6️⃣ Build release notes
212233
let releaseNotesText = `## Draft Release Notes\n\n`;
213234
for (const [sectionName, items] of Object.entries(sections)) {
214235
if (!items.length) continue;
215236
items.sort((a, b) => parseInt(a.match(/#(\d+)/)[1]) - parseInt(b.match(/#(\d+)/)[1]));
216237
releaseNotesText += `### ${sectionName}\n`;
217-
items.forEach(i => (releaseNotesText += `- ${i}\n`));
238+
items.forEach(i => releaseNotesText += `- ${i}\n`);
218239
releaseNotesText += `\n`;
219240
}
220241

221242
console.log(releaseNotesText);
222243

223-
// 7️⃣ Update or create draft release
224244
let draftRelease = null;
225245
try {
226246
const { data: releases } = await octokit.repos.listReleases({ owner: OWNER, repo: REPO, per_page: 10 });
@@ -249,9 +269,10 @@ async function main() {
249269
console.log(`✅ Draft release created: ${newTag}`);
250270
}
251271

252-
console.log("✅ Release processing completed");
272+
console.log(`✅ Release processing completed`);
253273
}
254274

275+
// --- Run ---
255276
main().catch(err => {
256277
console.error("Error:", err);
257278
process.exit(1);

0 commit comments

Comments
 (0)