From 7170120325ebf3a632714a39474ede1ec7e034c2 Mon Sep 17 00:00:00 2001 From: Solvely-Colin <211764741+Solvely-Colin@users.noreply.github.com> Date: Sun, 24 May 2026 17:56:28 -0400 Subject: [PATCH] Make next Discord changelog release-only --- .github/workflows/discord-changelog.yml | 296 ++++++------------------ 1 file changed, 72 insertions(+), 224 deletions(-) diff --git a/.github/workflows/discord-changelog.yml b/.github/workflows/discord-changelog.yml index f4b572402..30ffbba78 100644 --- a/.github/workflows/discord-changelog.yml +++ b/.github/workflows/discord-changelog.yml @@ -1,21 +1,9 @@ name: Discord changelog on: - push: - branches: - - main - - master - pull_request: - types: - - opened - - reopened - - synchronize - - closed release: types: - published - create: - delete: workflow_dispatch: inputs: release_tag: @@ -25,14 +13,13 @@ on: permissions: contents: read - pull-requests: read jobs: discord-changelog: runs-on: ubuntu-latest - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + if: github.event_name == 'release' || github.event_name == 'workflow_dispatch' steps: - - name: Post changelog to Discord + - name: Post release changelog to Discord uses: actions/github-script@v7 env: DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_CHANGELOG_WEBHOOK }} @@ -46,12 +33,40 @@ jobs: return; } - let eventName = context.eventName; - let event = context.payload; - const repoFullName = `${context.repo.owner}/${context.repo.repo}`; - const repoUrl = `https://github.com/${repoFullName}`; + if (context.eventName !== "release" && context.eventName !== "workflow_dispatch") { + core.info(`Skipping ${context.eventName}; this workflow only posts releases.`); + return; + } + + const release = await getRelease(); + const messages = buildReleaseMessages(release); + + for (const content of messages) { + const response = await fetch(webhookUrl, { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify({ + username: "Repo Changelog", + content, + allowed_mentions: { + parse: [], + }, + }), + }); + + if (!response.ok) { + core.setFailed(`Discord webhook failed with ${response.status}: ${await response.text()}`); + return; + } + } + + async function getRelease() { + if (context.eventName === "release") { + return context.payload.release; + } - if (eventName === "workflow_dispatch") { const releaseTag = process.env.RELEASE_TAG; const response = releaseTag ? await github.rest.repos.getReleaseByTag({ @@ -64,201 +79,26 @@ jobs: repo: context.repo.repo, }); - eventName = "release"; - event = { - action: "published", - repository: { - full_name: repoFullName, - html_url: repoUrl, - }, - sender: { - login: context.actor, - }, - release: response.data, - }; + return response.data; } - const discordPayload = buildDiscordPayload(eventName, event); - const discordResponse = await fetch(webhookUrl, { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify(discordPayload), - }); + function buildReleaseMessages(release) { + const tag = release.tag_name || release.name || "release"; + const title = release.name || tag; + const notes = formatReleaseNotes(release.body || "") || "No release notes provided."; + const fullMessage = `**${title} Released**\n\n${notes}`; - if (!discordResponse.ok) { - core.setFailed( - `Discord webhook failed with ${discordResponse.status}: ${await discordResponse.text()}`, - ); - } - - function buildDiscordPayload(eventName, event) { - return { - username: "Repo Changelog", - content: buildContent(eventName, event), - embeds: [ - { - ...buildEmbed(eventName, event), - footer: { - text: `GitHub ${eventName} event`, - }, - timestamp: new Date().toISOString(), - }, - ], - }; - } - - function buildContent(eventName, event) { - const repo = getRepoName(event); - - if (eventName === "release") { - const tag = event.release?.tag_name || event.release?.name || "release"; - const action = event.action === "published" ? "published" : event.action || "updated"; - - return `**${repo}** ${action} **${tag}**`; - } - - return `**${repo}** update from ${getActor(event)}`; - } - - function buildEmbed(eventName, event) { - if (eventName === "release") { - return buildReleaseEmbed(event); - } - - if (eventName === "push") { - return buildPushEmbed(event); - } - - if (eventName === "pull_request") { - return buildPullRequestEmbed(event); - } - - if (eventName === "create" || eventName === "delete") { - return buildRefEmbed(eventName, event); - } - - return { - title: `GitHub ${eventName} event`, - description: `Received a ${eventName} event for ${getRepoName(event)}.`, - url: event.repository?.html_url, - color: 0x99aab5, - }; - } - - function buildReleaseEmbed(event) { - const release = event.release || {}; - const tag = release.tag_name || "release"; - const titleTag = release.name || tag; - const author = release.author?.login || event.sender?.login || "unknown"; - - return { - title: `${titleTag} Released`, - description: formatReleaseNotes(release.body || "") || "No release notes provided.", - url: release.html_url || event.repository?.html_url, - color: 0x57f287, - fields: [ - { - name: "Repository", - value: link(getRepoName(event), event.repository?.html_url), - inline: true, - }, - { - name: "Tag", - value: tag, - inline: true, - }, - { - name: "Published by", - value: author, - inline: true, - }, - ], - }; - } - - function buildPushEmbed(event) { - const repo = getRepoName(event); - const branch = trimRef(event.ref); - const commits = Array.isArray(event.commits) ? event.commits : []; - const commitLines = commits.slice(0, 8).map(formatCommit).join("\n"); - const overflow = commits.length > 8 ? `\n...and ${commits.length - 8} more.` : ""; - const commitWord = commits.length === 1 ? "commit" : "commits"; - - return { - title: `${commits.length} ${commitWord} pushed to ${branch}`, - description: commitLines - ? `${commitLines}${overflow}` - : "No commit list was included in this event.", - url: event.compare || event.repository?.html_url, - color: 0x5865f2, - fields: [ - { - name: "Repository", - value: link(repo, event.repository?.html_url), - inline: true, - }, - { - name: "Branch", - value: branch, - inline: true, - }, - ], - }; - } - - function buildPullRequestEmbed(event) { - const pr = event.pull_request || {}; - const action = - event.action === "closed" && pr.merged ? "merged" : event.action || "updated"; - const number = pr.number ? `#${pr.number}` : ""; - - return { - title: `Pull request ${number} ${action}`, - description: link(pr.title || "Pull request", pr.html_url), - url: pr.html_url || event.repository?.html_url, - color: pr.merged ? 0x57f287 : 0xfee75c, - fields: [ - { - name: "Author", - value: pr.user?.login || event.sender?.login || "unknown", - inline: true, - }, - { - name: "Branch", - value: `${pr.head?.ref || "head"} -> ${pr.base?.ref || "base"}`, - inline: true, - }, - ], - }; - } - - function buildRefEmbed(eventName, event) { - const action = eventName === "create" ? "created" : "deleted"; - const type = event.ref_type || "ref"; - const ref = event.ref || "unknown"; - - return { - title: `${capitalize(type)} ${action}: ${ref}`, - description: `${getActor(event)} ${action} ${type} \`${ref}\`.`, - url: event.repository?.html_url, - color: eventName === "create" ? 0x57f287 : 0xed4245, - }; - } - - function formatCommit(commit) { - const shortSha = commit.id ? commit.id.slice(0, 7) : "commit"; - const message = truncate(oneLine(commit.message || "No commit message"), 140); - const author = commit.author?.name ? ` - ${commit.author.name}` : ""; - - return `- ${link(shortSha, commit.url)} ${message}${author}`; + return splitDiscordMessage(fullMessage, 1900); } function formatReleaseNotes(value) { return truncate( value .replace(/\r\n/g, "\n") + .replace(/https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/pull\/(\d+)/g, "#$1") + .replace(/https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/issues\/(\d+)/g, "#$1") + .replace(/https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/commit\/([0-9a-f]{7})[0-9a-f]*/gi, "$1") + .replace(/https:\/\/github\.com\/\S+/g, "") .trim() .split("\n") .map((line) => { @@ -272,30 +112,38 @@ jobs: ); } - function getRepoName(event) { - return event.repository?.full_name || repoFullName; - } + function splitDiscordMessage(value, maxLength) { + if (value.length <= maxLength) { + return [value]; + } - function getActor(event) { - return event.sender?.login || event.pusher?.name || context.actor || "GitHub"; - } + const chunks = []; + let remaining = value; - function link(label, url) { - return url ? `[${label}](${url})` : label; - } + while (remaining.length > maxLength) { + let splitAt = remaining.lastIndexOf("\n", maxLength); - function trimRef(ref) { - return (ref || "unknown").replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, ""); - } + if (splitAt < 500) { + splitAt = remaining.lastIndexOf(" ", maxLength); + } - function oneLine(value) { - return value.replace(/\s+/g, " ").trim(); + if (splitAt < 500) { + splitAt = maxLength; + } + + chunks.push(remaining.slice(0, splitAt).trim()); + remaining = remaining.slice(splitAt).trim(); + } + + if (remaining) { + chunks.push(remaining); + } + + return chunks.map((chunk, index) => + index === 0 ? chunk : `**Changelog continued**\n\n${chunk}`, + ); } function truncate(value, maxLength) { return value.length <= maxLength ? value : `${value.slice(0, maxLength - 3)}...`; } - - function capitalize(value) { - return `${value.slice(0, 1).toUpperCase()}${value.slice(1)}`; - }