Skip to content

Commit 40be675

Browse files
feat(index.js): Using AdaptiveCard instead of MessageCard (deprecated), enhanced user experience, bug fixes related to pull req info
1 parent 2d3863f commit 40be675

8 files changed

+249
-94
lines changed
47.4 KB
Loading
10.4 KB
Loading

index.js

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
const { getArguments } = require('./lib/notes');
1+
const { getArguments, getNotes, getReleaseDate } = require('./lib/notes');
22
const { updateValues, readPackageJson, publishNotification, fetchPullRequestInfo } = require('./lib/payloadUtils');
3-
const messagePayload = require('./lib/teams_payload.json');
3+
const { execSync } = require('child_process');
44

55
// Main function that returns a promise
66
async function postNotification() {
@@ -9,24 +9,38 @@ async function postNotification() {
99
// Fetch Jira issue link based on the repository URL
1010
const jiraIssueLink = fetchPullRequestInfo(repoUrl);
1111
// Get version information and release details
12-
const { versionNumber, releaseType, releaseUrl } = getArguments(repoUrl);
12+
const { versionNumber, releaseType, releaseUrl, headerColor, repoImg } = getArguments(repoUrl);
13+
14+
const notificationType = execSync("echo $TEAMS_NOTIFICATION_TYPE").toString().trim();
15+
const messagePayload = require(notificationType ? './lib/messagecard_payload.json' : './lib/default_payload.json');
16+
1317
// Function to create a custom value object with 'find' and 'replace' keys
1418
const createCustomValue = (find, replace) => ({ find, replace });
1519

16-
// Array to store custom values based on project and release information
17-
const customValues = [
18-
// Create custom value for the project name
19-
createCustomValue({ name: "Project:" }, { value: projectName }),
20-
// Create custom value for the release type and version number also replace the name
21-
createCustomValue({ name: "Release Type:" }, { name: releaseType, value: versionNumber }),
22-
// Add Release Notes custom value if releaseUrl is provided
23-
releaseUrl && createCustomValue({ name: "Release Notes" }, { uri: releaseUrl }),
24-
// Add Github Repository custom value if repoUrl is provided
25-
repoUrl && createCustomValue({ name: "Github Repository" }, { uri: repoUrl }),
26-
// Add Jira Issue custom value if jiraIssueLink is provided
27-
jiraIssueLink && createCustomValue({ name: "Jira Issue" }, { uri: jiraIssueLink }),
28-
].filter(Boolean); // Remove any falsy values from the array (e.g., if releaseUrl, repoUrl, or jiraIssueLink is not provided)
20+
const customValues = [];
21+
const pushCustomValue = (nameKey, titleKey) => (label, value) => {
22+
const key = notificationType ? nameKey : titleKey;
23+
customValues.push(createCustomValue({ [key]: label }, value));
24+
};
2925

26+
const pushValue = pushCustomValue("name", "id");
27+
28+
if (!notificationType) {
29+
pushValue("projectName", { text: projectName });
30+
pushValue("releaseInfo", { text: `${releaseType} v${versionNumber} (${getReleaseDate()})` });
31+
pushValue("changeLog", { text: getNotes() });
32+
pushValue("header", { style: headerColor });
33+
pushValue("releaseNotes", { url: releaseUrl ?? "" });
34+
pushValue("githubRepository", { url: repoUrl ?? "" });
35+
pushValue("jiraIssue", { url: jiraIssueLink ?? "" });
36+
pushValue("productImage", { url: repoImg ?? "" });
37+
} else {
38+
pushValue("Project:", { value: projectName });
39+
pushValue("Release Type:", { "title": releaseType, value: versionNumber });
40+
pushValue("Release Notes", { uri: releaseUrl ?? "" });
41+
pushValue("Github Repository", { uri: repoUrl ?? "" });
42+
pushValue("Jira Issue", { uri: jiraIssueLink ?? "" });
43+
}
3044

3145
// Override values of the messagePayload directly based on customValues
3246
if (Object.keys(customValues).length !== 0) {

lib/default_payload.json

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
{
2+
"type": "message",
3+
"width": "stretch",
4+
"attachments": [
5+
{
6+
"contentType": "application/vnd.microsoft.card.adaptive",
7+
"contentUrl": null,
8+
"content": {
9+
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
10+
"type": "AdaptiveCard",
11+
"version": "1.5",
12+
"verticalContentAlignment": "Center",
13+
"body": [
14+
{
15+
"type": "ColumnSet",
16+
"columns": [
17+
{
18+
"type": "Column",
19+
"width": "50px",
20+
"items": [
21+
{
22+
"type": "Image",
23+
"style": "person",
24+
"url": "",
25+
"size": "small",
26+
"height": "50px"
27+
}
28+
],
29+
"id": "productImage"
30+
},
31+
{
32+
"type": "Column",
33+
"items": [
34+
{
35+
"type": "TextBlock",
36+
"weight": "bolder",
37+
"text": "",
38+
"wrap": true,
39+
"style": "columnHeader",
40+
"id": "projectName"
41+
},
42+
{
43+
"type": "TextBlock",
44+
"spacing": "none",
45+
"text": "",
46+
"isSubtle": true,
47+
"wrap": true,
48+
"size": "small",
49+
"id": "releaseInfo"
50+
}
51+
],
52+
"width": "stretch"
53+
}
54+
],
55+
"id": "header",
56+
"bleed": true,
57+
"style": ""
58+
},
59+
{
60+
"type": "TextBlock",
61+
"text": "",
62+
"wrap": true,
63+
"fontType": "Monospace",
64+
"id": "changeLog",
65+
"isSubtle": true
66+
}
67+
],
68+
"actions": [
69+
{
70+
"type": "Action.OpenUrl",
71+
"title": "Github Repository",
72+
"url": "",
73+
"id": "githubRepository"
74+
},
75+
{
76+
"type": "Action.OpenUrl",
77+
"title": "Release Notes",
78+
"url": "",
79+
"id": "releaseNotes"
80+
},
81+
{
82+
"type": "Action.OpenUrl",
83+
"title": "Jira Issue",
84+
"url": "",
85+
"id": "jiraIssue"
86+
},
87+
{
88+
"type": "Action.OpenUrl",
89+
"title": "Email Us",
90+
"url": "mailto:middleware@hexonet.net",
91+
"id": "emailUs"
92+
}
93+
]
94+
}
95+
}
96+
]
97+
}

lib/messagecard_payload.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"@type": "MessageCard",
3+
"@context": "http://schema.org/extensions",
4+
"themeColor": "39ff14",
5+
"summary": "A new change has just been reported by 3rd-party Software Integrations",
6+
"sections": [
7+
{
8+
"facts": [
9+
{
10+
"name": "Project:",
11+
"value": ""
12+
},
13+
{
14+
"name": "Release Type:",
15+
"value": ""
16+
}
17+
],
18+
"markdown": true
19+
}
20+
],
21+
"potentialAction": [
22+
{
23+
"@type": "OpenUri",
24+
"name": "Github Repository",
25+
"targets": [
26+
{
27+
"os": "default",
28+
"uri": ""
29+
}
30+
]
31+
},
32+
{
33+
"@type": "OpenUri",
34+
"name": "Release Notes",
35+
"targets": [
36+
{
37+
"os": "default",
38+
"uri": ""
39+
}
40+
]
41+
},
42+
{
43+
"@type": "OpenUri",
44+
"name": "Jira Issue",
45+
"targets": [
46+
{
47+
"os": "default",
48+
"uri": ""
49+
}
50+
]
51+
},
52+
{
53+
"@type": "OpenUri",
54+
"name": "Email Us",
55+
"targets": [
56+
{
57+
"os": "default",
58+
"uri": "https://outlook.office.com/mail/deeplink/compose?to=middleware@hexonet.net"
59+
}
60+
]
61+
}
62+
]
63+
}

lib/notes.js

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,56 @@
11
const arg = require('./arguments');
2-
async function generateNotes() {
2+
function getNotes() {
33
// Decode the notes to handle any special characters.
44
let cleanedNotes = decodeURIComponent(arg.notes);
55

66
// Define the regex to match links and commit ids in the notes.
7-
const regex = /\(\[([^[\]]*)\]\([^()]*\)\)|\[([^[\]]*)\]\([^()]*\)/gi;
8-
9-
// Replace the links and commit ids with the text in the square brackets.
10-
cleanedNotes = cleanedNotes.replace(regex, '$2');
7+
const regex = /\*\*([^:]+):\*\* (.*) \(\[([a-f0-9]{7})\]\(.*?\)\)/g; // match the commit data with category and message
8+
const result = cleanedNotes.match(regex); // extract the commit data as an array
9+
cleanedNotes = '';
10+
result.forEach(item => {
11+
item = item.replace(/\(\[([a-f0-9]{7})\]\(.*?\)\)/, ''); // remove the parentheses and the commit hash
12+
item = item.trim(); // remove any extra spaces
13+
cleanedNotes += item + '\n\n'; // add the formatted item to the output string with a line break
14+
});
1115

1216
// Return the cleaned release notes.
13-
return cleanedNotes;
17+
return `Changelog:\n\n${cleanedNotes}`;
18+
}
19+
20+
function getReleaseDate() {
21+
let cleanedNotes = decodeURIComponent(arg.notes);
22+
const regex = /\(\d{4}-\d{2}-\d{2}\)/; // match the date in parentheses
23+
const result = cleanedNotes.match(regex); // extract the date as a string
24+
cleanedNotes = result[0].replace(/\(|\)/g, ''); // remove the parentheses
25+
return cleanedNotes; // return the date
1426
}
27+
1528
function getArguments(repoUrl) {
1629
let releaseType;
30+
let headerColor;
1731
switch (arg?.type) {
1832
case "minor":
1933
releaseType = "Feature Release:";
34+
headerColor = "accent";
2035
break;
2136
case "major":
2237
releaseType = "Major Release:"
38+
headerColor = "good";
2339
break;
2440
default:
2541
releaseType = "Patch Release:";
26-
42+
headerColor = "attention";
2743
}
2844
let releaseUrl = false;
2945
let versionNumber = arg?.update ?? "Development Changes";
3046
if (/\d+\.\d+\.\d+/.test(versionNumber)) {
3147
releaseUrl = `${repoUrl}/releases/tag/v${versionNumber}`;
3248
}
33-
return { versionNumber: versionNumber, releaseType, releaseUrl }
49+
let repoImg = "";
50+
if (/whmcs|blesta/i.test(repoUrl)) {
51+
repoImg = `https://github.com/centralnicgroup-opensource/rtldev-middleware-gulp-release-notification-plugin/blob/main/assets/${repoUrl.split('/').slice(-1)}.png`;
52+
}
53+
return { versionNumber: versionNumber, releaseType, releaseUrl, headerColor, repoImg }
3454
}
35-
module.exports = { generateNotes, getArguments };
55+
56+
module.exports = { getNotes, getArguments, getReleaseDate };

lib/payloadUtils.js

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ const updateValues = (payload, customValues) => {
1313
const { replace } = matchingFind;
1414

1515
for (const replaceKey in replace) {
16-
if (payload[prop][replaceKey] !== undefined) {
16+
if (replace[replaceKey] === "") {
17+
delete payload[prop];
18+
} else if (payload[prop][replaceKey] !== undefined) {
1719
payload[prop][replaceKey] = replace[replaceKey];
1820
} else {
1921
findAndReplaceNestedKey(payload[prop], replaceKey, replace[replaceKey]);
2022
}
2123
}
2224
}
2325
}
26+
payload = removeNulls(payload);
2427
}
2528
};
2629

@@ -37,7 +40,7 @@ const findAndReplaceNestedKey = (obj, targetKey, replacement) => {
3740

3841
// Helper function to check if two objects match
3942
const isObjectMatch = (obj, criteria) =>
40-
Object.entries(criteria).every(([key, value]) => obj[key] === value);
43+
Object.entries(criteria).every(([key, value]) => obj && obj.hasOwnProperty(key) && obj[key] === value);
4144

4245
const readPackageJson = async () => {
4346
try {
@@ -68,14 +71,14 @@ retryFn.counter = 1;
6871
const fetchPullRequestInfo = (repoUrl) => {
6972
const usernameAndRepo = repoUrl.split('/').slice(-2).join('/');
7073
const commitSHA = execSync("echo $COMMIT_SHA").toString().trim();
71-
// need to check if it works without token or not
72-
const githubToken = execSync("echo $RTLDEV_MW_CI_TOKEN || echo $GH_TOKEN || echo $GITHUB_TOKEN").toString().trim();
73-
if (!commitSHA || !usernameAndRepo) {
74+
const githubToken = execSync("echo ${RTLDEV_MW_CI_TOKEN:-${GH_TOKEN:-$GITHUB_TOKEN}}").toString().trim();
75+
76+
if (!githubToken || !commitSHA || !usernameAndRepo) {
7477
return;
7578
}
7679

7780
try {
78-
const result = JSON.parse(execSync(`curl -s -H "Authorization: token ${githubToken}" https://api.github.com/repos/${usernameAndRepo}/commits/${commitSHA}/pulls`, { encoding: 'utf-8' }))[0];
81+
const result = JSON.parse(execSync(`curl -s -L -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${githubToken}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/${usernameAndRepo}/commits/${commitSHA}/pulls`, { encoding: 'utf-8' }))[0];
7982

8083
if (result?.title || result?.head?.ref) {
8184
const jiraID = result?.title.match(/(RSRMID|GI)-\d+/g) || result?.head?.ref.match(/(RSRMID|GI)-\d+/g);
@@ -106,4 +109,24 @@ const publishNotification = async (payload) => {
106109
return;
107110
}
108111
}
112+
113+
const removeNulls = (obj) => {
114+
if (obj && typeof obj === "object") {
115+
if (Array.isArray(obj)) {
116+
// Remove null entries from arrays
117+
obj = obj.filter(item => item !== null);
118+
} else {
119+
// Remove null entries from objects
120+
for (const key in obj) {
121+
if (obj[key] === null) {
122+
delete obj[key];
123+
} else {
124+
obj[key] = removeNulls(obj[key]);
125+
}
126+
}
127+
}
128+
}
129+
return obj;
130+
}
131+
109132
module.exports = { updateValues, isObjectMatch, findAndReplaceNestedKey, readPackageJson, retryFn, fetchPullRequestInfo, publishNotification };

0 commit comments

Comments
 (0)