From fd402244d01a6191c0b1dd7cc0b8a46aa635e67a Mon Sep 17 00:00:00 2001 From: litetex Date: Sat, 4 Apr 2020 18:05:12 +0200 Subject: [PATCH 01/16] Added support for latest version syntax --- __tests__/installer.test.ts | 24 ++++++- src/installer.ts | 140 ++++++++++++++++++++++++++++-------- 2 files changed, 134 insertions(+), 30 deletions(-) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index 23f443979..9e8ad3f7c 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -28,6 +28,28 @@ describe('installer tests', () => { } }, 100000); + /* + it('Check if get version works', async () => { + + + const dotnetInstaller = new installer.DotnetCoreInstaller('3.1.201'); + let arr = await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo("3.1.201")); + console.log(arr); + + expect(true).toBe(true); + + }, 400000); + + it('Check if get latest version works', async () => { + + const dotnetInstaller = new installer.DotnetCoreInstaller('3.1.x'); + let arr = await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo("3.1.x")); + console.log(arr); + + expect(true).toBe(true); + + }, 400000);*/ + it('Acquires version of dotnet if no matching version is installed', async () => { await getDotnet('2.2.205'); const dotnetDir = path.join(toolDir, 'dncs', '2.2.205', os.arch()); @@ -38,7 +60,7 @@ describe('installer tests', () => { } else { expect(fs.existsSync(path.join(dotnetDir, 'dotnet'))).toBe(true); } - }, 100000); + }, 400000); it('Throws if no location contains correct dotnet version', async () => { let thrown = false; diff --git a/src/installer.ts b/src/installer.ts index cd5d64d06..1807a9b8e 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -9,6 +9,7 @@ import {chmodSync} from 'fs'; import * as os from 'os'; import * as path from 'path'; import * as semver from 'semver'; +import { stringWriter } from 'xmlbuilder'; const IS_WINDOWS = process.platform === 'win32'; @@ -27,31 +28,96 @@ if (!tempDirectory) { tempDirectory = path.join(baseLocation, 'actions', 'temp'); } -export class DotnetCoreInstaller { +class DotNetVersionInfo { + major: number; + minor: number; + patch?: number; + constructor(version: string) { - if (semver.valid(semver.clean(version) || '') == null) { - throw 'Implicit version not permitted'; + //todo: add support for previews! + let regexResult = version.match(/^(\d+\.)(\d+\.)(\*|x|\d+)$/); + if(regexResult == null) { + throw 'Invalid version. Supported formats: 1.2.3, 1.2, 1.2.x, 1.2.*'; + } + + let parts : string[] = (regexResult as RegExpMatchArray).slice(1); + + this.major = +(parts[0].replace('.','')); + this.minor = +(parts[1].replace('.','')); + + if(parts.length > 2) { + // just set if it is a number + if(!isNaN(Number(parts[2]))) { + this.patch = +parts[2]; + } + } + } + + public isGeneric() : boolean { + return this.patch ? true : false; + } + + public toString() : string { + let version = this.major + "." + this.minor; + + if(this.patch) + version += "." + this.patch; + + return version; + } +} + +class ResolvedVersionInfo { + downloadUrls: string[]; + resolvedVersion: string; + + constructor(downloadUrls: string[], resolvedVersion: string) { + if(downloadUrls.length === 0) { + throw 'DownloadUrls can not be empty'; + } + + if(!resolvedVersion) { + throw 'Resolved version is invalid'; } - this.version = version; + + this.downloadUrls = downloadUrls; + this.resolvedVersion = resolvedVersion; + } +} + +export class DotnetCoreInstaller { + constructor(version: string) { + this.versionInfo = new DotNetVersionInfo(version); this.cachedToolName = 'dncs'; this.arch = 'x64'; } public async installDotnet() { // Check cache - let toolPath: string; + let toolPath: string = ""; let osSuffixes = await this.detectMachineOS(); let parts = osSuffixes[0].split('-'); if (parts.length > 1) { this.arch = parts[1]; } - toolPath = this.getLocalTool(); + + // If version is not generic -> look up cache + if(!this.versionInfo.isGeneric()) + toolPath = this.getLocalTool(this.versionInfo.toString()); if (!toolPath) { // download, extract, cache - console.log('Getting a download url', this.version); - let downloadUrls = await this.getDownloadUrls(osSuffixes, this.version); - toolPath = await this.downloadAndInstall(downloadUrls); + console.log('Getting a download url', this.versionInfo.toString()); + let resolvedVersionInfo = await this.resolveInfos(osSuffixes, this.versionInfo); + + //Check if cache exists for resolved version + toolPath = this.getLocalTool(resolvedVersionInfo.resolvedVersion); + if(!toolPath) { + //If not exists install it + toolPath = await this.downloadAndInstall(resolvedVersionInfo); + } else { + console.log('Using cached tool'); + } } else { console.log('Using cached tool'); } @@ -63,9 +129,9 @@ export class DotnetCoreInstaller { core.addPath(toolPath); } - private getLocalTool(): string { - console.log('Checking tool cache'); - return tc.find(this.cachedToolName, this.version, this.arch); + private getLocalTool(version: string): string { + console.log('Checking tool cache', version); + return tc.find(this.cachedToolName, version, this.arch); } private async detectMachineOS(): Promise { @@ -141,16 +207,16 @@ export class DotnetCoreInstaller { return osSuffix; } - private async downloadAndInstall(downloadUrls: string[]) { + private async downloadAndInstall(resolvedVersionInfo: ResolvedVersionInfo) { let downloaded = false; let downloadPath = ''; - for (const url of downloadUrls) { + for (const url of resolvedVersionInfo.downloadUrls) { try { downloadPath = await tc.downloadTool(url); downloaded = true; break; } catch (error) { - console.log('Could Not Download', url, JSON.stringify(error)); + console.log('Could not Download', url, JSON.stringify(error)); } } @@ -169,22 +235,21 @@ export class DotnetCoreInstaller { let cachedDir = await tc.cacheDir( extPath, this.cachedToolName, - this.version, + resolvedVersionInfo.resolvedVersion, this.arch ); - console.log('Successfully installed', this.version); + console.log('Successfully installed', resolvedVersionInfo.resolvedVersion); return cachedDir; } // OsSuffixes - The suffix which is a part of the file name ex- linux-x64, windows-x86 // Type - SDK / Runtime // Version - Version of the SDK/Runtime - private async getDownloadUrls( + private async resolveInfos( osSuffixes: string[], - version: string - ): Promise { - let downloadUrls: string[] = []; + versionInfo: DotNetVersionInfo + ): Promise { const httpClient = new hc.HttpClient('actions/setup-dotnet', [], { allowRetries: true, @@ -192,7 +257,7 @@ export class DotnetCoreInstaller { }); const releasesJsonUrl: string = await this.getReleasesJsonUrl( httpClient, - version.split('.') + [String(versionInfo.major), String(versionInfo.minor)] ); const releasesResponse = await httpClient.getJson(releasesJsonUrl); @@ -200,13 +265,22 @@ export class DotnetCoreInstaller { let releasesInfo: any[] = releasesResult['releases']; releasesInfo = releasesInfo.filter((releaseInfo: any) => { return ( - releaseInfo['sdk']['version'] === version || - releaseInfo['sdk']['version-display'] === version + semver.satisfies(releaseInfo['sdk']['version'], versionInfo.toString()) || + semver.satisfies(releaseInfo['sdk']['version-display'], versionInfo.toString()) ); }); + //Sort for latest version + releasesInfo = releasesInfo.sort((a,b) => semver.rcompare(a['sdk']['version'],b['sdk']['version'])); + + let downloadedVersion : string = ''; + let downloadUrls: string[] = []; + if (releasesInfo.length != 0) { let release = releasesInfo[0]; + + downloadedVersion = release['sdk']['version']; + let files: any[] = release['sdk']['files']; files = files.filter((file: any) => { if (file['rid'] == osSuffixes[0] || file['rid'] == osSuffixes[1]) { @@ -225,18 +299,26 @@ export class DotnetCoreInstaller { } } else { console.log( - `Could not fetch download information for version ${version}` + `Could not fetch download information for version ${versionInfo.toString()}` ); - downloadUrls = await this.getFallbackDownloadUrls(version); + + if(!versionInfo.isGeneric()) { + console.log('Using fallback'); + + downloadUrls = await this.getFallbackDownloadUrls(versionInfo.toString()); + downloadedVersion = versionInfo.toString(); + } else { + console.log('Unable to use fallback, version is generic!'); + } } if (downloadUrls.length == 0) { - throw `Could not construct download URL. Please ensure that specified version ${version} is valid.`; + throw `Could not construct download URL. Please ensure that specified version ${versionInfo.toString()}/${downloadedVersion} is valid.`; } core.debug(`Got download urls ${downloadUrls}`); - return downloadUrls; + return new ResolvedVersionInfo(downloadUrls, downloadedVersion); } private async getReleasesJsonUrl( @@ -361,7 +443,7 @@ export class DotnetCoreInstaller { return [primaryUrl, legacyUrl]; } - private version: string; + private versionInfo: DotNetVersionInfo; private cachedToolName: string; private arch: string; } From 561f9e2075e32af4b372555cba33ec410e00324f Mon Sep 17 00:00:00 2001 From: litetex Date: Sat, 4 Apr 2020 18:09:19 +0200 Subject: [PATCH 02/16] Updated doc --- README.md | 2 +- action.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ea327fd5a..d9f87922c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ steps: - uses: actions/checkout@master - uses: actions/setup-dotnet@v1 with: - dotnet-version: '3.1.100' # SDK Version to use. + dotnet-version: '3.1.x' # SDK Version to use. x will use the latest version of the 3.1 channel - run: dotnet build ``` diff --git a/action.yml b/action.yml index 242387b8a..3f702f218 100644 --- a/action.yml +++ b/action.yml @@ -6,7 +6,7 @@ branding: color: green inputs: dotnet-version: - description: 'SDK version to use. Example: 2.2.104' + description: 'SDK version to use. Examples: 2.2.104, 3.1, 3.1.x' source-url: description: 'Optional package source for which to set up authentication. Will consult any existing NuGet.config in the root of the repo and provide a temporary NuGet.config using the NUGET_AUTH_TOKEN environment variable as a ClearTextPassword' owner: From a5cce9bf9f2a76b36773dfd29cd36d265c042429 Mon Sep 17 00:00:00 2001 From: litetex Date: Sat, 4 Apr 2020 19:23:59 +0200 Subject: [PATCH 03/16] Refactoring * Added more tests * Allowed also exact versions again --- __tests__/installer.test.ts | 89 +++++++++++++++++++++++++++++++++++- src/installer.ts | 91 +++++++++++++++++++++++-------------- 2 files changed, 143 insertions(+), 37 deletions(-) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index 9e8ad3f7c..7c2a82de1 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -13,6 +13,92 @@ import * as installer from '../src/installer'; const IS_WINDOWS = process.platform === 'win32'; +describe('version tests', () => { + + it('Exact normal version', async() => { + let versInfo = new installer.DotNetVersionInfo('3.1.201'); + + expect(versInfo.isExactVersion()).toBe(true); + expect(versInfo.version()).toBe('3.1.201'); + }); + + it('Exact preview version', async() => { + let versInfo = new installer.DotNetVersionInfo('3.1.201-preview1'); + + expect(versInfo.isExactVersion()).toBe(true); + expect(versInfo.version()).toBe('3.1.201-preview1'); + }); + + it('Generic x version', async() => { + let versInfo = new installer.DotNetVersionInfo('3.1.x'); + + expect(versInfo.isExactVersion()).toBe(false); + expect(versInfo.version()).toBe('3.1'); + }); + + it('Generic * version', async() => { + let versInfo = new installer.DotNetVersionInfo('1.1.*'); + + expect(versInfo.isExactVersion()).toBe(false); + expect(versInfo.version()).toBe('1.1'); + }); + + it('Generic -no patch- version', async() => { + let versInfo = new installer.DotNetVersionInfo('2.0'); + + expect(versInfo.isExactVersion()).toBe(false); + expect(versInfo.version()).toBe('2.0'); + }); + + it('Generic -no minor- version', async() => { + expect(() => { + new installer.DotNetVersionInfo('2'); + }).toThrow(); + }); + + it('empty version', async() => { + expect(() => { + new installer.DotNetVersionInfo(''); + }).toThrow(); + }); + + it('malformed no patch but dot version', async() => { + expect(() => { + new installer.DotNetVersionInfo('1.2.'); + }).toThrow(); + }); + + it('malformed generic minor version', async() => { + expect(() => { + new installer.DotNetVersionInfo('1.*.2'); + }).toThrow(); + }); + + it('malformed generic major version', async() => { + expect(() => { + new installer.DotNetVersionInfo('*.2.2'); + }).toThrow(); + }); + + it('malformed letter version', async() => { + expect(() => { + new installer.DotNetVersionInfo('a.b.c'); + }).toThrow(); + }); + + it('malformed letter preview version', async() => { + expect(() => { + new installer.DotNetVersionInfo('a.b.c-preview'); + }).toThrow(); + }); + + it('malformed letter -no minor- version', async() => { + expect(() => { + new installer.DotNetVersionInfo('a.b'); + }).toThrow(); + }); +}) + describe('installer tests', () => { beforeAll(async () => { await io.rmRF(toolDir); @@ -28,7 +114,6 @@ describe('installer tests', () => { } }, 100000); - /* it('Check if get version works', async () => { @@ -48,7 +133,7 @@ describe('installer tests', () => { expect(true).toBe(true); - }, 400000);*/ + }, 400000); it('Acquires version of dotnet if no matching version is installed', async () => { await getDotnet('2.2.205'); diff --git a/src/installer.ts b/src/installer.ts index 1807a9b8e..a4f5e3c95 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -10,6 +10,7 @@ import * as os from 'os'; import * as path from 'path'; import * as semver from 'semver'; import { stringWriter } from 'xmlbuilder'; +import { timingSafeEqual } from 'crypto'; const IS_WINDOWS = process.platform === 'win32'; @@ -28,16 +29,34 @@ if (!tempDirectory) { tempDirectory = path.join(baseLocation, 'actions', 'temp'); } -class DotNetVersionInfo { - major: number; - minor: number; - patch?: number; +export class DotNetVersionInfo { + + private fullversion : string; + private isExactVersionSet: boolean = false; + + private major: number; + private minor: number; + private patch?: number; constructor(version: string) { - //todo: add support for previews! - let regexResult = version.match(/^(\d+\.)(\d+\.)(\*|x|\d+)$/); + + // Check for exact match + if(semver.valid(semver.clean(version) || '') != null) { + + this.fullversion = semver.clean(version) as string; + this.isExactVersionSet = true; + + this.major = semver.major(this.fullversion); + this.minor = semver.minor(this.fullversion); + this.patch = semver.patch(this.fullversion); + + return; + } + + //Note: No support for previews when using generic + let regexResult = version.match(/^(\d+\.)(\d+)?(\.\*|\.x|)$/); if(regexResult == null) { - throw 'Invalid version. Supported formats: 1.2.3, 1.2, 1.2.x, 1.2.*'; + throw 'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*'; } let parts : string[] = (regexResult as RegExpMatchArray).slice(1); @@ -45,25 +64,18 @@ class DotNetVersionInfo { this.major = +(parts[0].replace('.','')); this.minor = +(parts[1].replace('.','')); - if(parts.length > 2) { - // just set if it is a number - if(!isNaN(Number(parts[2]))) { - this.patch = +parts[2]; - } - } + this.fullversion = this.major + '.' + this.minor; } - public isGeneric() : boolean { - return this.patch ? true : false; + /** + * If true exacatly one version should be resolved + */ + public isExactVersion() : boolean { + return this.isExactVersionSet; } - public toString() : string { - let version = this.major + "." + this.minor; - - if(this.patch) - version += "." + this.patch; - - return version; + public version() : string { + return this.fullversion; } } @@ -102,12 +114,12 @@ export class DotnetCoreInstaller { } // If version is not generic -> look up cache - if(!this.versionInfo.isGeneric()) - toolPath = this.getLocalTool(this.versionInfo.toString()); + if(this.versionInfo.isExactVersion()) + toolPath = this.getLocalTool(this.versionInfo.version()); if (!toolPath) { // download, extract, cache - console.log('Getting a download url', this.versionInfo.toString()); + console.log('Getting a download url', this.versionInfo.version()); let resolvedVersionInfo = await this.resolveInfos(osSuffixes, this.versionInfo); //Check if cache exists for resolved version @@ -246,7 +258,7 @@ export class DotnetCoreInstaller { // OsSuffixes - The suffix which is a part of the file name ex- linux-x64, windows-x86 // Type - SDK / Runtime // Version - Version of the SDK/Runtime - private async resolveInfos( + async resolveInfos( osSuffixes: string[], versionInfo: DotNetVersionInfo ): Promise { @@ -255,9 +267,10 @@ export class DotnetCoreInstaller { allowRetries: true, maxRetries: 3 }); + const releasesJsonUrl: string = await this.getReleasesJsonUrl( httpClient, - [String(versionInfo.major), String(versionInfo.minor)] + versionInfo.version().split('.') ); const releasesResponse = await httpClient.getJson(releasesJsonUrl); @@ -265,12 +278,20 @@ export class DotnetCoreInstaller { let releasesInfo: any[] = releasesResult['releases']; releasesInfo = releasesInfo.filter((releaseInfo: any) => { return ( - semver.satisfies(releaseInfo['sdk']['version'], versionInfo.toString()) || - semver.satisfies(releaseInfo['sdk']['version-display'], versionInfo.toString()) + semver.satisfies(releaseInfo['sdk']['version'], versionInfo.version()) || + semver.satisfies(releaseInfo['sdk']['version-display'], versionInfo.version()) ); }); - //Sort for latest version + // Exclude versions that are newer than the latest if using not exact + if(!versionInfo.isExactVersion()) { + + let latestSdk : string = releasesResponse['latest-sdk']; + + releasesInfo = releasesInfo.filter((releaseInfo: any) => semver.lte(releaseInfo['sdk']['version'], latestSdk)); + } + + // Sort for latest version releasesInfo = releasesInfo.sort((a,b) => semver.rcompare(a['sdk']['version'],b['sdk']['version'])); let downloadedVersion : string = ''; @@ -299,21 +320,21 @@ export class DotnetCoreInstaller { } } else { console.log( - `Could not fetch download information for version ${versionInfo.toString()}` + `Could not fetch download information for version ${versionInfo.version()}` ); - if(!versionInfo.isGeneric()) { + if(versionInfo.isExactVersion()) { console.log('Using fallback'); - downloadUrls = await this.getFallbackDownloadUrls(versionInfo.toString()); - downloadedVersion = versionInfo.toString(); + downloadUrls = await this.getFallbackDownloadUrls(versionInfo.version()); + downloadedVersion = versionInfo.version(); } else { console.log('Unable to use fallback, version is generic!'); } } if (downloadUrls.length == 0) { - throw `Could not construct download URL. Please ensure that specified version ${versionInfo.toString()}/${downloadedVersion} is valid.`; + throw `Could not construct download URL. Please ensure that specified version ${versionInfo.version()}/${downloadedVersion} is valid.`; } core.debug(`Got download urls ${downloadUrls}`); From 5411b6342a7ceeb75b4864a786f795e229e57079 Mon Sep 17 00:00:00 2001 From: litetex Date: Sat, 4 Apr 2020 19:25:06 +0200 Subject: [PATCH 04/16] Removed dummy tests --- __tests__/installer.test.ts | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index 7c2a82de1..798d9eccb 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -114,27 +114,6 @@ describe('installer tests', () => { } }, 100000); - it('Check if get version works', async () => { - - - const dotnetInstaller = new installer.DotnetCoreInstaller('3.1.201'); - let arr = await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo("3.1.201")); - console.log(arr); - - expect(true).toBe(true); - - }, 400000); - - it('Check if get latest version works', async () => { - - const dotnetInstaller = new installer.DotnetCoreInstaller('3.1.x'); - let arr = await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo("3.1.x")); - console.log(arr); - - expect(true).toBe(true); - - }, 400000); - it('Acquires version of dotnet if no matching version is installed', async () => { await getDotnet('2.2.205'); const dotnetDir = path.join(toolDir, 'dncs', '2.2.205', os.arch()); From ebefd7bccb874a8e6bd96c8e7773d6e0da8f87da Mon Sep 17 00:00:00 2001 From: litetex Date: Sat, 4 Apr 2020 19:34:45 +0200 Subject: [PATCH 05/16] More tests and more fixes --- __tests__/installer.test.ts | 8 ++++++++ src/installer.ts | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index 798d9eccb..a3e35ce2a 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -114,6 +114,14 @@ describe('installer tests', () => { } }, 100000); + it('Resolving a normal generic version works', async() => { + const dotnetInstaller = new installer.DotnetCoreInstaller('3.1.x'); + let versInfo = await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo("3.1.x")); + + expect(versInfo.resolvedVersion.startsWith('3.1.')); + }, 100000); + + it('Acquires version of dotnet if no matching version is installed', async () => { await getDotnet('2.2.205'); const dotnetDir = path.join(toolDir, 'dncs', '2.2.205', os.arch()); diff --git a/src/installer.ts b/src/installer.ts index a4f5e3c95..23524ad00 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -285,8 +285,7 @@ export class DotnetCoreInstaller { // Exclude versions that are newer than the latest if using not exact if(!versionInfo.isExactVersion()) { - - let latestSdk : string = releasesResponse['latest-sdk']; + let latestSdk : string = releasesResult['latest-sdk']; releasesInfo = releasesInfo.filter((releaseInfo: any) => semver.lte(releaseInfo['sdk']['version'], latestSdk)); } From 3426e118588c742f99f7551479914469692818a7 Mon Sep 17 00:00:00 2001 From: litetex Date: Sat, 4 Apr 2020 19:44:50 +0200 Subject: [PATCH 06/16] More tests --- __tests__/installer.test.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index a3e35ce2a..98f557d41 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -116,11 +116,27 @@ describe('installer tests', () => { it('Resolving a normal generic version works', async() => { const dotnetInstaller = new installer.DotnetCoreInstaller('3.1.x'); - let versInfo = await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo("3.1.x")); + let versInfo = await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo('3.1.x')); expect(versInfo.resolvedVersion.startsWith('3.1.')); }, 100000); + it('Resolving a nonexistent generic version fails', async() => { + const dotnetInstaller = new installer.DotnetCoreInstaller('999.1.x'); + try{ + await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo('999.1.x')); + fail(); + } catch { + expect(true); + } + }, 100000); + + it('Resolving a exact version works', async() => { + const dotnetInstaller = new installer.DotnetCoreInstaller('3.1.201'); + let versInfo = await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo('3.1.201')); + + expect(versInfo.resolvedVersion).toBe('3.1.201'); + }, 100000); it('Acquires version of dotnet if no matching version is installed', async () => { await getDotnet('2.2.205'); From 08fb271bd71eefc8c864ab0fe6c52949fa3fb77a Mon Sep 17 00:00:00 2001 From: Alex <40789489+litetex@users.noreply.github.com> Date: Sat, 4 Apr 2020 19:47:16 +0200 Subject: [PATCH 07/16] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d9f87922c..6d2412adb 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ steps: - uses: actions/checkout@master - uses: actions/setup-dotnet@v1 with: - dotnet-version: '3.1.x' # SDK Version to use. x will use the latest version of the 3.1 channel + dotnet-version: '3.1.x' # SDK Version to use; x will use the latest version of the 3.1 channel - run: dotnet build ``` @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-16.04 strategy: matrix: - dotnet: [ '2.2.103', '3.0.100', '3.1.100' ] + dotnet: [ '2.2.103', '3.0.100', '3.1.x' ] name: Dotnet ${{ matrix.dotnet }} sample steps: - uses: actions/checkout@master @@ -49,7 +49,7 @@ steps: # Authenticates packages to push to GPR - uses: actions/setup-dotnet@v1 with: - dotnet-version: '3.1.100' # SDK Version to use. + dotnet-version: '3.1.x' # SDK Version to use. source-url: https://nuget.pkg.github.com//index.json env: NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} From 3eb0d2f3ddf22826e7074e8599c8528ee59c01ca Mon Sep 17 00:00:00 2001 From: litetex Date: Sun, 5 Apr 2020 15:37:29 +0200 Subject: [PATCH 08/16] Reworked version parser * Updated tests --- __tests__/installer.test.ts | 127 ++++++++++++++---------------------- src/installer.ts | 37 ++++++++++- 2 files changed, 85 insertions(+), 79 deletions(-) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index 98f557d41..ff9604c40 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -3,6 +3,7 @@ import fs = require('fs'); import os = require('os'); import path = require('path'); import hc = require('@actions/http-client'); +import each from 'jest-each'; const toolDir = path.join(__dirname, 'runner', 'tools'); const tempDir = path.join(__dirname, 'runner', 'temp'); @@ -14,89 +15,59 @@ import * as installer from '../src/installer'; const IS_WINDOWS = process.platform === 'win32'; describe('version tests', () => { - - it('Exact normal version', async() => { - let versInfo = new installer.DotNetVersionInfo('3.1.201'); - - expect(versInfo.isExactVersion()).toBe(true); - expect(versInfo.version()).toBe('3.1.201'); - }); - - it('Exact preview version', async() => { - let versInfo = new installer.DotNetVersionInfo('3.1.201-preview1'); + each([ + '3.1.999', + '3.1.101-preview' + ]).test("Exact version '%s' should be the same", vers => { + let versInfo = new installer.DotNetVersionInfo(vers); expect(versInfo.isExactVersion()).toBe(true); - expect(versInfo.version()).toBe('3.1.201-preview1'); - }); - - it('Generic x version', async() => { - let versInfo = new installer.DotNetVersionInfo('3.1.x'); - - expect(versInfo.isExactVersion()).toBe(false); - expect(versInfo.version()).toBe('3.1'); - }); - - it('Generic * version', async() => { - let versInfo = new installer.DotNetVersionInfo('1.1.*'); - - expect(versInfo.isExactVersion()).toBe(false); - expect(versInfo.version()).toBe('1.1'); - }); + expect(versInfo.version()).toBe(vers); + }) - it('Generic -no patch- version', async() => { - let versInfo = new installer.DotNetVersionInfo('2.0'); + each([ + ['3.1.x', '3.1'], + ['1.1.*', '1.1'], + ['2.0', '2.0'] + ]).test("Generic version '%s' should be '%s'", (vers, resVers) => { + let versInfo = new installer.DotNetVersionInfo(vers); expect(versInfo.isExactVersion()).toBe(false); - expect(versInfo.version()).toBe('2.0'); - }); - - it('Generic -no minor- version', async() => { - expect(() => { - new installer.DotNetVersionInfo('2'); - }).toThrow(); - }); - - it('empty version', async() => { - expect(() => { - new installer.DotNetVersionInfo(''); - }).toThrow(); - }); - - it('malformed no patch but dot version', async() => { - expect(() => { - new installer.DotNetVersionInfo('1.2.'); - }).toThrow(); - }); - - it('malformed generic minor version', async() => { - expect(() => { - new installer.DotNetVersionInfo('1.*.2'); - }).toThrow(); - }); - - it('malformed generic major version', async() => { - expect(() => { - new installer.DotNetVersionInfo('*.2.2'); - }).toThrow(); - }); - - it('malformed letter version', async() => { - expect(() => { - new installer.DotNetVersionInfo('a.b.c'); - }).toThrow(); - }); - - it('malformed letter preview version', async() => { - expect(() => { - new installer.DotNetVersionInfo('a.b.c-preview'); - }).toThrow(); - }); - - it('malformed letter -no minor- version', async() => { - expect(() => { - new installer.DotNetVersionInfo('a.b'); - }).toThrow(); - }); + expect(versInfo.version()).toBe(resVers); + }) + + each([ + "", + ".", + "..", + " . ", + ". ", + " .", + " . . ", + " .. ", + " . ", + "-1.-1", + "-1", + "-1.-1.-1", + "..3", + "1..3", + "1..", + ".2.3", + ".2.x", + "1", + "2.x", + "*.*.1", + "*.1", + "*.", + "1.2.", + "1.2.-abc", + "a.b", + "a.b.c", + "a.b.c-preview", + " 0 . 1 . 2 ", + ]).test("Malformed version '%s' should throw", vers => { + expect(() => new installer.DotNetVersionInfo(vers)).toThrow(); + }) }) describe('installer tests', () => { diff --git a/src/installer.ts b/src/installer.ts index 23524ad00..36cea8cba 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -54,6 +54,19 @@ export class DotNetVersionInfo { } //Note: No support for previews when using generic + let parts: string[] = version.split('.'); + + if(parts.length < 2 || parts.length > 3) + this.throwInvalidVersionFormat(); + + if(parts.length == 3 && parts[2] !== "x" && parts[2] !== "*") { + this.throwInvalidVersionFormat(); + } + + this.major = this.getVersionNumberOrThrow(parts[0]); + this.minor = this.getVersionNumberOrThrow(parts[1]); + + /* let regexResult = version.match(/^(\d+\.)(\d+)?(\.\*|\.x|)$/); if(regexResult == null) { throw 'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*'; @@ -62,11 +75,33 @@ export class DotNetVersionInfo { let parts : string[] = (regexResult as RegExpMatchArray).slice(1); this.major = +(parts[0].replace('.','')); - this.minor = +(parts[1].replace('.','')); + this.minor = +(parts[1].replace('.',''));*/ this.fullversion = this.major + '.' + this.minor; } + private getVersionNumberOrThrow(input: string) : number { + try + { + if(!input || input.trim() === "") + this.throwInvalidVersionFormat(); + + let number = Number(input); + + if(Number.isNaN(number) || number < 0) + this.throwInvalidVersionFormat(); + + return number; + } catch { + this.throwInvalidVersionFormat(); + return -1; + } + } + + private throwInvalidVersionFormat() { + throw 'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*' + } + /** * If true exacatly one version should be resolved */ From c5ee7a11347f49e1cfc6eaa7dd656f660ca0ea19 Mon Sep 17 00:00:00 2001 From: litetex Date: Thu, 9 Apr 2020 19:26:10 +0200 Subject: [PATCH 09/16] Cleanup * Doc * Refactored and removed some stuff --- __tests__/installer.test.ts | 3 ++- src/installer.ts | 34 ++++++++++------------------------ 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index ff9604c40..3e933bab7 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -3,6 +3,7 @@ import fs = require('fs'); import os = require('os'); import path = require('path'); import hc = require('@actions/http-client'); + import each from 'jest-each'; const toolDir = path.join(__dirname, 'runner', 'tools'); @@ -119,7 +120,7 @@ describe('installer tests', () => { } else { expect(fs.existsSync(path.join(dotnetDir, 'dotnet'))).toBe(true); } - }, 400000); + }, 400000); //This needs some time to download on "slower" internet connections it('Throws if no location contains correct dotnet version', async () => { let thrown = false; diff --git a/src/installer.ts b/src/installer.ts index 36cea8cba..d8cf0bda0 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -29,27 +29,21 @@ if (!tempDirectory) { tempDirectory = path.join(baseLocation, 'actions', 'temp'); } +/** + * Represents the inputted version information + */ export class DotNetVersionInfo { private fullversion : string; private isExactVersionSet: boolean = false; - private major: number; - private minor: number; - private patch?: number; - constructor(version: string) { // Check for exact match if(semver.valid(semver.clean(version) || '') != null) { - this.fullversion = semver.clean(version) as string; this.isExactVersionSet = true; - this.major = semver.major(this.fullversion); - this.minor = semver.minor(this.fullversion); - this.patch = semver.patch(this.fullversion); - return; } @@ -63,21 +57,10 @@ export class DotNetVersionInfo { this.throwInvalidVersionFormat(); } - this.major = this.getVersionNumberOrThrow(parts[0]); - this.minor = this.getVersionNumberOrThrow(parts[1]); - - /* - let regexResult = version.match(/^(\d+\.)(\d+)?(\.\*|\.x|)$/); - if(regexResult == null) { - throw 'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*'; - } - - let parts : string[] = (regexResult as RegExpMatchArray).slice(1); - - this.major = +(parts[0].replace('.','')); - this.minor = +(parts[1].replace('.',''));*/ + let major = this.getVersionNumberOrThrow(parts[0]); + let minor = this.getVersionNumberOrThrow(parts[1]); - this.fullversion = this.major + '.' + this.minor; + this.fullversion = major + '.' + minor; } private getVersionNumberOrThrow(input: string) : number { @@ -114,6 +97,9 @@ export class DotNetVersionInfo { } } +/** + * Represents a resolved version from the Web-Api + */ class ResolvedVersionInfo { downloadUrls: string[]; resolvedVersion: string; @@ -292,7 +278,7 @@ export class DotnetCoreInstaller { // OsSuffixes - The suffix which is a part of the file name ex- linux-x64, windows-x86 // Type - SDK / Runtime - // Version - Version of the SDK/Runtime + // versionInfo - versionInfo of the SDK/Runtime async resolveInfos( osSuffixes: string[], versionInfo: DotNetVersionInfo From 3de417d35cdf0a1d0ecd0ca566180f9b319c79af Mon Sep 17 00:00:00 2001 From: litetex Date: Thu, 9 Apr 2020 19:29:16 +0200 Subject: [PATCH 10/16] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d2412adb..23feafcc6 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-16.04 strategy: matrix: - dotnet: [ '2.2.103', '3.0.100', '3.1.x' ] + dotnet: [ '2.2.103', '3.0', '3.1.x' ] name: Dotnet ${{ matrix.dotnet }} sample steps: - uses: actions/checkout@master From 3894eede2df78c337eb464bea43b631ee9a64d2a Mon Sep 17 00:00:00 2001 From: litetex Date: Thu, 9 Apr 2020 19:30:41 +0200 Subject: [PATCH 11/16] Update installer.ts * Removed unecessary imports --- src/installer.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/installer.ts b/src/installer.ts index d8cf0bda0..c15d1ce2f 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -9,8 +9,6 @@ import {chmodSync} from 'fs'; import * as os from 'os'; import * as path from 'path'; import * as semver from 'semver'; -import { stringWriter } from 'xmlbuilder'; -import { timingSafeEqual } from 'crypto'; const IS_WINDOWS = process.platform === 'win32'; From 7b228edf4337904875cbbd50d2145bdbeaf44e5a Mon Sep 17 00:00:00 2001 From: litetex Date: Thu, 9 Apr 2020 19:38:39 +0200 Subject: [PATCH 12/16] Pretty printed code --- __tests__/installer.test.ts | 116 +++++++++++++++++++----------------- src/installer.ts | 78 +++++++++++++----------- 2 files changed, 105 insertions(+), 89 deletions(-) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index 3e933bab7..bd60bdc1e 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -16,60 +16,59 @@ import * as installer from '../src/installer'; const IS_WINDOWS = process.platform === 'win32'; describe('version tests', () => { - each([ - '3.1.999', - '3.1.101-preview' - ]).test("Exact version '%s' should be the same", vers => { - let versInfo = new installer.DotNetVersionInfo(vers); + each(['3.1.999', '3.1.101-preview']).test( + "Exact version '%s' should be the same", + vers => { + let versInfo = new installer.DotNetVersionInfo(vers); - expect(versInfo.isExactVersion()).toBe(true); - expect(versInfo.version()).toBe(vers); - }) + expect(versInfo.isExactVersion()).toBe(true); + expect(versInfo.version()).toBe(vers); + } + ); - each([ - ['3.1.x', '3.1'], - ['1.1.*', '1.1'], - ['2.0', '2.0'] - ]).test("Generic version '%s' should be '%s'", (vers, resVers) => { - let versInfo = new installer.DotNetVersionInfo(vers); + each([['3.1.x', '3.1'], ['1.1.*', '1.1'], ['2.0', '2.0']]).test( + "Generic version '%s' should be '%s'", + (vers, resVers) => { + let versInfo = new installer.DotNetVersionInfo(vers); - expect(versInfo.isExactVersion()).toBe(false); - expect(versInfo.version()).toBe(resVers); - }) + expect(versInfo.isExactVersion()).toBe(false); + expect(versInfo.version()).toBe(resVers); + } + ); each([ - "", - ".", - "..", - " . ", - ". ", - " .", - " . . ", - " .. ", - " . ", - "-1.-1", - "-1", - "-1.-1.-1", - "..3", - "1..3", - "1..", - ".2.3", - ".2.x", - "1", - "2.x", - "*.*.1", - "*.1", - "*.", - "1.2.", - "1.2.-abc", - "a.b", - "a.b.c", - "a.b.c-preview", - " 0 . 1 . 2 ", + '', + '.', + '..', + ' . ', + '. ', + ' .', + ' . . ', + ' .. ', + ' . ', + '-1.-1', + '-1', + '-1.-1.-1', + '..3', + '1..3', + '1..', + '.2.3', + '.2.x', + '1', + '2.x', + '*.*.1', + '*.1', + '*.', + '1.2.', + '1.2.-abc', + 'a.b', + 'a.b.c', + 'a.b.c-preview', + ' 0 . 1 . 2 ' ]).test("Malformed version '%s' should throw", vers => { expect(() => new installer.DotNetVersionInfo(vers)).toThrow(); - }) -}) + }); +}); describe('installer tests', () => { beforeAll(async () => { @@ -86,26 +85,35 @@ describe('installer tests', () => { } }, 100000); - it('Resolving a normal generic version works', async() => { + it('Resolving a normal generic version works', async () => { const dotnetInstaller = new installer.DotnetCoreInstaller('3.1.x'); - let versInfo = await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo('3.1.x')); + let versInfo = await dotnetInstaller.resolveInfos( + ['win-x64'], + new installer.DotNetVersionInfo('3.1.x') + ); expect(versInfo.resolvedVersion.startsWith('3.1.')); }, 100000); - it('Resolving a nonexistent generic version fails', async() => { + it('Resolving a nonexistent generic version fails', async () => { const dotnetInstaller = new installer.DotnetCoreInstaller('999.1.x'); - try{ - await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo('999.1.x')); + try { + await dotnetInstaller.resolveInfos( + ['win-x64'], + new installer.DotNetVersionInfo('999.1.x') + ); fail(); } catch { expect(true); } }, 100000); - it('Resolving a exact version works', async() => { + it('Resolving a exact version works', async () => { const dotnetInstaller = new installer.DotnetCoreInstaller('3.1.201'); - let versInfo = await dotnetInstaller.resolveInfos(["win-x64"],new installer.DotNetVersionInfo('3.1.201')); + let versInfo = await dotnetInstaller.resolveInfos( + ['win-x64'], + new installer.DotNetVersionInfo('3.1.201') + ); expect(versInfo.resolvedVersion).toBe('3.1.201'); }, 100000); diff --git a/src/installer.ts b/src/installer.ts index c15d1ce2f..8d9e6cc1f 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -31,14 +31,12 @@ if (!tempDirectory) { * Represents the inputted version information */ export class DotNetVersionInfo { - - private fullversion : string; + private fullversion: string; private isExactVersionSet: boolean = false; constructor(version: string) { - // Check for exact match - if(semver.valid(semver.clean(version) || '') != null) { + if (semver.valid(semver.clean(version) || '') != null) { this.fullversion = semver.clean(version) as string; this.isExactVersionSet = true; @@ -48,11 +46,10 @@ export class DotNetVersionInfo { //Note: No support for previews when using generic let parts: string[] = version.split('.'); - if(parts.length < 2 || parts.length > 3) - this.throwInvalidVersionFormat(); + if (parts.length < 2 || parts.length > 3) this.throwInvalidVersionFormat(); - if(parts.length == 3 && parts[2] !== "x" && parts[2] !== "*") { - this.throwInvalidVersionFormat(); + if (parts.length == 3 && parts[2] !== 'x' && parts[2] !== '*') { + this.throwInvalidVersionFormat(); } let major = this.getVersionNumberOrThrow(parts[0]); @@ -61,17 +58,14 @@ export class DotNetVersionInfo { this.fullversion = major + '.' + minor; } - private getVersionNumberOrThrow(input: string) : number { - try - { - if(!input || input.trim() === "") - this.throwInvalidVersionFormat(); + private getVersionNumberOrThrow(input: string): number { + try { + if (!input || input.trim() === '') this.throwInvalidVersionFormat(); let number = Number(input); - if(Number.isNaN(number) || number < 0) - this.throwInvalidVersionFormat(); - + if (Number.isNaN(number) || number < 0) this.throwInvalidVersionFormat(); + return number; } catch { this.throwInvalidVersionFormat(); @@ -80,17 +74,17 @@ export class DotNetVersionInfo { } private throwInvalidVersionFormat() { - throw 'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*' + throw 'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*'; } /** * If true exacatly one version should be resolved */ - public isExactVersion() : boolean { + public isExactVersion(): boolean { return this.isExactVersionSet; } - public version() : string { + public version(): string { return this.fullversion; } } @@ -103,11 +97,11 @@ class ResolvedVersionInfo { resolvedVersion: string; constructor(downloadUrls: string[], resolvedVersion: string) { - if(downloadUrls.length === 0) { + if (downloadUrls.length === 0) { throw 'DownloadUrls can not be empty'; } - if(!resolvedVersion) { + if (!resolvedVersion) { throw 'Resolved version is invalid'; } @@ -125,7 +119,7 @@ export class DotnetCoreInstaller { public async installDotnet() { // Check cache - let toolPath: string = ""; + let toolPath: string = ''; let osSuffixes = await this.detectMachineOS(); let parts = osSuffixes[0].split('-'); if (parts.length > 1) { @@ -133,17 +127,20 @@ export class DotnetCoreInstaller { } // If version is not generic -> look up cache - if(this.versionInfo.isExactVersion()) + if (this.versionInfo.isExactVersion()) toolPath = this.getLocalTool(this.versionInfo.version()); if (!toolPath) { // download, extract, cache console.log('Getting a download url', this.versionInfo.version()); - let resolvedVersionInfo = await this.resolveInfos(osSuffixes, this.versionInfo); + let resolvedVersionInfo = await this.resolveInfos( + osSuffixes, + this.versionInfo + ); //Check if cache exists for resolved version toolPath = this.getLocalTool(resolvedVersionInfo.resolvedVersion); - if(!toolPath) { + if (!toolPath) { //If not exists install it toolPath = await this.downloadAndInstall(resolvedVersionInfo); } else { @@ -281,7 +278,6 @@ export class DotnetCoreInstaller { osSuffixes: string[], versionInfo: DotNetVersionInfo ): Promise { - const httpClient = new hc.HttpClient('actions/setup-dotnet', [], { allowRetries: true, maxRetries: 3 @@ -297,22 +293,32 @@ export class DotnetCoreInstaller { let releasesInfo: any[] = releasesResult['releases']; releasesInfo = releasesInfo.filter((releaseInfo: any) => { return ( - semver.satisfies(releaseInfo['sdk']['version'], versionInfo.version()) || - semver.satisfies(releaseInfo['sdk']['version-display'], versionInfo.version()) + semver.satisfies( + releaseInfo['sdk']['version'], + versionInfo.version() + ) || + semver.satisfies( + releaseInfo['sdk']['version-display'], + versionInfo.version() + ) ); }); // Exclude versions that are newer than the latest if using not exact - if(!versionInfo.isExactVersion()) { - let latestSdk : string = releasesResult['latest-sdk']; + if (!versionInfo.isExactVersion()) { + let latestSdk: string = releasesResult['latest-sdk']; - releasesInfo = releasesInfo.filter((releaseInfo: any) => semver.lte(releaseInfo['sdk']['version'], latestSdk)); + releasesInfo = releasesInfo.filter((releaseInfo: any) => + semver.lte(releaseInfo['sdk']['version'], latestSdk) + ); } // Sort for latest version - releasesInfo = releasesInfo.sort((a,b) => semver.rcompare(a['sdk']['version'],b['sdk']['version'])); + releasesInfo = releasesInfo.sort((a, b) => + semver.rcompare(a['sdk']['version'], b['sdk']['version']) + ); - let downloadedVersion : string = ''; + let downloadedVersion: string = ''; let downloadUrls: string[] = []; if (releasesInfo.length != 0) { @@ -341,10 +347,12 @@ export class DotnetCoreInstaller { `Could not fetch download information for version ${versionInfo.version()}` ); - if(versionInfo.isExactVersion()) { + if (versionInfo.isExactVersion()) { console.log('Using fallback'); - downloadUrls = await this.getFallbackDownloadUrls(versionInfo.version()); + downloadUrls = await this.getFallbackDownloadUrls( + versionInfo.version() + ); downloadedVersion = versionInfo.version(); } else { console.log('Unable to use fallback, version is generic!'); From 994f1363610f658ef4e3eb0ad4c3f3b597df2a63 Mon Sep 17 00:00:00 2001 From: litetex Date: Thu, 9 Apr 2020 19:51:25 +0200 Subject: [PATCH 13/16] Updated install-dotnet.sh ... to newer version that you get when you download it :arrow_up: --- externals/install-dotnet.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/externals/install-dotnet.sh b/externals/install-dotnet.sh index 31303d94d..0c20299a5 100755 --- a/externals/install-dotnet.sh +++ b/externals/install-dotnet.sh @@ -172,7 +172,7 @@ get_current_os_name() { return 0 elif [ "$uname" = "FreeBSD" ]; then echo "freebsd" - return 0 + return 0 elif [ "$uname" = "Linux" ]; then local linux_platform_name linux_platform_name="$(get_linux_platform_name)" || { echo "linux" && return 0 ; } @@ -728,11 +728,12 @@ downloadcurl() { # Append feed_credential as late as possible before calling curl to avoid logging feed_credential remote_path="${remote_path}${feed_credential}" + local curl_options="--retry 20 --retry-delay 2 --connect-timeout 15 -sSL -f --create-dirs " local failed=false if [ -z "$out_path" ]; then - curl --retry 10 -sSL -f --create-dirs "$remote_path" || failed=true + curl $curl_options "$remote_path" || failed=true else - curl --retry 10 -sSL -f --create-dirs -o "$out_path" "$remote_path" || failed=true + curl $curl_options -o "$out_path" "$remote_path" || failed=true fi if [ "$failed" = true ]; then say_verbose "Curl download failed" @@ -748,12 +749,12 @@ downloadwget() { # Append feed_credential as late as possible before calling wget to avoid logging feed_credential remote_path="${remote_path}${feed_credential}" - + local wget_options="--tries 20 --waitretry 2 --connect-timeout 15 " local failed=false if [ -z "$out_path" ]; then - wget -q --tries 10 -O - "$remote_path" || failed=true + wget -q $wget_options -O - "$remote_path" || failed=true else - wget --tries 10 -O "$out_path" "$remote_path" || failed=true + wget $wget_options -O "$out_path" "$remote_path" || failed=true fi if [ "$failed" = true ]; then say_verbose "Wget download failed" From a8eaac54ab5fe1b2fba429e760af3c666cbb88c0 Mon Sep 17 00:00:00 2001 From: litetex Date: Thu, 21 May 2020 13:02:58 +0200 Subject: [PATCH 14/16] Tests are now covering the correct preview-format --- __tests__/installer.test.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index bd60bdc1e..0237c322b 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -16,7 +16,7 @@ import * as installer from '../src/installer'; const IS_WINDOWS = process.platform === 'win32'; describe('version tests', () => { - each(['3.1.999', '3.1.101-preview']).test( + each(['3.1.999', '3.1.101-preview.3']).test( "Exact version '%s' should be the same", vers => { let versInfo = new installer.DotNetVersionInfo(vers); @@ -108,7 +108,7 @@ describe('installer tests', () => { } }, 100000); - it('Resolving a exact version works', async () => { + it('Resolving a exact stable version works', async () => { const dotnetInstaller = new installer.DotnetCoreInstaller('3.1.201'); let versInfo = await dotnetInstaller.resolveInfos( ['win-x64'], @@ -118,6 +118,16 @@ describe('installer tests', () => { expect(versInfo.resolvedVersion).toBe('3.1.201'); }, 100000); + it('Resolving a exact preview version works', async () => { + const dotnetInstaller = new installer.DotnetCoreInstaller('5.0.0-preview.4'); + let versInfo = await dotnetInstaller.resolveInfos( + ['win-x64'], + new installer.DotNetVersionInfo('5.0.0-preview.4') + ); + + expect(versInfo.resolvedVersion).toBe('5.0.0-preview.4'); + }, 100000); + it('Acquires version of dotnet if no matching version is installed', async () => { await getDotnet('2.2.205'); const dotnetDir = path.join(toolDir, 'dncs', '2.2.205', os.arch()); From 983c8ef1bdbffea6c988008df31d9c35b7aec8a6 Mon Sep 17 00:00:00 2001 From: litetex Date: Thu, 21 May 2020 13:23:34 +0200 Subject: [PATCH 15/16] Fixed format :scream: --- __tests__/installer.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index 0237c322b..47d5585d8 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -119,7 +119,9 @@ describe('installer tests', () => { }, 100000); it('Resolving a exact preview version works', async () => { - const dotnetInstaller = new installer.DotnetCoreInstaller('5.0.0-preview.4'); + const dotnetInstaller = new installer.DotnetCoreInstaller( + '5.0.0-preview.4' + ); let versInfo = await dotnetInstaller.resolveInfos( ['win-x64'], new installer.DotNetVersionInfo('5.0.0-preview.4') From e169331466e39805c1210cf3eb256ad2a28df36d Mon Sep 17 00:00:00 2001 From: Zachary Eisinger Date: Fri, 29 May 2020 14:04:53 -0700 Subject: [PATCH 16/16] Rebuild dist package --- dist/index.js | 145 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 117 insertions(+), 28 deletions(-) diff --git a/dist/index.js b/dist/index.js index 82de6153f..fca144532 100644 --- a/dist/index.js +++ b/dist/index.js @@ -17392,7 +17392,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.DotnetCoreInstaller = void 0; +exports.DotnetCoreInstaller = exports.DotNetVersionInfo = void 0; // Load tempDirectory before it gets wiped by tool-cache let tempDirectory = process.env['RUNNER_TEMPDIRECTORY'] || ''; const core = __importStar(__webpack_require__(470)); @@ -17421,30 +17421,103 @@ if (!tempDirectory) { } tempDirectory = path.join(baseLocation, 'actions', 'temp'); } -class DotnetCoreInstaller { +/** + * Represents the inputted version information + */ +class DotNetVersionInfo { constructor(version) { - if (semver.valid(semver.clean(version) || '') == null) { - throw 'Implicit version not permitted'; + this.isExactVersionSet = false; + // Check for exact match + if (semver.valid(semver.clean(version) || '') != null) { + this.fullversion = semver.clean(version); + this.isExactVersionSet = true; + return; + } + //Note: No support for previews when using generic + let parts = version.split('.'); + if (parts.length < 2 || parts.length > 3) + this.throwInvalidVersionFormat(); + if (parts.length == 3 && parts[2] !== 'x' && parts[2] !== '*') { + this.throwInvalidVersionFormat(); + } + let major = this.getVersionNumberOrThrow(parts[0]); + let minor = this.getVersionNumberOrThrow(parts[1]); + this.fullversion = major + '.' + minor; + } + getVersionNumberOrThrow(input) { + try { + if (!input || input.trim() === '') + this.throwInvalidVersionFormat(); + let number = Number(input); + if (Number.isNaN(number) || number < 0) + this.throwInvalidVersionFormat(); + return number; + } + catch (_a) { + this.throwInvalidVersionFormat(); + return -1; } - this.version = version; + } + throwInvalidVersionFormat() { + throw 'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*'; + } + /** + * If true exacatly one version should be resolved + */ + isExactVersion() { + return this.isExactVersionSet; + } + version() { + return this.fullversion; + } +} +exports.DotNetVersionInfo = DotNetVersionInfo; +/** + * Represents a resolved version from the Web-Api + */ +class ResolvedVersionInfo { + constructor(downloadUrls, resolvedVersion) { + if (downloadUrls.length === 0) { + throw 'DownloadUrls can not be empty'; + } + if (!resolvedVersion) { + throw 'Resolved version is invalid'; + } + this.downloadUrls = downloadUrls; + this.resolvedVersion = resolvedVersion; + } +} +class DotnetCoreInstaller { + constructor(version) { + this.versionInfo = new DotNetVersionInfo(version); this.cachedToolName = 'dncs'; this.arch = 'x64'; } installDotnet() { return __awaiter(this, void 0, void 0, function* () { // Check cache - let toolPath; + let toolPath = ''; let osSuffixes = yield this.detectMachineOS(); let parts = osSuffixes[0].split('-'); if (parts.length > 1) { this.arch = parts[1]; } - toolPath = this.getLocalTool(); + // If version is not generic -> look up cache + if (this.versionInfo.isExactVersion()) + toolPath = this.getLocalTool(this.versionInfo.version()); if (!toolPath) { // download, extract, cache - console.log('Getting a download url', this.version); - let downloadUrls = yield this.getDownloadUrls(osSuffixes, this.version); - toolPath = yield this.downloadAndInstall(downloadUrls); + console.log('Getting a download url', this.versionInfo.version()); + let resolvedVersionInfo = yield this.resolveInfos(osSuffixes, this.versionInfo); + //Check if cache exists for resolved version + toolPath = this.getLocalTool(resolvedVersionInfo.resolvedVersion); + if (!toolPath) { + //If not exists install it + toolPath = yield this.downloadAndInstall(resolvedVersionInfo); + } + else { + console.log('Using cached tool'); + } } else { console.log('Using cached tool'); @@ -17455,9 +17528,9 @@ class DotnetCoreInstaller { core.addPath(toolPath); }); } - getLocalTool() { - console.log('Checking tool cache'); - return tc.find(this.cachedToolName, this.version, this.arch); + getLocalTool(version) { + console.log('Checking tool cache', version); + return tc.find(this.cachedToolName, version, this.arch); } detectMachineOS() { return __awaiter(this, void 0, void 0, function* () { @@ -17517,18 +17590,18 @@ class DotnetCoreInstaller { return osSuffix; }); } - downloadAndInstall(downloadUrls) { + downloadAndInstall(resolvedVersionInfo) { return __awaiter(this, void 0, void 0, function* () { let downloaded = false; let downloadPath = ''; - for (const url of downloadUrls) { + for (const url of resolvedVersionInfo.downloadUrls) { try { downloadPath = yield tc.downloadTool(url); downloaded = true; break; } catch (error) { - console.log('Could Not Download', url, JSON.stringify(error)); + console.log('Could not Download', url, JSON.stringify(error)); } } if (!downloaded) { @@ -17541,31 +17614,40 @@ class DotnetCoreInstaller { : yield tc.extractTar(downloadPath); // cache tool console.log('Caching tool'); - let cachedDir = yield tc.cacheDir(extPath, this.cachedToolName, this.version, this.arch); - console.log('Successfully installed', this.version); + let cachedDir = yield tc.cacheDir(extPath, this.cachedToolName, resolvedVersionInfo.resolvedVersion, this.arch); + console.log('Successfully installed', resolvedVersionInfo.resolvedVersion); return cachedDir; }); } // OsSuffixes - The suffix which is a part of the file name ex- linux-x64, windows-x86 // Type - SDK / Runtime - // Version - Version of the SDK/Runtime - getDownloadUrls(osSuffixes, version) { + // versionInfo - versionInfo of the SDK/Runtime + resolveInfos(osSuffixes, versionInfo) { return __awaiter(this, void 0, void 0, function* () { - let downloadUrls = []; const httpClient = new hc.HttpClient('actions/setup-dotnet', [], { allowRetries: true, maxRetries: 3 }); - const releasesJsonUrl = yield this.getReleasesJsonUrl(httpClient, version.split('.')); + const releasesJsonUrl = yield this.getReleasesJsonUrl(httpClient, versionInfo.version().split('.')); const releasesResponse = yield httpClient.getJson(releasesJsonUrl); const releasesResult = releasesResponse.result || {}; let releasesInfo = releasesResult['releases']; releasesInfo = releasesInfo.filter((releaseInfo) => { - return (releaseInfo['sdk']['version'] === version || - releaseInfo['sdk']['version-display'] === version); + return (semver.satisfies(releaseInfo['sdk']['version'], versionInfo.version()) || + semver.satisfies(releaseInfo['sdk']['version-display'], versionInfo.version())); }); + // Exclude versions that are newer than the latest if using not exact + if (!versionInfo.isExactVersion()) { + let latestSdk = releasesResult['latest-sdk']; + releasesInfo = releasesInfo.filter((releaseInfo) => semver.lte(releaseInfo['sdk']['version'], latestSdk)); + } + // Sort for latest version + releasesInfo = releasesInfo.sort((a, b) => semver.rcompare(a['sdk']['version'], b['sdk']['version'])); + let downloadedVersion = ''; + let downloadUrls = []; if (releasesInfo.length != 0) { let release = releasesInfo[0]; + downloadedVersion = release['sdk']['version']; let files = release['sdk']['files']; files = files.filter((file) => { if (file['rid'] == osSuffixes[0] || file['rid'] == osSuffixes[1]) { @@ -17582,14 +17664,21 @@ class DotnetCoreInstaller { } } else { - console.log(`Could not fetch download information for version ${version}`); - downloadUrls = yield this.getFallbackDownloadUrls(version); + console.log(`Could not fetch download information for version ${versionInfo.version()}`); + if (versionInfo.isExactVersion()) { + console.log('Using fallback'); + downloadUrls = yield this.getFallbackDownloadUrls(versionInfo.version()); + downloadedVersion = versionInfo.version(); + } + else { + console.log('Unable to use fallback, version is generic!'); + } } if (downloadUrls.length == 0) { - throw `Could not construct download URL. Please ensure that specified version ${version} is valid.`; + throw `Could not construct download URL. Please ensure that specified version ${versionInfo.version()}/${downloadedVersion} is valid.`; } core.debug(`Got download urls ${downloadUrls}`); - return downloadUrls; + return new ResolvedVersionInfo(downloadUrls, downloadedVersion); }); } getReleasesJsonUrl(httpClient, versionParts) {