diff --git a/Tasks/UsePythonVersion/Tests/L0.ts b/Tasks/UsePythonVersion/Tests/L0.ts index f31bf6497822..352975c577ca 100644 --- a/Tasks/UsePythonVersion/Tests/L0.ts +++ b/Tasks/UsePythonVersion/Tests/L0.ts @@ -28,6 +28,48 @@ describe('UsePythonVersion L0 Suite', function () { mockery.resetCache(); }) + it('converts Python prerelease versions to the semantic version format', function () { + mockery.registerMock('vsts-task-lib/task', mockTask); + mockery.registerMock('vsts-task-tool-lib/tool', {}); + const uut = reload(); + + const testCases = [ + { + versionSpec: '3.x', + expected: '3.x' + }, + { + versionSpec: '3.3.6', + expected: '3.3.6' + }, + { + versionSpec: '3.7.0b2', + expected: '3.7.0-b2' + }, + { + versionSpec: '3.7.0rc', + expected: '3.7.0-rc' + }, + { + versionSpec: '14.22.100a1000', + expected: '14.22.100-a1000' + }, + { + versionSpec: '3.6.6b2 || >= 3.7.0rc', + expected: '3.6.6-b2 || >= 3.7.0-rc' + }, + { + versionSpec: '3.7rc1', // invalid + expected: '3.7rc1' + }, + ]; + + for (let tc of testCases) { // Node 5 can't handle destructuring assignment + const actual = uut.pythonVersionToSemantic(tc.versionSpec); + assert.strictEqual(actual, tc.expected); + } + }) + it('finds version in cache', async function () { let buildVariables: any = {}; const mockBuildVariables = { diff --git a/Tasks/UsePythonVersion/task.json b/Tasks/UsePythonVersion/task.json index 15cca6118cb6..136a53277851 100644 --- a/Tasks/UsePythonVersion/task.json +++ b/Tasks/UsePythonVersion/task.json @@ -12,7 +12,7 @@ "author": "Microsoft Corporation", "version": { "Major": 0, - "Minor": 132, + "Minor": 133, "Patch": 0 }, "preview": true, diff --git a/Tasks/UsePythonVersion/task.loc.json b/Tasks/UsePythonVersion/task.loc.json index caccb31bf317..39307723bbed 100644 --- a/Tasks/UsePythonVersion/task.loc.json +++ b/Tasks/UsePythonVersion/task.loc.json @@ -12,7 +12,7 @@ "author": "Microsoft Corporation", "version": { "Major": 0, - "Minor": 132, + "Minor": 133, "Patch": 0 }, "preview": true, diff --git a/Tasks/UsePythonVersion/usepythonversion.ts b/Tasks/UsePythonVersion/usepythonversion.ts index 98173fd2d08a..d5d4834fa913 100644 --- a/Tasks/UsePythonVersion/usepythonversion.ts +++ b/Tasks/UsePythonVersion/usepythonversion.ts @@ -31,8 +31,19 @@ interface TaskParameters { readonly addToPath: boolean } +export function pythonVersionToSemantic(versionSpec: string) { + const prereleaseVersion = /(\d+\.\d+\.\d+)([a|b|rc]\d*)/g; + return versionSpec.replace(prereleaseVersion, '$1-$2'); +} + export async function usePythonVersion(parameters: TaskParameters, platform: Platform): Promise { - const installDir: string | null = tool.findLocalTool('Python', parameters.versionSpec); + // Python's prelease versions look like `3.7.0b2`. + // This is the one part of Python versioning that does not look like semantic versioning, which specifies `3.7.0-b2`. + // If the version spec contains prerelease versions, we need to convert them to the semantic version equivalent + const semanticVersionSpec = pythonVersionToSemantic(parameters.versionSpec); + task.debug(`Semantic version spec of ${parameters.versionSpec} is ${semanticVersionSpec}`); + + const installDir: string | null = tool.findLocalTool('Python', semanticVersionSpec); if (!installDir) { // Fail and list available versions throw new Error([