Skip to content

Commit b246940

Browse files
authored
feat(ci): weekly version-watch cron with email or issue alerts (#134)
## Summary Weekly **Monday 08:00 UTC** cron that surveils every package this project supports for **SemVer-major bumps** OR **explicit breaking-change keywords** in upstream release notes. The watcher reports through SMTP email when configured, GitHub issue otherwise. ## Anti-overkill carve-out (explicit) - ❌ Patches not alerted (Renovate) - ❌ Non-breaking minors not alerted (Renovate) - ❌ Prereleases (alpha / beta / rc / next / canary) not alerted (still in flight) - ✅ SemVer-major bumps : alerted - ✅ Breaking-change keywords (BREAKING CHANGE / 💥 / etc.) : alerted ## Files added - \`.github/workflows/version-watch.yml\` — schedule + workflow_dispatch (with dry-run input) - \`scripts/version-watch.mjs\` — fetch npm + GH releases, render Markdown report (writes to GITHUB_OUTPUT for the workflow) - \`.github/version-watch-config.json\` — watchlist baseline of 23 packages ## Local dry-run \`\`\`bash node scripts/version-watch.mjs --dry-run \`\`\` Confirmed: detects \`typescript v5 → v6\` and \`tailwind-variants v0 → v3\` correctly. ## To complete after merge (out-of-scope for this PR) - Configure repo secrets \`SMTP_HOST\` / \`SMTP_PORT\` / \`SMTP_USER\` / \`SMTP_PASS\` via the GitHub UI to enable the email path. Without these, the watcher will fall back to a GitHub issue (still useful). ## Test plan - [x] \`node scripts/version-watch.mjs --dry-run\` produces a clean report locally - [x] GH Actions allowlist updated via API to include \`dawidd6/action-send-mail@*\` - [x] Action SHA pinning preserved (Scorecard-friendly) - [ ] After merge, trigger \`gh workflow run version-watch.yml -f dry-run=true\` to confirm the workflow boots cleanly
1 parent d000dca commit b246940

4 files changed

Lines changed: 446 additions & 0 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tailwindcss-obfuscator": patch
3+
---
4+
5+
CI: added a weekly version-watch cron (`Monday 08:00 UTC`) that surveils every package this project supports for SemVer-major bumps OR explicit breaking-change keywords in upstream release notes. Anti-overkill: patches and minors are intentionally not surfaced (Renovate handles those). Notification path is SMTP email when `SMTP_*` secrets are configured, GitHub-issue fallback otherwise. Adjustable via `.github/version-watch-config.json`.

.github/version-watch-config.json

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
"$schema": "./version-watch-schema.json",
3+
"description": "Watchlist for the version-watch workflow. The workflow alerts on SemVer-major bumps OR breaking-change keywords in upstream release notes. Patches and minors are intentionally NOT alerted on (Renovate handles those routinely).",
4+
"packages": [
5+
{
6+
"name": "tailwindcss",
7+
"currentMajor": 4,
8+
"alertOn": ["major", "breaking"],
9+
"githubRepo": "tailwindlabs/tailwindcss"
10+
},
11+
{
12+
"name": "@tailwindcss/vite",
13+
"currentMajor": 4,
14+
"alertOn": ["major"],
15+
"githubRepo": "tailwindlabs/tailwindcss"
16+
},
17+
{
18+
"name": "@tailwindcss/postcss",
19+
"currentMajor": 4,
20+
"alertOn": ["major"],
21+
"githubRepo": "tailwindlabs/tailwindcss"
22+
},
23+
{ "name": "vite", "currentMajor": 8, "alertOn": ["major"], "githubRepo": "vitejs/vite" },
24+
{
25+
"name": "next",
26+
"currentMajor": 16,
27+
"alertOn": ["major", "breaking"],
28+
"githubRepo": "vercel/next.js"
29+
},
30+
{ "name": "nuxt", "currentMajor": 4, "alertOn": ["major"], "githubRepo": "nuxt/nuxt" },
31+
{ "name": "astro", "currentMajor": 6, "alertOn": ["major"], "githubRepo": "withastro/astro" },
32+
{
33+
"name": "@sveltejs/kit",
34+
"currentMajor": 2,
35+
"alertOn": ["major"],
36+
"githubRepo": "sveltejs/kit"
37+
},
38+
{ "name": "svelte", "currentMajor": 5, "alertOn": ["major"], "githubRepo": "sveltejs/svelte" },
39+
{
40+
"name": "@rspack/core",
41+
"currentMajor": 2,
42+
"alertOn": ["major"],
43+
"githubRepo": "web-infra-dev/rspack"
44+
},
45+
{
46+
"name": "@farmfe/core",
47+
"currentMajor": 1,
48+
"alertOn": ["major"],
49+
"githubRepo": "farm-fe/farm"
50+
},
51+
{ "name": "rollup", "currentMajor": 4, "alertOn": ["major"], "githubRepo": "rollup/rollup" },
52+
{ "name": "webpack", "currentMajor": 5, "alertOn": ["major"], "githubRepo": "webpack/webpack" },
53+
{
54+
"name": "esbuild",
55+
"currentMajor": 0,
56+
"alertOn": ["major", "breaking"],
57+
"githubRepo": "evanw/esbuild"
58+
},
59+
{
60+
"name": "@babel/parser",
61+
"currentMajor": 7,
62+
"alertOn": ["major"],
63+
"githubRepo": "babel/babel"
64+
},
65+
{
66+
"name": "vitest",
67+
"currentMajor": 4,
68+
"alertOn": ["major"],
69+
"githubRepo": "vitest-dev/vitest"
70+
},
71+
{
72+
"name": "typescript",
73+
"currentMajor": 5,
74+
"alertOn": ["major"],
75+
"githubRepo": "microsoft/TypeScript"
76+
},
77+
{ "name": "react", "currentMajor": 19, "alertOn": ["major"], "githubRepo": "facebook/react" },
78+
{ "name": "vue", "currentMajor": 3, "alertOn": ["major"], "githubRepo": "vuejs/core" },
79+
{ "name": "solid-js", "currentMajor": 1, "alertOn": ["major"], "githubRepo": "solidjs/solid" },
80+
{ "name": "qwik", "currentMajor": 1, "alertOn": ["major"], "githubRepo": "QwikDev/qwik" },
81+
{
82+
"name": "tailwind-variants",
83+
"currentMajor": 0,
84+
"alertOn": ["major", "breaking"],
85+
"githubRepo": "heroui-inc/tailwind-variants"
86+
},
87+
{
88+
"name": "class-variance-authority",
89+
"currentMajor": 0,
90+
"alertOn": ["major"],
91+
"githubRepo": "joe-bell/cva"
92+
}
93+
]
94+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: Version Watch
2+
3+
# Surveils every package this project supports for SemVer-major bumps OR
4+
# explicit breaking-change keywords in the upstream release notes. Anti-
5+
# overkill: patches and minors are NOT surfaced (Renovate handles those).
6+
#
7+
# When something needs human attention, the maintainer is notified through:
8+
# 1. SMTP email to contact@josedacosta.info, when SMTP_* secrets are set
9+
# 2. A GitHub issue with the `version-watch` label, otherwise (fallback)
10+
#
11+
# To acknowledge an alert and silence it for the next major: bump
12+
# `currentMajor` in `.github/version-watch-config.json` and merge.
13+
14+
on:
15+
schedule:
16+
- cron: "0 8 * * 1" # every Monday at 08:00 UTC (after the OSSF Scorecard run)
17+
workflow_dispatch:
18+
inputs:
19+
dry-run:
20+
description: "Run without sending email or opening an issue (logs the report only)"
21+
type: boolean
22+
default: false
23+
24+
permissions: {}
25+
26+
jobs:
27+
watch:
28+
name: Check upstream versions
29+
runs-on: ubuntu-latest
30+
permissions:
31+
contents: read # read the watchlist
32+
issues: write # fallback notification channel
33+
timeout-minutes: 10
34+
steps:
35+
- name: Checkout repo
36+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
37+
with:
38+
persist-credentials: false
39+
40+
- name: Setup Node
41+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
42+
with:
43+
node-version: 22
44+
45+
- name: Run version watcher
46+
id: watch
47+
env:
48+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49+
run: node scripts/version-watch.mjs
50+
51+
- name: Send email notification (when SMTP secrets are configured)
52+
if: ${{ steps.watch.outputs.alertCountIsPositive == 'true' && env.SMTP_HOST != '' && inputs.dry-run != true }}
53+
env:
54+
SMTP_HOST: ${{ secrets.SMTP_HOST }}
55+
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
56+
with:
57+
server_address: ${{ secrets.SMTP_HOST }}
58+
server_port: ${{ secrets.SMTP_PORT }}
59+
username: ${{ secrets.SMTP_USER }}
60+
password: ${{ secrets.SMTP_PASS }}
61+
from: tailwindcss-obfuscator version-watch <${{ secrets.SMTP_USER }}>
62+
to: contact@josedacosta.info
63+
subject: "[tailwindcss-obfuscator] ${{ steps.watch.outputs.alertCount }} upstream major/breaking version(s) detected"
64+
body: ${{ steps.watch.outputs.report }}
65+
convert_markdown: true
66+
67+
- name: Fallback — open a GitHub issue (when no SMTP config OR email step skipped)
68+
if: ${{ steps.watch.outputs.alertCountIsPositive == 'true' && env.SMTP_HOST == '' && inputs.dry-run != true }}
69+
env:
70+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
71+
REPORT: ${{ steps.watch.outputs.report }}
72+
ALERT_COUNT: ${{ steps.watch.outputs.alertCount }}
73+
run: |
74+
# Reuse the existing open issue with the version-watch label, if any.
75+
existing=$(gh issue list --label version-watch --state open --json number --jq '.[0].number')
76+
if [ -n "$existing" ]; then
77+
gh issue comment "$existing" --body "$REPORT"
78+
else
79+
gh issue create \
80+
--title "[version-watch] $ALERT_COUNT upstream major/breaking version(s) detected" \
81+
--label version-watch \
82+
--body "$REPORT"
83+
fi

0 commit comments

Comments
 (0)