-
Notifications
You must be signed in to change notification settings - Fork 2
182 lines (171 loc) · 8.03 KB
/
Copy pathtests.yml
File metadata and controls
182 lines (171 loc) · 8.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
name: Tests
on:
push:
branches:
- main
pull_request:
jobs:
unit-tests:
name: Unit tests
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [24.x]
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
- name: Install Dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Unit tests
run: npm run test
notify-slack:
name: Notify on failure
needs:
- unit-tests
if: failure()
runs-on: ubuntu-latest
steps:
- name: Resolve Slack recipients
id: resolve
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_GHBOT_TOKEN }}
SLACK_GITHUB_FIELD_ID: "Xf0A2BPU8U77"
CHANNEL_ID: "C067BD0377F"
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
ACTOR: ${{ github.actor }}
EVENT_NAME: ${{ github.event_name }}
with:
script: |
const token = process.env.SLACK_BOT_TOKEN;
const fieldId = process.env.SLACK_GITHUB_FIELD_ID;
// Resolve a GitHub login -> Slack user ID by scanning workspace profiles and
// matching the custom profile field (GitHub). Returns undefined if not found.
async function resolveSlackId(login) {
const target = login.toLowerCase();
let cursor;
do {
const params = new URLSearchParams({ limit: '200' });
if (cursor) params.set('cursor', cursor);
const res = await fetch(`https://slack.com/api/users.list?${params}`, {
headers: { Authorization: `Bearer ${token}` }
});
const data = await res.json();
if (!data.ok) { core.setFailed(`Slack users.list error: ${data.error}`); return; }
for (const member of data.members) {
if (member.deleted || member.is_bot) continue;
const profileRes = await fetch(`https://slack.com/api/users.profile.get?user=${member.id}`, {
headers: { Authorization: `Bearer ${token}` }
});
const profileData = await profileRes.json();
if (!profileData.ok) continue;
const ghField = profileData.profile?.fields?.[fieldId]?.value;
if (!ghField) continue;
if (ghField.toLowerCase() === target) return member.id;
}
cursor = data.response_metadata?.next_cursor;
} while (cursor);
return undefined;
}
// Push to main - notify the channel, mentioning the actor who pushed.
if (process.env.EVENT_NAME !== 'pull_request') {
const actor = process.env.ACTOR;
const mention = actor ? await resolveSlackId(actor) : undefined;
if (!mention) core.warning(`No Slack user found with GitHub username: ${actor}`);
core.setOutput('recipients', JSON.stringify([{ slack_id: process.env.CHANNEL_ID, role: 'Channel', mention: mention || null }]));
return;
}
// Pull request - notify the author. Bot authors have no Slack profile: skip silently.
const author = process.env.PR_AUTHOR;
if (!author || author.endsWith('[bot]')) {
core.setOutput('recipients', '[]');
return;
}
const slackId = await resolveSlackId(author);
if (!slackId) {
core.warning(`No Slack user found with GitHub username: ${author}`);
core.setOutput('recipients', '[]');
return;
}
core.setOutput('recipients', JSON.stringify([{ slack_id: slackId, github: author.toLowerCase(), role: 'Author' }]));
- name: Send Slack notification
if: steps.resolve.outputs.recipients != '' && steps.resolve.outputs.recipients != '[]'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_GHBOT_TOKEN }}
RECIPIENTS: ${{ steps.resolve.outputs.recipients }}
EVENT_NAME: ${{ github.event_name }}
REF_NAME: ${{ github.ref_name }}
PR_NUMBER: ${{ github.event.pull_request.number }}
COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
ACTOR: ${{ github.actor }}
SERVER_URL: ${{ github.server_url }}
REPOSITORY: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
with:
script: |
const token = process.env.SLACK_BOT_TOKEN;
const recipients = JSON.parse(process.env.RECIPIENTS);
const isBranch = process.env.EVENT_NAME !== 'pull_request';
const refName = process.env.REF_NAME;
const prNumber = process.env.PR_NUMBER;
const commitSha = process.env.COMMIT_SHA;
const actor = process.env.ACTOR;
const serverUrl = process.env.SERVER_URL;
const repository = process.env.REPOSITORY;
const runId = process.env.RUN_ID;
const headerText = isBranch
? `Tests failed against ${refName} branch`
: `Tests failed against #${prNumber} pull request`;
const summaryIcon = isBranch ? 'no_entry' : 'warning';
const summaryText = isBranch
? ' Deployment to FFC environments will not happen until this issue is resolved.'
: ' Please resolve the problem before merging your changes into the main branch.';
const refLink = isBranch
? `*Branch:* ${refName}`
: `*Pull request:* <${serverUrl}/${repository}/pull/${prNumber}|${prNumber}>`;
let failures = 0;
for (const r of recipients) {
// Channel posts mention the actor's Slack ID (a real ping) if resolved;
// otherwise (and for DMs) show the plain GitHub login.
const authorText = r.mention ? `<@${r.mention}>` : actor;
const blocks = [
{ type: 'header', text: { type: 'plain_text', text: `:x: ${headerText}`, emoji: true } },
{ type: 'divider' },
{ type: 'rich_text', elements: [ { type: 'rich_text_section', elements: [
{ type: 'emoji', name: summaryIcon },
{ type: 'text', text: summaryText, style: { bold: true } }
] } ] },
{ type: 'divider' },
{ type: 'section', fields: [
{ type: 'mrkdwn', text: `*Author:* ${authorText}` },
{ type: 'mrkdwn', text: `<${serverUrl}/${repository}/actions/runs/${runId}|View failed workflow>` },
{ type: 'mrkdwn', text: `*Last commit:* <${serverUrl}/${repository}/commit/${commitSha}|${commitSha}>` },
{ type: 'mrkdwn', text: refLink }
] }
];
const res = await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify({ channel: r.slack_id, blocks })
});
const data = await res.json();
if (!data.ok) {
core.warning(`Slack chat.postMessage failed for ${r.slack_id}: ${data.error}`);
failures++;
} else {
core.info(`Notified ${r.slack_id} (${r.role})`);
}
}
if (failures > 0 && failures === recipients.length) {
core.setFailed(`All ${failures} Slack notifications failed`);
}