Skip to content

chore: Release 1.1.3 (#112) #253

chore: Release 1.1.3 (#112)

chore: Release 1.1.3 (#112) #253

Workflow file for this run

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`);
}