From d231623c59498ae56e13bee45a70100423053e9b Mon Sep 17 00:00:00 2001 From: Paul Nothaft Date: Fri, 29 May 2026 10:25:12 +0200 Subject: [PATCH] feat(admin): link version numbers in sidebar to GitHub release notes (#566) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #566. The admin sidebar showed the running frontend + backend versions as plain text. Wraps each version (and the "update available" indicator) in an anchor pointing at the corresponding GitHub release tag, opening in a new tab so the admin session isn't disrupted. A small githubReleaseUrl helper (extracted to its own module for testability) does the version → URL mapping. Because release-please tags every release as `vX.Y.Z[-beta.N]`, the version string already carries the channel suffix and a pure template covers both stable and beta without branching. Three unit tests pin the URL template — stable, beta-with-suffix, and a defensive check that the leading `v` isn't double-prefixed if a caller accidentally passes a tag-shaped value. --- frontend/src/components/admin/VersionInfo.tsx | 41 ++++++++++++++++--- .../utils/__tests__/githubReleaseUrl.test.ts | 30 ++++++++++++++ frontend/src/utils/githubReleaseUrl.ts | 14 +++++++ 3 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 frontend/src/utils/__tests__/githubReleaseUrl.test.ts create mode 100644 frontend/src/utils/githubReleaseUrl.ts diff --git a/frontend/src/components/admin/VersionInfo.tsx b/frontend/src/components/admin/VersionInfo.tsx index 7063722f..653cac38 100644 --- a/frontend/src/components/admin/VersionInfo.tsx +++ b/frontend/src/components/admin/VersionInfo.tsx @@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { Info, ArrowUpCircle } from 'lucide-react'; import { api } from '../../config/api'; +import { githubReleaseUrl as releaseUrl } from '../../utils/githubReleaseUrl'; import packageJson from '../../../package.json'; // Frontend version from package.json @@ -63,20 +64,48 @@ export const VersionInfo: React.FC = () => { {channelBadge}
-
Frontend: v{FRONTEND_VERSION}
+
+ Frontend:{' '} + + v{FRONTEND_VERSION} + +
{versionInfo && ( -
Backend: v{versionInfo.backend}
+
+ Backend:{' '} + + v{versionInfo.backend} + +
)}
- {updateInfo?.enabled && updateInfo?.updateAvailable && ( -
+ {updateInfo?.enabled && updateInfo?.updateAvailable && updateInfo.latest && ( + {t('admin.updates.updateAvailableShort', 'v{{version}} available', { - version: updateInfo.latest?.forChannel + version: updateInfo.latest.forChannel })} -
+ )} ); diff --git a/frontend/src/utils/__tests__/githubReleaseUrl.test.ts b/frontend/src/utils/__tests__/githubReleaseUrl.test.ts new file mode 100644 index 00000000..6a3afde0 --- /dev/null +++ b/frontend/src/utils/__tests__/githubReleaseUrl.test.ts @@ -0,0 +1,30 @@ +/** + * Regression coverage for the version → release-page URL template (#566). + * + * release-please tags every release as `vX.Y.Z[-beta.N]`. If anyone + * later refactors the helper (e.g. switches the prefix, the repo path, + * or strips the channel suffix), these assertions will fail loud + * instead of silently producing dead links in the admin sidebar. + */ +import { describe, expect, it } from 'vitest'; +import { githubReleaseUrl } from '../githubReleaseUrl'; + +describe('githubReleaseUrl', () => { + it('maps a stable version to its tag page', () => { + expect(githubReleaseUrl('3.55.0')).toBe( + 'https://github.com/the-luap/picpeak/releases/tag/v3.55.0', + ); + }); + + it('maps a beta version (with channel suffix) to its tag page', () => { + expect(githubReleaseUrl('3.55.0-beta.0')).toBe( + 'https://github.com/the-luap/picpeak/releases/tag/v3.55.0-beta.0', + ); + }); + + it('does not double-prefix the v when given a bare version', () => { + // Defensive — the helper is the single source of truth for the + // leading "v". Callers must pass the bare version string. + expect(githubReleaseUrl('3.55.0')).not.toContain('vv'); + }); +}); diff --git a/frontend/src/utils/githubReleaseUrl.ts b/frontend/src/utils/githubReleaseUrl.ts new file mode 100644 index 00000000..adaf6180 --- /dev/null +++ b/frontend/src/utils/githubReleaseUrl.ts @@ -0,0 +1,14 @@ +/** + * Build the GitHub release page URL for a given version string. + * + * Version strings already carry their channel suffix (e.g. `3.55.0` + * for stable, `3.55.0-beta.0` for beta). release-please tags every + * release as `vX.Y.Z[-beta.N]`, so a pure string template covers + * both channels without branching on the suffix. + * + * Used by the admin sidebar version display to deep-link to release + * notes for the running version (#566) and by the update-available + * indicator to link to the upgrade target's notes. + */ +export const githubReleaseUrl = (version: string): string => + `https://github.com/the-luap/picpeak/releases/tag/v${version}`;