From c08ad3a69a3e02736da362f045da4d11135e113a Mon Sep 17 00:00:00 2001 From: ajnart Date: Sun, 16 Aug 2026 15:24:07 +0200 Subject: [PATCH] refactor(version): centralize Homarr version in @homarr/version package Replace fragile deep-relative 'package.json' imports across the monorepo with a single source of truth: the new @homarr/version leaf package reads the version once from the root package.json and exports it as VERSION. Consumers now import by package name incl. the new User-Agent header in fetchWithTrustedCertificatesAsync (packages/core): - core, api (info router), analytics, request-handler (update checker) This also removes all manually-maintained relative version imports. --- packages/analytics/package.json | 1 + .../analytics/src/send-server-analytics.ts | 4 ++-- packages/api/package.json | 1 + packages/api/src/router/info.ts | 5 +++-- packages/core/package.json | 1 + .../core/src/infrastructure/http/request.ts | 5 ++--- packages/request-handler/package.json | 1 + .../request-handler/src/update-checker.ts | 7 +++--- packages/version/index.ts | 3 +++ packages/version/package.json | 20 +++++++++++++++++ packages/version/tsconfig.json | 8 +++++++ pnpm-lock.yaml | 22 +++++++++++++++++++ 12 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 packages/version/index.ts create mode 100644 packages/version/package.json create mode 100644 packages/version/tsconfig.json diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 2c99a29b4..c97ef407a 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -26,6 +26,7 @@ "@homarr/core": "workspace:^0.1.0", "@homarr/db": "workspace:^0.1.0", "@homarr/docker": "workspace:^0.1.0", + "@homarr/version": "workspace:^0.1.0", "posthog-node": "catalog:" }, "devDependencies": { diff --git a/packages/analytics/src/send-server-analytics.ts b/packages/analytics/src/send-server-analytics.ts index b851f86ec..497ad0e40 100644 --- a/packages/analytics/src/send-server-analytics.ts +++ b/packages/analytics/src/send-server-analytics.ts @@ -28,8 +28,8 @@ import { users, } from "@homarr/db/schema"; import { env as dockerEnv } from "@homarr/docker/env"; +import { VERSION } from "@homarr/version"; -import packageJson from "../../../package.json"; import { getPostHogClient } from "./client"; const logger = createLogger({ module: "analytics" }); @@ -121,7 +121,7 @@ export const sendServerAnalyticsAsync = async (): Promise => { const enabledAuthProviders = (["credentials", "oidc", "ldap"] as const).filter(isProviderEnabled); const properties: Record = { - homarrVersion: packageJson.version, + homarrVersion: VERSION, databaseType: isMysql() ? "mysql" : isPostgresql() ? "postgresql" : "sqlite", dockerEnabled: Boolean(dockerEnv.ENABLE_DOCKER), kubernetesEnabled: Boolean(dockerEnv.ENABLE_KUBERNETES), diff --git a/packages/api/package.json b/packages/api/package.json index 92f68ea36..60f38eab8 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -46,6 +46,7 @@ "@homarr/server-settings": "workspace:^0.1.0", "@homarr/translation": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0", + "@homarr/version": "workspace:^0.1.0", "@kubernetes/client-node": "catalog:", "@tanstack/react-query": "catalog:", "@trpc/client": "catalog:", diff --git a/packages/api/src/router/info.ts b/packages/api/src/router/info.ts index 156f6387a..a9b1fbe61 100644 --- a/packages/api/src/router/info.ts +++ b/packages/api/src/router/info.ts @@ -1,6 +1,7 @@ import z from "zod/v4"; -import packageJson from "../../../../package.json"; +import { VERSION } from "@homarr/version"; + import { createTRPCRouter, isDemoMode, isDemoReadOnly, publicProcedure, protectedProcedure } from "../trpc"; export const infoRouter = createTRPCRouter({ @@ -16,7 +17,7 @@ export const infoRouter = createTRPCRouter({ }) .query(() => { return { - version: packageJson.version, + version: VERSION, }; }), isDemoMode: publicProcedure.query(() => isDemoMode), diff --git a/packages/core/package.json b/packages/core/package.json index 9c46dd637..e96a94c90 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -36,6 +36,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@homarr/version": "workspace:^0.1.0", "@t3-oss/env-nextjs": "catalog:", "better-sqlite3": "catalog:", "dns-caching": "catalog:", diff --git a/packages/core/src/infrastructure/http/request.ts b/packages/core/src/infrastructure/http/request.ts index 20af8232f..81e581e6b 100644 --- a/packages/core/src/infrastructure/http/request.ts +++ b/packages/core/src/infrastructure/http/request.ts @@ -10,13 +10,12 @@ import { getTrustedCertificateHostnamesAsync, } from "@homarr/core/infrastructure/certificates"; import { UndiciHttpAgent } from "@homarr/core/infrastructure/http"; +import { VERSION } from "@homarr/version"; import type { TrustedCertificateHostname } from "../certificates/hostnames"; import { withTimeoutAsync } from "./timeout"; -import packageJson from "../../../../../package.json"; - -export const getDefaultUserAgent = () => `Homarr/${packageJson.version} (+https://homarr.dev)`; +export const getDefaultUserAgent = () => `Homarr/${VERSION} (+https://homarr.dev)`; export const createCustomCheckServerIdentity = ( trustedHostnames: TrustedCertificateHostname[], diff --git a/packages/request-handler/package.json b/packages/request-handler/package.json index 29609d87d..9d6e03601 100644 --- a/packages/request-handler/package.json +++ b/packages/request-handler/package.json @@ -29,6 +29,7 @@ "@homarr/docker": "workspace:^0.1.0", "@homarr/integrations": "workspace:^0.1.0", "@homarr/redis": "workspace:^0.1.0", + "@homarr/version": "workspace:^0.1.0", "dayjs": "catalog:", "octokit": "catalog:", "superjson": "catalog:", diff --git a/packages/request-handler/src/update-checker.ts b/packages/request-handler/src/update-checker.ts index 9bc2085cd..7945fc72e 100644 --- a/packages/request-handler/src/update-checker.ts +++ b/packages/request-handler/src/update-checker.ts @@ -6,8 +6,7 @@ import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/h import { createLogger } from "@homarr/core/infrastructure/logs"; import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error"; import { createGetSetChannel, createLockChannel } from "@homarr/redis"; - -import packageJson from "../../../package.json"; +import { VERSION } from "@homarr/version"; const logger = createLogger({ module: "updateCheckerRequestHandler" }); @@ -22,7 +21,7 @@ type UpdateCheckCacheEntry = { checkedAt: number | null; }; -const updateCheckCacheVersion = `v2:${packageJson.version}`; +const updateCheckCacheVersion = `v2:${VERSION}`; const freshUpdateCheckChannel = createGetSetChannel( `update-checker:fresh:${updateCheckCacheVersion}`, ); @@ -69,7 +68,7 @@ const getCachedAvailableUpdatesAsync = async (): Promise try { const attemptedAt = Date.now(); - const availableUpdates = await getAvailableUpdatesAsync(packageJson.version); + const availableUpdates = await getAvailableUpdatesAsync(VERSION); const result: UpdateCheckCacheEntry = { availableUpdates, attemptedAt, diff --git a/packages/version/index.ts b/packages/version/index.ts new file mode 100644 index 000000000..31b768021 --- /dev/null +++ b/packages/version/index.ts @@ -0,0 +1,3 @@ +import packageJson from "../../package.json"; + +export const VERSION = packageJson.version; diff --git a/packages/version/package.json b/packages/version/package.json new file mode 100644 index 000000000..25b10702a --- /dev/null +++ b/packages/version/package.json @@ -0,0 +1,20 @@ +{ + "name": "@homarr/version", + "version": "0.1.0", + "private": true, + "license": "Apache-2.0", + "type": "module", + "exports": { + ".": "./index.ts" + }, + "scripts": { + "clean": "rm -rf .turbo node_modules", + "format": "oxfmt .", + "lint": "oxlint", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@homarr/tsconfig": "workspace:^0.1.0", + "typescript": "catalog:" + } +} diff --git a/packages/version/tsconfig.json b/packages/version/tsconfig.json new file mode 100644 index 000000000..cbe8483d9 --- /dev/null +++ b/packages/version/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@homarr/tsconfig/base.json", + "compilerOptions": { + "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" + }, + "include": ["*.ts", "src"], + "exclude": ["node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d620d34f4..8d00a775c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1241,6 +1241,9 @@ importers: '@homarr/docker': specifier: workspace:^0.1.0 version: link:../docker + '@homarr/version': + specifier: workspace:^0.1.0 + version: link:../version posthog-node: specifier: 'catalog:' version: 5.41.0(rxjs@7.8.2) @@ -1311,6 +1314,9 @@ importers: '@homarr/validation': specifier: workspace:^0.1.0 version: link:../validation + '@homarr/version': + specifier: workspace:^0.1.0 + version: link:../version '@kubernetes/client-node': specifier: 'catalog:' version: 1.4.0(supports-color@10.2.2) @@ -1520,6 +1526,9 @@ importers: packages/core: dependencies: + '@homarr/version': + specifier: workspace:^0.1.0 + version: link:../version '@t3-oss/env-nextjs': specifier: 'catalog:' version: 0.13.11(arktype@2.1.20)(typescript@7.0.2)(zod@4.4.3) @@ -2256,6 +2265,9 @@ importers: '@homarr/redis': specifier: workspace:^0.1.0 version: link:../redis + '@homarr/version': + specifier: workspace:^0.1.0 + version: link:../version dayjs: specifier: 'catalog:' version: 1.11.21 @@ -2513,6 +2525,15 @@ importers: specifier: 'catalog:' version: 7.0.2 + packages/version: + devDependencies: + '@homarr/tsconfig': + specifier: workspace:^0.1.0 + version: link:../../tooling/typescript + typescript: + specifier: 'catalog:' + version: 7.0.2 + packages/widgets: dependencies: '@dnd-kit/core': @@ -9407,6 +9428,7 @@ packages: crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + deprecated: Active development of CryptoJS has been discontinued. This library is no longer maintained. crypto-random-string@4.0.0: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==}