Skip to content

Require release notes for every change and show them in the app - #74

Merged
JeremySNR merged 3 commits into
mainfrom
release-notes
Sep 23, 2026
Merged

JeremySNR merged 3 commits into
mainfrom
release-notes

Conversation

@JeremySNR

@JeremySNR JeremySNR commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #73. Retarget to main once #72 and #73 merge.

Release notes

  • Added: "What's new" after each update, and any time from Settings → Updates. When an update is available, its notes show there before you download it.

What does this change?

  • PR check: scripts/check-release-notes.mjs runs in CI. It fails a PR that changes src/, resources/ or package.json without new bullets under ## [Unreleased] in CHANGELOG.md. The failure message says what to add. The new no release notes label skips it for internal-only changes, and the check re-runs when the label changes.
  • Release gate: the release workflow fails fast, before the three platform builds, if the version has no CHANGELOG section. Previously it published empty notes silently.
  • In the app:
    • "What's new" opens once after an update. Fresh installs record the version silently; existing users updating from older versions do see it.
    • Settings → Updates has a "What's new in this version" link.
    • Settings → Updates shows an available update's notes (the GitHub release body) before download.
    • ### Validation sections are hidden in the app.
  • Docs: CONTRIBUTING has a Release notes section on how to write notes for users. The PR template has a Release notes section.

How did you test it?

  • npm test (571): changelog parsing, update notes (drafts ignored), current version must have notes
  • npm run typecheck, npm run lint
  • Check script: passes with notes, fails for a code-only commit, skips with the label, and changelog-section.mjs --require fails for a version without notes
  • Checked the change in the running app: the smoke walkthrough screenshots and dismisses the dialog (opaque; validation hidden)

🤖 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/, or package.json must add bullets under ## [Unreleased] (or use the no release notes label). A new CI job runs scripts/check-release-notes.mjs and 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.md for 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. ### Validation sections are stripped in-app. Update checks now surface releaseNotes from published releases (not drafts).

Shared parsing: src/shared/releaseNotes.ts mirrors 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.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Create PR

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.

Comment thread src/renderer/src/components/WhatsNew.tsx
Comment thread src/renderer/src/components/SettingsModal.tsx
Comment thread .github/workflows/ci.yml
branches: [main]
pull_request:
# labeled/unlabeled so adding "no release notes" re-runs the check.
types: [opened, synchronize, reopened, labeled, unlabeled]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit afca87d. Configure here.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

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.

Create PR

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.

Comment thread scripts/check-release-notes.mjs
@JeremySNR
JeremySNR force-pushed the release-notes branch 3 times, most recently from 5eca595 to 5b42de6 Compare September 23, 2026 08:58
JeremySNR and others added 3 commits September 23, 2026 10:02
- 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>
@JeremySNR
JeremySNR changed the base branch from editing-cuts to main September 23, 2026 09:03
@JeremySNR JeremySNR closed this Sep 23, 2026
@JeremySNR JeremySNR reopened this Sep 23, 2026
@JeremySNR
JeremySNR merged commit ad73da9 into main Sep 23, 2026
6 checks passed
@JeremySNR
JeremySNR deleted the release-notes branch September 23, 2026 11:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant