Changelog and What's new: CHANGELOG.md cut into each release, shown in the update band, a What's new card and once after updating

CHANGELOG.md keeps an Unreleased section (user-facing changes add a line in
the same commit); tool/release.sh cuts it into the version, the manifest
gets a structured "changes" field next to the one-line "notes" older
apps read, and the Gitea release text is that section. The app's update
band shows the top items, What's new opens a card (Added / Changed /
Fixed), and the first start after an update shows it once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-18 02:11:37 +03:00
1 parent ec0849eb01
commit 638a30e16c
22 files changed
+1637 -58

No files matched your search

+28 -5
View File
@@ -2540,8 +2540,8 @@ company `foxyz`, product `Starsync`; the window class is
One implementation, `packages/core/lib/src/updater.dart` (pure Dart, tested
in `packages/core/test/updater_test.dart`), with two front ends: the app and
the dedicated server. Both binaries ship in the same archive, so either one
updates the whole install folder. Reads a manifest (`version`, `protocol`, `url`, optional `sha256`, `notes`)
from `updateManifestUrl`, compares versions, downloads the tarball, checks
updates the whole install folder. Reads a manifest (`version`, `protocol`, `url`, optional `sha256`, `notes`,
optional `changes`) from `updateManifestUrl`, compares versions, downloads the tarball, checks
sha256 when present, then extracts into a staging dir inside the install
folder (same filesystem, so moves are renames; the folder must be
user-writable). Each top-level entry is moved aside and the new one moved in,
@@ -2582,9 +2582,11 @@ does the same from the app binary. In the app, `UpdateController`
(`apps/desktop/lib/src/update/`) runs the same code for the update bar
(`UpdateBar`, `screens/home/update_indicator.dart`): a band across the
window under the strip while an update is available: "Starsync X.Y.Z is
ready", the notes, Later (hidden until the next check or start) and
"Update and restart" (download, apply, then the app close above with Hive
closed and the new binary started), its progress in the band. A release
ready", the first three changes (each with its kind's icon; the notes
when the manifest has no `changes`), What's new, Later (hidden until the
next check or start) and "Update and restart" (download, apply, then the
app close above with Hive closed and the new binary started), its
progress in the band. A release
that is behind also says which version it runs; a copy that is not a
packaged install says to replace the folder by hand. The shell keeps the
band off the full player while a video is on (not finished); it shows on
@@ -2599,6 +2601,27 @@ prerelease (`tool/dev_release.sh`), and take the newer of the two
(`fetchLatestManifest`); the release X.Y.Z outranks its test builds, so
testers land back on stable. `0.0.0-dev` (a dev run) checks nothing.
**What's new.** The manifest's `notes` is one line (all 1.2.x and 1.3.x
read; they ignore other keys) and `changes` (`{"added": [...], "changed":
[...], "fixed": [...]}`, `ReleaseChanges`, read leniently: anything
malformed is no `changes`) the version's lists; both come from
`CHANGELOG.md` ([Changelog](DEVELOPMENT.md#changelog)). The card
(`WhatsNewCard`, `widgets/whats_new_card.dart`) shows them as Added /
Changed / Fixed sections, each with its icon and colour, on `AppGlass` in
a popover, the rows easing in one after another, the list scrolling in a
short window; notes alone show as one paragraph. Three ways open it: the
band's What's new (the offered version, with Update and restart), the
first start of a new version, and Settings → About ("What's new in X.Y.Z",
once this version's notes are known). The first start (`WhatsNew`,
`update/whats_new.dart`): before restarting, the update keeps the new
version's notes in settings (`UpdateController.beforeRestart`); an update
made otherwise (1.3.x's updater, `--upgrade`, by hand) gets them from the
first check whose manifest is this very version (`UpdateController.latest`).
The card shows once per version (`whatsNewSeen` in settings), never on a
fresh install (the settings box was empty), and not over the full player
while a video is on (it waits until the video ends or the player
leaves full).
## Decisions
- Desktop + mpv only, no web clients: exists because Jellyfin SyncPlay is bad.
+29 -6
View File
@@ -254,17 +254,22 @@ Traps:
User pushes tags; CI does the rest.
1. `tool/release.sh [patch|minor|major] [--dry-run]`: derives the next
version from the latest tag, bumps the three pubspecs in lockstep, writes
`updates/manifest.json` (per-platform `assets` URLs are predictable; `url`
stays the Linux tarball for builds <= 0.2.1), commits `Release vX.Y.Z`,
makes an annotated tag.
version from the latest tag, refuses when the [changelog](#changelog)'s
Unreleased has no items (warns when it has no summary) and prints it,
renames Unreleased to `X.Y.Z` under a fresh empty Unreleased, bumps the
three pubspecs in lockstep, writes `updates/manifest.json` (per-platform
`assets` URLs are predictable; `url` stays the Linux tarball for builds
<= 0.2.1; `notes` is the section's summary, `changes` its lists), commits
`Release vX.Y.Z` (with `CHANGELOG.md`), makes an annotated tag.
`--dry-run` stops after printing.
2. Review, then `git push --follow-tags`.
3. `.gitea/workflows/release.yml` runs one job per platform: Linux builds
with `tool/package_linux.sh`, Windows (runner `yuuka-windows`, host mode,
PowerShell only; `tool/setup_flutter_windows.ps1` also adds Visual
Studio's C++ ATL component once, which the notifications plugin needs) with `tool/package_windows.ps1`. Each then runs
`dart tool/release_publish.dart vX.Y.Z <archive>`, which creates the
release if the other job hasn't yet and uploads (or replaces) its asset.
release if the other job hasn't yet (its text: the version's changelog
section) and uploads (or replaces) its asset.
By hand: package, then the same `dart tool/release_publish.dart` line.
Test builds (Linux, no tag, no CI): `tool/dev_release.sh [--minor|--major]
@@ -274,7 +279,8 @@ repository read/write) builds the working tree here as `<next>-dev.N` (N
one past the published one; `<next>` the pubspec's next patch, or next
minor / major when asked, and once such a line is published later runs
follow it until a release moves the pubspec) and replaces the assets of the
`dev` prerelease with the tarball and its `manifest.json`. Dev builds
`dev` prerelease with the tarball and its `manifest.json` (`changes`:
the changelog's Unreleased, which also becomes the prerelease's text). Dev builds
update from it ([Updater](ARCHITECTURE.md#updater)). A stable install
moves onto it once with the command the script prints (run with the app
open: it finds the install from the running process and copies the new
@@ -285,6 +291,23 @@ Tokens come from the environment (`RELEASE_TOKEN`, or Actions-provided
updater skips the check when absent. Workflows live in `.gitea/workflows`
(the forge reads `.gitea` before `.github`).
### Changelog
`CHANGELOG.md` is what people read: the update band's first items, the
What's new card ([Updater](ARCHITECTURE.md#updater)) and the release page.
A user-facing change adds a line to its `## Unreleased` section in the same
commit, under `### Added`, `### Changed` or `### Fixed` (no other
headings), as a `- ` item in plain words for the people watching, not
commit-speak ("Chat pop-ups stay away while the chat panel is open", not
"Notifier: suppress toasts when panel open"). Internal changes (tests,
refactors, docs, CI) get no line. The paragraph under the version heading
is an optional one-line summary: it becomes the manifest's `notes`, the
only thing 1.2.x/1.3.x apps show (without it, the first items); refresh it
before cutting. Versions carry no date (the tag and release page have it).
`tool/changelog.dart` reads and cuts the file (`cut X.Y.Z`, `json
<version|unreleased>`, `markdown <version|unreleased>`); its tests are
`tool/test/changelog_test.dart` (`cd tool && dart test`).
## Debugging your server
The app's own server runs on its own isolate (`ServerIsolate`,