Show the running version on Settings > General

The General page ends with a Version block naming the release this
server runs, so an update can be confirmed from the app.

The version is COBBLE_VERSION, the release tag the image was built with
(Dockerfile ARG, passed by the release workflow, which falls back to
package.json's version on a workflow-dispatch build), else package.json's
version, which a development server reports too, with (development)
after it. runningVersion (src/utils/running-version.ts) drops an
unusable version string rather than showing it.

Also includes the working tree's pending signup guard in
src/pages/__root.server.ts: a user who still holds a session while the
install has no users is sent to /auth/logout (and may load it).
This commit is contained in:
2026-10-22 16:03:56 +01:00
parent 89e57cc467
commit 524f0dde36
13 changed files with 215 additions and 3 deletions
+64
View File
@@ -0,0 +1,64 @@
import { describe, expect, test } from "bun:test";
import runningVersion from "./running-version";
describe("runningVersion", () => {
test("a release reports the build's version", () => {
expect(
runningVersion({
env: { NODE_ENV: "production", COBBLE_VERSION: "v0.1.1" },
}),
).toEqual({ version: "v0.1.1", mode: "production" });
});
test("a production build without a version falls back to the source's", () => {
expect(
runningVersion({
env: { NODE_ENV: "production" },
package_version: "0.1.1",
}),
).toEqual({ version: "0.1.1", mode: "production" });
});
test("a development server reports the source's version too", () => {
expect(
runningVersion({
env: { NODE_ENV: "development" },
package_version: "0.1.1",
}),
).toEqual({ version: "0.1.1", mode: "development" });
});
test("a development server with a build version prefers it", () => {
expect(
runningVersion({
env: { NODE_ENV: "development", COBBLE_VERSION: "v0.2.0" },
package_version: "0.1.1",
}),
).toEqual({ version: "v0.2.0", mode: "development" });
});
test("no NODE_ENV at all is a development server", () => {
expect(runningVersion({ env: {}, package_version: "0.1.1" })).toEqual({
version: "0.1.1",
mode: "development",
});
});
test("an unusable version is dropped rather than shown", () => {
expect(
runningVersion({
env: { NODE_ENV: "production", COBBLE_VERSION: "bad version!" },
package_version: "",
}),
).toEqual({ version: null, mode: "production" });
});
test("an empty build version falls back to the source's", () => {
expect(
runningVersion({
env: { NODE_ENV: "production", COBBLE_VERSION: " " },
package_version: "0.1.1",
}),
).toEqual({ version: "0.1.1", mode: "production" });
});
});
+52
View File
@@ -0,0 +1,52 @@
import package_json from "@/package.json";
/**
* What the General settings page shows as the running Cobble.
*/
export type RunningVersion = {
/**
* The release's version: the image tag the installer pulled
* (`COBBLE_VERSION`, set when the image was built), else
* `package.json`'s version, which a development server reports too.
*/
version: string | null;
/**
* Where the live server runs (`NODE_ENV`): `production`,
* `development` or the environment's own name when it set another.
*/
mode: string;
};
type Params = {
/**
* The process environment (`process.env`).
*/
env: Record<string, string | undefined>;
/**
* `package.json`'s version, for a build that wasn't given one.
*/
package_version?: string;
};
const VersionPattern = /^[A-Za-z0-9._-]{1,64}$/;
/**
* The version a running Cobble reports, development included: the image's
* build argument when it has one (the release tag), else the source's own
* version, so a dev server names the code it is working on.
*/
export default function runningVersion({
env,
package_version = package_json.version,
}: Params): RunningVersion {
const build_version = env.COBBLE_VERSION?.trim();
const version =
build_version && VersionPattern.test(build_version)
? build_version
: package_version && VersionPattern.test(package_version)
? package_version
: null;
return { version, mode: env.NODE_ENV || "development" };
}