Skip to content

Commit 90cde6e

Browse files
committed
Bug fixes
1 parent ed844eb commit 90cde6e

1 file changed

Lines changed: 46 additions & 28 deletions

File tree

.github/workflows/_slack-pr-nag.yml

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Slack Open PR Nag Bot
22

33
on:
44
schedule:
5-
# Runs at 7am ET
6-
- cron: '0 7 * * 1-5'
7-
timezone: 'America/New_York'
5+
# NOTE: GitHub Actions only supports UTC. The 'timezone' property is invalid here.
6+
# 11:00 UTC is 7:00 AM EST / 8:00 AM EDT.
7+
- cron: '0 11 * * 1-5'
88
workflow_dispatch: # Allows manual testing
99

1010
permissions:
@@ -26,7 +26,6 @@ jobs:
2626
MANUAL_RUN: ${{ github.event_name == 'workflow_dispatch' }}
2727

2828
# CENTRAL USER REGISTRY
29-
# Maps GitHub usernames to both their Slack Member ID and Document Display Name
3029
USER_MAPPING: |
3130
{
3231
"aaronb-stacks": { "id": "U067F1SRYT0", "name": "Aaron" },
@@ -38,7 +37,7 @@ jobs:
3837
"federico-stacks": { "id": "U08ASJ1LKEV", "name": "Federico" },
3938
"francesco-stacks": { "id": "U07MCETKTKN", "name": "Francesco" },
4039
"hstove-stacks": { "id": "U068W8VBSQM", "name": "Hank" },
41-
"radu-stacks": { "id": "U0ANRRXD080", "name": "Radu" },
40+
"radu-stacks": { "id": "U09NA1TP8PK", "name": "Radu" },
4241
"rob-stacks": { "id": "U07UNFAS4BD", "name": "Roberto" },
4342
"simone-stacks": { "id": "U09NA1TP8PK", "name": "Simone" }
4443
}
@@ -61,27 +60,30 @@ jobs:
6160
let userMap = {};
6261
try {
6362
const rawMap = JSON.parse(process.env.USER_MAPPING || '{}');
64-
// Ensure keys are lowercase for foolproof matching
6563
userMap = Object.fromEntries(Object.entries(rawMap).map(([k, v]) => [k.toLowerCase(), v]));
6664
} catch (e) {
67-
core.setFailed('Error parsing USER_MAPPING JSON. Check your JSON syntax.');
65+
core.setFailed('Error parsing USER_MAPPING JSON.');
6866
return;
6967
}
7068
71-
// Whitelist is derived from the mapping keys
7269
const allowedUsers = new Set(Object.keys(userMap));
7370
74-
// Helper to get doc display name from the mapping block
7571
const getDocName = (username) => {
7672
const clean = username.toLowerCase();
77-
if (userMap[clean] && userMap[clean].name) {
78-
return userMap[clean].name;
79-
}
80-
// Intelligent fallback: strip suffix and capitalize
73+
if (userMap[clean] && userMap[clean].name) return userMap[clean].name;
8174
const base = clean.replace('-stacks', '');
8275
return base.charAt(0).toUpperCase() + base.slice(1);
8376
};
8477
78+
// Helper to clean titles so they do not break Slack's control sequences
79+
const sanitizeTitle = (title) => {
80+
return title
81+
.replace(/&/g, '&')
82+
.replace(/</g, '&lt;')
83+
.replace(/>/g, '&gt;')
84+
.replace(/\|/g, '—'); // Swap out pipes to protect link parsing
85+
};
86+
8587
// 2. Fetch open Pull Requests
8688
const { data: prs } = await github.rest.pulls.list({
8789
owner,
@@ -102,28 +104,44 @@ jobs:
102104
return;
103105
}
104106
105-
// 3. Build the plain text list strings
106-
const lines = [];
107-
lines.push(`*📋 Open PRs*${manualRun ? ' _(Manual Run)_' : ''}\n`);
107+
// 3. Construct the payload blocks incrementally to avoid limits
108+
const payload = { blocks: [] };
109+
110+
// Add initial title header block
111+
payload.blocks.push({
112+
type: 'section',
113+
text: {
114+
type: 'mrkdwn',
115+
text: `*📋 Open PRs*${manualRun ? ' _(Manual Run)_' : ''}\n`
116+
}
117+
});
108118
119+
// Distribute lines across multiple blocks dynamically
120+
let currentLines = [];
109121
for (const pr of filteredSortedPrs) {
110122
const author = getDocName(pr.user.login);
123+
const title = sanitizeTitle(pr.title);
111124
const reviewers = pr.requested_reviewers.map(r => getDocName(r.login)).join(', ') || 'Anyone';
112125
113-
lines.push(`${author} <${pr.html_url}|${pr.title}> (${reviewers})`);
114-
}
126+
currentLines.push(`${author} <${pr.html_url}|${title}> (${reviewers})`);
115127
116-
const payload = {
117-
blocks: [
118-
{
128+
// Create a new layout block every 8 PRs to comfortably avoid the 3000 char ceiling
129+
if (currentLines.length === 8) {
130+
payload.blocks.push({
119131
type: 'section',
120-
text: {
121-
type: 'mrkdwn',
122-
text: lines.join('\n')
123-
}
124-
}
125-
]
126-
};
132+
text: { type: 'mrkdwn', text: currentLines.join('\n') }
133+
});
134+
currentLines = [];
135+
}
136+
}
137+
138+
// Append any remaining PR lines
139+
if (currentLines.length > 0) {
140+
payload.blocks.push({
141+
type: 'section',
142+
text: { type: 'mrkdwn', text: currentLines.join('\n') }
143+
});
144+
}
127145
128146
// 4. Send payload via Webhook
129147
const response = await fetch(webhookUrl, {

0 commit comments

Comments
 (0)