send-slack-triage.ts2.6 KBView on GitHub /**
* Send bug triage to #customer-success via Slack webhook.
*
* Usage (inline via stdin — no file needed):
* echo "<markdown>" | pnpm run send-slack-triage
*
* Usage (from file):
* pnpm run send-slack-triage -- docs/bug-triage-{call}-{date}.md
*
* Requires: SLACK_ALERTS_WEBHOOK_URL or SLACK_CUSTOMER_SUCCESS_WEBHOOK_URL in .env
*/
import { readFile } from 'fs/promises';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const webhookUrl =
process.env.SLACK_CUSTOMER_SUCCESS_WEBHOOK_URL || process.env.SLACK_ALERTS_WEBHOOK_URL;
if (!webhookUrl) {
console.error(
'Missing webhook URL. Set SLACK_CUSTOMER_SUCCESS_WEBHOOK_URL or SLACK_ALERTS_WEBHOOK_URL in .env'
);
process.exit(1);
}
// Read triage content: from file arg, or stdin if no arg given
const pathArg = process.argv.slice(2).find((a) => a !== '--' && !a.startsWith('-'));
let triage: string;
if (pathArg) {
const triagePath = join(process.cwd(), pathArg);
triage = await readFile(triagePath, 'utf8');
} else {
// Read from stdin (piped content — no file creation needed)
const chunks: Buffer[] = [];
for await (const chunk of process.stdin) chunks.push(chunk as Buffer);
triage = Buffer.concat(chunks).toString('utf8').trim();
if (!triage) {
console.error('No triage content provided. Pass a file path or pipe content via stdin.');
process.exit(1);
}
}
// Format for Slack: convert markdown to Slack mrkdwn (simplified)
const slackText = triage
.replace(/^# .+$/gm, (m) => `*${m.replace(/^#+ /, '')}*`)
.replace(/^## .+$/gm, (m) => `\n*${m.replace(/^##+ /, '')}*`)
.replace(/^### .+$/gm, (m) => `\n*${m.replace(/^###+ /, '')}*`)
.replace(/^- \[ \] /gm, '• ')
.replace(/^- \[x\] /gm, '• ✓ ')
// Convert markdown links [text](url) to Slack mrkdwn <url|text>
.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, '<$2|$1>')
// Convert bare URLs in ticket lines to Slack links
.replace(/^- ((?:Isabelle|Jesse): )(https?:\/\/\S+)$/gm, '• $1<$2|$2>')
.replace(/\*\*(.+?)\*\*/g, '*$1*')
.replace(/`([^`]+)`/g, '`$1`');
// Use first heading as title, or fallback
const titleMatch = triage.match(/^# (.+)$/m);
const title = titleMatch ? titleMatch[1] : 'Bug Triage';
const payload = {
channel: '#customer-success',
text: `*${title}*\n\n${slackText}`,
};
const res = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!res.ok) {
console.error('Slack webhook failed:', res.status, await res.text());
process.exit(1);
}
console.log('Triage sent to #customer-success');