From 455898f3c966a320a94d0c16848efb4ce82c6971 Mon Sep 17 00:00:00 2001 From: dtonon Date: Wed, 23 Sep 2026 23:11:52 +0200 Subject: [PATCH] Add release to the footer --- src/app.d.ts | 4 +++- src/lib/auth.svelte.ts | 4 ++++ src/lib/version.ts | 23 +++++++++++++++++++ src/routes/+layout.svelte | 47 +++++++++++++++++++++++++++++++++++++-- vite.config.ts | 34 +++++++++++++++++++++++++++- 5 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 src/lib/version.ts diff --git a/src/app.d.ts b/src/app.d.ts index 53a4264..b1e3e88 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -9,8 +9,10 @@ import type { import type { ThreadDetail } from "$lib/forum/thread"; declare global { - // Injected by vite.config.ts from PUBLIC_SSR + // Injected by vite.config.ts from PUBLIC_SSR, package.json and git const __SQUALK_SSR__: boolean; + const __SQUALK_VERSION__: string; + const __SQUALK_COMMIT__: string; namespace App { // interface Error {} diff --git a/src/lib/auth.svelte.ts b/src/lib/auth.svelte.ts index 0b96b3a..86cf060 100644 --- a/src/lib/auth.svelte.ts +++ b/src/lib/auth.svelte.ts @@ -122,6 +122,10 @@ function closeBunker() { bunker = null; } +export function loginMethod(): string | null { + return user ? localStorage.getItem(METHOD_KEY) : null; +} + function clearStoredSession() { localStorage.removeItem(PUBKEY_KEY); localStorage.removeItem(METHOD_KEY); diff --git a/src/lib/version.ts b/src/lib/version.ts new file mode 100644 index 0000000..0bc23c5 --- /dev/null +++ b/src/lib/version.ts @@ -0,0 +1,23 @@ +import { MODE, RELAY_URL, SSR_ENABLED } from "$lib/config"; + +export const VERSION = __SQUALK_VERSION__; +export const COMMIT = __SQUALK_COMMIT__; + +const REPO = "https://github.com/dtonon/squalk"; +// A release build links to its GitHub release, anything else to its commit +export const VERSION_URL = VERSION.includes("+") + ? `${REPO}/commit/${COMMIT}` + : `${REPO}/releases/tag/v${VERSION}`; + +// Plain-text block to paste in a bug report +export function diagnostics(loginMethod: string | null): string { + return [ + `Squalk ${VERSION}`, + `Commit: ${COMMIT || "unknown"}`, + `Mode: ${MODE}`, + `SSR: ${SSR_ENABLED ? "yes" : "no"}`, + `Relay: ${RELAY_URL}`, + `Login: ${loginMethod ?? "none"}`, + `Browser: ${navigator.userAgent}`, + ].join("\n"); +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 5eb4227..3b48d52 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -15,7 +15,9 @@ import { page } from "$app/state"; import { afterNavigate } from "$app/navigation"; import { onMount, untrack } from "svelte"; - import { auth, restoreSession } from "$lib/auth.svelte"; + import { auth, restoreSession, loginMethod } from "$lib/auth.svelte"; + import { showToast } from "$lib/toast.svelte"; + import { VERSION, VERSION_URL, diagnostics } from "$lib/version"; import { loadGroup } from "$lib/group.svelte"; import { ensureMembershipChecked } from "$lib/join.svelte"; import { loadGroups, groupsStore } from "$lib/groups.svelte"; @@ -42,6 +44,15 @@ let { children } = $props(); + async function copyDiagnostics() { + try { + await navigator.clipboard.writeText(diagnostics(loginMethod())); + showToast("Version info copied, paste it in your bug report"); + } catch { + showToast("Could not copy to clipboard"); + } + } + // Optional env overrides for the theme colors. Set as inline custom props on // so they outrank the @theme `:root` defaults; hover shades are derived // by darkening the base 15% so admins only set one value per color. @@ -233,6 +244,8 @@ diff --git a/vite.config.ts b/vite.config.ts index a02ee62..5847dd3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,6 +1,33 @@ import tailwindcss from "@tailwindcss/vite"; import { sveltekit } from "@sveltejs/kit/vite"; import { defineConfig, loadEnv } from "vite"; +import { execSync } from "node:child_process"; +import { readFileSync } from "node:fs"; + +function git(cmd: string): string { + try { + return execSync(`git ${cmd}`, { stdio: ["ignore", "pipe", "ignore"] }) + .toString() + .trim(); + } catch { + return ""; + } +} + +// Version comes from package.json; a build that is not exactly the tagged +// release commit (or has local changes) carries the commit as build metadata, +// e.g. 0.3.0+a1b2c3d or 0.3.0+a1b2c3d.dirty, so bug reports tell them apart. +function version(): { version: string; commit: string } { + const pkg = JSON.parse(readFileSync("package.json", "utf8")); + const commit = git("rev-parse --short HEAD"); + const tagged = git("tag --points-at HEAD") + .split("\n") + .includes(`v${pkg.version}`); + const dirty = git("status --porcelain --untracked-files=no") !== ""; + let v = pkg.version; + if (commit && (!tagged || dirty)) v += `+${commit}${dirty ? ".dirty" : ""}`; + return { version: v, commit }; +} // PUBLIC_SSR is a build-time decision (it also picks the adapter in // svelte.config.js), so it is compiled in as a constant rather than read from @@ -8,8 +35,13 @@ import { defineConfig, loadEnv } from "vite"; export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), "PUBLIC_"); const ssr = (process.env.PUBLIC_SSR ?? env.PUBLIC_SSR) === "yes"; + const build = version(); return { plugins: [tailwindcss(), sveltekit()], - define: { __SQUALK_SSR__: JSON.stringify(ssr) }, + define: { + __SQUALK_SSR__: JSON.stringify(ssr), + __SQUALK_VERSION__: JSON.stringify(build.version), + __SQUALK_COMMIT__: JSON.stringify(build.commit), + }, }; });