Require release notes for every change and show them in the app - #74
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for all 3 issues found in the latest run.
- ✅ Fixed: What's New appears after first project
- Stopped memoizing localStorage so the silent fresh-install write is re-read when the first project arrives and pending stays false.
- ✅ Fixed: Settings notes dialog is clipped
- WhatsNewDialog now portals to document.body above Settings so backdrop-filter/overflow ancestors no longer clip the overlay.
- ✅ Fixed: Label changes rerun entire CI
- mac-package, checks, pipeline, and smoke now skip labeled/unlabeled events so only the release-notes job re-runs on label edits.
Or push these changes by commenting:
@cursor push 2b22a75dff
Preview (2b22a75dff)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,6 +23,8 @@
run: node scripts/check-release-notes.mjs
mac-package:
name: packaged Apple Silicon inference and export
+ # Skip on label edits — those only need the release-notes job.
+ if: github.event.action != 'labeled' && github.event.action != 'unlabeled'
runs-on: macos-latest
timeout-minutes: 20
steps:
@@ -43,6 +45,7 @@
path: .tmp/mac-package-check/
checks:
+ if: github.event.action != 'labeled' && github.event.action != 'unlabeled'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -57,6 +60,7 @@
pipeline:
name: offline pipeline test (ffmpeg, captions, renders)
+ if: github.event.action != 'labeled' && github.event.action != 'unlabeled'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -69,6 +73,7 @@
smoke:
name: UI smoke test (Xvfb screenshots)
+ if: github.event.action != 'labeled' && github.event.action != 'unlabeled'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
diff --git a/src/renderer/src/components/WhatsNew.tsx b/src/renderer/src/components/WhatsNew.tsx
--- a/src/renderer/src/components/WhatsNew.tsx
+++ b/src/renderer/src/components/WhatsNew.tsx
@@ -1,4 +1,5 @@
-import { useEffect, useMemo, useState } from 'react'
+import { useEffect, useState } from 'react'
+import { createPortal } from 'react-dom'
import { Sparkles, X } from 'lucide-react'
import changelog from '../../../../CHANGELOG.md?raw'
import { changelogSection, parseNotes, type NotesBlock } from '@shared/releaseNotes'
@@ -43,8 +44,9 @@
window.addEventListener('keydown', onKey)
return () => window.removeEventListener('keydown', onKey)
}, [onClose])
- return (
- <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-6 backdrop-blur-sm" onClick={onClose}>
+ // Portal to body so Settings' backdrop-filter / overflow ancestors cannot clip us.
+ return createPortal(
+ <div className="fixed inset-0 z-[60] flex items-center justify-center bg-black/80 p-6 backdrop-blur-sm" onClick={onClose}>
<div role="dialog" aria-label={`What's new in Cutawan ${version}`} data-testid="whats-new"
className="max-h-[80vh] w-full max-w-lg overflow-y-auto rounded-2xl border border-zinc-700 bg-zinc-900 p-6 shadow-2xl shadow-black/70"
onClick={(e) => e.stopPropagation()}>
@@ -61,7 +63,8 @@
{blocks.length ? <NotesList blocks={blocks} /> : <p className="text-xs text-zinc-500">No release notes for this version.</p>}
</div>
</div>
- </div>
+ </div>,
+ document.body,
)
}
@@ -76,9 +79,10 @@
const version = useStore((s) => s.settings?.setupComplete ? s.settings.appVersion : null)
const hasProjects = useStore((s) => s.projects.length > 0)
const [dismissed, setDismissed] = useState<string | null>(null)
- const seen = useMemo(() => {
- try { return localStorage.getItem(SEEN_KEY) } catch { return version }
- }, [version])
+ // Read every render (not memoized) so a silent fresh-install write is visible
+ // when the first project arrives and this re-renders.
+ let seen: string | null
+ try { seen = localStorage.getItem(SEEN_KEY) } catch { seen = version }
const pending = Boolean(version && seen !== version && (seen || hasProjects) && bundledNotes(version).length)
// A fresh install (or a version without notes) is recorded silently.
useEffect(() => {You can send follow-ups to the cloud agent here.
| branches: [main] | ||
| pull_request: | ||
| # labeled/unlabeled so adding "no release notes" re-runs the check. | ||
| types: [opened, synchronize, reopened, labeled, unlabeled] |
There was a problem hiding this comment.
Label changes rerun entire CI
Medium Severity
labeled and unlabeled were added on the whole pull_request workflow so the release-notes job can re-run. Every other job, including the 20-minute mac-package build plus pipeline and smoke, now also runs when any label is added or removed.
Reviewed by Cursor Bugbot for commit afca87d. Configure here.
5c58202 to
46bb9d8
Compare
afca87d to
3138688
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Single label crashes notes check
- Normalized PR_LABELS so a single-label JSON string from GHA is wrapped into an array before .map, restoring the skip and validation paths.
Or push these changes by commenting:
@cursor push 724306c4bb
Preview (724306c4bb)
diff --git a/scripts/check-release-notes.mjs b/scripts/check-release-notes.mjs
--- a/scripts/check-release-notes.mjs
+++ b/scripts/check-release-notes.mjs
@@ -13,7 +13,10 @@
import { readFileSync } from 'node:fs'
const baseRef = process.env.BASE_REF || 'origin/main'
-const labels = JSON.parse(process.env.PR_LABELS || '[]').map((l) => String(l).toLowerCase())
+// GHA `labels.*.name` collapses a one-element list to a scalar, so toJSON may
+// yield a string rather than an array when the PR has exactly one label.
+const parsed = JSON.parse(process.env.PR_LABELS || '[]')
+const labels = (Array.isArray(parsed) ? parsed : [parsed]).map((l) => String(l).toLowerCase())
if (labels.includes('no release notes')) {
console.log('Skipped: labelled "no release notes".')
process.exit(0)You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 3138688. Configure here.
46bb9d8 to
2b7c434
Compare
5eca595 to
5b42de6
Compare
- CI fails a pull request that changes the app without CHANGELOG [Unreleased] notes, unless labelled "no release notes". - The release workflow refuses to build a version with no notes, before spending build time, instead of publishing empty notes. - Settings → Updates shows an available update's notes before download and this version's notes on demand; "What's new" opens once after updating. Validation sections stay on GitHub only. - Contributing guide and PR template explain how to write notes for users. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Read the seen-notes version every render, so a fresh install's silent record stops the dialog appearing once the first project exists. - Portal the What's new dialog to the body so Settings cannot clip it. - Accept a release PR that renames [Unreleased] to the new version. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
5b42de6 to
f1fc567
Compare



Stacked on #73. Retarget to
mainonce #72 and #73 merge.Release notes
What does this change?
scripts/check-release-notes.mjsruns in CI. It fails a PR that changessrc/,resources/orpackage.jsonwithout new bullets under## [Unreleased]in CHANGELOG.md. The failure message says what to add. The newno release noteslabel skips it for internal-only changes, and the check re-runs when the label changes.### Validationsections are hidden in the app.How did you test it?
npm test(571): changelog parsing, update notes (drafts ignored), current version must have notesnpm run typecheck,npm run lintchangelog-section.mjs --requirefails for a version without notes🤖 Generated with Claude Code
Note
Low Risk
Mostly docs, CI gates, and read-only UI over changelog/update metadata; update evaluation only adds an optional field from existing GitHub API data.
Overview
This PR makes CHANGELOG.md the single source of truth for what users see at release time, and enforces that contributors keep it up to date.
Process and CI: Pull requests that touch
src/,resources/, orpackage.jsonmust add bullets under## [Unreleased](or use the no release notes label). A new CI job runsscripts/check-release-notes.mjsand re-runs when labels change. The release workflow now fails early if the version being built has no changelog section (changelog-section.mjs --require), instead of publishing empty GitHub release notes. CONTRIBUTING and the PR template document how to write user-facing notes.In the app: Users get a What's new dialog once after updating (bundled
CHANGELOG.mdfor the running version; fresh installs skip it). Settings → Updates adds a link to reopen current-version notes and shows parsed notes for an available update from the GitHub release body.### Validationsections are stripped in-app. Update checks now surfacereleaseNotesfrom published releases (not drafts).Shared parsing:
src/shared/releaseNotes.tsmirrors the release script logic; tests cover extraction and that the shipped package version has notes.Reviewed by Cursor Bugbot for commit f1fc567. Bugbot is set up for automated code reviews on this repo. Configure here.