mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
## Summary
Adds a GitHub Actions workflow + TypeScript script that posts to Slack
when a merged PR improves unit or E2E test coverage.
## Changes
- **What**: New `coverage-slack-notify.yaml` workflow triggered on push
to main. Compares current coverage against previous baselines, generates
Slack Block Kit payload with progress bars and milestone celebrations,
posts to `#p-frontend-automated-testing`.
- **Script**: `scripts/coverage-slack-notify.ts` — parses lcov files,
computes deltas, detects milestone crossings (every 5%), builds Slack
payload. Pure functions exported for testability.
- **Tests**: 26 unit tests in `scripts/coverage-slack-notify.test.ts`
covering all pure functions including edge cases (malformed lcov, exact
boundaries, zero coverage).
### Security hardening
- All `${{ }}` expressions moved from `run:` blocks to `env:` variables
- `SLACK_BOT_TOKEN` passed via env var, not inline
- Unique heredoc delimiter (timestamp-based) prevents payload injection
- `parseInt` fallback (`|| 0`) guards against malformed lcov
- PR regex anchored to first line of commit message
### Robustness
- `continue-on-error: true` on Slack post step (outage does not fail the
job)
- Baseline save guarded by `steps.unit-tests.outcome == success`
(prevents corrupt baselines on test failure)
- Channel ID commented for maintainability
- Top-level `text` field added for Slack mobile push notifications
- Author linked to GitHub profile instead of bare `@username`
## Review Focus
- Workflow step ordering and conditional logic
- Security of expression handling and secret management
- Slack payload structure and Block Kit formatting
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10977-feat-add-Slack-notification-workflow-for-coverage-improvements-33d6d73d3650819c8950f483c83f297c)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
import { execFileSync } from 'node:child_process'
|
|
import { existsSync } from 'node:fs'
|
|
|
|
const args: string[] = process.argv.slice(2)
|
|
|
|
function getArg(name: string): string | undefined {
|
|
const prefix = `--${name}=`
|
|
const arg = args.find((a) => a.startsWith(prefix))
|
|
return arg ? arg.slice(prefix.length) : undefined
|
|
}
|
|
|
|
const sizeStatus = getArg('size-status') ?? 'pending'
|
|
const perfStatus = getArg('perf-status') ?? 'pending'
|
|
const coverageStatus = getArg('coverage-status') ?? 'skip'
|
|
|
|
const lines: string[] = []
|
|
|
|
if (sizeStatus === 'ready') {
|
|
try {
|
|
const sizeReport = execFileSync('node', ['scripts/size-report.js'], {
|
|
encoding: 'utf-8'
|
|
}).trimEnd()
|
|
lines.push(sizeReport)
|
|
} catch {
|
|
lines.push('## 📦 Bundle Size')
|
|
lines.push('')
|
|
lines.push(
|
|
'> ⚠️ Failed to render bundle size report. Check the CI workflow logs.'
|
|
)
|
|
}
|
|
} else if (sizeStatus === 'failed') {
|
|
lines.push('## 📦 Bundle Size')
|
|
lines.push('')
|
|
lines.push('> ⚠️ Size data collection failed. Check the CI workflow logs.')
|
|
} else {
|
|
lines.push('## 📦 Bundle Size')
|
|
lines.push('')
|
|
lines.push('> ⏳ Size data collection in progress…')
|
|
}
|
|
|
|
lines.push('')
|
|
|
|
if (perfStatus === 'ready' && existsSync('test-results/perf-metrics.json')) {
|
|
try {
|
|
const perfReport = execFileSync(
|
|
'pnpm',
|
|
['exec', 'tsx', 'scripts/perf-report.ts'],
|
|
{ encoding: 'utf-8' }
|
|
).trimEnd()
|
|
lines.push(perfReport)
|
|
} catch {
|
|
lines.push('## ⚡ Performance')
|
|
lines.push('')
|
|
lines.push(
|
|
'> ⚠️ Failed to render performance report. Check the CI workflow logs.'
|
|
)
|
|
}
|
|
} else if (
|
|
perfStatus === 'failed' ||
|
|
(perfStatus === 'ready' && !existsSync('test-results/perf-metrics.json'))
|
|
) {
|
|
lines.push('## ⚡ Performance')
|
|
lines.push('')
|
|
lines.push('> ⚠️ Performance tests failed. Check the CI workflow logs.')
|
|
} else {
|
|
lines.push('## ⚡ Performance')
|
|
lines.push('')
|
|
lines.push('> ⏳ Performance tests in progress…')
|
|
}
|
|
|
|
if (coverageStatus === 'ready' && existsSync('temp/coverage/coverage.lcov')) {
|
|
try {
|
|
const coverageReport = execFileSync(
|
|
'pnpm',
|
|
[
|
|
'exec',
|
|
'tsx',
|
|
'scripts/coverage-report.ts',
|
|
'temp/coverage/coverage.lcov'
|
|
],
|
|
{ encoding: 'utf-8' }
|
|
).trimEnd()
|
|
lines.push('')
|
|
lines.push(coverageReport)
|
|
} catch {
|
|
lines.push('')
|
|
lines.push('## 🔬 E2E Coverage')
|
|
lines.push('')
|
|
lines.push(
|
|
'> ⚠️ Failed to render coverage report. Check the CI workflow logs.'
|
|
)
|
|
}
|
|
} else if (coverageStatus === 'failed') {
|
|
lines.push('')
|
|
lines.push('## 🔬 E2E Coverage')
|
|
lines.push('')
|
|
lines.push('> ⚠️ Coverage collection failed. Check the CI workflow logs.')
|
|
}
|
|
|
|
process.stdout.write(lines.join('\n') + '\n')
|