From 3747f670e2ef1f7b04d45ec1e938e856313aad8a Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 17 Aug 2026 02:56:06 +0100 Subject: [PATCH 1/7] style: auto formatting --- .github/scripts/update-changelog.mjs | 36 ++++++++-------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/.github/scripts/update-changelog.mjs b/.github/scripts/update-changelog.mjs index 70b0ec7..0802e15 100644 --- a/.github/scripts/update-changelog.mjs +++ b/.github/scripts/update-changelog.mjs @@ -7,7 +7,7 @@ * before this script is called, so we can assume the PR should be included. */ -import { readFileSync, writeFileSync } from "fs"; +import {readFileSync, writeFileSync} from "fs"; /** * Maps conventional commit types to changelog sections @@ -62,10 +62,8 @@ export const INCLUDED_TYPES = Object.keys(TYPE_TO_SECTION); * Build regex pattern to match conventional commit type prefix * Matches: type(scope)?: or type!: with optional whitespace after colon */ -const COMMIT_TYPE_REGEX = new RegExp( - `^(${INCLUDED_TYPES.join("|")})(\\(.+?\\))?!?:\\s*`, - "i", -); +const COMMIT_TYPE_REGEX = new RegExp(`^(${INCLUDED_TYPES.join("|")})(\\(.+?\\))?!?:\\s*`, "i"); + /** * Extracts the conventional commit type from a PR title @@ -100,12 +98,10 @@ function findOrCreateUnreleased(changelog) { const headerIndex = lines.findIndex((line) => line.startsWith("# Changelog")); // Find if Unreleased section exists - const unreleasedIndex = lines.findIndex((line) => - line.match(/^## \[?Unreleased\]?/i), - ); + const unreleasedIndex = lines.findIndex((line) => line.match(/^## \[?Unreleased\]?/i)); if (unreleasedIndex !== -1) { - return { hasUnreleased: true, lines, unreleasedIndex }; + return {hasUnreleased: true, lines, unreleasedIndex}; } // Create Unreleased section - find first release section to insert before it @@ -130,7 +126,7 @@ function findOrCreateUnreleased(changelog) { lines.splice(insertIndex, 0, ...unreleasedSection); - return { hasUnreleased: false, lines, unreleasedIndex: insertIndex + 1 }; + return {hasUnreleased: false, lines, unreleasedIndex: insertIndex + 1}; } /** @@ -199,18 +195,12 @@ function addEntryToSection(lines, unreleasedIndex, section, entry) { } // Skip all existing sections to add new section at the end - while ( - insertIndex < nextSectionIndex && - lines[insertIndex].startsWith("### ") - ) { + while (insertIndex < nextSectionIndex && lines[insertIndex].startsWith("### ")) { // Skip section header insertIndex++; // Skip all content until the next section header or end of Unreleased - while ( - insertIndex < nextSectionIndex && - !lines[insertIndex].startsWith("### ") - ) { + while (insertIndex < nextSectionIndex && !lines[insertIndex].startsWith("### ")) { insertIndex++; } } @@ -229,10 +219,7 @@ function addEntryToSection(lines, unreleasedIndex, section, entry) { // Skip existing entries using markers as definitive boundaries. // For entries without a marker (backward compatibility), stop at the next // entry title ("- ") or section header ("### "). - while ( - insertIndex < nextSectionIndex && - lines[insertIndex].startsWith("- ") - ) { + while (insertIndex < nextSectionIndex && lines[insertIndex].startsWith("- ")) { insertIndex++; // skip the entry title line // Advance past description lines/blank lines up to the marker while ( @@ -244,10 +231,7 @@ function addEntryToSection(lines, unreleasedIndex, section, entry) { insertIndex++; } // Skip the marker if present - if ( - insertIndex < nextSectionIndex && - lines[insertIndex] === "" - ) { + if (insertIndex < nextSectionIndex && lines[insertIndex] === "") { insertIndex++; } // Skip any blank lines between entries From a4b534a96627764c43ebe3e99f2fdbfb95c91c50 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 17 Aug 2026 02:57:42 +0100 Subject: [PATCH 2/7] style: re-order functions in the update-changelog CI script for readability --- .github/scripts/update-changelog.mjs | 308 +++++++++++++-------------- 1 file changed, 143 insertions(+), 165 deletions(-) diff --git a/.github/scripts/update-changelog.mjs b/.github/scripts/update-changelog.mjs index 0802e15..c49137b 100644 --- a/.github/scripts/update-changelog.mjs +++ b/.github/scripts/update-changelog.mjs @@ -64,6 +64,75 @@ export const INCLUDED_TYPES = Object.keys(TYPE_TO_SECTION); */ const COMMIT_TYPE_REGEX = new RegExp(`^(${INCLUDED_TYPES.join("|")})(\\(.+?\\))?!?:\\s*`, "i"); +/** + * Main function to update the changelog + * + * @param {object} params An object containing the parameters for the function + * @param {object} params.pr Pull request object from GitHub context + * @param {import('@actions/core')} params.core GitHub Actions core module + */ +export default async function updateChangelog({pr, core}) { + try { + const prNumber = pr.number; + const prTitle = pr.title; + const prUrl = pr.html_url; + const prAuthor = pr.user.login; + const prBody = pr.body; + + console.log(`📝 Processing PR #${prNumber}: ${prTitle}`); + + // Extract type from PR title + const type = extractType(prTitle); + if (!type) { + console.log(`âš ī¸ No valid conventional commit type found in PR title. Skipping changelog update.`); + return; + } + + const section = TYPE_TO_SECTION[type]; + console.log(`📂 Type: ${type} → Section: ${section}`); + + // Read current changelog + const changelogPath = "CHANGELOG.md"; + let changelog = ""; + try { + changelog = readFileSync(changelogPath, "utf8"); + } catch (error) { + console.log("CHANGELOG.md not found, creating new one"); + changelog = "# Changelog\n\n"; + } + + // Get or create Unreleased section + const {lines, unreleasedIndex} = findOrCreateUnreleased(changelog); + + // Check if this PR is already in the changelog + if (isDuplicateEntry(lines, unreleasedIndex, prNumber)) { + console.log(`â„šī¸ PR #${prNumber} already exists in the changelog. Skipping.`); + return; + } + + // Format PR entry with cleaned title + const cleanedTitle = cleanTitle(prTitle); + const entry = buildEntry(type, section, cleanedTitle, prNumber, prUrl, prAuthor, prBody); + + // Add entry to the appropriate section + const updatedLines = addEntryToSection(lines, unreleasedIndex, section, entry); + + // Write updated changelog + const updatedChangelog = updatedLines.join("\n"); + writeFileSync(changelogPath, updatedChangelog); + + console.log(`✅ Updated CHANGELOG.md with PR #${prNumber}`); + + // Set outputs for the workflow to use + core.setOutput("changelog-updated", "true"); + core.setOutput("pr-number", prNumber); + core.setOutput("pr-title", cleanedTitle); + core.setOutput("pr-author", prAuthor); + } catch (error) { + console.error("❌ Error updating changelog:", error); + core.setFailed(`Failed to update changelog: ${error.message}`); + } +} /** * Extracts the conventional commit type from a PR title @@ -75,19 +144,6 @@ function extractType(title) { return match ? match[1].toLowerCase() : null; } -/** - * Strips the conventional commit type prefix from a PR title - * @param {string} title - PR title - * @returns {string} - Cleaned title - */ -function cleanTitle(title) { - // Remove the type prefix (e.g., "feat: ", "fix(scope): ") - const cleaned = title.replace(COMMIT_TYPE_REGEX, ""); - - if (cleaned.length === 0) return title; // Fallback to original if something went wrong - return cleaned; -} - /** * Gets or creates the Unreleased section in the changelog * @param {string} changelog - Current changelog content @@ -157,6 +213,80 @@ function isDuplicateEntry(lines, unreleasedIndex, prNumber) { return false; } +/** + * Strips the conventional commit type prefix from a PR title + * @param {string} title - PR title + * @returns {string} - Cleaned title + */ +function cleanTitle(title) { + // Remove the type prefix (e.g., "feat: ", "fix(scope): ") + const cleaned = title.replace(COMMIT_TYPE_REGEX, ""); + + if (cleaned.length === 0) return title; // Fallback to original if something went wrong + return cleaned; +} + +/** + * Builds the full changelog entry line for a PR + * @param {string} type - Conventional commit type (e.g., "feat", "fix", "revert") + * @param {string} section - Section name resolved from TYPE_TO_SECTION + * @param {string} cleanedTitle - PR title with the type prefix stripped + * @param {number} prNumber - PR number + * @param {string} prUrl - PR HTML URL + * @param {string} prAuthor - PR author login + * @param {string|null} prBody - PR body/description + * @returns {string} - Formatted entry line + */ +function buildEntry(type, section, cleanedTitle, prNumber, prUrl, prAuthor, prBody) { + const prefix = TYPE_TO_PREFIX[type] ?? section; + const dedupedTitle = removeLeadingDuplicateVerb(prefix, cleanedTitle); + const titlePart = dedupedTitle ? ` ${dedupedTitle}` : ` ${cleanedTitle.trim()}`; + return `- ${prefix}${titlePart} ([#${prNumber}](${prUrl})) by @${prAuthor}${formatPRDescription(prBody)}\n`; +} + +/** + * Removes duplicated leading verbs based on the resolved changelog prefix. + * Example: prefix "Added" + title "added support for x" => "support for x" + * @param {string} prefix - Resolved changelog entry prefix + * @param {string} title - Cleaned PR title + * @returns {string} - Title without duplicated leading verb + */ +function removeLeadingDuplicateVerb(prefix, title) { + const trimmedTitle = title.trim(); + if (!trimmedTitle) return ""; + + const pattern = PREFIX_TO_LEADING_VERB_REGEX[prefix.toLowerCase()]; + if (!pattern) return trimmedTitle; + + return trimmedTitle.replace(pattern, "").trimStart(); +} + +/** + * Formats the PR description with indentation for nesting under a list item + * @param {string|null} prBody - PR description/body text + * @returns {string} - Formatted description string (empty if no body) + */ +function formatPRDescription(prBody) { + if (!prBody || prBody.trim() === "") { + return ""; + } + + // Convert markdown headings to bold text + const withoutHeadings = prBody.replace(/^#{1,6}\s+(.+)$/gm, "**$1**"); + + // Indent each line with 1 tab (4 spaces) to nest under the list item + // Skip indentation on empty lines to avoid trailing whitespace + const indented = withoutHeadings + .split("\n") + .map((line) => (line ? `${DESCRIPTION_INDENT}${line}` : "")) + .join("\n"); + // Always separate the description from the entry title with a blank line so + // that markdown renders the description on its own line. Strip any leading + // newlines from `indented` first to avoid double blank lines when prBody + // itself starts with a blank line. + return `\n\n${indented.replace(/^\n+/, "")}`; +} + /** * Adds a PR entry to the appropriate section within Unreleased * @param {array} lines - Changelog lines @@ -246,155 +376,3 @@ function addEntryToSection(lines, unreleasedIndex, section, entry) { return lines; } - -/** - * Formats the PR description with indentation for nesting under a list item - * @param {string|null} prBody - PR description/body text - * @returns {string} - Formatted description string (empty if no body) - */ -function formatPRDescription(prBody) { - if (!prBody || prBody.trim() === "") { - return ""; - } - - // Convert markdown headings to bold text - const withoutHeadings = prBody.replace(/^#{1,6}\s+(.+)$/gm, "**$1**"); - - // Indent each line with 1 tab (4 spaces) to nest under the list item - // Skip indentation on empty lines to avoid trailing whitespace - const indented = withoutHeadings - .split("\n") - .map((line) => (line ? `${DESCRIPTION_INDENT}${line}` : "")) - .join("\n"); - // Always separate the description from the entry title with a blank line so - // that markdown renders the description on its own line. Strip any leading - // newlines from `indented` first to avoid double blank lines when prBody - // itself starts with a blank line. - return `\n\n${indented.replace(/^\n+/, "")}`; -} - -/** - * Builds the full changelog entry line for a PR - * @param {string} type - Conventional commit type (e.g., "feat", "fix", "revert") - * @param {string} section - Section name resolved from TYPE_TO_SECTION - * @param {string} cleanedTitle - PR title with the type prefix stripped - * @param {number} prNumber - PR number - * @param {string} prUrl - PR HTML URL - * @param {string} prAuthor - PR author login - * @param {string|null} prBody - PR body/description - * @returns {string} - Formatted entry line - */ -function buildEntry( - type, - section, - cleanedTitle, - prNumber, - prUrl, - prAuthor, - prBody, -) { - const prefix = TYPE_TO_PREFIX[type] ?? section; - const dedupedTitle = removeLeadingDuplicateVerb(prefix, cleanedTitle); - const titlePart = dedupedTitle ? ` ${dedupedTitle}` : ` ${cleanedTitle.trim()}`; - return `- ${prefix}${titlePart} ([#${prNumber}](${prUrl})) by @${prAuthor}${formatPRDescription(prBody)}\n`; -} - -/** - * Removes duplicated leading verbs based on the resolved changelog prefix. - * Example: prefix "Added" + title "added support for x" => "support for x" - * @param {string} prefix - Resolved changelog entry prefix - * @param {string} title - Cleaned PR title - * @returns {string} - Title without duplicated leading verb - */ -function removeLeadingDuplicateVerb(prefix, title) { - const trimmedTitle = title.trim(); - if (!trimmedTitle) return ""; - - const pattern = PREFIX_TO_LEADING_VERB_REGEX[prefix.toLowerCase()]; - if (!pattern) return trimmedTitle; - - return trimmedTitle.replace(pattern, "").trimStart(); -} - -/** - * Main function to update the changelog - */ -export default async function updateChangelog({ pr, core }) { - try { - const prNumber = pr.number; - const prTitle = pr.title; - const prUrl = pr.html_url; - const prAuthor = pr.user.login; - const prBody = pr.body; - - console.log(`📝 Processing PR #${prNumber}: ${prTitle}`); - - // Extract type from PR title - const type = extractType(prTitle); - if (!type) { - console.log( - `âš ī¸ No valid conventional commit type found in PR title. Skipping changelog update.`, - ); - return; - } - - const section = TYPE_TO_SECTION[type]; - console.log(`📂 Type: ${type} → Section: ${section}`); - - // Read current changelog - const changelogPath = "CHANGELOG.md"; - let changelog = ""; - try { - changelog = readFileSync(changelogPath, "utf8"); - } catch (error) { - console.log("CHANGELOG.md not found, creating new one"); - changelog = "# Changelog\n\n"; - } - - // Get or create Unreleased section - const { lines, unreleasedIndex } = findOrCreateUnreleased(changelog); - - // Check if this PR is already in the changelog - if (isDuplicateEntry(lines, unreleasedIndex, prNumber)) { - console.log( - `â„šī¸ PR #${prNumber} already exists in the changelog. Skipping.`, - ); - return; - } - - // Format PR entry with cleaned title - const cleanedTitle = cleanTitle(prTitle); - const entry = buildEntry( - type, - section, - cleanedTitle, - prNumber, - prUrl, - prAuthor, - prBody, - ); - - // Add entry to the appropriate section - const updatedLines = addEntryToSection( - lines, - unreleasedIndex, - section, - entry, - ); - - // Write updated changelog - const updatedChangelog = updatedLines.join("\n"); - writeFileSync(changelogPath, updatedChangelog); - - console.log(`✅ Updated CHANGELOG.md with PR #${prNumber}`); - - // Set outputs for the workflow to use - core.setOutput("changelog-updated", "true"); - core.setOutput("pr-number", prNumber); - core.setOutput("pr-title", cleanedTitle); - core.setOutput("pr-author", prAuthor); - } catch (error) { - console.error("❌ Error updating changelog:", error); - core.setFailed(`Failed to update changelog: ${error.message}`); - } -} From 2ebd21730d96b9e4ceac28446cd2beba57097e61 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 17 Aug 2026 03:15:12 +0100 Subject: [PATCH 3/7] ci: add `context` and `github` params and change functions to `async`. - Added `context` and `github` params to: - The main `updateChangelog` function passing them from it's call in the CI step. - `buildEntry` function - `formatPRDescription` function. - The corresponding docblocks. - Refactored `buildEntry` function so the return statement isn't one long string. Split multiple sections into variables for ease. - Changed `formatPRDescription` and `buildEntry` functions to be `async`, and their function calls now `await` them. --- .github/scripts/update-changelog.mjs | 25 +++++++++++++++++-------- .github/workflows/changelog-ci.yml | 4 +++- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/.github/scripts/update-changelog.mjs b/.github/scripts/update-changelog.mjs index c49137b..62be6bd 100644 --- a/.github/scripts/update-changelog.mjs +++ b/.github/scripts/update-changelog.mjs @@ -70,8 +70,10 @@ const COMMIT_TYPE_REGEX = new RegExp(`^(${INCLUDED_TYPES.join("|")})(\\(.+?\\))? * @param {object} params An object containing the parameters for the function * @param {object} params.pr Pull request object from GitHub context * @param {import('@actions/core')} params.core GitHub Actions core module + * @param {import('@actions/github-script').AsyncFunctionArguments["context"]} params.context GitHub Actions context + * @param {import('@actions/github-script').AsyncFunctionArguments["github"]} params.github Octokit instance */ -export default async function updateChangelog({pr, core}) { +export default async function updateChangelog({pr, core, context, github}) { try { const prNumber = pr.number; const prTitle = pr.title; @@ -112,7 +114,7 @@ export default async function updateChangelog({pr, core}) { // Format PR entry with cleaned title const cleanedTitle = cleanTitle(prTitle); - const entry = buildEntry(type, section, cleanedTitle, prNumber, prUrl, prAuthor, prBody); + const entry = await buildEntry(type, section, cleanedTitle, prNumber, prUrl, prAuthor, prBody, context, github); // Add entry to the appropriate section const updatedLines = addEntryToSection(lines, unreleasedIndex, section, entry); @@ -235,13 +237,19 @@ function cleanTitle(title) { * @param {string} prUrl - PR HTML URL * @param {string} prAuthor - PR author login * @param {string|null} prBody - PR body/description - * @returns {string} - Formatted entry line + * @param {import('@actions/github-script').AsyncFunctionArguments["context"]} context GitHub Actions context + * @param {import('@actions/github-script').AsyncFunctionArguments["github"]} github Octokit instance + * @returns {Promise} Formatted entry line */ -function buildEntry(type, section, cleanedTitle, prNumber, prUrl, prAuthor, prBody) { +async function buildEntry(type, section, cleanedTitle, prNumber, prUrl, prAuthor, prBody, context, github) { const prefix = TYPE_TO_PREFIX[type] ?? section; const dedupedTitle = removeLeadingDuplicateVerb(prefix, cleanedTitle); const titlePart = dedupedTitle ? ` ${dedupedTitle}` : ` ${cleanedTitle.trim()}`; - return `- ${prefix}${titlePart} ([#${prNumber}](${prUrl})) by @${prAuthor}${formatPRDescription(prBody)}\n`; + const description = await formatPRDescription(prBody, context, github); + const prLink = `([#${prNumber}](${prUrl}))`; + const entryEnd = `\n`; + + return `- ${prefix}${titlePart} ${prLink} by @${prAuthor}${description}${entryEnd}`; } /** @@ -263,10 +271,11 @@ function removeLeadingDuplicateVerb(prefix, title) { /** * Formats the PR description with indentation for nesting under a list item - * @param {string|null} prBody - PR description/body text - * @returns {string} - Formatted description string (empty if no body) + * @param {import('@actions/github-script').AsyncFunctionArguments["context"]} context GitHub Actions context + * @param {import('@actions/github-script').AsyncFunctionArguments["github"]} github Octokit instance + * @returns {Promise} Formatted description string (empty if no body) */ -function formatPRDescription(prBody) { +async function formatPRDescription(prBody, context, github) { if (!prBody || prBody.trim() === "") { return ""; } diff --git a/.github/workflows/changelog-ci.yml b/.github/workflows/changelog-ci.yml index 6c2057b..42cf4a1 100644 --- a/.github/workflows/changelog-ci.yml +++ b/.github/workflows/changelog-ci.yml @@ -166,7 +166,9 @@ jobs: const pr = JSON.parse(Buffer.from(process.env.RESOLVED_PR_BASE64, 'base64').toString('utf8')); return await updateChangelog({ pr, - core + core, + context, + github }); env: RESOLVED_PR_BASE64: "${{ steps.resolve-pr.outputs.pr-json-base64 }}" From 33e1f390323caf326444b342afbeb2fc5a4d7843 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 17 Aug 2026 03:20:16 +0100 Subject: [PATCH 4/7] ci: update docblocks of the update changelog CI script. - Remove the `-` in the params description because it's rendered as a bullet point in vscode intellisense. - Updated the return type of `findOrCreateUnreleased` function. --- .github/scripts/update-changelog.mjs | 59 ++++++++++++++++------------ 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/.github/scripts/update-changelog.mjs b/.github/scripts/update-changelog.mjs index 62be6bd..94ae6ff 100644 --- a/.github/scripts/update-changelog.mjs +++ b/.github/scripts/update-changelog.mjs @@ -138,8 +138,9 @@ export default async function updateChangelog({pr, core, context, github}) { /** * Extracts the conventional commit type from a PR title - * @param {string} title - PR title - * @returns {string|null} - The type or null if not found + * + * @param {string} title PR title + * @returns {string|null} The type or null if not found */ function extractType(title) { const match = title.match(COMMIT_TYPE_REGEX); @@ -148,8 +149,9 @@ function extractType(title) { /** * Gets or creates the Unreleased section in the changelog - * @param {string} changelog - Current changelog content - * @returns {object} - { hasUnreleased, lines, unreleasedIndex } + * + * @param {string} changelog Current changelog content + * @returns {{ hasUnreleased: boolean, lines: string[], unreleasedIndex: number }} */ function findOrCreateUnreleased(changelog) { const lines = changelog.split("\n"); @@ -189,10 +191,11 @@ function findOrCreateUnreleased(changelog) { /** * Checks if a PR entry already exists in the Unreleased section - * @param {array} lines - Changelog lines - * @param {number} unreleasedIndex - Index of Unreleased header - * @param {number} prNumber - PR number to check - * @returns {boolean} - True if PR already exists + * + * @param {array} lines Changelog lines + * @param {number} unreleasedIndex Index of Unreleased header + * @param {number} prNumber PR number to check + * @returns {boolean} True if PR already exists */ function isDuplicateEntry(lines, unreleasedIndex, prNumber) { // Find the next version header (##) or end of file @@ -217,8 +220,9 @@ function isDuplicateEntry(lines, unreleasedIndex, prNumber) { /** * Strips the conventional commit type prefix from a PR title - * @param {string} title - PR title - * @returns {string} - Cleaned title + * + * @param {string} title PR title + * @returns {string} Cleaned title */ function cleanTitle(title) { // Remove the type prefix (e.g., "feat: ", "fix(scope): ") @@ -230,13 +234,14 @@ function cleanTitle(title) { /** * Builds the full changelog entry line for a PR - * @param {string} type - Conventional commit type (e.g., "feat", "fix", "revert") - * @param {string} section - Section name resolved from TYPE_TO_SECTION - * @param {string} cleanedTitle - PR title with the type prefix stripped - * @param {number} prNumber - PR number - * @param {string} prUrl - PR HTML URL - * @param {string} prAuthor - PR author login - * @param {string|null} prBody - PR body/description + * + * @param {string} type Conventional commit type (e.g., "feat", "fix", "revert") + * @param {string} section Section name resolved from TYPE_TO_SECTION + * @param {string} cleanedTitle PR title with the type prefix stripped + * @param {number} prNumber PR number + * @param {string} prUrl PR HTML URL + * @param {string} prAuthor PR author login + * @param {string|null} prBody PR body/description * @param {import('@actions/github-script').AsyncFunctionArguments["context"]} context GitHub Actions context * @param {import('@actions/github-script').AsyncFunctionArguments["github"]} github Octokit instance * @returns {Promise} Formatted entry line @@ -255,9 +260,10 @@ async function buildEntry(type, section, cleanedTitle, prNumber, prUrl, prAuthor /** * Removes duplicated leading verbs based on the resolved changelog prefix. * Example: prefix "Added" + title "added support for x" => "support for x" - * @param {string} prefix - Resolved changelog entry prefix - * @param {string} title - Cleaned PR title - * @returns {string} - Title without duplicated leading verb + * + * @param {string} prefix Resolved changelog entry prefix + * @param {string} title Cleaned PR title + * @returns {string} Title without duplicated leading verb */ function removeLeadingDuplicateVerb(prefix, title) { const trimmedTitle = title.trim(); @@ -271,6 +277,8 @@ function removeLeadingDuplicateVerb(prefix, title) { /** * Formats the PR description with indentation for nesting under a list item + * + * @param {string|null} prBody PR description/body text * @param {import('@actions/github-script').AsyncFunctionArguments["context"]} context GitHub Actions context * @param {import('@actions/github-script').AsyncFunctionArguments["github"]} github Octokit instance * @returns {Promise} Formatted description string (empty if no body) @@ -298,11 +306,12 @@ async function formatPRDescription(prBody, context, github) { /** * Adds a PR entry to the appropriate section within Unreleased - * @param {array} lines - Changelog lines - * @param {number} unreleasedIndex - Index of Unreleased header - * @param {string} section - Section name (Added, Fixed, etc.) - * @param {string} entry - PR entry to add - * @returns {array} - Updated lines + * + * @param {array} lines Changelog lines + * @param {number} unreleasedIndex Index of Unreleased header + * @param {string} section Section name (Added, Fixed, etc.) + * @param {string} entry PR entry to add + * @returns {array} Updated lines */ function addEntryToSection(lines, unreleasedIndex, section, entry) { // Find the next version header (##) or end of file From 07a1de1fde9f869838b164cb1d7bf4f231e03b34 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 17 Aug 2026 04:30:04 +0100 Subject: [PATCH 5/7] build: added dev dependencies to allow prettier to wrap arrays onto multi-lines. - Added the `prettier-plugin-multiline-arrays` dev dependency to force prettier to wrap arrays onto muliple lines. - Added the prettier dev dependency because the prettier vscode extension only supports prettier plugins when installed locally in a project. - Specified the prettier plugin and it's `multilineArraysWrapThreshold` option in the prettierrc.json file to enable the usage of the plugin. --- .prettierrc.json | 4 ++++ package.json | 2 ++ 2 files changed, 6 insertions(+) diff --git a/.prettierrc.json b/.prettierrc.json index be0684a..83bd5a6 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -5,6 +5,7 @@ "printWidth": 150, "bracketSameLine": true, "bracketSpacing": false, + "multilineArraysWrapThreshold": 4, "overrides": [ { "files": [ @@ -34,5 +35,8 @@ "trailingComma": "es5" } } + ], + "plugins": [ + "prettier-plugin-multiline-arrays" ] } diff --git a/package.json b/package.json index 61c069d..b0e82fa 100644 --- a/package.json +++ b/package.json @@ -138,6 +138,8 @@ "@types/node": "^22.9.0", "@types/vscode": "^1.110", "mocha": "^10.8.2", + "prettier": "^3.9.6", + "prettier-plugin-multiline-arrays": "^4.1.11", "typescript": "^5.7" }, "dependencies": { From 8cf4cf1cee9796bbb674c22f2bd75fd05da1c248 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 17 Aug 2026 04:37:15 +0100 Subject: [PATCH 6/7] style: auto formatting --- .github/scripts/check-changelog-exclusions.mjs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/.github/scripts/check-changelog-exclusions.mjs b/.github/scripts/check-changelog-exclusions.mjs index 0ef2400..fcb6623 100644 --- a/.github/scripts/check-changelog-exclusions.mjs +++ b/.github/scripts/check-changelog-exclusions.mjs @@ -3,7 +3,7 @@ * Used by the changelog-ci workflow to determine early if processing should continue */ -import { INCLUDED_TYPES } from "./update-changelog.mjs"; +import {INCLUDED_TYPES} from "./update-changelog.mjs"; /** * Labels that should exclude PRs from the changelog @@ -40,10 +40,7 @@ const ALL_COMMIT_TYPES = [...INCLUDED_TYPES, ...EXCLUDED_TYPES]; * Regex to match conventional commit type prefix in PR titles, * including both included and excluded types. */ -const typeRegex = new RegExp( - `^(${ALL_COMMIT_TYPES.join("|")})(\\(.+?\\))?!?:`, - "i", -); +const typeRegex = new RegExp(`^(${ALL_COMMIT_TYPES.join("|")})(\\(.+?\\))?!?:`, "i"); /** * Checks if the PR has any labels that are in the EXCLUDED_LABELS list. @@ -72,7 +69,7 @@ function getExcludedLabel(labels) { /** * Checks if a PR should be excluded from the changelog */ -export default async function checkExclusions({ pr, core }) { +export default async function checkExclusions({pr, core}) { try { const prTitle = pr.title; @@ -96,9 +93,7 @@ export default async function checkExclusions({ pr, core }) { // If no conventional commit type is found, skip the PR. if (!match) { - console.log( - "âš ī¸ No conventional commit type found in PR title. Should skip.", - ); + console.log("âš ī¸ No conventional commit type found in PR title. Should skip."); shouldSkip = true; skipReason = "no conventional commit type"; } @@ -110,9 +105,7 @@ export default async function checkExclusions({ pr, core }) { // If the commit type is in the EXCLUDED_TYPES list, skip the PR. if (EXCLUDED_TYPES.includes(type)) { - console.log( - `âš ī¸ Conventional commit type "${type}" is excluded. Should skip.`, - ); + console.log(`âš ī¸ Conventional commit type "${type}" is excluded. Should skip.`); shouldSkip = true; skipReason = `excluded type: ${type}`; } From 0e3280d42ceca6dd9a3145bf51571a3201c53db4 Mon Sep 17 00:00:00 2001 From: yCodeTech Date: Mon, 17 Aug 2026 04:38:05 +0100 Subject: [PATCH 7/7] style: re-ordered functions in the check-changelog-exclusions CI script for readability --- .../scripts/check-changelog-exclusions.mjs | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/.github/scripts/check-changelog-exclusions.mjs b/.github/scripts/check-changelog-exclusions.mjs index fcb6623..1c43c38 100644 --- a/.github/scripts/check-changelog-exclusions.mjs +++ b/.github/scripts/check-changelog-exclusions.mjs @@ -42,30 +42,6 @@ const ALL_COMMIT_TYPES = [...INCLUDED_TYPES, ...EXCLUDED_TYPES]; */ const typeRegex = new RegExp(`^(${ALL_COMMIT_TYPES.join("|")})(\\(.+?\\))?!?:`, "i"); -/** - * Checks if the PR has any labels that are in the EXCLUDED_LABELS list. - * The PR should be excluded from the changelog update process if any excluded label is found. - * - * @param {string[]} labels - Array of PR labels - * @returns {boolean} - True if any label is excluded - */ -function hasExcludedLabel(labels) { - return labels.some((label) => - EXCLUDED_LABELS.includes(label.name.toLowerCase()), - ); -} - -/** - * Gets the name of the excluded label, if any. - * @param {string[]} labels - Array of PR labels - * @returns {string|undefined} - The name of the excluded label, if any - */ -function getExcludedLabel(labels) { - return labels.find((label) => - EXCLUDED_LABELS.includes(label.name.toLowerCase()), - )?.name; -} - /** * Checks if a PR should be excluded from the changelog */ @@ -128,3 +104,23 @@ export default async function checkExclusions({pr, core}) { core.setFailed(`Failed to check exclusions: ${error.message}`); } } + +/** + * Checks if the PR has any labels that are in the EXCLUDED_LABELS list. + * The PR should be excluded from the changelog update process if any excluded label is found. + * + * @param {string[]} labels - Array of PR labels + * @returns {boolean} - True if any label is excluded + */ +function hasExcludedLabel(labels) { + return labels.some((label) => EXCLUDED_LABELS.includes(label.name.toLowerCase())); +} + +/** + * Gets the name of the excluded label, if any. + * @param {string[]} labels - Array of PR labels + * @returns {string|undefined} - The name of the excluded label, if any + */ +function getExcludedLabel(labels) { + return labels.find((label) => EXCLUDED_LABELS.includes(label.name.toLowerCase()))?.name; +}