From 3cee3bd92ddbc4e44f1f720b815804e4a611c263 Mon Sep 17 00:00:00 2001 From: UnschooledGamer <76094069+UnschooledGamer@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:00:02 +0530 Subject: [PATCH 1/6] feat: Add translation check workflow for pull requests * Introduced a new GitHub Actions workflow to check for changes in translation files & a new workflow for labeling based upon the changed files on pull requests. * The workflow detects changes in JSON files located in the src/lang directory and runs a lint check if any changes are found. * Added a Workflow to label Pull requests based on their changed files. --- .github/labeler.yml | 13 ++++++++++++ .github/workflows/add-pr-labels.yml | 17 ++++++++++++++++ .github/workflows/ci.yml | 31 +++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/add-pr-labels.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000..848bd7a5c --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,13 @@ +translations: + - any: + - changed-files: + - any-glob-to-any-file: 'src/lang/*.json' + +docs: + - any: + - changed-files: + - any-glob-to-any-file: '**/*.md' + +enhancement: + - any: + - head-branch: ['^feature', 'feature', '^feat', '^add'] \ No newline at end of file diff --git a/.github/workflows/add-pr-labels.yml b/.github/workflows/add-pr-labels.yml new file mode 100644 index 000000000..f9b979fbb --- /dev/null +++ b/.github/workflows/add-pr-labels.yml @@ -0,0 +1,17 @@ +name: Add Pull Requests Labels +on: + pull_request_target: + types: [opened] + +jobs: + add-labels: + timeout-minutes: 5 + runs-on: ubuntu-latest + + if: github.event.action == 'opened' + permissions: + contents: read + pull-requests: write + + steps: + - uses: actions/labeler@v6 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 788d228c4..0ae6e0907 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,3 +33,34 @@ jobs: - name: Run Biome run: biome ci . + + translation-check: + name: Translation Check (On PR Only) + timeout-minutes: 5 + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + if: github.event_name == 'pull_request' + steps: + - name: Checkout Repository + uses: actions/checkout@v5 + - name: Use Node.js + uses: actions/setup-node@v5 + with: + cache: npm + cache-dependency-path: '**/package-lock.json' + + - name: Detect Changed Files + uses: dorny/paths-filter@v3 + id: file-changes + with: + list-files: shell + filters: | + translation: + - 'src/lang/*.json' + token: ${{ secrets.GITHUB_TOKEN }} + - name: Translation Files Check (if changed) + if: steps.file-changes.outputs.translation == 'true' + run: | + npm run lint check \ No newline at end of file From ca4a2adf91abab8ad89777fa9bb609ab4bd2ce5e Mon Sep 17 00:00:00 2001 From: UnschooledGamer <76094069+UnschooledGamer@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:53:06 +0530 Subject: [PATCH 2/6] update: enhance nightly build workflow by adding a step to generate release notes and updating conditions for releasing nightly versions. The new step checks for the existence of a nightly tag and captures release notes if required. --- .github/workflows/nightly-build.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 70af04d54..e9e3c520d 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -186,6 +186,7 @@ - name: Check Nightly Tag and Force Update #if: github.event_name == 'push' && contains(github.event.ref, 'tags/nightly') == false if: ${{ ! inputs.skip_tagging_and_releases }} + id: check-nightly-tag-force-update run: | # Check if the nightly tag exists and get the commit it points to if git show-ref --quiet refs/tags/nightly; then @@ -208,10 +209,18 @@ echo "Nightly tag already points to this commit. Skipping update." fi + + - name: Generate Release Notes (Experimental) + if: ${{ success() && env.releaseRequired == 'true' }} + id: gen-release-notes + continue-on-error: true + run: | + RELEASE_NOTES=$(node utils/scripts/generate-release-notes.js ${{ github.repository_owner }} Acode ${{ github.sha }} --format md --from-tag ${{ env.TAG_COMMIT }} --important-only --quiet --changelog-only) + echo "RELEASE_NOTES=$RELEASE_NOTES" >> $GITHUB_ENV - name: Release Nightly Version # Only run this step, if not called from another workflow. And a previous step is successful with releasedRequired=true id: release - if: ${{ ! inputs.skip_tagging_and_releases && success() && env.releaseRequired == 'true' }} + if: ${{ ! inputs.skip_tagging_and_releases && steps.check-nightly-tag-force-update.outcome == 'success' && env.releaseRequired == 'true' }} uses: softprops/action-gh-release@v2 with: prerelease: true @@ -224,6 +233,8 @@ [Compare Changes](https://github.com/${{ github.repository }}/compare/${{ env.TAG_COMMIT }}...${{ github.sha }}) + ${{ env.RELEASE_NOTES }} + - name: Update Last Comment by bot (If ran in PR) if: inputs.is_PR uses: marocchino/sticky-pull-request-comment@v2 From c11cc85ce89f32e74f6c3de8f4a4f1b8b9bb9acf Mon Sep 17 00:00:00 2001 From: UnschooledGamer <76094069+UnschooledGamer@users.noreply.github.com> Date: Tue, 16 Dec 2025 12:20:33 +0530 Subject: [PATCH 3/6] feat: enhance release notification workflow by adding a required release body input and integrating it into the Discord webhook message. Introduced a new script for generating release notes based on commit history. --- .../workflows/community-release-notifier.yml | 6 + .github/workflows/nightly-build.yml | 2 + utils/scripts/generate-release-notes.js | 218 ++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 utils/scripts/generate-release-notes.js diff --git a/.github/workflows/community-release-notifier.yml b/.github/workflows/community-release-notifier.yml index 9b418a1b2..cc91e9ae0 100644 --- a/.github/workflows/community-release-notifier.yml +++ b/.github/workflows/community-release-notifier.yml @@ -12,6 +12,11 @@ on: required: true description: "release URL" type: 'string' + body: + required: true + description: "Release Body" + type: 'string' + default: '' secrets: DISCORD_WEBHOOK_RELEASE_NOTES: description: 'Discord Webhook for Notifying Releases to Discord' @@ -30,6 +35,7 @@ jobs: stringToTruncate: | ๐Ÿ“ข Acode [${{ github.event.release.tag_name || inputs.tag_name }}](<${{ github.event.release.url || inputs.url }}>) was just Released ๐ŸŽ‰! + ${{ github.event.release.body || inputs.body }} - name: Discord Webhook Action (Publishing) uses: tsickert/discord-webhook@c840d45a03a323fbc3f7507ac7769dbd91bfb164 # v5.3.0 diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index e9e3c520d..091472136 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -65,6 +65,7 @@ outputs: release_output_url: ${{ steps.release.outputs.url }} updated_version: ${{ steps.update-version.outputs.UPDATED_VERSION}} + RELEASE_NOTES: ${{ env.RELEASE_NOTES }} steps: - name: Fast Fail if secrets are missing if: ${{ env.KEYSTORE_CONTENT == '' || env.BUILD_JSON_CONTENT == '' }} @@ -256,5 +257,6 @@ with: tag_name: ${{ needs.build.outputs.updated_version }} url: ${{ needs.build.outputs.release_output_url }} + body: ${{ needs.build.outputs.RELEASE_NOTES }} secrets: DISCORD_WEBHOOK_RELEASE_NOTES: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }} diff --git a/utils/scripts/generate-release-notes.js b/utils/scripts/generate-release-notes.js new file mode 100644 index 000000000..662461e62 --- /dev/null +++ b/utils/scripts/generate-release-notes.js @@ -0,0 +1,218 @@ +#!/usr/bin/env node +/** + * โœจ @ UnschooledGamer (baked With AI, Modified by @ UnschooledGamer) ~ 2025. + * + * GitHub Release Notes Generator + * + * Features: + * - Auto categorizes commits by type + * - Optional compact "plain" output to save space + * - Option to include only important tags (feat, fix, refactor, perf) + * - Option to use only merge commits + * + * Usage: + * GITHUB_TOKEN= node generate-release-notes.js [options] + * + * Options: + * --plain Output minimal Markdown (no emojis, compact) + * --important-only Include only features, fixes, refactors, and perf + * --merge-only Include only merge commits + * --help Show usage + * --format [md/json] Output Format + * --fromTag v1.11.0 The From/Previous Tag + * --quiet Suppress output to stdout + */ + +const args = process.argv.slice(2); + +function getArgValue(flag) { + const idx = args.indexOf(flag); + return idx !== -1 && !args[idx + 1].startsWith("-") ? args[idx + 1] : null; +} + +if (args.includes("--help") || args.length < 3) { + console.log(` +Usage: GITHUB_TOKEN= node generate-release-notes.js [options] +โœจ @ UnschooledGamer (baked With AI, Modified by @ UnschooledGamer) ~ 2025 + +Options: + --plain Compact, no emojis (saves space) + --important-only Only include Features, Fixes, Refactors, Perf + --merge-only Include only merge commits + --help Show this help message + --format [md/json] Output Format + --fromTag v1.11.0 The From/Previous Tag + --quiet Suppress output to stdout +`); + process.exit(0); +} + +const [owner, repo, currentTag, previousTagArg] = args; +const token = process.env.GITHUB_TOKEN; +if (!token) { + console.error("โŒ Missing GITHUB_TOKEN environment variable."); + process.exit(1); +} + +const flags = { + plain: args.includes("--plain"), + importantOnly: args.includes("--important-only"), + mergeOnly: args.includes("--merge-only"), + quiet: args.includes("--quiet") || args.includes("--stdout-only"), + format: getArgValue("--format") || "md", + fromTag: getArgValue("--from-tag"), + changelogOnly: args.includes("--changelog-only"), +}; + +function log(...msg) { + if (!flags.quiet) console.error(...msg); +} + +const headers = { + "Authorization": `token ${token}`, + "Accept": "application/vnd.github+json", + "User-Agent": "release-notes-script" +}; + +async function getPreviousTag() { + const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/tags`, { headers }); + const tags = await res.json(); + if (!Array.isArray(tags) || tags.length < 2) return null; + return tags[1].name; +} + +async function getCommits(previousTag, currentTag) { + const url = `https://api.github.com/repos/${owner}/${repo}/compare/${previousTag}...${currentTag}`; + const res = await fetch(url, { headers }); + if (!res.ok) throw new Error(`Failed to fetch commits: ${res.status}`); + const data = await res.json(); + return data.commits || []; +} + +function categorizeCommits(commits, { mergeOnly, importantOnly }) { + const sections = { + feat: [], + fix: [], + perf: [], + refactor: [], + docs: [], + chore: [], + test: [], + add: [], + revert: [], + update: [], + other: [], + }; + + for (const c of commits) { + const msg = c.commit.message.split("\n")[0]; + const isMerge = msg.startsWith("Merge pull request") || msg.startsWith("Merge branch"); + + if (mergeOnly && !isMerge) continue; + + const type = Object.keys(sections).find(k => msg.toLowerCase().startsWith(`${k}:`) || msg.toLowerCase().startsWith(`${k} `)) || "other"; + + if (importantOnly && !["feat", "fix", "refactor", "perf", "add", "revert", "update"].includes(type)) continue; + + const author = c.author?.login + ? `[${c.author.login}](https://github.com/${c.author.login})` + : "unknown"; + + const entry = `- ${msg} (${c.sha.slice(0, 7)}) by ${author}`; + sections[type].push(entry); + } + + return sections; +} + +const emojis = { + feat: flags.plain ? "" : "โœจ ", + fix: flags.plain ? "" : "๐Ÿž ", + perf: flags.plain ? "" : "โšก ", + refactor: flags.plain ? "" : "๐Ÿ”ง ", + docs: flags.plain ? "" : "๐Ÿ“ ", + chore: flags.plain ? "" : "๐Ÿงน ", + test: flags.plain ? "" : "๐Ÿงช ", + other: flags.plain ? "" : "๐Ÿ“ฆ ", + revert: flags.plain ? "" : "โช ", + add: flags.plain ? "" : "โž• ", + update: flags.plain ? "" : "๐Ÿ”„ ", +}; + +function formatMarkdown(tag, prevTag, sections, { plain }) { + + const lines = [ + flags.changelogOnly ? "" : `Changes since [${prevTag}](https://github.com/${owner}/${repo}/releases/tag/${prevTag})`, + "", + ]; + + for (const [type, list] of Object.entries(sections)) { + if (list.length === 0) continue; + const header = plain ? `## ${type}` : `## ${emojis[type]}${type[0].toUpperCase() + type.slice(1)}`; + lines.push(header, "", list.join("\n"), ""); + } + + // Compact single-line mode for super small output + // if (plain) { + // const compact = Object.entries(sections) + // .filter(([_, list]) => list.length) + // .map(([type, list]) => `${type.toUpperCase()}: ${list.length} commits`) + // .join(" | "); + // lines.push(`\n_Summary: ${compact}_`); + // } + + return lines.join("\n"); +} + +function formatJSON(tag, prevTag, sections, plain = true) { + + const lines = [ + "", + flags.changelogOnly ? "" : `Changes since [${prevTag}](https://github.com/${owner}/${repo}/releases/tag/${prevTag})`, + "", + ]; + + // todo: split into function + for (const [type, list] of Object.entries(sections)) { + if (list.length === 0) continue; + const header = plain ? `## ${type}` : `## ${emojis[type]}${type[0].toUpperCase() + type.slice(1)}`; + lines.push(header, "", list.join("\n"), ""); + } + return JSON.stringify({ + release: tag, + previous: prevTag, + sections: Object.fromEntries( + Object.entries(sections).filter(([_, v]) => v.length) + ), + notes: lines.join("\n") + }, null, 2); +} + + +async function main() { + log(`๐Ÿ” Generating release notes for ${owner}/${repo} @ ${currentTag}...`); + + const prevTag = flags.fromTag || await getPreviousTag(); + if (!prevTag) { + console.error("No previous tag found. Use --from-tag to specify one."); + process.exit(1); + } + + const commits = await getCommits(prevTag, currentTag); + if(!commits.length) { + console.error("No commits found."); + process.exit(1); + } + const categorized = categorizeCommits(commits, flags); + let output; + + if (flags.format === "json") { + output = formatJSON(currentTag, prevTag, categorized); + } else { + output = formatMarkdown(currentTag, prevTag, categorized, flags); + } + + process.stdout.write(output + "\n"); +} + +main().catch(err => console.error(err)); From 2221f1afaae51ae1e18332657c79b93dd6f6adf5 Mon Sep 17 00:00:00 2001 From: UnschooledGamer <76094069+UnschooledGamer@users.noreply.github.com> Date: Tue, 16 Dec 2025 12:32:12 +0530 Subject: [PATCH 4/6] fix: multi-lines for the Release notes CI --- .github/workflows/nightly-build.yml | 6 +++++- utils/scripts/generate-release-notes.js | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 091472136..4474c66aa 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -217,7 +217,11 @@ continue-on-error: true run: | RELEASE_NOTES=$(node utils/scripts/generate-release-notes.js ${{ github.repository_owner }} Acode ${{ github.sha }} --format md --from-tag ${{ env.TAG_COMMIT }} --important-only --quiet --changelog-only) - echo "RELEASE_NOTES=$RELEASE_NOTES" >> $GITHUB_ENV + { + echo "RELEASE_NOTES<> $GITHUB_ENV - name: Release Nightly Version # Only run this step, if not called from another workflow. And a previous step is successful with releasedRequired=true id: release diff --git a/utils/scripts/generate-release-notes.js b/utils/scripts/generate-release-notes.js index 662461e62..d9176a891 100644 --- a/utils/scripts/generate-release-notes.js +++ b/utils/scripts/generate-release-notes.js @@ -27,9 +27,8 @@ const args = process.argv.slice(2); function getArgValue(flag) { const idx = args.indexOf(flag); - return idx !== -1 && !args[idx + 1].startsWith("-") ? args[idx + 1] : null; + return idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith("-") ? args[idx + 1] : null; } - if (args.includes("--help") || args.length < 3) { console.log(` Usage: GITHUB_TOKEN= node generate-release-notes.js [options] @@ -41,8 +40,10 @@ Options: --merge-only Include only merge commits --help Show this help message --format [md/json] Output Format - --fromTag v1.11.0 The From/Previous Tag + --from-tag v1.11.0 The From/Previous Tag --quiet Suppress output to stdout + --stdout-only Output to stdout only + --changelog-only Output changelog only `); process.exit(0); } From 10ced259ef5f937c2ef3bd98f5787ae5b0be40c2 Mon Sep 17 00:00:00 2001 From: UnschooledGamer <76094069+UnschooledGamer@users.noreply.github.com> Date: Tue, 16 Dec 2025 12:33:57 +0530 Subject: [PATCH 5/6] chore: fmt --- utils/scripts/generate-release-notes.js | 304 +++++++++++++----------- 1 file changed, 165 insertions(+), 139 deletions(-) diff --git a/utils/scripts/generate-release-notes.js b/utils/scripts/generate-release-notes.js index d9176a891..cb0c7a4a8 100644 --- a/utils/scripts/generate-release-notes.js +++ b/utils/scripts/generate-release-notes.js @@ -1,7 +1,7 @@ #!/usr/bin/env node /** * โœจ @ UnschooledGamer (baked With AI, Modified by @ UnschooledGamer) ~ 2025. - * + * * GitHub Release Notes Generator * * Features: @@ -26,11 +26,13 @@ const args = process.argv.slice(2); function getArgValue(flag) { - const idx = args.indexOf(flag); - return idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith("-") ? args[idx + 1] : null; + const idx = args.indexOf(flag); + return idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith("-") + ? args[idx + 1] + : null; } if (args.includes("--help") || args.length < 3) { - console.log(` + console.log(` Usage: GITHUB_TOKEN= node generate-release-notes.js [options] โœจ @ UnschooledGamer (baked With AI, Modified by @ UnschooledGamer) ~ 2025 @@ -45,175 +47,199 @@ Options: --stdout-only Output to stdout only --changelog-only Output changelog only `); - process.exit(0); + process.exit(0); } const [owner, repo, currentTag, previousTagArg] = args; const token = process.env.GITHUB_TOKEN; if (!token) { - console.error("โŒ Missing GITHUB_TOKEN environment variable."); - process.exit(1); + console.error("โŒ Missing GITHUB_TOKEN environment variable."); + process.exit(1); } const flags = { - plain: args.includes("--plain"), - importantOnly: args.includes("--important-only"), - mergeOnly: args.includes("--merge-only"), - quiet: args.includes("--quiet") || args.includes("--stdout-only"), - format: getArgValue("--format") || "md", - fromTag: getArgValue("--from-tag"), - changelogOnly: args.includes("--changelog-only"), + plain: args.includes("--plain"), + importantOnly: args.includes("--important-only"), + mergeOnly: args.includes("--merge-only"), + quiet: args.includes("--quiet") || args.includes("--stdout-only"), + format: getArgValue("--format") || "md", + fromTag: getArgValue("--from-tag"), + changelogOnly: args.includes("--changelog-only"), }; function log(...msg) { - if (!flags.quiet) console.error(...msg); + if (!flags.quiet) console.error(...msg); } const headers = { - "Authorization": `token ${token}`, - "Accept": "application/vnd.github+json", - "User-Agent": "release-notes-script" + Authorization: `token ${token}`, + Accept: "application/vnd.github+json", + "User-Agent": "release-notes-script", }; async function getPreviousTag() { - const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/tags`, { headers }); - const tags = await res.json(); - if (!Array.isArray(tags) || tags.length < 2) return null; - return tags[1].name; + const res = await fetch( + `https://api.github.com/repos/${owner}/${repo}/tags`, + { headers }, + ); + const tags = await res.json(); + if (!Array.isArray(tags) || tags.length < 2) return null; + return tags[1].name; } async function getCommits(previousTag, currentTag) { - const url = `https://api.github.com/repos/${owner}/${repo}/compare/${previousTag}...${currentTag}`; - const res = await fetch(url, { headers }); - if (!res.ok) throw new Error(`Failed to fetch commits: ${res.status}`); - const data = await res.json(); - return data.commits || []; + const url = `https://api.github.com/repos/${owner}/${repo}/compare/${previousTag}...${currentTag}`; + const res = await fetch(url, { headers }); + if (!res.ok) throw new Error(`Failed to fetch commits: ${res.status}`); + const data = await res.json(); + return data.commits || []; } function categorizeCommits(commits, { mergeOnly, importantOnly }) { - const sections = { - feat: [], - fix: [], - perf: [], - refactor: [], - docs: [], - chore: [], - test: [], - add: [], - revert: [], - update: [], - other: [], - }; - - for (const c of commits) { - const msg = c.commit.message.split("\n")[0]; - const isMerge = msg.startsWith("Merge pull request") || msg.startsWith("Merge branch"); - - if (mergeOnly && !isMerge) continue; - - const type = Object.keys(sections).find(k => msg.toLowerCase().startsWith(`${k}:`) || msg.toLowerCase().startsWith(`${k} `)) || "other"; - - if (importantOnly && !["feat", "fix", "refactor", "perf", "add", "revert", "update"].includes(type)) continue; - - const author = c.author?.login - ? `[${c.author.login}](https://github.com/${c.author.login})` - : "unknown"; - - const entry = `- ${msg} (${c.sha.slice(0, 7)}) by ${author}`; - sections[type].push(entry); - } - - return sections; + const sections = { + feat: [], + fix: [], + perf: [], + refactor: [], + docs: [], + chore: [], + test: [], + add: [], + revert: [], + update: [], + other: [], + }; + + for (const c of commits) { + const msg = c.commit.message.split("\n")[0]; + const isMerge = + msg.startsWith("Merge pull request") || msg.startsWith("Merge branch"); + + if (mergeOnly && !isMerge) continue; + + const type = + Object.keys(sections).find( + (k) => + msg.toLowerCase().startsWith(`${k}:`) || + msg.toLowerCase().startsWith(`${k} `), + ) || "other"; + + if ( + importantOnly && + !["feat", "fix", "refactor", "perf", "add", "revert", "update"].includes( + type, + ) + ) + continue; + + const author = c.author?.login + ? `[${c.author.login}](https://github.com/${c.author.login})` + : "unknown"; + + const entry = `- ${msg} (${c.sha.slice(0, 7)}) by ${author}`; + sections[type].push(entry); + } + + return sections; } const emojis = { - feat: flags.plain ? "" : "โœจ ", - fix: flags.plain ? "" : "๐Ÿž ", - perf: flags.plain ? "" : "โšก ", - refactor: flags.plain ? "" : "๐Ÿ”ง ", - docs: flags.plain ? "" : "๐Ÿ“ ", - chore: flags.plain ? "" : "๐Ÿงน ", - test: flags.plain ? "" : "๐Ÿงช ", - other: flags.plain ? "" : "๐Ÿ“ฆ ", - revert: flags.plain ? "" : "โช ", - add: flags.plain ? "" : "โž• ", - update: flags.plain ? "" : "๐Ÿ”„ ", + feat: flags.plain ? "" : "โœจ ", + fix: flags.plain ? "" : "๐Ÿž ", + perf: flags.plain ? "" : "โšก ", + refactor: flags.plain ? "" : "๐Ÿ”ง ", + docs: flags.plain ? "" : "๐Ÿ“ ", + chore: flags.plain ? "" : "๐Ÿงน ", + test: flags.plain ? "" : "๐Ÿงช ", + other: flags.plain ? "" : "๐Ÿ“ฆ ", + revert: flags.plain ? "" : "โช ", + add: flags.plain ? "" : "โž• ", + update: flags.plain ? "" : "๐Ÿ”„ ", }; function formatMarkdown(tag, prevTag, sections, { plain }) { - - const lines = [ - flags.changelogOnly ? "" : `Changes since [${prevTag}](https://github.com/${owner}/${repo}/releases/tag/${prevTag})`, - "", - ]; - - for (const [type, list] of Object.entries(sections)) { - if (list.length === 0) continue; - const header = plain ? `## ${type}` : `## ${emojis[type]}${type[0].toUpperCase() + type.slice(1)}`; - lines.push(header, "", list.join("\n"), ""); - } - - // Compact single-line mode for super small output - // if (plain) { - // const compact = Object.entries(sections) - // .filter(([_, list]) => list.length) - // .map(([type, list]) => `${type.toUpperCase()}: ${list.length} commits`) - // .join(" | "); - // lines.push(`\n_Summary: ${compact}_`); - // } - - return lines.join("\n"); + const lines = [ + flags.changelogOnly + ? "" + : `Changes since [${prevTag}](https://github.com/${owner}/${repo}/releases/tag/${prevTag})`, + "", + ]; + + for (const [type, list] of Object.entries(sections)) { + if (list.length === 0) continue; + const header = plain + ? `## ${type}` + : `## ${emojis[type]}${type[0].toUpperCase() + type.slice(1)}`; + lines.push(header, "", list.join("\n"), ""); + } + + // Compact single-line mode for super small output + // if (plain) { + // const compact = Object.entries(sections) + // .filter(([_, list]) => list.length) + // .map(([type, list]) => `${type.toUpperCase()}: ${list.length} commits`) + // .join(" | "); + // lines.push(`\n_Summary: ${compact}_`); + // } + + return lines.join("\n"); } function formatJSON(tag, prevTag, sections, plain = true) { - - const lines = [ - "", - flags.changelogOnly ? "" : `Changes since [${prevTag}](https://github.com/${owner}/${repo}/releases/tag/${prevTag})`, - "", - ]; - - // todo: split into function - for (const [type, list] of Object.entries(sections)) { - if (list.length === 0) continue; - const header = plain ? `## ${type}` : `## ${emojis[type]}${type[0].toUpperCase() + type.slice(1)}`; - lines.push(header, "", list.join("\n"), ""); - } - return JSON.stringify({ - release: tag, - previous: prevTag, - sections: Object.fromEntries( - Object.entries(sections).filter(([_, v]) => v.length) - ), - notes: lines.join("\n") - }, null, 2); + const lines = [ + "", + flags.changelogOnly + ? "" + : `Changes since [${prevTag}](https://github.com/${owner}/${repo}/releases/tag/${prevTag})`, + "", + ]; + + // todo: split into function + for (const [type, list] of Object.entries(sections)) { + if (list.length === 0) continue; + const header = plain + ? `## ${type}` + : `## ${emojis[type]}${type[0].toUpperCase() + type.slice(1)}`; + lines.push(header, "", list.join("\n"), ""); + } + return JSON.stringify( + { + release: tag, + previous: prevTag, + sections: Object.fromEntries( + Object.entries(sections).filter(([_, v]) => v.length), + ), + notes: lines.join("\n"), + }, + null, + 2, + ); } - async function main() { - log(`๐Ÿ” Generating release notes for ${owner}/${repo} @ ${currentTag}...`); - - const prevTag = flags.fromTag || await getPreviousTag(); - if (!prevTag) { - console.error("No previous tag found. Use --from-tag to specify one."); - process.exit(1); - } - - const commits = await getCommits(prevTag, currentTag); - if(!commits.length) { - console.error("No commits found."); - process.exit(1); - } - const categorized = categorizeCommits(commits, flags); - let output; - - if (flags.format === "json") { - output = formatJSON(currentTag, prevTag, categorized); - } else { - output = formatMarkdown(currentTag, prevTag, categorized, flags); - } - - process.stdout.write(output + "\n"); + log(`๐Ÿ” Generating release notes for ${owner}/${repo} @ ${currentTag}...`); + + const prevTag = flags.fromTag || (await getPreviousTag()); + if (!prevTag) { + console.error("No previous tag found. Use --from-tag to specify one."); + process.exit(1); + } + + const commits = await getCommits(prevTag, currentTag); + if (!commits.length) { + console.error("No commits found."); + process.exit(1); + } + const categorized = categorizeCommits(commits, flags); + let output; + + if (flags.format === "json") { + output = formatJSON(currentTag, prevTag, categorized); + } else { + output = formatMarkdown(currentTag, prevTag, categorized, flags); + } + + process.stdout.write(output + "\n"); } -main().catch(err => console.error(err)); +main().catch((err) => console.error(err)); From c749f8111555d83a0417995ccfa0277a2b0c0a5b Mon Sep 17 00:00:00 2001 From: UnschooledGamer <76094069+UnschooledGamer@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:12:42 +0530 Subject: [PATCH 6/6] chore: update nightly build workflow to use a fine-grained GITHUB_TOKEN for generating nightly release notes --- .github/workflows/nightly-build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 2252bfcd8..87eb5e749 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -225,7 +225,8 @@ echo "Nightly tag already points to this commit. Skipping update." fi - + # ๐Ÿšจโš ๏ธ WARNING: the GITHUB_TOKEN under this step, has access to write & read access to Contents, Pull Requests + # Which is why, it uses a fine-granted token with Read-Only Access to Public Repos Only. - name: Generate Release Notes (Experimental) if: ${{ success() && env.releaseRequired == 'true' }} id: gen-release-notes @@ -237,6 +238,8 @@ echo "$RELEASE_NOTES" echo "EOF" } >> $GITHUB_ENV + env: + GITHUB_TOKEN: ${{ secrets.NIGHTLY_RELEASE_NOTES_GH_TOKEN }} - name: Release Nightly Version # Only run this step, if not called from another workflow. And a previous step is successful with releasedRequired=true id: release