|
1 | 1 | import { MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client"; |
2 | 2 | import { assertStatus } from "@mittwald/api-client-commons"; |
3 | | -import { gt } from "semver"; |
| 3 | +import { coerce, gt } from "semver"; |
4 | 4 | import { ProcessRenderer } from "../../../rendering/process/process.js"; |
5 | 5 | import { getAppInstallationFromUuid, getAppNameFromUuid } from "./uuid.js"; |
6 | 6 | import { compare } from "semver"; |
7 | 7 |
|
8 | 8 | type AppAppVersion = MittwaldAPIV2.Components.Schemas.AppAppVersion; |
9 | 9 | type AppVersion = MittwaldAPIV2.Components.Schemas.AppAppVersion; |
10 | 10 |
|
| 11 | +type ObjectWithVersions = { |
| 12 | + internalVersion: string; |
| 13 | + externalVersion: string; |
| 14 | +}; |
| 15 | + |
11 | 16 | export async function normalizeToAppVersionUuid( |
12 | 17 | apiClient: MittwaldAPIV2Client, |
13 | 18 | version: string, |
@@ -142,10 +147,52 @@ export async function getAppVersionUuidFromAppVersion( |
142 | 147 | ); |
143 | 148 | } |
144 | 149 |
|
145 | | -export function sortArrayByExternalVersion( |
146 | | - versions: AppVersion[], |
147 | | -): AppVersion[] { |
148 | | - return versions.sort((a: AppVersion, b: AppVersion) => |
149 | | - compare(b.externalVersion, a.externalVersion), |
150 | | - ); |
| 150 | +export function sortArrayByExternalVersion<T extends ObjectWithVersions>( |
| 151 | + versions: T[], |
| 152 | +): T[] { |
| 153 | + return versions.sort(compareVersionsBy("external")); |
| 154 | +} |
| 155 | + |
| 156 | +export function sortArrayByInternalVersion<T extends ObjectWithVersions>( |
| 157 | + versions: T[], |
| 158 | +): T[] { |
| 159 | + return versions.sort(compareVersionsBy("internal")); |
| 160 | +} |
| 161 | + |
| 162 | +export function compareVersionsBy<T extends ObjectWithVersions>( |
| 163 | + field: "internal" | "external", |
| 164 | +): (a: T, b: T) => -1 | 0 | 1 { |
| 165 | + const fullField = `${field}Version` as const; |
| 166 | + return (a, b) => { |
| 167 | + const aCoerced = coerce(a[fullField]); |
| 168 | + const bCoerced = coerce(b[fullField]); |
| 169 | + |
| 170 | + if (!aCoerced || !bCoerced) { |
| 171 | + return naiveVersionCompare(a.internalVersion, b.internalVersion); |
| 172 | + } |
| 173 | + |
| 174 | + return compare(aCoerced, bCoerced); |
| 175 | + }; |
| 176 | +} |
| 177 | + |
| 178 | +/** |
| 179 | + * A naive version comparison function that compares version strings in the |
| 180 | + * format "x.y.z". This function does not handle pre-release or build metadata. |
| 181 | + */ |
| 182 | +function naiveVersionCompare(a: string, b: string): -1 | 0 | 1 { |
| 183 | + const aParts = a.split(".").map((part) => parseInt(part, 10)); |
| 184 | + const bParts = b.split(".").map((part) => parseInt(part, 10)); |
| 185 | + |
| 186 | + for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) { |
| 187 | + const aPart = aParts[i] || 0; |
| 188 | + const bPart = bParts[i] || 0; |
| 189 | + |
| 190 | + if (aPart > bPart) { |
| 191 | + return 1; |
| 192 | + } |
| 193 | + if (aPart < bPart) { |
| 194 | + return -1; |
| 195 | + } |
| 196 | + } |
| 197 | + return 0; |
151 | 198 | } |
0 commit comments