mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-19 01:45:48 +08:00
ci: Unify Slack notifications across workflows (#30483)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
dc72a8042c
commit
6eb4b4c1a5
+49
-2
@@ -455,6 +455,15 @@ Scripts in `.github/scripts/`:
|
||||
| `validate-docs-links.js`| Check doc URLs | `util-check-docs-urls.yml`|
|
||||
| `send-build-stats.mjs` | Build telemetry | `setup-nodejs` action |
|
||||
|
||||
### Slack Scripts
|
||||
|
||||
See [Slack Notifications](#slack-notifications) for the calling pattern.
|
||||
|
||||
| Script | Purpose |
|
||||
|---------------------------------|-------------------------------------------------------------------------------|
|
||||
| `slack/notify.mjs` | CLI + `sendSlackMessage` export. POSTs `chat.postMessage`, fails on `ok:false`. |
|
||||
| `slack/build-trivy-blocks.mjs` | `--blocks trivy` — vulnerability digest |
|
||||
|
||||
---
|
||||
|
||||
## Telemetry
|
||||
@@ -556,7 +565,7 @@ Supply chain security ensures artifacts haven't been tampered with. We provide t
|
||||
|
||||
- **Runs on:** stable/nightly/rc Docker builds
|
||||
- **Scans:** n8n image, runners image
|
||||
- **Output:** Slack `#notify-security-scan-outputs` (all), `#mission-security` (critical)
|
||||
- **Output:** Slack `#updates-security` when vulnerabilities are detected
|
||||
|
||||
### SBOM
|
||||
|
||||
@@ -634,6 +643,44 @@ cosign verify-attestation --type openvex \
|
||||
|
||||
---
|
||||
|
||||
## Slack Notifications
|
||||
|
||||
All workflows post via `.github/scripts/slack/notify.mjs` — a direct `fetch` to `chat.postMessage` that exits non-zero on any Slack error. No third-party action; no silent swallowing.
|
||||
|
||||
```yaml
|
||||
notify-on-failure:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
if: ${{ always() && contains(needs.*.result, 'failure') }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
sparse-checkout: .github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Notify Slack
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel '#alerts-build' \
|
||||
--text 'Build failed - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
```
|
||||
|
||||
If notify is a step inside an existing checked-out job, skip the `checkout` and gate with step-level `if: failure()` instead.
|
||||
|
||||
> `if: failure()` at the **step** level of a dedicated notify job is a no-op when a `needs:` dependency fails (the job is skipped before steps evaluate). Always gate the **job** with `if: ${{ always() && contains(needs.*.result, 'failure') }}`.
|
||||
|
||||
**Rich payloads (Block Kit):** add `build-<name>-blocks.mjs` whose default export returns a blocks array, then pass `--blocks <name>` plus any workflow-specific args. Builders read repo / run context from `GITHUB_*` runner env vars. Kebab-case flags become camelCase keys for the builder (`--image-ref` → `imageRef`).
|
||||
|
||||
| Token | Bot | Channels |
|
||||
|------------------------------|----------------|-------------------------------------------------------------|
|
||||
| `QBOT_SLACK_TOKEN` | QBot | Default — engineering / build / security |
|
||||
| `RELEASE_HELPER_SLACK_TOKEN` | Release Helper | `#releases` (C036AELNMV0) |
|
||||
|
||||
Adding a new channel requires inviting the bot first; the first run otherwise fails loudly with `not_in_channel`. Private-repo workflows (`sec-publish-fix*.yml`) need `QBOT_SLACK_TOKEN` set in `n8n-io/n8n-private`; the scripts themselves are mirrored by `sec-sync-public-to-private.yml`.
|
||||
|
||||
---
|
||||
|
||||
## Secrets
|
||||
|
||||
### By Category
|
||||
@@ -641,7 +688,7 @@ cosign verify-attestation --type openvex \
|
||||
| Category | Secrets |
|
||||
|---------------------|-------------------------------------------------------------|
|
||||
| Package Publishing | `NPM_TOKEN`, `DOCKER_USERNAME`, `DOCKER_PASSWORD` |
|
||||
| Notifications | `SLACK_WEBHOOK_URL`, `QBOT_SLACK_TOKEN` |
|
||||
| Notifications | `QBOT_SLACK_TOKEN`, `RELEASE_HELPER_SLACK_TOKEN` |
|
||||
| Code Quality | `CODECOV_TOKEN`, `CHROMATIC_PROJECT_TOKEN`, `CURRENTS_RECORD_KEY` |
|
||||
| Error Tracking | `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`, `SENTRY_*_PROJECT` |
|
||||
| Cloud/CDN | `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID` |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "workflow-scripts",
|
||||
"scripts": {
|
||||
"test": "node --test --experimental-test-module-mocks ./*.test.mjs ./quality/*.test.mjs"
|
||||
"test": "node --test --experimental-test-module-mocks ./*.test.mjs ./quality/*.test.mjs ./slack/*.test.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/github": "9.0.0",
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Build Block Kit blocks for the nightly Trivy vulnerability digest.
|
||||
*
|
||||
* Workflow-specific inputs (passed via notify.mjs flags):
|
||||
* results — path to trivy-results.json
|
||||
* imageRef — full image reference (ghcr.io/n8n-io/n8n:nightly)
|
||||
*
|
||||
* Repo / run context is read from the GitHub Actions runner env
|
||||
* (GITHUB_REPOSITORY, GITHUB_SERVER_URL, GITHUB_RUN_ID). Pass `env` to
|
||||
* override in tests.
|
||||
*/
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
const SEVERITY_RANK = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
|
||||
const SEVERITY_EMOJI = {
|
||||
CRITICAL: ':red_circle:',
|
||||
HIGH: ':large_orange_circle:',
|
||||
MEDIUM: ':large_yellow_circle:',
|
||||
LOW: ':large_green_circle:',
|
||||
};
|
||||
const MAX_CVE_BLOCKS = 8;
|
||||
|
||||
export default function buildTrivyBlocks({ results, imageRef, env = process.env }) {
|
||||
const repoName = env.GITHUB_REPOSITORY;
|
||||
const repoUrl = `${env.GITHUB_SERVER_URL}/${repoName}`;
|
||||
const runUrl = `${repoUrl}/actions/runs/${env.GITHUB_RUN_ID}`;
|
||||
|
||||
const report = JSON.parse(readFileSync(results, 'utf8'));
|
||||
|
||||
const allVulns = (report.Results ?? [])
|
||||
.flatMap((r) => r.Vulnerabilities ?? [])
|
||||
.filter((v) => v && v.VulnerabilityID);
|
||||
|
||||
const seen = new Set();
|
||||
const uniqueVulns = [];
|
||||
for (const v of allVulns) {
|
||||
if (seen.has(v.VulnerabilityID)) continue;
|
||||
seen.add(v.VulnerabilityID);
|
||||
uniqueVulns.push(v);
|
||||
}
|
||||
|
||||
const counts = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 };
|
||||
for (const v of uniqueVulns) {
|
||||
if (v.Severity in counts) counts[v.Severity]++;
|
||||
}
|
||||
|
||||
const cvssOf = (v) => v?.CVSS?.nvd?.V3Score ?? 0;
|
||||
|
||||
uniqueVulns.sort((a, b) => {
|
||||
const sevDiff = (SEVERITY_RANK[a.Severity] ?? 99) - (SEVERITY_RANK[b.Severity] ?? 99);
|
||||
if (sevDiff !== 0) return sevDiff;
|
||||
return cvssOf(b) - cvssOf(a);
|
||||
});
|
||||
|
||||
const cveBlock = (v) => ({
|
||||
type: 'section',
|
||||
text: {
|
||||
type: 'mrkdwn',
|
||||
text: [
|
||||
`${SEVERITY_EMOJI[v.Severity] ?? ':white_circle:'} *<https://nvd.nist.gov/vuln/detail/${v.VulnerabilityID}|${v.VulnerabilityID}>* (CVSS: \`${v.CVSS?.nvd?.V3Score ?? 'N/A'}\`)`,
|
||||
`*Package:* \`${v.PkgName}@${v.InstalledVersion}\` → \`${v.FixedVersion ?? 'No fix available'}\``,
|
||||
].join('\n'),
|
||||
},
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
type: 'header',
|
||||
text: { type: 'plain_text', text: ':warning: Trivy Scan: Vulnerabilities Detected' },
|
||||
},
|
||||
{
|
||||
type: 'section',
|
||||
fields: [
|
||||
{ type: 'mrkdwn', text: `*Repository:*\n<${repoUrl}|${repoName}>` },
|
||||
{ type: 'mrkdwn', text: `*Image:*\n\`${imageRef}\`` },
|
||||
{ type: 'mrkdwn', text: `*Critical:*\n:red_circle: ${counts.CRITICAL}` },
|
||||
{ type: 'mrkdwn', text: `*High:*\n:large_orange_circle: ${counts.HIGH}` },
|
||||
{ type: 'mrkdwn', text: `*Medium:*\n:large_yellow_circle: ${counts.MEDIUM}` },
|
||||
{ type: 'mrkdwn', text: `*Low:*\n:large_green_circle: ${counts.LOW}` },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'context',
|
||||
elements: [
|
||||
{ type: 'mrkdwn', text: `:shield: ${uniqueVulns.length} unique CVEs affecting packages` },
|
||||
],
|
||||
},
|
||||
{ type: 'divider' },
|
||||
...uniqueVulns.slice(0, MAX_CVE_BLOCKS).map(cveBlock),
|
||||
{ type: 'divider' },
|
||||
{
|
||||
type: 'actions',
|
||||
elements: [
|
||||
{
|
||||
type: 'button',
|
||||
text: { type: 'plain_text', text: ':github: View Full Report' },
|
||||
style: 'primary',
|
||||
url: runUrl,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import { mkdtempSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import buildTrivyBlocks from './build-trivy-blocks.mjs';
|
||||
|
||||
const ENV = {
|
||||
GITHUB_REPOSITORY: 'n8n-io/n8n',
|
||||
GITHUB_SERVER_URL: 'https://github.com',
|
||||
GITHUB_RUN_ID: '1',
|
||||
};
|
||||
|
||||
function build(report) {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'trivy-blocks-'));
|
||||
const path = join(dir, 'trivy-results.json');
|
||||
writeFileSync(path, JSON.stringify(report));
|
||||
return buildTrivyBlocks({
|
||||
results: path,
|
||||
imageRef: 'ghcr.io/n8n-io/n8n:nightly',
|
||||
env: ENV,
|
||||
});
|
||||
}
|
||||
|
||||
test('counts vulnerabilities by severity', () => {
|
||||
const blocks = build({
|
||||
Results: [{
|
||||
Vulnerabilities: [
|
||||
{ VulnerabilityID: 'CVE-1', Severity: 'CRITICAL', PkgName: 'a', InstalledVersion: '1' },
|
||||
{ VulnerabilityID: 'CVE-2', Severity: 'HIGH', PkgName: 'b', InstalledVersion: '2' },
|
||||
{ VulnerabilityID: 'CVE-3', Severity: 'HIGH', PkgName: 'c', InstalledVersion: '3' },
|
||||
{ VulnerabilityID: 'CVE-4', Severity: 'MEDIUM', PkgName: 'd', InstalledVersion: '4' },
|
||||
{ VulnerabilityID: 'CVE-5', Severity: 'LOW', PkgName: 'e', InstalledVersion: '5' },
|
||||
],
|
||||
}],
|
||||
});
|
||||
const counts = blocks.find((b) => b.type === 'section' && b.fields);
|
||||
const fields = Object.fromEntries(
|
||||
counts.fields.map((f) => f.text.split('\n')).map(([k, v]) => [k, v]),
|
||||
);
|
||||
assert.equal(fields['*Critical:*'], ':red_circle: 1');
|
||||
assert.equal(fields['*High:*'], ':large_orange_circle: 2');
|
||||
assert.equal(fields['*Medium:*'], ':large_yellow_circle: 1');
|
||||
assert.equal(fields['*Low:*'], ':large_green_circle: 1');
|
||||
});
|
||||
|
||||
test('dedupes vulnerabilities by CVE id', () => {
|
||||
const blocks = build({
|
||||
Results: [{
|
||||
Vulnerabilities: [
|
||||
{ VulnerabilityID: 'CVE-1', Severity: 'HIGH', PkgName: 'a', InstalledVersion: '1' },
|
||||
{ VulnerabilityID: 'CVE-1', Severity: 'HIGH', PkgName: 'a', InstalledVersion: '1' },
|
||||
{ VulnerabilityID: 'CVE-2', Severity: 'HIGH', PkgName: 'b', InstalledVersion: '2' },
|
||||
],
|
||||
}],
|
||||
});
|
||||
const ctx = blocks.find((b) => b.type === 'context');
|
||||
assert.match(ctx.elements[0].text, /2 unique CVEs/);
|
||||
});
|
||||
|
||||
test('sorts by severity then CVSS', () => {
|
||||
const blocks = build({
|
||||
Results: [{
|
||||
Vulnerabilities: [
|
||||
{ VulnerabilityID: 'CVE-LOW-1', Severity: 'LOW', PkgName: 'a', InstalledVersion: '1' },
|
||||
{ VulnerabilityID: 'CVE-HIGH-LOWCVSS', Severity: 'HIGH', PkgName: 'b', InstalledVersion: '2', CVSS: { nvd: { V3Score: 5 } } },
|
||||
{ VulnerabilityID: 'CVE-HIGH-HIGHCVSS', Severity: 'HIGH', PkgName: 'c', InstalledVersion: '3', CVSS: { nvd: { V3Score: 9 } } },
|
||||
{ VulnerabilityID: 'CVE-CRIT-1', Severity: 'CRITICAL', PkgName: 'd', InstalledVersion: '4' },
|
||||
],
|
||||
}],
|
||||
});
|
||||
const cveSections = blocks.filter((b) => b.type === 'section' && b.text?.type === 'mrkdwn');
|
||||
const ids = cveSections.map((b) => b.text.text.match(/CVE-[A-Z0-9-]+/)?.[0]);
|
||||
assert.deepEqual(ids, ['CVE-CRIT-1', 'CVE-HIGH-HIGHCVSS', 'CVE-HIGH-LOWCVSS', 'CVE-LOW-1']);
|
||||
});
|
||||
|
||||
test('caps CVE detail blocks at 8', () => {
|
||||
const vulns = Array.from({ length: 20 }, (_, i) => ({
|
||||
VulnerabilityID: `CVE-${i}`,
|
||||
Severity: 'HIGH',
|
||||
PkgName: `p${i}`,
|
||||
InstalledVersion: '1',
|
||||
}));
|
||||
const blocks = build({ Results: [{ Vulnerabilities: vulns }] });
|
||||
const cveSections = blocks.filter((b) => b.type === 'section' && b.text?.type === 'mrkdwn');
|
||||
assert.equal(cveSections.length, 8);
|
||||
});
|
||||
|
||||
test('emits view-report button with run url from GH env', () => {
|
||||
const blocks = build({ Results: [{ Vulnerabilities: [
|
||||
{ VulnerabilityID: 'CVE-1', Severity: 'HIGH', PkgName: 'a', InstalledVersion: '1' },
|
||||
] }] });
|
||||
const actions = blocks.find((b) => b.type === 'actions');
|
||||
assert.equal(actions.elements[0].url, 'https://github.com/n8n-io/n8n/actions/runs/1');
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Send a Slack chat.postMessage from a GitHub Actions step.
|
||||
*
|
||||
* Usage:
|
||||
* node .github/scripts/slack/notify.mjs \
|
||||
* --channel '#alerts-build' \
|
||||
* --text 'CI failed on master'
|
||||
*
|
||||
* Rich blocks via a builder module under .github/scripts/slack/:
|
||||
* node .github/scripts/slack/notify.mjs \
|
||||
* --channel C0AHNJU9XFA \
|
||||
* --text 'Trivy: 14 high vulns in nightly' \
|
||||
* --blocks trivy \
|
||||
* --results trivy-results.json \
|
||||
* --image-ref $IMAGE_REF
|
||||
*
|
||||
* --blocks <name> resolves to ./build-<name>-blocks.mjs (default export receives the args).
|
||||
* Builders read repo / run context from $GITHUB_* runner env vars.
|
||||
*
|
||||
* Bot token: $SLACK_TOKEN (required).
|
||||
*
|
||||
* Exits non-zero on any Slack API error so the step fails loudly.
|
||||
*/
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
export async function sendSlackMessage({ token, channel, text, blocks }) {
|
||||
const payload = { channel, text };
|
||||
if (blocks) payload.blocks = blocks;
|
||||
|
||||
const res = await fetch('https://slack.com/api/chat.postMessage', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
const body = await res.json();
|
||||
if (!body.ok) {
|
||||
console.error('Slack chat.postMessage failed:');
|
||||
console.error(JSON.stringify(body, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`Slack message posted to ${body.channel} at ts=${body.ts}`);
|
||||
return body;
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const out = {};
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const a = argv[i];
|
||||
if (!a.startsWith('--')) continue;
|
||||
const key = a.slice(2);
|
||||
const next = argv[i + 1];
|
||||
const value = next && !next.startsWith('--') ? next : 'true';
|
||||
out[key] = value;
|
||||
if (value !== 'true') i++;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function kebabToCamel(s) {
|
||||
return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
|
||||
const channel = args.channel;
|
||||
const text = args.text;
|
||||
const blocksName = args.blocks;
|
||||
|
||||
if (!channel) throw new Error('--channel is required');
|
||||
if (!text) throw new Error('--text is required');
|
||||
|
||||
const token = process.env.SLACK_TOKEN;
|
||||
if (!token) throw new Error('SLACK_TOKEN env var is required');
|
||||
|
||||
let blocks;
|
||||
if (blocksName) {
|
||||
const { default: builder } = await import(`./build-${blocksName}-blocks.mjs`);
|
||||
const builderArgs = Object.fromEntries(
|
||||
Object.entries(args)
|
||||
.filter(([k]) => k !== 'channel' && k !== 'text' && k !== 'blocks')
|
||||
.map(([k, v]) => [kebabToCamel(k), v]),
|
||||
);
|
||||
blocks = builder(builderArgs);
|
||||
}
|
||||
|
||||
await sendSlackMessage({ token, channel, text, blocks });
|
||||
}
|
||||
|
||||
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
await main();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { test, mock } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import { sendSlackMessage } from './notify.mjs';
|
||||
|
||||
function mockFetch(responseBody, status = 200) {
|
||||
return mock.method(globalThis, 'fetch', async () => ({
|
||||
status,
|
||||
json: async () => responseBody,
|
||||
}));
|
||||
}
|
||||
|
||||
test('posts channel + text to chat.postMessage', async (t) => {
|
||||
const fetchMock = mockFetch({ ok: true, channel: 'C123', ts: '1.0' });
|
||||
t.after(() => fetchMock.mock.restore());
|
||||
|
||||
await sendSlackMessage({ token: 'xoxb-test', channel: '#x', text: 'hi' });
|
||||
|
||||
const [url, init] = fetchMock.mock.calls[0].arguments;
|
||||
assert.equal(url, 'https://slack.com/api/chat.postMessage');
|
||||
assert.equal(init.method, 'POST');
|
||||
assert.equal(init.headers.Authorization, 'Bearer xoxb-test');
|
||||
assert.equal(init.headers['Content-Type'], 'application/json; charset=utf-8');
|
||||
assert.deepEqual(JSON.parse(init.body), { channel: '#x', text: 'hi' });
|
||||
});
|
||||
|
||||
test('includes blocks when provided', async (t) => {
|
||||
const fetchMock = mockFetch({ ok: true, channel: 'C123', ts: '1.0' });
|
||||
t.after(() => fetchMock.mock.restore());
|
||||
|
||||
const blocks = [{ type: 'section', text: { type: 'mrkdwn', text: 'x' } }];
|
||||
await sendSlackMessage({ token: 't', channel: '#x', text: 'hi', blocks });
|
||||
|
||||
const [, init] = fetchMock.mock.calls[0].arguments;
|
||||
assert.deepEqual(JSON.parse(init.body), { channel: '#x', text: 'hi', blocks });
|
||||
});
|
||||
|
||||
test('exits non-zero on Slack ok:false', async (t) => {
|
||||
const fetchMock = mockFetch({ ok: false, error: 'not_in_channel' });
|
||||
const exitMock = mock.method(process, 'exit', () => {
|
||||
throw new Error('exit called');
|
||||
});
|
||||
const errMock = mock.method(console, 'error', () => {});
|
||||
t.after(() => {
|
||||
fetchMock.mock.restore();
|
||||
exitMock.mock.restore();
|
||||
errMock.mock.restore();
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
sendSlackMessage({ token: 't', channel: '#x', text: 'hi' }),
|
||||
/exit called/,
|
||||
);
|
||||
assert.equal(exitMock.mock.calls[0].arguments[0], 1);
|
||||
});
|
||||
@@ -55,42 +55,10 @@ jobs:
|
||||
|
||||
- name: Send Slack notification on failure
|
||||
if: failure() && inputs.notify_on_failure == true
|
||||
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
payload: |
|
||||
{
|
||||
"channel": "C035KBDA917",
|
||||
"text": "🚨 Windows build failed for `${{ github.repository }}` on branch `${{ github.ref_name }}`",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "header",
|
||||
"text": { "type": "plain_text", "text": "🚨 Windows Build Failed" }
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"fields": [
|
||||
{ "type": "mrkdwn", "text": "*Repository:*\n<${{ github.server_url }}/${{ github.repository }}|${{ github.repository }}>" },
|
||||
{ "type": "mrkdwn", "text": "*Branch:*\n`${{ github.ref_name }}`" },
|
||||
{ "type": "mrkdwn", "text": "*Commit:*\n`${{ github.sha }}`" },
|
||||
{ "type": "mrkdwn", "text": "*Trigger:*\n${{ github.event_name }}" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": { "type": "mrkdwn", "text": ":warning: *Cross-platform compatibility issue detected*\nThis likely indicates Unix-specific commands in package.json scripts or build configuration that don't work on Windows." }
|
||||
},
|
||||
{
|
||||
"type": "actions",
|
||||
"elements": [
|
||||
{
|
||||
"type": "button",
|
||||
"text": { "type": "plain_text", "text": ":github: View Workflow Run" },
|
||||
"style": "danger",
|
||||
"url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
shell: bash
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel C035KBDA917 \
|
||||
--text "🚨 Windows build failed for \`$GITHUB_REPOSITORY\` on \`$GITHUB_REF_NAME\` (\`$GITHUB_SHA\`) — ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
|
||||
|
||||
@@ -29,19 +29,16 @@ jobs:
|
||||
|
||||
- name: Notify Slack about new packages
|
||||
if: steps.detect.outcome == 'failure' && steps.detect.outputs.packages != ''
|
||||
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.RELEASE_HELPER_SLACK_TOKEN }}
|
||||
payload: |
|
||||
channel: C036AELNMV0
|
||||
text: |-
|
||||
:warning: *New unpublished packages detected* after merging <${{ github.event.pull_request.html_url }}|PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }}>
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.RELEASE_HELPER_SLACK_TOKEN }}
|
||||
MESSAGE: |-
|
||||
:warning: *New unpublished packages detected* after merging <${{ github.event.pull_request.html_url }}|PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }}>
|
||||
|
||||
The following packages do not exist on npm yet: `${{ steps.detect.outputs.packages }}`
|
||||
The following packages do not exist on npm yet: `${{ steps.detect.outputs.packages }}`
|
||||
|
||||
*If a package is not intended for npm*, set `"private": true` in its `package.json` to exclude it from future checks.
|
||||
*If a package is not intended for npm*, set `"private": true` in its `package.json` to exclude it from future checks.
|
||||
|
||||
*Otherwise, to unblock the next release:*
|
||||
1. Run the <${{ github.server_url }}/${{ github.repository }}/actions/workflows/release-publish-new-package.yml|Release: Publish New Package> workflow for each package
|
||||
2. Configure Trusted Publishing on npmjs.com (owner: `n8n-io`, repo: `n8n`, workflow: `release-publish.yml`)
|
||||
*Otherwise, to unblock the next release:*
|
||||
1. Run the <${{ github.server_url }}/${{ github.repository }}/actions/workflows/release-publish-new-package.yml|Release: Publish New Package> workflow for each package
|
||||
2. Configure Trusted Publishing on npmjs.com (owner: `n8n-io`, repo: `n8n`, workflow: `release-publish.yml`)
|
||||
run: node .github/scripts/slack/notify.mjs --channel C036AELNMV0 --text "$MESSAGE"
|
||||
|
||||
@@ -51,12 +51,16 @@ jobs:
|
||||
name: Notify Slack on failure
|
||||
runs-on: ubuntu-latest
|
||||
needs: [unit-test, lint, performance, build-github]
|
||||
if: ${{ always() && contains(needs.*.result, 'failure') }}
|
||||
steps:
|
||||
- name: Notify Slack on failure
|
||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
if: failure()
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#alerts-build'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: ${{ github.ref_name }} branch (build or test or lint) failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
sparse-checkout: .github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Notify Slack
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel '#alerts-build' \
|
||||
--text '${{ github.ref_name }} branch (build or test or lint) failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})'
|
||||
|
||||
@@ -453,9 +453,14 @@ jobs:
|
||||
needs: [build-and-push-docker]
|
||||
if: needs.build-and-push-docker.result == 'failure' && github.event_name == 'schedule'
|
||||
steps:
|
||||
- uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
status: ${{ needs.build-and-push-docker.result }}
|
||||
channel: '#team-catalysts'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: Nightly Docker build failed - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
sparse-checkout: .github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Notify Slack
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel '#team-catalysts' \
|
||||
--text 'Nightly Docker build failed - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
|
||||
@@ -55,10 +55,14 @@ jobs:
|
||||
needs: [docker-smoke-test]
|
||||
if: needs.docker-smoke-test.result == 'failure' && github.event_name == 'schedule'
|
||||
steps:
|
||||
- uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
payload: |
|
||||
channel: C0A9RLY8Y20
|
||||
text: "🚨 Nightly Docker smoke test failed (no-cache build) - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
sparse-checkout: .github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Notify Slack
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel C0A9RLY8Y20 \
|
||||
--text '🚨 Nightly Docker smoke test failed (no-cache build) - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
|
||||
@@ -20,11 +20,14 @@ jobs:
|
||||
if: needs.create-release-pr.result == 'success' && needs.create-release-pr.outputs.pull-request-number != ''
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Post to Slack
|
||||
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.RELEASE_HELPER_SLACK_TOKEN }}
|
||||
payload: |
|
||||
channel: C036AELNMV0
|
||||
text: ":rocket: Minor release PR created. <${{ github.server_url }}/${{ github.repository }}/pull/${{ needs.create-release-pr.outputs.pull-request-number }}|View PR> — close it to cancel the release."
|
||||
sparse-checkout: .github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Notify Slack
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.RELEASE_HELPER_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel C036AELNMV0 \
|
||||
--text ':rocket: Minor release PR created. <${{ github.server_url }}/${{ github.repository }}/pull/${{ needs.create-release-pr.outputs.pull-request-number }}|View PR> — close it to cancel the release.'
|
||||
|
||||
@@ -67,11 +67,14 @@ jobs:
|
||||
if: needs.create-release-pr.result == 'success' && needs.create-release-pr.outputs.pull-request-number != ''
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Post to Slack
|
||||
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.RELEASE_HELPER_SLACK_TOKEN }}
|
||||
payload: |
|
||||
channel: C036AELNMV0
|
||||
text: ":rocket: Patch release PR created for *${{ inputs.track }}* track. <${{ github.server_url }}/${{ github.repository }}/pull/${{ needs.create-release-pr.outputs.pull-request-number }}|View PR> — close it to cancel the release."
|
||||
sparse-checkout: .github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Notify Slack
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.RELEASE_HELPER_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel C036AELNMV0 \
|
||||
--text ':rocket: Patch release PR created for *${{ inputs.track }}* track. <${{ github.server_url }}/${{ github.repository }}/pull/${{ needs.create-release-pr.outputs.pull-request-number }}|View PR> — close it to cancel the release.'
|
||||
|
||||
@@ -55,12 +55,21 @@ jobs:
|
||||
- name: Push to ${{ inputs.target-branch }}
|
||||
run: git push origin "HEAD:${TARGET_BRANCH}"
|
||||
|
||||
- name: Notify Slack on failure
|
||||
if: failure()
|
||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
notify-on-failure:
|
||||
name: Notify Slack on failure
|
||||
needs: [merge-tag-to-branch]
|
||||
if: ${{ always() && needs.merge-tag-to-branch.result == 'failure' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#updates-and-product-releases'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: |
|
||||
<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}| Release tag merge to ${{ inputs.target-branch }} failed for n8n@${{ inputs.version }} >
|
||||
ref: master
|
||||
sparse-checkout: .github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Notify Slack
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel '#updates-and-product-releases' \
|
||||
--text '<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Release tag merge to ${{ inputs.target-branch }} failed for n8n@${{ inputs.version }}>'
|
||||
|
||||
@@ -12,7 +12,7 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
SLACK_WEBHOOK_URL:
|
||||
QBOT_SLACK_TOKEN:
|
||||
required: true
|
||||
|
||||
workflow_dispatch:
|
||||
@@ -80,10 +80,9 @@ jobs:
|
||||
|
||||
- name: Notify Slack on failure
|
||||
if: failure()
|
||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#alerts-build'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: |
|
||||
<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}| SBOM generation and attachment failed for release ${{ inputs.release_tag_ref }} >
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel '#alerts-build' \
|
||||
--text '<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|SBOM generation and attachment failed for release ${{ inputs.release_tag_ref }}>'
|
||||
|
||||
@@ -51,11 +51,21 @@ jobs:
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
|
||||
- name: Notify on failure
|
||||
if: failure()
|
||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
notify-on-failure:
|
||||
name: Notify Slack on failure
|
||||
needs: [sync-security-fix]
|
||||
if: ${{ always() && needs.sync-security-fix.result == 'failure' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#alerts-security'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: 'Security fix PR creation failed (1.x). Run "Security: Sync from Public" workflow, rebase your branch, reopen PR. (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})'
|
||||
ref: master
|
||||
sparse-checkout: .github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Notify Slack
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel '#alerts-security' \
|
||||
--text 'Security fix PR creation failed (1.x). Run "Security: Sync from Public" workflow, rebase your branch, reopen PR. (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})'
|
||||
|
||||
@@ -53,9 +53,9 @@ jobs:
|
||||
|
||||
- name: Notify on failure
|
||||
if: failure()
|
||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#alerts-security'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: 'Security fix PR creation failed. Run "Security: Sync from Public" workflow, rebase your branch, reopen PR. (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})'
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel '#alerts-security' \
|
||||
--text 'Security fix PR creation failed. Run "Security: Sync from Public" workflow, rebase your branch, reopen PR. (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})'
|
||||
|
||||
@@ -21,7 +21,6 @@ permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
QBOT_SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
SLACK_CHANNEL_ID: C0AHNJU9XFA #updates-security
|
||||
|
||||
jobs:
|
||||
@@ -37,6 +36,7 @@ jobs:
|
||||
security/trivy.yaml
|
||||
security/trivy-ignore-policy.rego
|
||||
.github/scripts/retry.mjs
|
||||
.github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Pull Docker image with retry
|
||||
@@ -166,98 +166,16 @@ jobs:
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
|
||||
- name: Generate Slack Blocks JSON
|
||||
- name: Send Slack notification
|
||||
if: steps.process_results.outputs.vulnerabilities_found == 'true'
|
||||
id: generate_blocks
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
IMAGE_REF: ${{ inputs.image_ref }}
|
||||
run: |
|
||||
BLOCKS_JSON=$(jq -c --arg image_ref "$IMAGE_REF" \
|
||||
--arg repo_url "${{ github.server_url }}/${{ github.repository }}" \
|
||||
--arg repo_name "${{ github.repository }}" \
|
||||
--arg run_url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
||||
--arg critical_count "${{ steps.process_results.outputs.critical_count }}" \
|
||||
--arg high_count "${{ steps.process_results.outputs.high_count }}" \
|
||||
--arg medium_count "${{ steps.process_results.outputs.medium_count }}" \
|
||||
--arg low_count "${{ steps.process_results.outputs.low_count }}" \
|
||||
--arg unique_cves "${{ steps.process_results.outputs.unique_cves }}" \
|
||||
'
|
||||
# Function to create a vulnerability block with emoji indicators
|
||||
def vuln_block: {
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "\(if .Severity == "CRITICAL" then ":red_circle:" elif .Severity == "HIGH" then ":large_orange_circle:" elif .Severity == "MEDIUM" then ":large_yellow_circle:" else ":large_green_circle:" end) *<https://nvd.nist.gov/vuln/detail/\(.VulnerabilityID)|\(.VulnerabilityID)>* (CVSS: `\(.CVSS.nvd.V3Score // "N/A")`)\n*Package:* `\(.PkgName)@\(.InstalledVersion)` → `\(.FixedVersion // "No fix available")`"
|
||||
}
|
||||
};
|
||||
|
||||
# Main structure
|
||||
[
|
||||
{
|
||||
"type": "header",
|
||||
"text": { "type": "plain_text", "text": ":warning: Trivy Scan: Vulnerabilities Detected" }
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"fields": [
|
||||
{ "type": "mrkdwn", "text": "*Repository:*\n<\($repo_url)|\($repo_name)>" },
|
||||
{ "type": "mrkdwn", "text": "*Image:*\n`\($image_ref)`" },
|
||||
{ "type": "mrkdwn", "text": "*Critical:*\n:red_circle: \($critical_count)" },
|
||||
{ "type": "mrkdwn", "text": "*High:*\n:large_orange_circle: \($high_count)" },
|
||||
{ "type": "mrkdwn", "text": "*Medium:*\n:large_yellow_circle: \($medium_count)" },
|
||||
{ "type": "mrkdwn", "text": "*Low:*\n:large_green_circle: \($low_count)" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "context",
|
||||
"elements": [
|
||||
{ "type": "mrkdwn", "text": ":shield: \($unique_cves) unique CVEs affecting packages" }
|
||||
]
|
||||
},
|
||||
{ "type": "divider" }
|
||||
] +
|
||||
(
|
||||
# Group vulnerabilities by CVE to avoid duplicates in notification
|
||||
[.Results[] | select(.Vulnerabilities != null) | .Vulnerabilities[]] |
|
||||
group_by(.VulnerabilityID) |
|
||||
map(.[0]) |
|
||||
sort_by(
|
||||
(if .Severity == "CRITICAL" then 0
|
||||
elif .Severity == "HIGH" then 1
|
||||
elif .Severity == "MEDIUM" then 2
|
||||
elif .Severity == "LOW" then 3
|
||||
else 4 end),
|
||||
-((.CVSS.nvd.V3Score // 0) | tonumber? // 0)
|
||||
) |
|
||||
.[:8] |
|
||||
map(. | vuln_block)
|
||||
) +
|
||||
[
|
||||
{ "type": "divider" },
|
||||
{
|
||||
"type": "actions",
|
||||
"elements": [
|
||||
{
|
||||
"type": "button",
|
||||
"text": { "type": "plain_text", "text": ":github: View Full Report" },
|
||||
"style": "primary",
|
||||
"url": $run_url
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
' trivy-results.json)
|
||||
|
||||
echo "slack_blocks=$BLOCKS_JSON" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Send Slack Notification
|
||||
if: steps.process_results.outputs.vulnerabilities_found == 'true'
|
||||
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
payload: |
|
||||
channel: ${{ env.SLACK_CHANNEL_ID }}
|
||||
text: "🚨 Trivy Scan: ${{ steps.process_results.outputs.critical_count }} Critical, ${{ steps.process_results.outputs.high_count }} High, ${{ steps.process_results.outputs.medium_count }} Medium, ${{ steps.process_results.outputs.low_count }} Low vulnerabilities found in ${{ inputs.image_ref }}"
|
||||
blocks: ${{ steps.generate_blocks.outputs.slack_blocks }}
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel "$SLACK_CHANNEL_ID" \
|
||||
--text "🚨 Trivy Scan: ${{ steps.process_results.outputs.critical_count }} Critical, ${{ steps.process_results.outputs.high_count }} High, ${{ steps.process_results.outputs.medium_count }} Medium, ${{ steps.process_results.outputs.low_count }} Low vulnerabilities found in $IMAGE_REF" \
|
||||
--blocks trivy \
|
||||
--results trivy-results.json \
|
||||
--image-ref "$IMAGE_REF"
|
||||
|
||||
|
||||
@@ -105,11 +105,16 @@ jobs:
|
||||
name: Notify Cats on failure
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
if: failure()
|
||||
if: ${{ always() && contains(needs.*.result, 'failure') }}
|
||||
steps:
|
||||
- uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#team-catalysts'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: Benchmark run failed for n8n tag `${{ inputs.n8n_tag || 'nightly' }}` - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
sparse-checkout: .github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Notify Slack
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel '#team-catalysts' \
|
||||
--text "Benchmark run failed for n8n tag \`${{ inputs.n8n_tag || 'nightly' }}\` - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
|
||||
@@ -36,11 +36,16 @@ jobs:
|
||||
name: Notify Slack on failure
|
||||
runs-on: ubuntu-slim
|
||||
needs: [e2e]
|
||||
if: failure() && github.event_name == 'schedule'
|
||||
if: ${{ always() && github.event_name == 'schedule' && contains(needs.*.result, 'failure') }}
|
||||
steps:
|
||||
- uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#project-secure-expression-evaluation'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: "VM expression E2E nightly failed - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
sparse-checkout: .github/scripts/slack
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Notify Slack
|
||||
env:
|
||||
SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }}
|
||||
run: |
|
||||
node .github/scripts/slack/notify.mjs \
|
||||
--channel '#project-secure-expression-evaluation' \
|
||||
--text 'VM expression E2E nightly failed - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
|
||||
Reference in New Issue
Block a user