From 7f1e3d35416faa7ced09b5d115fb5ba6843c5d6b Mon Sep 17 00:00:00 2001 From: Rehuz Date: Fri, 24 Jul 2026 12:55:46 +0530 Subject: [PATCH] =?UTF-8?q?fix(admin):=20prevent=20fallback=20to=20'latest?= =?UTF-8?q?'=20string=20when=20dockerhub=20tags=20c=E2=80=A6=20(#22885)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #22849 ### What changed? When the `AdminPanelVersionService` hits a DockerHub API error (or filters out all valid tags), it was previously hardcoded to return `'latest'`. This caused the frontend to display `Latest version: latest`. I updated the GraphQL DTO to make `latestVersion` nullable, and modified the service fallback and unit tests to return and expect `null` instead. The frontend (`SettingsAdminVersionDisplay`) already has logic to handle a falsy version and gracefully display `No latest version found`, so this backend fix entirely resolves the UX issue without touching the frontend. Review in cubic --- .../twenty-front/src/generated-admin/graphql.ts | 4 ++-- .../__tests__/admin-panel.service.spec.ts | 4 ++-- .../admin-panel/dtos/version-info.dto.ts | 4 ++-- .../services/admin-panel-version.service.ts | 13 +++++++++---- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/twenty-front/src/generated-admin/graphql.ts b/packages/twenty-front/src/generated-admin/graphql.ts index 447187c6d1..3fd11dec6b 100644 --- a/packages/twenty-front/src/generated-admin/graphql.ts +++ b/packages/twenty-front/src/generated-admin/graphql.ts @@ -929,7 +929,7 @@ export type VersionDistributionEntry = { export type VersionInfo = { __typename?: 'VersionInfo'; currentVersion?: Maybe; - latestVersion: Scalars['String']['output']; + latestVersion?: Maybe; }; export type WorkerQueueMetrics = { @@ -1267,7 +1267,7 @@ export type GetUpgradeStatusQuery = { __typename?: 'Query', getUpgradeStatus: Ar export type GetVersionInfoQueryVariables = Exact<{ [key: string]: never; }>; -export type GetVersionInfoQuery = { __typename?: 'Query', versionInfo: { __typename?: 'VersionInfo', currentVersion?: string | null, latestVersion: string } }; +export type GetVersionInfoQuery = { __typename?: 'Query', versionInfo: { __typename?: 'VersionInfo', currentVersion?: string | null, latestVersion?: string | null } }; export type WorkspaceBillingAdminPanelQueryVariables = Exact<{ workspaceId: Scalars['UUID']['input']; diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts index 131a5ba6bb..62e2edc55c 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/__tests__/admin-panel.service.spec.ts @@ -337,7 +337,7 @@ describe('AdminPanelVersionService', () => { expect(result).toEqual({ currentVersion: '1.0.0', - latestVersion: 'latest', + latestVersion: null, }); }); @@ -353,7 +353,7 @@ describe('AdminPanelVersionService', () => { expect(result).toEqual({ currentVersion: '1.0.0', - latestVersion: 'latest', + latestVersion: null, }); }); diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/version-info.dto.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/version-info.dto.ts index 210e6fb012..2a1bbb49a9 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/version-info.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/dtos/version-info.dto.ts @@ -5,6 +5,6 @@ export class VersionInfoDTO { @Field(() => String, { nullable: true }) currentVersion?: string; - @Field(() => String) - latestVersion: string; + @Field(() => String, { nullable: true }) + latestVersion?: string | null; } diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/services/admin-panel-version.service.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/services/admin-panel-version.service.ts index 9e3857dbec..b4abecb21b 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/services/admin-panel-version.service.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/services/admin-panel-version.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import semver from 'semver'; import * as z from 'zod'; @@ -9,6 +9,8 @@ import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twent @Injectable() export class AdminPanelVersionService { + private readonly logger = new Logger(AdminPanelVersionService.name); + constructor( private readonly twentyConfigService: TwentyConfigService, private readonly secureHttpClientService: SecureHttpClientService, @@ -36,15 +38,18 @@ export class AdminPanelVersionService { .filter((name) => name !== 'latest' && semver.valid(name)); if (versions.length === 0) { - return { currentVersion, latestVersion: 'latest' }; + return { currentVersion, latestVersion: null }; } versions.sort((a, b) => semver.compare(b, a)); const latestVersion = versions[0]; return { currentVersion, latestVersion }; - } catch { - return { currentVersion, latestVersion: 'latest' }; + } catch (error) { + this.logger.warn( + `Failed to fetch latest version from DockerHub: ${error instanceof Error ? error.message : String(error)}`, + ); + return { currentVersion, latestVersion: null }; } } }