fix(releases-widget): Search multiple docker hub pages for releases (#4158)

This commit is contained in:
castielwaverly
2025-10-29 14:33:50 -04:00
committed by GitHub
parent 9e4b93abcf
commit e0526c5ea7
14 changed files with 280 additions and 390 deletions

View File

@@ -1,47 +1,21 @@
import type {
DetailsProviderResponse,
ReleaseProviderResponse,
ReleasesRepository,
ReleasesResponse,
} from "./releases-providers-types";
import type { ReleaseProviderResponse, ReleaseResponse } from "./releases-providers-types";
export const getLatestRelease = (
releases: ReleaseProviderResponse[],
repository: ReleasesRepository,
details?: DetailsProviderResponse,
): ReleasesResponse => {
versionRegex?: string,
): ReleaseProviderResponse | null => {
const validReleases = releases.filter((result) => {
if (result.latestRelease) {
return repository.versionRegex ? new RegExp(repository.versionRegex).test(result.latestRelease) : true;
return versionRegex ? new RegExp(versionRegex).test(result.latestRelease) : true;
}
return true;
});
const latest =
validReleases.length === 0
? ({
id: repository.id,
error: { code: "noMatchingVersion" },
} as ReleasesResponse)
: validReleases.reduce(
(latest, result) => {
return {
...details,
...(result.latestReleaseAt > latest.latestReleaseAt ? result : latest),
id: repository.id,
};
},
{
id: "",
latestRelease: "",
latestReleaseAt: new Date(0),
},
);
return latest;
return validReleases.length === 0
? null
: validReleases.reduce((latest, current) => (current.latestReleaseAt > latest.latestReleaseAt ? current : latest));
};
export interface ReleasesProviderIntegration {
getLatestMatchingReleaseAsync(repository: ReleasesRepository): Promise<ReleasesResponse>;
getLatestMatchingReleaseAsync(identifier: string, versionRegex?: string): Promise<ReleaseResponse>;
}

View File

@@ -1,5 +1,11 @@
import type { TranslationObject } from "@homarr/translation";
export interface ReleasesRepository extends Record<string, unknown> {
id: string;
identifier: string;
versionRegex?: string;
}
export interface DetailsProviderResponse {
projectUrl?: string;
projectDescription?: string;
@@ -19,35 +25,10 @@ export interface ReleaseProviderResponse {
isPreRelease?: boolean;
}
export interface ReleasesRepository {
id: string;
identifier: string;
versionRegex?: string;
}
type ReleasesErrorKeys = keyof TranslationObject["widget"]["releases"]["error"]["messages"];
export interface ReleasesResponse {
id: string;
latestRelease?: string;
latestReleaseAt?: Date;
releaseUrl?: string;
releaseDescription?: string;
isPreRelease?: boolean;
projectUrl?: string;
projectDescription?: string;
isFork?: boolean;
isArchived?: boolean;
createdAt?: Date;
starsCount?: number;
openIssues?: number;
forksCount?: number;
export type ReleaseData = DetailsProviderResponse & ReleaseProviderResponse;
error?:
| {
code: ReleasesErrorKeys;
}
| {
message: string;
};
}
export type ReleaseError = { code: ReleasesErrorKeys } | { code: "unexpected"; message: string };
export type ReleaseResponse = { success: true; data: ReleaseData } | { success: false; error: ReleaseError };