Skip to content

Commit

Permalink
feat: added additional description formatting (#23)
Browse files Browse the repository at this point in the history
remove: removed HTML comments added by the 'Generate release notes' button
feat: description trimming
style: reduce consecutive whitespace/newlines into a minimum of 2 to allow separation in paragraphs
style: parse common Github URLs to more appropriate display
feat: added max_description option
feat: added reduce_headings option
  • Loading branch information
rhullah committed Dec 11, 2023
1 parent 254bf79 commit 8ca9da2
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 34 deletions.
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ A GitHub action that parses a GitHub release and posts it to a Discord channel a

## Configuration

| Variable | Required | Default | Description |
|-----------------|----------|----------------------------------------------------------------------------------------------------------------|--------------------------------------------|
| webhook_url || | Discord's webhook url. Use GH repo secrets.|
| color || "2105893" | Decimal color value for embed. |
| username || | String username for webhook. |
| avatar_url || | String url to webhook avatar picture. |
| content || | String content for webhook. |
| footer_title || | String title for the webhook footer. |
| footer_icon_url || | String url for the webhook footer picture. |
| footer_timestamp|| | Boolean to enable footer timestamp. |
| Variable | Required | Default | Description |
|-----------------|----------|-------------------------------------------------------------------------------------------------------|-------------------------------------------------|
| webhook_url || | Discord's webhook url. Use GH repo secrets. |
| color || "2105893" | Decimal color value for embed. |
| username || | String username for webhook. |
| avatar_url || | String url to webhook avatar picture. |
| content || | String content for webhook. |
| footer_title || | String title for the webhook footer. |
| footer_icon_url || | String url for the webhook footer picture. |
| footer_timestamp|| | Boolean to enable footer timestamp. |
| max_description || "4096" | Max length for the description. |
| reduce_headings || false | Converts H3 to bold, h2 to bold & underline. |

## Example Usage

Expand Down
8 changes: 8 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ inputs:
footer_timestamp:
description: Timestamp for the footer.
required: false
max_description:
description: Max length for the description.
required: false
default: '4096'
reduce_headings:
description: Converts H3 to bold, h2 to bold & underline.
required: false
default: 'false'
runs:
using: 'node16'
main: 'index.js'
Expand Down
125 changes: 101 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,119 @@ import fetch from 'node-fetch';

/**
* Stylizes a markdown body into an appropriate embed message style.
* H3s converted to bold and underlined.
* H2s converted to bold.
* Redundant whitespace and newlines removed.
* @param description
* @returns {*}
* Remove HTML comments (commonly added by 'Generate release notes' button)
* Better URL linking for common Github links: PRs, Issues, Compare
* Redundant whitespace and newlines removed, keeping at max 2 to provide space between paragraphs
* Trim leading/trailing whitespace
* If reduce_headings:
* H3s converted to bold and underlined
* H2s converted to bold
* @param {string} description
*/
const formatDescription = (description) => {
return description
.replace(/### (.*?)\n/g,function (substring) {
const newString = substring.slice(4).replace(/(\r\n|\n|\r)/gm, "")
return `**__${newString}__**`
let edit = description
.replace(/<!--.*?-->/gs, '')
.replace(
new RegExp(
"https://github.com/(.+)/(.+)/(issues|pull|commit|compare)/(\\S+)",
"g"
),
(match, user, repo, type, id) => {
return `[${getTypePrefix(type) + id}](${match})`
}
)
.replace(/\n\s*\n/g, (ws) => {
const nlCount = (ws.match(/\n/g) || []).length
return nlCount >= 2 ? '\n\n' : '\n'
})
.replace(/## (.*?)\n/g,function (substring) {
const newString = substring.slice(3).replace(/(\r\n|\n|\r)/gm, "")
return `**${newString}**`
})
.replace(/\n\s*\n/g, '\n')
.trim()

if (core.getBooleanInput('reduce_headings')) {
edit = edit
.replace(/^###\s+(.+)$/gm, '**__$1__**')
.replace(/^##\s+(.+)$/gm, '**$1**')
}

return edit
}

/**
* Get a prefix to use for Github link display
* @param {'issues' | 'pull' | 'commit' | 'compare'} type
*/
function getTypePrefix (type) {
switch (type) {
case 'issues':
return 'Issue #'
case 'pull':
return 'PR #'
case 'commit':
return 'Commit #'
case 'compare':
return ''
default:
return '#'
}
}

/**
* Gets the max description length if set to a valid number,
* otherwise the default of 4096
*/
function getMaxDescription () {
try {
const max = core.getInput('max_description')
if (typeof max === 'string' && max.length) {
// 4096 is max for Embed Description
// https://discord.com/developers/docs/resources/channel#embed-object-embed-limits
return Math.min(parseInt(max, 10), 4096)
}
} catch (err) {
core.warning(`max_description not a valid number: ${err}`)
}
return 4096
}

/**
* Get the context of the action, returns a GitHub Release payload.
* @returns {Promise<{html_url, body: (*|string), name: string}>}
*/
async function getContext () {
function getContext () {
const payload = github.context.payload;

return {
body: payload.release.body.length < 1500
? payload.release.body
: payload.release.body.substring(0, 1500) + ` ([...](${payload.release.html_url}))`,
name: payload.release.name,
body: payload.release.body,
name: payload.release.name,
html_url: payload.release.html_url
}
}

/**
*
* @param {string} str
* @param {number} maxLength
* @param {string=} url
*/
function limit(str, maxLength, url) {
if (str.length <= maxLength)
return str
let replacement = '…'
if (url) {
replacement = `([${replacement}](${url}))`
}
maxLength = maxLength - replacement.length
str = str.substring(0, maxLength)

const lastWhitespace = str.search(/[^\s]*$/)
if (lastWhitespace > -1) {
str = str.substring(0, lastWhitespace)
}

return str + replacement
}

/**
* Handles the action.
* Get inputs, creates a stylized response webhook, and sends it to the channel.
* @returns {Promise<void>}
*/
async function run () {
const webhookUrl = core.getInput('webhook_url');
Expand All @@ -56,22 +130,25 @@ async function run () {

if (!webhookUrl) return core.setFailed('webhook_url not set. Please set it.');

const {body, html_url, name} = await getContext();
const {body, html_url, name} = getContext();

const description = formatDescription(body);

let embedMsg = {
title: name,
title: limit(name, 256),
url: html_url,
color: color,
description: description,
footer: {}
}

if (footerTitle != '') embedMsg.footer.text = footerTitle;
if (footerTitle != '') embedMsg.footer.text = limit(footerTitle, 2048);
if (footerIconUrl != '') embedMsg.footer.icon_url = footerIconUrl;
if (footerTimestamp == 'true') embedMsg.timestamp = new Date().toISOString();

let embedSize = embedMsg.title.length + (embedMsg.footer?.text?.length ?? 0)
embedMsg.description = limit(embedMsg.description, Math.min(getMaxDescription(), 6000 - embedSize), embedMsg.url)

let requestBody = {
embeds: [embedMsg]
}
Expand Down

0 comments on commit 8ca9da2

Please sign in to comment.