From d3f1cb05e5970f6bcbda803c2d85751412da23e3 Mon Sep 17 00:00:00 2001 From: dewanshparashar Date: Thu, 23 Jul 2026 13:47:51 +0530 Subject: [PATCH 1/2] feat: support Slack webhook URL as alternative to token+channel for all monitors Each monitor's Slack poster now works on an either-or config basis: - SLACK_WEBHOOK_URL env var (preferred: incoming webhooks are scoped to a single channel, less prone to abuse), or - SLACK_TOKEN + SLACK_CHANNEL (existing behavior, unchanged) If both are configured, the webhook takes precedence. Co-Authored-By: Claude Fable 5 --- .env.sample | 8 ++ README.md | 13 +- packages/assertion-monitor/README.md | 5 +- .../reportAssertionMonitorAlertToSlack.ts | 1 + packages/batch-poster-monitor/README.md | 5 +- .../reportBatchPosterAlertToSlack.ts | 1 + packages/retryable-monitor/README.md | 5 +- .../handlers/slack/postSlackMessage.ts | 1 + .../utils/__test__/createSlackPoster.test.ts | 112 ++++++++++++++++++ packages/utils/createSlackPoster.ts | 36 +++++- packages/utils/index.ts | 1 + packages/utils/postSlackMessageViaWebhook.ts | 36 ++++++ 12 files changed, 213 insertions(+), 11 deletions(-) create mode 100644 packages/utils/postSlackMessageViaWebhook.ts diff --git a/.env.sample b/.env.sample index 11681223..23426ea8 100644 --- a/.env.sample +++ b/.env.sample @@ -1,11 +1,19 @@ NODE_ENV= +# Slack alerting supports either-or configuration per monitor: +# - preferred: a Slack incoming webhook URL (scoped to a single channel) +# - fallback: a bot token + channel +# If both are set, the webhook URL takes precedence. + +RETRYABLE_MONITORING_SLACK_WEBHOOK_URL= RETRYABLE_MONITORING_SLACK_TOKEN= RETRYABLE_MONITORING_SLACK_CHANNEL= RETRYABLE_MONITORING_PRIVATE_KEY= +BATCH_POSTER_MONITORING_SLACK_WEBHOOK_URL= BATCH_POSTER_MONITORING_SLACK_TOKEN= BATCH_POSTER_MONITORING_SLACK_CHANNEL= +ASSERTION_MONITORING_SLACK_WEBHOOK_URL= ASSERTION_MONITORING_SLACK_TOKEN= ASSERTION_MONITORING_SLACK_CHANNEL= diff --git a/README.md b/README.md index bfb75660..a794f213 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,18 @@ cp config.example.json config.json cp .env.sample .env ``` -2. Set up Slack alerts in `.env` (optional): +2. Set up Slack alerts in `.env` (optional). Each monitor supports either-or configuration — provide a Slack incoming webhook URL (preferred, since its permissions are scoped to a single channel), or a bot token + channel. If both are set, the webhook URL takes precedence. + +Option A — webhook URL (preferred): + +```bash +NODE_ENV=CI +RETRYABLE_MONITORING_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/... +BATCH_POSTER_MONITORING_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/... +ASSERTION_MONITORING_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/... +``` + +Option B — bot token + channel: ```bash NODE_ENV=CI diff --git a/packages/assertion-monitor/README.md b/packages/assertion-monitor/README.md index bbc7882c..f29fc80e 100644 --- a/packages/assertion-monitor/README.md +++ b/packages/assertion-monitor/README.md @@ -25,8 +25,9 @@ Examples: yarn assertion-monitor --configPath=custom.json Use custom config file Environment Variables: - ASSERTION_MONITORING_SLACK_TOKEN Slack API token for alerts - ASSERTION_MONITORING_SLACK_CHANNEL Slack channel for alerts + ASSERTION_MONITORING_SLACK_WEBHOOK_URL Slack incoming webhook URL for alerts (preferred; takes precedence over token+channel) + ASSERTION_MONITORING_SLACK_TOKEN Slack API token for alerts (alternative to webhook, use with channel) + ASSERTION_MONITORING_SLACK_CHANNEL Slack channel for alerts (alternative to webhook, use with token) ``` ## Monitor Details diff --git a/packages/assertion-monitor/reportAssertionMonitorAlertToSlack.ts b/packages/assertion-monitor/reportAssertionMonitorAlertToSlack.ts index a7f18b54..6bc02812 100644 --- a/packages/assertion-monitor/reportAssertionMonitorAlertToSlack.ts +++ b/packages/assertion-monitor/reportAssertionMonitorAlertToSlack.ts @@ -1,6 +1,7 @@ import { createSlackPoster } from 'utils' export const reportAssertionMonitorErrorToSlack = createSlackPoster({ + webhookUrlEnvVar: 'ASSERTION_MONITORING_SLACK_WEBHOOK_URL', tokenEnvVar: 'ASSERTION_MONITORING_SLACK_TOKEN', channelEnvVar: 'ASSERTION_MONITORING_SLACK_CHANNEL', }) diff --git a/packages/batch-poster-monitor/README.md b/packages/batch-poster-monitor/README.md index f341fd68..35c9d654 100644 --- a/packages/batch-poster-monitor/README.md +++ b/packages/batch-poster-monitor/README.md @@ -25,8 +25,9 @@ Examples: yarn batch-poster-monitor --configPath=custom.json Use custom config file Environment Variables: - BATCH_POSTER_MONITORING_SLACK_TOKEN Slack API token for alerts - BATCH_POSTER_MONITORING_SLACK_CHANNEL Slack channel for alerts + BATCH_POSTER_MONITORING_SLACK_WEBHOOK_URL Slack incoming webhook URL for alerts (preferred; takes precedence over token+channel) + BATCH_POSTER_MONITORING_SLACK_TOKEN Slack API token for alerts (alternative to webhook, use with channel) + BATCH_POSTER_MONITORING_SLACK_CHANNEL Slack channel for alerts (alternative to webhook, use with token) ``` ## Monitor Details diff --git a/packages/batch-poster-monitor/reportBatchPosterAlertToSlack.ts b/packages/batch-poster-monitor/reportBatchPosterAlertToSlack.ts index 52925c15..4feb02bc 100644 --- a/packages/batch-poster-monitor/reportBatchPosterAlertToSlack.ts +++ b/packages/batch-poster-monitor/reportBatchPosterAlertToSlack.ts @@ -1,6 +1,7 @@ import { createSlackPoster } from 'utils' export const reportBatchPosterErrorToSlack = createSlackPoster({ + webhookUrlEnvVar: 'BATCH_POSTER_MONITORING_SLACK_WEBHOOK_URL', tokenEnvVar: 'BATCH_POSTER_MONITORING_SLACK_TOKEN', channelEnvVar: 'BATCH_POSTER_MONITORING_SLACK_CHANNEL', }) diff --git a/packages/retryable-monitor/README.md b/packages/retryable-monitor/README.md index 51ef3ed7..b65171f3 100644 --- a/packages/retryable-monitor/README.md +++ b/packages/retryable-monitor/README.md @@ -29,8 +29,9 @@ Examples: yarn retryable-monitor --enableAlerting --writeToNotion Enables Slack alerts and syncs retryable data to Notion Environment Variables: - RETRYABLE_MONITORING_SLACK_TOKEN Slack API token for alerts - RETRYABLE_MONITORING_SLACK_CHANNEL Slack channel for alerts + RETRYABLE_MONITORING_SLACK_WEBHOOK_URL Slack incoming webhook URL for alerts (preferred; takes precedence over token+channel) + RETRYABLE_MONITORING_SLACK_TOKEN Slack API token for alerts (alternative to webhook, use with channel) + RETRYABLE_MONITORING_SLACK_CHANNEL Slack channel for alerts (alternative to webhook, use with token) RETRYABLE_MONITORING_NOTION_TOKEN Notion integration token RETRYABLE_MONITORING_NOTION_DB_ID Notion database ID ``` diff --git a/packages/retryable-monitor/handlers/slack/postSlackMessage.ts b/packages/retryable-monitor/handlers/slack/postSlackMessage.ts index b29963e5..487da249 100644 --- a/packages/retryable-monitor/handlers/slack/postSlackMessage.ts +++ b/packages/retryable-monitor/handlers/slack/postSlackMessage.ts @@ -1,6 +1,7 @@ import { createSlackPoster } from 'utils' export const postSlackMessage = createSlackPoster({ + webhookUrlEnvVar: 'RETRYABLE_MONITORING_SLACK_WEBHOOK_URL', tokenEnvVar: 'RETRYABLE_MONITORING_SLACK_TOKEN', channelEnvVar: 'RETRYABLE_MONITORING_SLACK_CHANNEL', }) diff --git a/packages/utils/__test__/createSlackPoster.test.ts b/packages/utils/__test__/createSlackPoster.test.ts index 271c244d..7f4733ae 100644 --- a/packages/utils/__test__/createSlackPoster.test.ts +++ b/packages/utils/__test__/createSlackPoster.test.ts @@ -5,7 +5,12 @@ vi.mock('../postSlackMessage', () => ({ postSlackMessage: vi.fn().mockResolvedValue({ ok: true }), })) +vi.mock('../postSlackMessageViaWebhook', () => ({ + postSlackMessageViaWebhook: vi.fn().mockResolvedValue({ ok: true }), +})) + import { postSlackMessage } from '../postSlackMessage' +import { postSlackMessageViaWebhook } from '../postSlackMessageViaWebhook' describe('createSlackPoster', () => { const originalEnv = process.env @@ -15,6 +20,7 @@ describe('createSlackPoster', () => { process.env = { ...originalEnv } delete process.env.TEST_SLACK_TOKEN delete process.env.TEST_SLACK_CHANNEL + delete process.env.TEST_SLACK_WEBHOOK_URL delete process.env.NODE_ENV }) @@ -40,6 +46,84 @@ describe('createSlackPoster', () => { expect(() => poster({ message: 'test' })).toThrow('Slack channel is required.') }) + test('throws with either-or message when webhook is supported but nothing is configured', () => { + const poster = createSlackPoster({ + tokenEnvVar: 'TEST_SLACK_TOKEN', + channelEnvVar: 'TEST_SLACK_CHANNEL', + webhookUrlEnvVar: 'TEST_SLACK_WEBHOOK_URL', + }) + expect(() => poster({ message: 'test' })).toThrow( + 'Slack configuration is required: set TEST_SLACK_WEBHOOK_URL (preferred) or both TEST_SLACK_TOKEN and TEST_SLACK_CHANNEL.' + ) + }) + + test('throws with either-or message when webhook is supported and only token is set', () => { + process.env.TEST_SLACK_TOKEN = 'token' + const poster = createSlackPoster({ + tokenEnvVar: 'TEST_SLACK_TOKEN', + channelEnvVar: 'TEST_SLACK_CHANNEL', + webhookUrlEnvVar: 'TEST_SLACK_WEBHOOK_URL', + }) + expect(() => poster({ message: 'test' })).toThrow( + 'Slack configuration is required: set TEST_SLACK_WEBHOOK_URL (preferred) or both TEST_SLACK_TOKEN and TEST_SLACK_CHANNEL.' + ) + }) + + test('posts via webhook when only webhook is configured', () => { + process.env.TEST_SLACK_WEBHOOK_URL = 'https://hooks.slack.com/services/x' + const poster = createSlackPoster({ + tokenEnvVar: 'TEST_SLACK_TOKEN', + channelEnvVar: 'TEST_SLACK_CHANNEL', + webhookUrlEnvVar: 'TEST_SLACK_WEBHOOK_URL', + }) + + poster({ message: 'Hello' }) + + expect(postSlackMessageViaWebhook).toHaveBeenCalledWith({ + webhookUrl: 'https://hooks.slack.com/services/x', + message: 'Hello', + }) + expect(postSlackMessage).not.toHaveBeenCalled() + }) + + test('webhook takes precedence when both webhook and token+channel are configured', () => { + process.env.TEST_SLACK_WEBHOOK_URL = 'https://hooks.slack.com/services/x' + process.env.TEST_SLACK_TOKEN = 'token' + process.env.TEST_SLACK_CHANNEL = 'channel' + const poster = createSlackPoster({ + tokenEnvVar: 'TEST_SLACK_TOKEN', + channelEnvVar: 'TEST_SLACK_CHANNEL', + webhookUrlEnvVar: 'TEST_SLACK_WEBHOOK_URL', + }) + + poster({ message: 'Hello' }) + + expect(postSlackMessageViaWebhook).toHaveBeenCalledWith({ + webhookUrl: 'https://hooks.slack.com/services/x', + message: 'Hello', + }) + expect(postSlackMessage).not.toHaveBeenCalled() + }) + + test('falls back to token+channel when webhook is supported but not set', () => { + process.env.TEST_SLACK_TOKEN = 'token' + process.env.TEST_SLACK_CHANNEL = 'channel' + const poster = createSlackPoster({ + tokenEnvVar: 'TEST_SLACK_TOKEN', + channelEnvVar: 'TEST_SLACK_CHANNEL', + webhookUrlEnvVar: 'TEST_SLACK_WEBHOOK_URL', + }) + + poster({ message: 'Hello' }) + + expect(postSlackMessage).toHaveBeenCalledWith({ + slackToken: 'token', + slackChannel: 'channel', + message: 'Hello', + }) + expect(postSlackMessageViaWebhook).not.toHaveBeenCalled() + }) + test('skips posting in DEV mode', () => { process.env.TEST_SLACK_TOKEN = 'token' process.env.TEST_SLACK_CHANNEL = 'channel' @@ -54,6 +138,20 @@ describe('createSlackPoster', () => { expect(postSlackMessage).not.toHaveBeenCalled() }) + test('skips posting in DEV mode with webhook config', () => { + process.env.TEST_SLACK_WEBHOOK_URL = 'https://hooks.slack.com/services/x' + process.env.NODE_ENV = 'DEV' + + const poster = createSlackPoster({ + tokenEnvVar: 'TEST_SLACK_TOKEN', + channelEnvVar: 'TEST_SLACK_CHANNEL', + webhookUrlEnvVar: 'TEST_SLACK_WEBHOOK_URL', + }) + + expect(poster({ message: 'test' })).toBeUndefined() + expect(postSlackMessageViaWebhook).not.toHaveBeenCalled() + }) + test('skips posting in CI mode when message is "success"', () => { process.env.TEST_SLACK_TOKEN = 'token' process.env.TEST_SLACK_CHANNEL = 'channel' @@ -68,6 +166,20 @@ describe('createSlackPoster', () => { expect(postSlackMessage).not.toHaveBeenCalled() }) + test('skips posting in CI mode when message is "success" with webhook config', () => { + process.env.TEST_SLACK_WEBHOOK_URL = 'https://hooks.slack.com/services/x' + process.env.NODE_ENV = 'CI' + + const poster = createSlackPoster({ + tokenEnvVar: 'TEST_SLACK_TOKEN', + channelEnvVar: 'TEST_SLACK_CHANNEL', + webhookUrlEnvVar: 'TEST_SLACK_WEBHOOK_URL', + }) + + expect(poster({ message: 'success' })).toBeUndefined() + expect(postSlackMessageViaWebhook).not.toHaveBeenCalled() + }) + test('posts in CI mode when message is not "success"', () => { process.env.TEST_SLACK_TOKEN = 'token' process.env.TEST_SLACK_CHANNEL = 'channel' diff --git a/packages/utils/createSlackPoster.ts b/packages/utils/createSlackPoster.ts index 8079ef08..7e924df2 100644 --- a/packages/utils/createSlackPoster.ts +++ b/packages/utils/createSlackPoster.ts @@ -1,25 +1,53 @@ import { postSlackMessage } from './postSlackMessage' +import { postSlackMessageViaWebhook } from './postSlackMessageViaWebhook' +/** + * Creates a Slack poster configured via environment variables, supporting + * either-or configuration: + * - webhook URL (preferred: permissions are scoped to a single channel), or + * - bot token + channel + * + * If both are set, the webhook takes precedence. + */ export const createSlackPoster = ({ tokenEnvVar, channelEnvVar, + webhookUrlEnvVar, }: { tokenEnvVar: string channelEnvVar: string + webhookUrlEnvVar?: string }) => { return ({ message }: { message: string }) => { + const slackWebhookUrl = webhookUrlEnvVar + ? process.env[webhookUrlEnvVar] + : undefined const slackToken = process.env[tokenEnvVar] const slackChannel = process.env[channelEnvVar] - if (!slackToken) throw new Error('Slack token is required.') - if (!slackChannel) throw new Error('Slack channel is required.') + if (!slackWebhookUrl && !(slackToken && slackChannel)) { + if (webhookUrlEnvVar) { + throw new Error( + `Slack configuration is required: set ${webhookUrlEnvVar} (preferred) or both ${tokenEnvVar} and ${channelEnvVar}.` + ) + } + if (!slackToken) throw new Error('Slack token is required.') + throw new Error('Slack channel is required.') + } if (process.env.NODE_ENV === 'DEV') return if (process.env.NODE_ENV === 'CI' && message === 'success') return + if (slackWebhookUrl) { + return postSlackMessageViaWebhook({ + webhookUrl: slackWebhookUrl, + message, + }) + } + return postSlackMessage({ - slackToken, - slackChannel, + slackToken: slackToken as string, + slackChannel: slackChannel as string, message, }) } diff --git a/packages/utils/index.ts b/packages/utils/index.ts index 61dc528f..7d7e9812 100644 --- a/packages/utils/index.ts +++ b/packages/utils/index.ts @@ -2,6 +2,7 @@ export * from './types' export * from './config' export { getExplorerUrlPrefixes } from './getExplorerUrlPrefixes' export { postSlackMessage } from './postSlackMessage' +export { postSlackMessageViaWebhook } from './postSlackMessageViaWebhook' export { createSlackPoster } from './createSlackPoster' export { parseAmount } from './amountUtils' export { resolveRollupAddress } from './resolveRollupAddress' diff --git a/packages/utils/postSlackMessageViaWebhook.ts b/packages/utils/postSlackMessageViaWebhook.ts new file mode 100644 index 00000000..a13fc899 --- /dev/null +++ b/packages/utils/postSlackMessageViaWebhook.ts @@ -0,0 +1,36 @@ +import { sanitizeSlackMessage } from './sanitizeSlackMessage' +import { formatGitHubCIInfo } from './githubCIUtils' + +export const postSlackMessageViaWebhook = async ({ + webhookUrl, + message, +}: { + webhookUrl: string + message: string +}) => { + // Append GitHub CI run information if available + const ciInfo = formatGitHubCIInfo() + const messageWithCIInfo = ciInfo ? `${message}\n\n${ciInfo}` : message + + console.log(`>>> Posting message to Slack (webhook) -> ${messageWithCIInfo}`) + + const response = await fetch(webhookUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + text: sanitizeSlackMessage(messageWithCIInfo), + unfurl_links: false, + }), + }) + + if (!response.ok) { + // never include the webhook URL itself in errors, it is a secret + throw new Error( + `Failed to post Slack message via webhook: ${ + response.status + } ${await response.text()}` + ) + } + + return response +} From 717364dcd36e6b0ed8e62b14cf62c1cda1c07235 Mon Sep 17 00:00:00 2001 From: dewanshparashar Date: Thu, 23 Jul 2026 14:29:32 +0530 Subject: [PATCH 2/2] chore: trim comments and minimize diff Co-Authored-By: Claude Fable 5 --- .env.sample | 5 ----- README.md | 16 ++++------------ packages/assertion-monitor/README.md | 6 +++--- packages/batch-poster-monitor/README.md | 6 +++--- packages/retryable-monitor/README.md | 6 +++--- packages/utils/createSlackPoster.ts | 8 -------- packages/utils/postSlackMessageViaWebhook.ts | 1 - 7 files changed, 13 insertions(+), 35 deletions(-) diff --git a/.env.sample b/.env.sample index 23426ea8..b647924c 100644 --- a/.env.sample +++ b/.env.sample @@ -1,10 +1,5 @@ NODE_ENV= -# Slack alerting supports either-or configuration per monitor: -# - preferred: a Slack incoming webhook URL (scoped to a single channel) -# - fallback: a bot token + channel -# If both are set, the webhook URL takes precedence. - RETRYABLE_MONITORING_SLACK_WEBHOOK_URL= RETRYABLE_MONITORING_SLACK_TOKEN= RETRYABLE_MONITORING_SLACK_CHANNEL= diff --git a/README.md b/README.md index a794f213..c3b0b895 100644 --- a/README.md +++ b/README.md @@ -70,25 +70,17 @@ cp config.example.json config.json cp .env.sample .env ``` -2. Set up Slack alerts in `.env` (optional). Each monitor supports either-or configuration — provide a Slack incoming webhook URL (preferred, since its permissions are scoped to a single channel), or a bot token + channel. If both are set, the webhook URL takes precedence. - -Option A — webhook URL (preferred): - -```bash -NODE_ENV=CI -RETRYABLE_MONITORING_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/... -BATCH_POSTER_MONITORING_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/... -ASSERTION_MONITORING_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/... -``` - -Option B — bot token + channel: +2. Set up Slack alerts in `.env` (optional). For each monitor, set either a webhook URL (preferred, scoped to a single channel) or a token + channel; the webhook takes precedence if both are set: ```bash NODE_ENV=CI +RETRYABLE_MONITORING_SLACK_WEBHOOK_URL=your-slack-webhook-url RETRYABLE_MONITORING_SLACK_TOKEN=your-slack-token RETRYABLE_MONITORING_SLACK_CHANNEL=your-slack-channel +BATCH_POSTER_MONITORING_SLACK_WEBHOOK_URL=your-slack-webhook-url BATCH_POSTER_MONITORING_SLACK_TOKEN=your-slack-token BATCH_POSTER_MONITORING_SLACK_CHANNEL=your-slack-channel +ASSERTION_MONITORING_SLACK_WEBHOOK_URL=your-slack-webhook-url ASSERTION_MONITORING_SLACK_TOKEN=your-slack-token ASSERTION_MONITORING_SLACK_CHANNEL=your-slack-channel ``` diff --git a/packages/assertion-monitor/README.md b/packages/assertion-monitor/README.md index f29fc80e..db5f6587 100644 --- a/packages/assertion-monitor/README.md +++ b/packages/assertion-monitor/README.md @@ -25,9 +25,9 @@ Examples: yarn assertion-monitor --configPath=custom.json Use custom config file Environment Variables: - ASSERTION_MONITORING_SLACK_WEBHOOK_URL Slack incoming webhook URL for alerts (preferred; takes precedence over token+channel) - ASSERTION_MONITORING_SLACK_TOKEN Slack API token for alerts (alternative to webhook, use with channel) - ASSERTION_MONITORING_SLACK_CHANNEL Slack channel for alerts (alternative to webhook, use with token) + ASSERTION_MONITORING_SLACK_WEBHOOK_URL Slack webhook URL for alerts (preferred over token+channel) + ASSERTION_MONITORING_SLACK_TOKEN Slack API token for alerts + ASSERTION_MONITORING_SLACK_CHANNEL Slack channel for alerts ``` ## Monitor Details diff --git a/packages/batch-poster-monitor/README.md b/packages/batch-poster-monitor/README.md index 35c9d654..7e69b4f0 100644 --- a/packages/batch-poster-monitor/README.md +++ b/packages/batch-poster-monitor/README.md @@ -25,9 +25,9 @@ Examples: yarn batch-poster-monitor --configPath=custom.json Use custom config file Environment Variables: - BATCH_POSTER_MONITORING_SLACK_WEBHOOK_URL Slack incoming webhook URL for alerts (preferred; takes precedence over token+channel) - BATCH_POSTER_MONITORING_SLACK_TOKEN Slack API token for alerts (alternative to webhook, use with channel) - BATCH_POSTER_MONITORING_SLACK_CHANNEL Slack channel for alerts (alternative to webhook, use with token) + BATCH_POSTER_MONITORING_SLACK_WEBHOOK_URL Slack webhook URL for alerts (preferred over token+channel) + BATCH_POSTER_MONITORING_SLACK_TOKEN Slack API token for alerts + BATCH_POSTER_MONITORING_SLACK_CHANNEL Slack channel for alerts ``` ## Monitor Details diff --git a/packages/retryable-monitor/README.md b/packages/retryable-monitor/README.md index b65171f3..765d82e6 100644 --- a/packages/retryable-monitor/README.md +++ b/packages/retryable-monitor/README.md @@ -29,9 +29,9 @@ Examples: yarn retryable-monitor --enableAlerting --writeToNotion Enables Slack alerts and syncs retryable data to Notion Environment Variables: - RETRYABLE_MONITORING_SLACK_WEBHOOK_URL Slack incoming webhook URL for alerts (preferred; takes precedence over token+channel) - RETRYABLE_MONITORING_SLACK_TOKEN Slack API token for alerts (alternative to webhook, use with channel) - RETRYABLE_MONITORING_SLACK_CHANNEL Slack channel for alerts (alternative to webhook, use with token) + RETRYABLE_MONITORING_SLACK_WEBHOOK_URL Slack webhook URL for alerts (preferred over token+channel) + RETRYABLE_MONITORING_SLACK_TOKEN Slack API token for alerts + RETRYABLE_MONITORING_SLACK_CHANNEL Slack channel for alerts RETRYABLE_MONITORING_NOTION_TOKEN Notion integration token RETRYABLE_MONITORING_NOTION_DB_ID Notion database ID ``` diff --git a/packages/utils/createSlackPoster.ts b/packages/utils/createSlackPoster.ts index 7e924df2..04ab9b9f 100644 --- a/packages/utils/createSlackPoster.ts +++ b/packages/utils/createSlackPoster.ts @@ -1,14 +1,6 @@ import { postSlackMessage } from './postSlackMessage' import { postSlackMessageViaWebhook } from './postSlackMessageViaWebhook' -/** - * Creates a Slack poster configured via environment variables, supporting - * either-or configuration: - * - webhook URL (preferred: permissions are scoped to a single channel), or - * - bot token + channel - * - * If both are set, the webhook takes precedence. - */ export const createSlackPoster = ({ tokenEnvVar, channelEnvVar, diff --git a/packages/utils/postSlackMessageViaWebhook.ts b/packages/utils/postSlackMessageViaWebhook.ts index a13fc899..4b719284 100644 --- a/packages/utils/postSlackMessageViaWebhook.ts +++ b/packages/utils/postSlackMessageViaWebhook.ts @@ -24,7 +24,6 @@ export const postSlackMessageViaWebhook = async ({ }) if (!response.ok) { - // never include the webhook URL itself in errors, it is a secret throw new Error( `Failed to post Slack message via webhook: ${ response.status