Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

Commit bbfc6ec

Browse files
committed
Merge branch 'develop' into milestone/custom-scalars
2 parents a39efcd + 2d09a3f commit bbfc6ec

File tree

3 files changed

+89
-123
lines changed

3 files changed

+89
-123
lines changed

scripts/generate-changeset.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function isBreakingChange(title, body) {
110110
}
111111

112112
// Check for conventional commit breaking change indicator (!)
113-
if (title.includes('!:')) {
113+
if (title.includes('!')) {
114114
return true;
115115
}
116116

@@ -156,12 +156,12 @@ async function generateChangeset() {
156156
const changeType = extractChangeType(title);
157157
const breaking = isBreakingChange(title, body);
158158

159-
// Use the createChangeset utility to create the changeset file
159+
// Store the complete title in the changeset
160160
const changesetPath = await createChangeset({
161-
title,
161+
title: title.trim(),
162162
pr,
163163
author,
164-
type: changeType,
164+
type: title.split(':')[0].trim(),
165165
breaking,
166166
branch,
167167
description: body

scripts/generate-release-notes.js

Lines changed: 22 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -150,102 +150,60 @@ async function readChangesets(options = {}) {
150150
return changesets.filter(changeset => changeset.branch === branch);
151151
}
152152

153-
// Ensure consistent formatting for changeset entries
154-
function formatChangesetEntry(changeset) {
155-
// Clean up the title (remove quotes, ensure proper format)
156-
const title = changeset.title.replace(/^['"]|['"]$/g, '');
157-
158-
return `* **${title}** (#${changeset.pr}) - @${changeset.author}`;
153+
/**
154+
* Format a changeset entry for the release notes
155+
* @param {Object} changeset The changeset object
156+
* @returns {string} Formatted entry
157+
*/
158+
function formatChangesetEntry(changeset, repoUrl) {
159+
return `- **${changeset.title}** ([#${changeset.pr}](${repoUrl}/pull/${changeset.pr})) - @${changeset.author}`;
159160
}
160161

161162
// Generate release notes in markdown format
162163
async function generateMarkdownReleaseNotes(changesets, repoUrl, token) {
163164
// Categorize changesets
164165
const categories = categorizeChangesets(changesets);
165166

166-
// Determine the appropriate version bump type
167-
const bumpType = determineBumpType(changesets);
168-
169167
// Start building the release notes
170168
let notes = ``;
171169

172170
// Add breaking changes section if there are any
173171
if (categories.breaking.length > 0) {
174172
notes += `### Breaking Changes ⚠️\n\n`;
175-
176173
for (const changeset of categories.breaking) {
177174
const prLink = repoUrl ? `([#${changeset.pr}](${repoUrl}/pull/${changeset.pr}))` : `(#${changeset.pr})`;
178-
const prefixRegex = /^(feat!?|fix!?|docs!?|style!?|refactor!?|perf!?|test!?|build!?|ci!?|chore!?|revert!?)(\([^)]+\))?:/;
179-
const match = changeset.title.match(prefixRegex);
180-
if (match) {
181-
const prefix = changeset.title.substring(0, match[0].length);
182-
const restOfTitle = changeset.title.substring(match[0].length).trim();
183-
notes += `- **${prefix}** ${restOfTitle} ${prLink} - @${changeset.author}\n`;
184-
} else {
185-
notes += `- **${changeset.title}** ${prLink} - @${changeset.author}\n`;
186-
}
175+
notes += `- ${changeset.title} ${prLink} - @${changeset.author}\n`;
187176
}
188-
189177
notes += `\n`;
190178
}
191179

192180
// Add features section if there are any
193181
if (categories.features.length > 0) {
194182
notes += `### Features ✨\n\n`;
195-
196183
for (const changeset of categories.features) {
197184
const prLink = repoUrl ? `([#${changeset.pr}](${repoUrl}/pull/${changeset.pr}))` : `(#${changeset.pr})`;
198-
const prefixRegex = /^(feat!?|fix!?|docs!?|style!?|refactor!?|perf!?|test!?|build!?|ci!?|chore!?|revert!?)(\([^)]+\))?:/;
199-
const match = changeset.title.match(prefixRegex);
200-
if (match) {
201-
const prefix = changeset.title.substring(0, match[0].length);
202-
const restOfTitle = changeset.title.substring(match[0].length).trim();
203-
notes += `- **${prefix}** ${restOfTitle} ${prLink} - @${changeset.author}\n`;
204-
} else {
205-
notes += `- **${changeset.title.replace(/^feat:\s*/i, '')}** ${prLink} - @${changeset.author}\n`;
206-
}
185+
notes += `- ${changeset.title} ${prLink} - @${changeset.author}\n`;
207186
}
208-
209187
notes += `\n`;
210188
}
211189

212190
// Add fixes section if there are any
213191
if (categories.fixes.length > 0) {
214192
notes += `### Fixes 🐛\n\n`;
215-
216193
for (const changeset of categories.fixes) {
217194
const prLink = repoUrl ? `([#${changeset.pr}](${repoUrl}/pull/${changeset.pr}))` : `(#${changeset.pr})`;
218-
const prefixRegex = /^(feat!?|fix!?|docs!?|style!?|refactor!?|perf!?|test!?|build!?|ci!?|chore!?|revert!?)(\([^)]+\))?:/;
219-
const match = changeset.title.match(prefixRegex);
220-
if (match) {
221-
const prefix = changeset.title.substring(0, match[0].length);
222-
const restOfTitle = changeset.title.substring(match[0].length).trim();
223-
notes += `- **${prefix}** ${restOfTitle} ${prLink} - @${changeset.author}\n`;
224-
} else {
225-
notes += `- **${changeset.title.replace(/^fix:\s*/i, '')}** ${prLink} - @${changeset.author}\n`;
226-
}
195+
notes += `- ${changeset.title} ${prLink} - @${changeset.author}\n`;
227196
}
228-
229197
notes += `\n`;
230198
}
231199

232200
// Add other changes section if there are any
233201
if (categories.other.length > 0) {
234202
notes += `### Other Changes 🔄\n\n`;
235-
236203
for (const changeset of categories.other) {
237204
const prLink = repoUrl ? `([#${changeset.pr}](${repoUrl}/pull/${changeset.pr}))` : `(#${changeset.pr})`;
238-
const prefixRegex = /^(feat!?|fix!?|docs!?|style!?|refactor!?|perf!?|test!?|build!?|ci!?|chore!?|revert!?)(\([^)]+\))?:/;
239-
const match = changeset.title.match(prefixRegex);
240-
if (match) {
241-
const prefix = changeset.title.substring(0, match[0].length);
242-
const restOfTitle = changeset.title.substring(match[0].length).trim();
243-
notes += `- **${prefix}** ${restOfTitle} ${prLink} - @${changeset.author}\n`;
244-
} else {
245-
notes += `- **${changeset.title.replace(/^[^:]+:\s*/i, '')}** ${prLink} - @${changeset.author}\n`;
246-
}
205+
notes += `- ${changeset.title} ${prLink} - @${changeset.author}\n`;
247206
}
248-
249207
notes += `\n`;
250208
}
251209

@@ -273,7 +231,6 @@ async function generateMarkdownReleaseNotes(changesets, repoUrl, token) {
273231
if (contributors.size > 0) {
274232
notes += `## Contributors\n\n`;
275233
notes += `Thanks to all the contributors who made this release possible!\n\n`;
276-
277234
for (const contributor of contributors) {
278235
if (firstTimeContributors.has(contributor)) {
279236
notes += `- @${contributor} 🎉 (First-time contributor)\n`;
@@ -359,28 +316,22 @@ async function generateJsonReleaseNotes(changesets, repoUrl, token) {
359316
async function generateReleaseNotes() {
360317
// Get repository URL
361318
const repoUrl = await getRepoUrl();
362-
363-
// Get GitHub token
364319
const token = getGitHubToken();
365320

366-
// Read all changesets, filtering by branch if specified
321+
// Read changesets with branch filter if specified
367322
const changesets = await readChangesets({ branch: argv.branch });
368323

369-
// Log the number of changesets found
370-
console.log(`Found ${changesets.length} changesets${argv.branch ? ` for branch ${argv.branch}` : ''}.`);
371-
372-
// Generate release notes in the requested format
373324
if (argv.format === 'json') {
374-
const jsonNotes = await generateJsonReleaseNotes(changesets, repoUrl, token);
375-
console.log(jsonNotes);
376-
} else {
377-
const markdownNotes = await generateMarkdownReleaseNotes(changesets, repoUrl, token);
378-
console.log(markdownNotes);
325+
return generateJsonReleaseNotes(changesets, repoUrl, token);
379326
}
327+
328+
return generateMarkdownReleaseNotes(changesets, repoUrl, token);
380329
}
381330

382-
// Run the script
383-
generateReleaseNotes().catch(err => {
384-
console.error('Error generating release notes:', err);
385-
process.exit(1);
386-
});
331+
// Main execution
332+
generateReleaseNotes()
333+
.then(notes => console.log(notes))
334+
.catch(error => {
335+
console.error('Error generating release notes:', error);
336+
process.exit(1);
337+
});

scripts/utils/changesets.js

Lines changed: 63 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -120,45 +120,29 @@ async function readAllChangesets(options = {}) {
120120
}
121121

122122
/**
123-
* Parse changeset content from a file
124-
*
123+
* Read and parse a changeset file
125124
* @param {string} content The content of the changeset file
126-
* @returns {Object} Parsed changeset object
125+
* @returns {Object} Parsed changeset data
127126
*/
128127
function parseChangesetContent(content) {
129-
// Use your existing parsing logic here
130-
// This should extract title, PR number, author, type, breaking flag, branch, etc.
131-
// from the changeset content
132-
133-
// Example implementation (adjust based on your actual changeset format):
134128
const lines = content.split('\n');
135-
const changeset = {
136-
title: '',
137-
pr: null,
138-
author: '',
139-
type: 'other',
140-
breaking: false,
141-
branch: undefined,
142-
description: ''
143-
};
129+
const changeset = {};
144130

145-
// Parse metadata lines (key: value format)
146-
const metadataLines = lines.filter(line => line.includes(':'));
147-
for (const line of metadataLines) {
148-
const [key, value] = line.split(':', 2).map(part => part.trim());
149-
if (key === 'title') {
150-
changeset.title = value.replace(/^['"]|['"]$/g, '');
151-
} else if (key === 'pr') changeset.pr = parseInt(value, 10) || null;
152-
else if (key === 'author') changeset.author = value;
153-
else if (key === 'type') changeset.type = value;
154-
else if (key === 'breaking') changeset.breaking = value === 'true';
155-
else if (key === 'branch') changeset.branch = value;
156-
}
157-
158-
// Extract description (everything after metadata)
159-
const descriptionStartIndex = metadataLines.length;
160-
if (descriptionStartIndex < lines.length) {
161-
changeset.description = lines.slice(descriptionStartIndex).join('\n').trim();
131+
let inFrontmatter = false;
132+
for (const line of lines) {
133+
if (line.trim() === '---') {
134+
inFrontmatter = !inFrontmatter;
135+
continue;
136+
}
137+
138+
if (inFrontmatter) {
139+
const [key, ...valueParts] = line.split(':');
140+
if (key && valueParts.length) {
141+
// Join value parts back together and clean up quotes and whitespace
142+
const value = valueParts.join(':').trim().replace(/^['"]|['"]$/g, '');
143+
changeset[key.trim()] = value;
144+
}
145+
}
162146
}
163147

164148
return changeset;
@@ -202,11 +186,12 @@ function categorizeChangesets(changesets = []) {
202186
};
203187

204188
changesets.forEach(changeset => {
205-
if (changeset.breaking) {
189+
// Check for ! in the title for breaking changes
190+
if (changeset.title.includes('!')) {
206191
categories.breaking.push(changeset);
207-
} else if (changeset.type === 'feat') {
192+
} else if (changeset.title.startsWith('feat:')) {
208193
categories.features.push(changeset);
209-
} else if (changeset.type === 'fix') {
194+
} else if (changeset.title.startsWith('fix:')) {
210195
categories.fixes.push(changeset);
211196
} else {
212197
categories.other.push(changeset);
@@ -229,7 +214,21 @@ function categorizeChangesets(changesets = []) {
229214
* @param {string} data.description PR description
230215
* @returns {Promise<string>} Path to the created changeset file
231216
*/
232-
async function createChangeset({ title, pr, author, type, breaking, branch = 'develop', description = '' }) {
217+
async function createChangeset(data) {
218+
const { title, pr, author, type, breaking, branch, description } = data;
219+
220+
// Use the full title in the changeset content
221+
const content = `---
222+
title: ${title}
223+
pr: ${pr}
224+
author: ${author}
225+
type: ${type}
226+
breaking: ${breaking}
227+
branch: ${branch}
228+
---
229+
230+
${description}`;
231+
233232
// Ensure the .changesets directory exists
234233
const changesetDir = path.join(process.cwd(), '.changesets');
235234
await fs.ensureDir(changesetDir);
@@ -239,16 +238,6 @@ async function createChangeset({ title, pr, author, type, breaking, branch = 'de
239238
const filename = `${timestamp}-pr-${pr}.md`;
240239
const filepath = path.join(changesetDir, filename);
241240

242-
// Create the changeset content with frontmatter
243-
const content = matter.stringify(description, {
244-
title,
245-
pr,
246-
author,
247-
type,
248-
breaking,
249-
branch
250-
});
251-
252241
// Write the changeset file
253242
await fs.writeFile(filepath, content);
254243

@@ -272,6 +261,32 @@ async function deleteAllChangesets() {
272261
return files.length;
273262
}
274263

264+
/**
265+
* Parse a PR title into its components
266+
* @param {string} title The full PR title
267+
* @returns {{prefix: string, description: string}} The parsed title components
268+
*/
269+
function parsePrTitle(title) {
270+
const [prefix, ...rest] = title.split(':');
271+
return {
272+
prefix: prefix.trim(),
273+
description: rest.join(':').trim() // Join back in case there were other colons
274+
};
275+
}
276+
277+
/**
278+
* Format a changeset entry for the release notes
279+
* @param {Object} changeset The changeset object
280+
* @returns {string} Formatted entry
281+
*/
282+
function formatChangesetEntry(changeset) {
283+
const { prefix, description } = parsePrTitle(changeset.title);
284+
const breaking = changeset.breaking ? '!' : '';
285+
286+
// Format as "**prefix**: description" or "**prefix!**: description" for breaking changes
287+
return `- **${prefix}${breaking}**: ${description} ([#${changeset.pr}](${repoUrl}/pull/${changeset.pr})) - @${changeset.author}`;
288+
}
289+
275290
module.exports = {
276291
getChangesetDir,
277292
ensureChangesetDir,

0 commit comments

Comments
 (0)