Skip to content

Commit 50fe244

Browse files
committed
Improve version parsing
1 parent 22aaf7f commit 50fe244

File tree

3 files changed

+67
-41
lines changed

3 files changed

+67
-41
lines changed

src/common/async.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Gitpod. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
export function timeout(millis: number): Promise<void> {
7+
return new Promise((resolve) => setTimeout(resolve, millis));
8+
}
9+
10+
export interface ITask<T> {
11+
(): T;
12+
}
13+
14+
export async function retry<T>(task: ITask<Promise<T>>, delay: number, retries: number): Promise<T> {
15+
let lastError: Error | undefined;
16+
17+
for (let i = 0; i < retries; i++) {
18+
try {
19+
return await task();
20+
} catch (error) {
21+
lastError = error;
22+
23+
await timeout(delay);
24+
}
25+
}
26+
27+
throw lastError;
28+
}

src/featureSupport.ts

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import * as semver from 'semver';
66
import fetch from 'node-fetch';
77
import Log from './common/logger';
8+
import { retry } from './common/async';
89

910
export class GitpodVersion {
1011
static MAX_VERSION = '9999.99.99';
@@ -16,16 +17,15 @@ export class GitpodVersion {
1617
readonly version: string;
1718
readonly raw: string;
1819

19-
constructor(gitpodVersion: string = '') {
20+
constructor(gitpodVersion: string) {
2021
this.raw = gitpodVersion;
2122
this.version = GitpodVersion.MIN_VERSION;
2223

23-
if (gitpodVersion.startsWith('release-')) {
24-
gitpodVersion = gitpodVersion.replace('release-', '');
25-
gitpodVersion = gitpodVersion.replace(/\.\d+$/, '');
26-
24+
// Check for yyyy.mm.dd format
25+
const match = /(?:\.|-|^)(\d{4}\.\d{1,2}\.\d{1,2})(?:\.|-|$)/.exec(gitpodVersion);
26+
if (match) {
2727
// Remove leading zeros to make it a valid semver
28-
const [yy, mm, dd] = gitpodVersion.split('.');
28+
const [yy, mm, dd] = match[1].split('.');
2929
gitpodVersion = `${parseInt(yy, 10)}.${parseInt(mm, 10)}.${parseInt(dd, 10)}`;
3030

3131
}
@@ -39,40 +39,33 @@ export class GitpodVersion {
3939
let cacheGitpodVersion: { host: string; version: GitpodVersion } | undefined;
4040
async function getOrFetchVersionInfo(serviceUrl: string, logger: Log) {
4141
if (serviceUrl === 'https://gitpod.io') {
42-
logger.info(`Using SaaS, constant Max version ${GitpodVersion.Max.version}`);
42+
// SaaS default allow all features, should proper handle SaaS feature support if needed in the future
4343
return {
44-
// SaaS default allow all features, should proper handle SaaS feature support if needed in the future
44+
host: serviceUrl,
4545
version: GitpodVersion.Max,
4646
};
4747
}
4848

4949
if (serviceUrl === cacheGitpodVersion?.host) {
50-
logger.info(`Using cached version ${cacheGitpodVersion.version} for ${serviceUrl}`);
5150
return cacheGitpodVersion;
5251
}
5352

54-
const fetchVersion = async (times: number = 3): Promise<string | undefined> => {
55-
try {
56-
const versionEndPoint = `${serviceUrl}/api/version`;
53+
let gitpodRawVersion: string | undefined;
54+
try {
55+
const versionEndPoint = `${serviceUrl}/api/version`;
56+
gitpodRawVersion = await retry(async () => {
5757
const resp = await fetch(versionEndPoint, { timeout: 1500 });
5858
if (!resp.ok) {
59-
throw new Error(`Response with ${resp.status} ${resp.statusText}`);
60-
}
61-
return await resp.text();
62-
} catch (e) {
63-
logger.error(`Failed to fetch version with from: ${serviceUrl} left attempt: ${times - 1}`, e);
64-
if (times - 1 > 0) {
65-
await new Promise(resolve => setTimeout(resolve, 1000));
66-
return fetchVersion(times - 1);
67-
} else {
68-
return undefined;
59+
throw new Error(`Responded with ${resp.status} ${resp.statusText}`);
6960
}
70-
}
71-
};
61+
return resp.text();
62+
}, 1000, 3);
63+
} catch (e) {
64+
logger.error(`Error while fetching ${serviceUrl}`, e);
65+
}
7266

73-
const gitpodRawVersion = await fetchVersion();
7467
if (!gitpodRawVersion) {
75-
logger.info(`Failed to fetch version from: ${serviceUrl} fallback to Min: ${GitpodVersion.Min.version}`);
68+
logger.info(`Failed to fetch version from ${serviceUrl}, some feature will be disabled`);
7669
return {
7770
host: serviceUrl,
7871
version: GitpodVersion.Min,
@@ -91,7 +84,7 @@ async function getOrFetchVersionInfo(serviceUrl: string, logger: Log) {
9184
export async function getGitpodVersion(gitpodHost: string, logger: Log) {
9285
const serviceUrl = new URL(gitpodHost).toString().replace(/\/$/, '');
9386
const versionInfo = await getOrFetchVersionInfo(serviceUrl, logger);
94-
return versionInfo.version || new GitpodVersion();
87+
return versionInfo.version;
9588
}
9689

9790
type Feature = |

src/test/featureSupport.test.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,24 @@ import { getGitpodVersion, GitpodVersion, isFeatureSupported } from '../featureS
99
suite('feature support', () => {
1010
test('isFeatureSupported with versions', () => {
1111
const cases: Array<{ str?: string; version: GitpodVersion; supported: boolean }> = [
12+
{ version: new GitpodVersion('release-0.0.0.0'), str: '0.0.0', supported: false },
1213
{ version: new GitpodVersion('release-2022.06.1.7'), str: '2022.6.1', supported: false },
1314
{ version: new GitpodVersion('release-2022.06.1.0'), str: '2022.6.1', supported: false },
15+
{ version: new GitpodVersion('sje-hotfix-multiple-registrie-2022.06.1.0'), str: '2022.6.1', supported: false },
16+
{ version: new GitpodVersion('release-2022.06.1.0-sje-hotfix-multiple-registrie'), str: '2022.6.1', supported: false },
17+
{ version: new GitpodVersion('release-2022.06.1.sje-hotfix-multiple-registrie'), str: '2022.6.1', supported: false },
1418
{ version: new GitpodVersion('release-2022.77.1.0'), str: '2022.77.1', supported: true },
15-
{ version: new GitpodVersion('release-0.0.0.0'), str: '0.0.0', supported: false },
16-
{ version: new GitpodVersion('abcd.0123.0.0.0'), str: '0.0.0', supported: false },
17-
{ version: new GitpodVersion('abcd.123.0.0'), supported: false },
18-
{ version: new GitpodVersion('123'), supported: false },
19-
{ version: new GitpodVersion('123..'), supported: false },
20-
{ version: new GitpodVersion('123.0'), supported: false },
21-
{ version: new GitpodVersion('123.0.1'), supported: false },
22-
{ version: new GitpodVersion('9123.0.1'), supported: true },
23-
{ version: new GitpodVersion(), str: '0.0.0', supported: false },
19+
{ version: new GitpodVersion('sje-hotfix-multiple-registrie-2022.77.1.0'), str: '2022.77.1', supported: true },
20+
{ version: new GitpodVersion('release-2022.77.1.0-sje-hotfix-multiple-registrie'), str: '2022.77.1', supported: true },
21+
{ version: new GitpodVersion('release-2022.77.1.sje-hotfix-multiple-registrie'), str: '2022.77.1', supported: true },
22+
{ version: new GitpodVersion('abcd.0123.0.0.0'), str: '123.0.0', supported: false },
23+
{ version: new GitpodVersion('abcd.123.0.0'), str: GitpodVersion.MIN_VERSION, supported: false },
24+
{ version: new GitpodVersion('sje-hotfix-multiple-registries.2'), str: GitpodVersion.MIN_VERSION, supported: false },
25+
{ version: new GitpodVersion('123'), str: GitpodVersion.MIN_VERSION, supported: false },
26+
{ version: new GitpodVersion('123..'), str: GitpodVersion.MIN_VERSION, supported: false },
27+
{ version: new GitpodVersion('123.0'), str: GitpodVersion.MIN_VERSION, supported: false },
28+
{ version: new GitpodVersion('123.0.1'), str: '123.0.1', supported: false },
29+
{ version: new GitpodVersion('9123.0.1'), str: '9123.0.1', supported: true },
2430
{ version: GitpodVersion.Max, str: GitpodVersion.MAX_VERSION, supported: true },
2531
{ version: GitpodVersion.Min, str: GitpodVersion.MIN_VERSION, supported: false },
2632

@@ -39,12 +45,11 @@ suite('feature support', () => {
3945
});
4046

4147
suite('fetch version info', function () {
42-
// this.timeout(10000);
43-
// const logger = { info: console.log, error: console.error } as Log;
44-
// @ts-ignore
45-
const logger = { info: (...args: any) => { }, error: (...args: any) => { } } as Log;
48+
this.timeout(10000);
49+
50+
const logger = { info: (..._args: any) => { }, error: (..._args: any) => { } } as Log;
4651

47-
test.skip('unknown host retry and fallback to min', async () => {
52+
test('unknown host retry and fallback to min', async () => {
4853
const version = await getGitpodVersion('https://unknown.gitpod.io', logger);
4954
equal(version.version, GitpodVersion.Min.version);
5055
});

0 commit comments

Comments
 (0)