Skip to content

Commit bb444ed

Browse files
committed
feat: GHActions add master pr merged send release email
KBDEV-1393
1 parent 33d5a34 commit bb444ed

2 files changed

Lines changed: 160 additions & 0 deletions

File tree

.github/scripts/sendPrEmail.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
const https = require('https');
2+
3+
const {
4+
AZURE_TENANT_ID,
5+
AZURE_CLIENT_ID,
6+
AZURE_CLIENT_SECRET,
7+
AZURE_SENDER_EMAIL,
8+
RECIPIENT_EMAIL,
9+
PR_TITLE,
10+
PR_BODY
11+
} = process.env;
12+
13+
/**
14+
* Simple HTTPS request helper
15+
*/
16+
const httpsRequest = async (options, body) =>
17+
new Promise((resolve, reject) => {
18+
const req = https.request(options, (res) => {
19+
let data = '';
20+
res.on('data', (chunk) => (data += chunk));
21+
res.on('end', () => resolve({ status: res.statusCode, data }));
22+
});
23+
req.on('error', reject);
24+
if (body) req.write(body);
25+
req.end();
26+
});
27+
28+
/**
29+
* Get Microsoft Graph access token (client credentials)
30+
*/
31+
const getGraphToken = async () => {
32+
const params = new URLSearchParams({
33+
client_id: AZURE_CLIENT_ID,
34+
client_secret: AZURE_CLIENT_SECRET,
35+
scope: 'https://graph.microsoft.com/.default',
36+
grant_type: 'client_credentials'
37+
});
38+
39+
const { status, data } = await httpsRequest(
40+
{
41+
method: 'POST',
42+
hostname: 'login.microsoftonline.com',
43+
path: `/${AZURE_TENANT_ID}/oauth2/v2.0/token`,
44+
headers: {
45+
'Content-Type': 'application/x-www-form-urlencoded',
46+
'Content-Length': params.toString().length
47+
}
48+
},
49+
params.toString()
50+
);
51+
52+
const json = JSON.parse(data);
53+
54+
if (status >= 300 || !json.access_token) {
55+
throw new Error(`Token request failed: ${data}`);
56+
}
57+
58+
return json.access_token;
59+
};
60+
61+
/**
62+
* Escape HTML
63+
*/
64+
const escapeHtml = (text) =>
65+
text
66+
.replace(/&/g, '&')
67+
.replace(/</g, '&lt;')
68+
.replace(/>/g, '&gt;');
69+
70+
/**
71+
* Minimal markdown formatter
72+
* - ### heading → bold, 14px
73+
* - **bold** → bold
74+
* - everything else → plain text
75+
*/
76+
const formatBody = (body = 'No description provided.') => {
77+
let content = escapeHtml(body);
78+
79+
content = content.replace(
80+
/^### (.*)$/gm,
81+
'<div style="font-size:14px;font-weight:bold;">$1</div>'
82+
);
83+
84+
content = content.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
85+
86+
return content.replace(/\n/g, '<br/>');
87+
};
88+
89+
/**
90+
* Send email via Microsoft Graph
91+
*/
92+
const sendMail = async (token) => {
93+
const payload = JSON.stringify({
94+
message: {
95+
subject: PR_TITLE,
96+
body: {
97+
contentType: 'HTML',
98+
content: formatBody(PR_BODY)
99+
},
100+
toRecipients: [
101+
{
102+
emailAddress: { address: RECIPIENT_EMAIL }
103+
}
104+
]
105+
}
106+
});
107+
108+
const { status, data } = await httpsRequest({
109+
method: 'POST',
110+
hostname: 'graph.microsoft.com',
111+
path: `/v1.0/users/${AZURE_SENDER_EMAIL}/sendMail`,
112+
headers: {
113+
Authorization: `Bearer ${token}`,
114+
'Content-Type': 'application/json',
115+
'Content-Length': Buffer.byteLength(payload)
116+
}
117+
}, payload);
118+
119+
if (status >= 300) {
120+
throw new Error(`Graph sendMail failed (${status}): ${data}`);
121+
}
122+
};
123+
124+
/**
125+
* Main
126+
*/
127+
const main = async () => {
128+
const token = await getGraphToken();
129+
await sendMail(token);
130+
console.log('Email sent successfully.');
131+
};
132+
133+
main().catch((err) => {
134+
console.error(err);
135+
process.exit(1);
136+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Email on master merge
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- master
7+
types: [closed]
8+
9+
jobs:
10+
send-email:
11+
if: github.event.pull_request.merged == true
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Send PR email
16+
run: node .github/scripts/sendPrEmail.js
17+
env:
18+
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
19+
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
20+
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
21+
AZURE_SENDER_EMAIL: ${{ secrets.AZURE_SENDER_EMAIL }}
22+
RECIPIENT_EMAIL: ${{ vars.OUTLOOK_TARGET_EMAIL }}
23+
PR_TITLE: ${{ github.event.pull_request.title }}
24+
PR_BODY: ${{ github.event.pull_request.body }}

0 commit comments

Comments
 (0)