mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-09 23:48:41 +01:00
Compare commits
3 Commits
cleanup/de
...
fix/show-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1ea2830a8 | ||
|
|
64105a8bac | ||
|
|
4d0a571ad0 |
6
.github/workflows/build-apps.yml
vendored
6
.github/workflows/build-apps.yml
vendored
@@ -11,6 +11,12 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [develop, master]
|
branches: [develop, master]
|
||||||
|
|
||||||
|
# Exposed to `expo prebuild` (app.config.js → extra.build) so Settings can show the
|
||||||
|
# branch + commit a CI build was made from. EAS cloud builds use EAS_BUILD_* instead.
|
||||||
|
env:
|
||||||
|
EXPO_PUBLIC_GIT_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||||
|
EXPO_PUBLIC_GIT_COMMIT: ${{ github.sha }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-android-phone:
|
build-android-phone:
|
||||||
if: (!contains(github.event.head_commit.message, '[skip ci]'))
|
if: (!contains(github.event.head_commit.message, '[skip ci]'))
|
||||||
|
|||||||
@@ -1,3 +1,41 @@
|
|||||||
|
const { execFileSync } = require("node:child_process");
|
||||||
|
|
||||||
|
// Build metadata, injected into `extra.build` and read at runtime via
|
||||||
|
// expo-constants (see utils/version.ts). Sources in priority order:
|
||||||
|
// EAS cloud build → GitHub Actions → explicit EXPO_PUBLIC_* → local git → null.
|
||||||
|
const git = (args) => {
|
||||||
|
try {
|
||||||
|
return execFileSync("git", args, { stdio: ["ignore", "pipe", "ignore"] })
|
||||||
|
.toString()
|
||||||
|
.trim();
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildMeta = {
|
||||||
|
commit:
|
||||||
|
(
|
||||||
|
process.env.EAS_BUILD_GIT_COMMIT_HASH ||
|
||||||
|
process.env.GITHUB_SHA ||
|
||||||
|
process.env.EXPO_PUBLIC_GIT_COMMIT ||
|
||||||
|
git(["rev-parse", "HEAD"]) ||
|
||||||
|
""
|
||||||
|
).slice(0, 7) || null,
|
||||||
|
branch:
|
||||||
|
process.env.EAS_BUILD_GIT_BRANCH ||
|
||||||
|
process.env.GITHUB_HEAD_REF ||
|
||||||
|
process.env.GITHUB_REF_NAME ||
|
||||||
|
process.env.EXPO_PUBLIC_GIT_BRANCH ||
|
||||||
|
git(["rev-parse", "--abbrev-ref", "HEAD"]) ||
|
||||||
|
null,
|
||||||
|
profile:
|
||||||
|
process.env.EAS_BUILD_PROFILE ||
|
||||||
|
process.env.EXPO_PUBLIC_BUILD_PROFILE ||
|
||||||
|
null,
|
||||||
|
builtAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = ({ config }) => {
|
module.exports = ({ config }) => {
|
||||||
if (process.env.EXPO_TV !== "1") {
|
if (process.env.EXPO_TV !== "1") {
|
||||||
config.plugins.push("expo-background-task");
|
config.plugins.push("expo-background-task");
|
||||||
@@ -22,6 +60,8 @@ module.exports = ({ config }) => {
|
|||||||
androidConfig.googleServicesFile = process.env.GOOGLE_SERVICES_JSON;
|
androidConfig.googleServicesFile = process.env.GOOGLE_SERVICES_JSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.extra = { ...config.extra, build: buildMeta };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(Object.keys(androidConfig).length > 0 && { android: androidConfig }),
|
...(Object.keys(androidConfig).length > 0 && { android: androidConfig }),
|
||||||
...config,
|
...config,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as Application from "expo-application";
|
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { View, type ViewProps } from "react-native";
|
import { View, type ViewProps } from "react-native";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
|
import { getVersionInfo } from "@/utils/version";
|
||||||
import { ListGroup } from "../list/ListGroup";
|
import { ListGroup } from "../list/ListGroup";
|
||||||
import { ListItem } from "../list/ListItem";
|
import { ListItem } from "../list/ListItem";
|
||||||
|
|
||||||
@@ -13,10 +13,9 @@ export const UserInfo: React.FC<Props> = ({ ...props }) => {
|
|||||||
const [user] = useAtom(userAtom);
|
const [user] = useAtom(userAtom);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const version =
|
// Graduated build identifier — see utils/version.ts:
|
||||||
Application?.nativeApplicationVersion ||
|
// dev → "0.54.1 · branch · commit", develop/CI → "0.54.1 · commit", production → "0.54.1 (42)".
|
||||||
Application?.nativeBuildVersion ||
|
const { display: version } = getVersionInfo();
|
||||||
"N/A";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View {...props}>
|
<View {...props}>
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import {
|
|||||||
} from "@/utils/secureCredentials";
|
} from "@/utils/secureCredentials";
|
||||||
import { store } from "@/utils/store";
|
import { store } from "@/utils/store";
|
||||||
import { clearTVDiscoverySafely } from "@/utils/tvDiscovery/sync";
|
import { clearTVDiscoverySafely } from "@/utils/tvDiscovery/sync";
|
||||||
|
import { APP_VERSION } from "@/utils/version";
|
||||||
|
|
||||||
interface Server {
|
interface Server {
|
||||||
address: string;
|
address: string;
|
||||||
@@ -53,7 +54,7 @@ const initialApi = (() => {
|
|||||||
const id = getOrSetDeviceId();
|
const id = getOrSetDeviceId();
|
||||||
const deviceName = getDeviceNameSync();
|
const deviceName = getDeviceNameSync();
|
||||||
const jellyfinInstance = new Jellyfin({
|
const jellyfinInstance = new Jellyfin({
|
||||||
clientInfo: { name: "Streamyfin", version: "0.54.1" },
|
clientInfo: { name: "Streamyfin", version: APP_VERSION },
|
||||||
deviceInfo: {
|
deviceInfo: {
|
||||||
name: deviceName,
|
name: deviceName,
|
||||||
id,
|
id,
|
||||||
@@ -135,7 +136,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
const id = getOrSetDeviceId();
|
const id = getOrSetDeviceId();
|
||||||
const deviceName = getDeviceNameSync();
|
const deviceName = getDeviceNameSync();
|
||||||
return new Jellyfin({
|
return new Jellyfin({
|
||||||
clientInfo: { name: "Streamyfin", version: "0.54.1" },
|
clientInfo: { name: "Streamyfin", version: APP_VERSION },
|
||||||
deviceInfo: {
|
deviceInfo: {
|
||||||
name: deviceName,
|
name: deviceName,
|
||||||
id,
|
id,
|
||||||
@@ -169,7 +170,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
return {
|
return {
|
||||||
authorization: `MediaBrowser Client="Streamyfin", Device=${
|
authorization: `MediaBrowser Client="Streamyfin", Device=${
|
||||||
Platform.OS === "android" ? "Android" : "iOS"
|
Platform.OS === "android" ? "Android" : "iOS"
|
||||||
}, DeviceId="${deviceId}", Version="0.54.1"`,
|
}, DeviceId="${deviceId}", Version="${APP_VERSION}"`,
|
||||||
};
|
};
|
||||||
}, [deviceId]);
|
}, [deviceId]);
|
||||||
|
|
||||||
|
|||||||
87
utils/version.ts
Normal file
87
utils/version.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user