Skip to content

Commit 8ca9da2

Browse files
authoredDec 11, 2023
feat: added additional description formatting (#23)
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
1 parent 254bf79 commit 8ca9da2

File tree

3 files changed

+121
-34
lines changed

3 files changed

+121
-34
lines changed
 

‎README.md

+12-10
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ A GitHub action that parses a GitHub release and posts it to a Discord channel a
77

88
## Configuration
99

10-
| Variable | Required | Default | Description |
11-
|-----------------|----------|----------------------------------------------------------------------------------------------------------------|--------------------------------------------|
12-
| webhook_url || | Discord's webhook url. Use GH repo secrets.|
13-
| color || "2105893" | Decimal color value for embed. |
14-
| username || | String username for webhook. |
15-
| avatar_url || | String url to webhook avatar picture. |
16-
| content || | String content for webhook. |
17-
| footer_title || | String title for the webhook footer. |
18-
| footer_icon_url || | String url for the webhook footer picture. |
19-
| footer_timestamp|| | Boolean to enable footer timestamp. |
10+
| Variable | Required | Default | Description |
11+
|-----------------|----------|-------------------------------------------------------------------------------------------------------|-------------------------------------------------|
12+
| webhook_url || | Discord's webhook url. Use GH repo secrets. |
13+
| color || "2105893" | Decimal color value for embed. |
14+
| username || | String username for webhook. |
15+
| avatar_url || | String url to webhook avatar picture. |
16+
| content || | String content for webhook. |
17+
| footer_title || | String title for the webhook footer. |
18+
| footer_icon_url || | String url for the webhook footer picture. |
19+
| footer_timestamp|| | Boolean to enable footer timestamp. |
20+
| max_description || "4096" | Max length for the description. |
21+
| reduce_headings || false | Converts H3 to bold, h2 to bold & underline. |
2022

2123
## Example Usage
2224

‎action.yml

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ inputs:
2727
footer_timestamp:
2828
description: Timestamp for the footer.
2929
required: false
30+
max_description:
31+
description: Max length for the description.
32+
required: false
33+
default: '4096'
34+
reduce_headings:
35+
description: Converts H3 to bold, h2 to bold & underline.
36+
required: false
37+
default: 'false'
3038
runs:
3139
using: 'node16'
3240
main: 'index.js'

‎index.js

+101-24
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,119 @@ import fetch from 'node-fetch';
44

55
/**
66
* Stylizes a markdown body into an appropriate embed message style.
7-
* H3s converted to bold and underlined.
8-
* H2s converted to bold.
9-
* Redundant whitespace and newlines removed.
10-
* @param description
11-
* @returns {*}
7+
* Remove HTML comments (commonly added by 'Generate release notes' button)
8+
* Better URL linking for common Github links: PRs, Issues, Compare
9+
* Redundant whitespace and newlines removed, keeping at max 2 to provide space between paragraphs
10+
* Trim leading/trailing whitespace
11+
* If reduce_headings:
12+
* H3s converted to bold and underlined
13+
* H2s converted to bold
14+
* @param {string} description
1215
*/
1316
const formatDescription = (description) => {
14-
return description
15-
.replace(/### (.*?)\n/g,function (substring) {
16-
const newString = substring.slice(4).replace(/(\r\n|\n|\r)/gm, "")
17-
return `**__${newString}__**`
17+
let edit = description
18+
.replace(/<!--.*?-->/gs, '')
19+
.replace(
20+
new RegExp(
21+
"https://github.com/(.+)/(.+)/(issues|pull|commit|compare)/(\\S+)",
22+
"g"
23+
),
24+
(match, user, repo, type, id) => {
25+
return `[${getTypePrefix(type) + id}](${match})`
26+
}
27+
)
28+
.replace(/\n\s*\n/g, (ws) => {
29+
const nlCount = (ws.match(/\n/g) || []).length
30+
return nlCount >= 2 ? '\n\n' : '\n'
1831
})
19-
.replace(/## (.*?)\n/g,function (substring) {
20-
const newString = substring.slice(3).replace(/(\r\n|\n|\r)/gm, "")
21-
return `**${newString}**`
22-
})
23-
.replace(/\n\s*\n/g, '\n')
32+
.trim()
33+
34+
if (core.getBooleanInput('reduce_headings')) {
35+
edit = edit
36+
.replace(/^###\s+(.+)$/gm, '**__$1__**')
37+
.replace(/^##\s+(.+)$/gm, '**$1**')
38+
}
39+
40+
return edit
41+
}
42+
43+
/**
44+
* Get a prefix to use for Github link display
45+
* @param {'issues' | 'pull' | 'commit' | 'compare'} type
46+
*/
47+
function getTypePrefix (type) {
48+
switch (type) {
49+
case 'issues':
50+
return 'Issue #'
51+
case 'pull':
52+
return 'PR #'
53+
case 'commit':
54+
return 'Commit #'
55+
case 'compare':
56+
return ''
57+
default:
58+
return '#'
59+
}
60+
}
61+
62+
/**
63+
* Gets the max description length if set to a valid number,
64+
* otherwise the default of 4096
65+
*/
66+
function getMaxDescription () {
67+
try {
68+
const max = core.getInput('max_description')
69+
if (typeof max === 'string' && max.length) {
70+
// 4096 is max for Embed Description
71+
// https://discord.com/developers/docs/resources/channel#embed-object-embed-limits
72+
return Math.min(parseInt(max, 10), 4096)
73+
}
74+
} catch (err) {
75+
core.warning(`max_description not a valid number: ${err}`)
76+
}
77+
return 4096
2478
}
2579

2680
/**
2781
* Get the context of the action, returns a GitHub Release payload.
28-
* @returns {Promise<{html_url, body: (*|string), name: string}>}
2982
*/
30-
async function getContext () {
83+
function getContext () {
3184
const payload = github.context.payload;
3285

3386
return {
34-
body: payload.release.body.length < 1500
35-
? payload.release.body
36-
: payload.release.body.substring(0, 1500) + ` ([...](${payload.release.html_url}))`,
37-
name: payload.release.name,
87+
body: payload.release.body,
88+
name: payload.release.name,
3889
html_url: payload.release.html_url
3990
}
4091
}
4192

93+
/**
94+
*
95+
* @param {string} str
96+
* @param {number} maxLength
97+
* @param {string=} url
98+
*/
99+
function limit(str, maxLength, url) {
100+
if (str.length <= maxLength)
101+
return str
102+
let replacement = '…'
103+
if (url) {
104+
replacement = `([${replacement}](${url}))`
105+
}
106+
maxLength = maxLength - replacement.length
107+
str = str.substring(0, maxLength)
108+
109+
const lastWhitespace = str.search(/[^\s]*$/)
110+
if (lastWhitespace > -1) {
111+
str = str.substring(0, lastWhitespace)
112+
}
113+
114+
return str + replacement
115+
}
116+
42117
/**
43118
* Handles the action.
44119
* Get inputs, creates a stylized response webhook, and sends it to the channel.
45-
* @returns {Promise<void>}
46120
*/
47121
async function run () {
48122
const webhookUrl = core.getInput('webhook_url');
@@ -56,22 +130,25 @@ async function run () {
56130

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

59-
const {body, html_url, name} = await getContext();
133+
const {body, html_url, name} = getContext();
60134

61135
const description = formatDescription(body);
62136

63137
let embedMsg = {
64-
title: name,
138+
title: limit(name, 256),
65139
url: html_url,
66140
color: color,
67141
description: description,
68142
footer: {}
69143
}
70144

71-
if (footerTitle != '') embedMsg.footer.text = footerTitle;
145+
if (footerTitle != '') embedMsg.footer.text = limit(footerTitle, 2048);
72146
if (footerIconUrl != '') embedMsg.footer.icon_url = footerIconUrl;
73147
if (footerTimestamp == 'true') embedMsg.timestamp = new Date().toISOString();
74148

149+
let embedSize = embedMsg.title.length + (embedMsg.footer?.text?.length ?? 0)
150+
embedMsg.description = limit(embedMsg.description, Math.min(getMaxDescription(), 6000 - embedSize), embedMsg.url)
151+
75152
let requestBody = {
76153
embeds: [embedMsg]
77154
}

0 commit comments

Comments
 (0)
Please sign in to comment.