mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-09 15:38:39 +01:00
CodeRabbit: in the production tier, a null version + present build produced "null (42)". Guard the template (version && build) and fall back to the build number alone, then "N/A".
88 lines
3.1 KiB
TypeScript
88 lines
3.1 KiB
TypeScript
import * as Application from "expo-application";
|
|
import Constants from "expo-constants";
|
|
|
|
/** Raw marketing version (app.json `version`), e.g. "0.54.1". Exposed so the Jellyfin
|
|
* clientInfo auto-tracks the app version instead of a hardcoded string. */
|
|
export const APP_VERSION = Application.nativeApplicationVersion ?? "unknown";
|
|
|
|
/** Build metadata injected at build time by `app.config.js` into `extra.build`. */
|
|
export interface BuildMeta {
|
|
commit?: string | null;
|
|
branch?: string | null;
|
|
profile?: string | null;
|
|
builtAt?: string | null;
|
|
}
|
|
|
|
export interface VersionInfo {
|
|
/** Marketing version (CFBundleShortVersionString / android versionName), e.g. "0.54.1". */
|
|
version: string | null;
|
|
/** Build number (CFBundleVersion / versionCode), e.g. "42". */
|
|
build: string | null;
|
|
/** Short git commit the build was made from, e.g. "a1b2c3d". */
|
|
commit: string | null;
|
|
/** Git branch the build was made from, e.g. "develop". */
|
|
branch: string | null;
|
|
/** EAS build profile, e.g. "production", "preview", or null for local. */
|
|
profile: string | null;
|
|
isDev: boolean;
|
|
isProduction: boolean;
|
|
/** Graduated label for the Settings "App version" row (see tiering below). */
|
|
display: string;
|
|
}
|
|
|
|
/**
|
|
* Resolve a graduated version string for Settings.
|
|
*
|
|
* Tiering (most → least detailed):
|
|
* - dev / local build → `version · branch · commit` (full context for debugging)
|
|
* - develop / CI / preview → `version · commit` (pin the exact source)
|
|
* - production (store / TestFlight) → `version (build)` (store-correlatable; the
|
|
* build number lets TestFlight reports pin a build whose version isn't a
|
|
* published release. Note: TestFlight and the public App Store ship the same
|
|
* binary — telling them apart needs a runtime iOS receipt check, intentionally
|
|
* not done here.)
|
|
*/
|
|
export function getVersionInfo(): VersionInfo {
|
|
// Read native/config values defensively — a version string must never crash Settings
|
|
// (e.g. a dev build whose native expo-constants is out of sync with the JS).
|
|
const read = <T>(fn: () => T): T | null => {
|
|
try {
|
|
return fn() ?? null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const version = read(() => Application.nativeApplicationVersion);
|
|
const build = read(() => Application.nativeBuildVersion);
|
|
const meta = (read(() => Constants.expoConfig?.extra?.build) ??
|
|
{}) as BuildMeta;
|
|
const commit = meta.commit ?? null;
|
|
const branch = meta.branch ?? null;
|
|
const profile = meta.profile ?? null;
|
|
const isDev = __DEV__ === true;
|
|
const isProduction =
|
|
typeof profile === "string" && profile.startsWith("production");
|
|
|
|
let display: string;
|
|
if (isDev) {
|
|
display = [version ?? "dev", branch, commit].filter(Boolean).join(" · ");
|
|
} else if (isProduction) {
|
|
display =
|
|
version && build ? `${version} (${build})` : (version ?? build ?? "N/A");
|
|
} else {
|
|
display = [version, commit].filter(Boolean).join(" · ") || version || "N/A";
|
|
}
|
|
|
|
return {
|
|
version,
|
|
build,
|
|
commit,
|
|
branch,
|
|
profile,
|
|
isDev,
|
|
isProduction,
|
|
display,
|
|
};
|
|
}
|