fix(admin): prevent fallback to 'latest' string when dockerhub tags c… (#22885)

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.


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22885?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Rehuz
2026-07-24 12:55:46 +05:30
committed by GitHub
parent 0a53cc745d
commit 7f1e3d3541
4 changed files with 15 additions and 10 deletions
@@ -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,
});
});
@@ -5,6 +5,6 @@ export class VersionInfoDTO {
@Field(() => String, { nullable: true })
currentVersion?: string;
@Field(() => String)
latestVersion: string;
@Field(() => String, { nullable: true })
latestVersion?: string | null;
}
@@ -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 };
}
}
}