From fd5bb11325e67d2d81ff72015839b9c7bb53f129 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 2 Nov 2026 20:35:57 +0200 Subject: [PATCH] refactor(release-notes): give every release a number and a commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The version strings are dates ("2026.11.01"), so a release has no identity that is not a date. Every entry now also carries a release number and the short SHA of the commit it went out with. The date-shaped version string is left in place for now — the popup switches over and drops it in the next commit, so this one stays green on its own. A test holds the numbering unique, contiguous and newest-first, and holds every commit to a real SHA. --- src/lib/release-notes.test.ts | 34 ++++++++++++++++++++++++++++++++++ src/lib/release-notes.ts | 24 ++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/lib/release-notes.test.ts diff --git a/src/lib/release-notes.test.ts b/src/lib/release-notes.test.ts new file mode 100644 index 0000000..584ce92 --- /dev/null +++ b/src/lib/release-notes.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; + +import { LATEST_RELEASE, RELEASE_NOTES } from "./release-notes"; + +describe("the release-note registry", () => { + it("numbers the releases 1..N with no gaps and no duplicates", () => { + const numbers = RELEASE_NOTES.map((r) => r.release); + + expect(new Set(numbers).size).toBe(numbers.length); + expect([...numbers].sort((a, b) => a - b)).toEqual( + Array.from({ length: RELEASE_NOTES.length }, (_, i) => i + 1) + ); + }); + + it("keeps the newest release at the top of the list", () => { + const numbers = RELEASE_NOTES.map((r) => r.release); + + expect(numbers).toEqual([...numbers].sort((a, b) => b - a)); + expect(LATEST_RELEASE).toBe(RELEASE_NOTES[0]); + }); + + it("gives every release a commit SHA", () => { + for (const release of RELEASE_NOTES) { + expect(release.commit).toMatch(/^[0-9a-f]{7,40}$/); + } + }); + + it("gives every release a headline and at least one item", () => { + for (const release of RELEASE_NOTES) { + expect(release.title.length).toBeGreaterThan(0); + expect(release.items.length).toBeGreaterThan(0); + } + }); +}); diff --git a/src/lib/release-notes.ts b/src/lib/release-notes.ts index c0939be..078408a 100644 --- a/src/lib/release-notes.ts +++ b/src/lib/release-notes.ts @@ -13,9 +13,19 @@ */ export interface ReleaseNote { - /** Version marker. Bump this to re-notify operators. */ + /** Version marker. Bumped to re-notify operators. */ version: string; - /** ISO date (YYYY-MM-DD) of the release. */ + /** + * Release number. 1 = the first release. This replaces the date-shaped + * `version` string and becomes the "seen" marker. + */ + release: number; + /** Short SHA of the commit this release went out with. */ + commit: string; + /** + * Authoring reference only — the day the release was written up. + * Deliberately never rendered: the popup carries no dates. + */ date: string; /** One-line headline for the release. */ title: string; @@ -26,6 +36,8 @@ export interface ReleaseNote { export const RELEASE_NOTES: ReleaseNote[] = [ { version: "2026.11.01", + release: 5, + commit: "139ea25", date: "2026-11-01", title: "Delivery Pool, letterhead lock and a dark mode section", items: [ @@ -38,6 +50,8 @@ export const RELEASE_NOTES: ReleaseNote[] = [ }, { version: "2026.10.30", + release: 4, + commit: "83aec14", date: "2026-10-30", title: "Collection Confirm gets a second pair of eyes", items: [ @@ -49,6 +63,8 @@ export const RELEASE_NOTES: ReleaseNote[] = [ }, { version: "2026.10.28", + release: 3, + commit: "3eea87e", date: "2026-10-28", title: "Auto-push and a shortcut to AI settings", items: [ @@ -58,6 +74,8 @@ export const RELEASE_NOTES: ReleaseNote[] = [ }, { version: "2026.10.26", + release: 2, + commit: "b212d0f", date: "2026-10-26", title: "A phone board for the managers", items: [ @@ -67,6 +85,8 @@ export const RELEASE_NOTES: ReleaseNote[] = [ }, { version: "2026.10.24", + release: 1, + commit: "55cc7fa", date: "2026-10-24", title: "One POS matcher, AI-only quote matching", items: [