Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UsePythonV0] Download python from registry #15820

Merged
merged 36 commits into from
Mar 5, 2022
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
06ee4bc
download python from registry if absent in cache
DaniilShmelev Jan 24, 2022
f6a42ca
add unit test
DaniilShmelev Jan 25, 2022
f688219
Merge branch 'master' into users/DaniilShmelev/download-python
DaniilShmelev Jan 25, 2022
d1eba94
fixes & debug messages
DaniilShmelev Jan 25, 2022
1ad4069
Merge branch 'users/DaniilShmelev/download-python' of https://github.…
DaniilShmelev Jan 25, 2022
3fa487e
fix manifest branch name
DaniilShmelev Jan 26, 2022
03c366e
fix debug message for python archive file name
DaniilShmelev Jan 26, 2022
c079cc0
Merge branch 'master' into users/DaniilShmelev/download-python
DaniilShmelev Jan 26, 2022
c23269c
remove unneeded function
DaniilShmelev Jan 26, 2022
ed86434
Merge branch 'users/DaniilShmelev/download-python' of https://github.…
DaniilShmelev Jan 26, 2022
619459c
fix unit tests failing on unix
DaniilShmelev Jan 31, 2022
bece6d6
only check os version once
DaniilShmelev Jan 31, 2022
3c8109b
add unit test for ubuntu version lookup
DaniilShmelev Jan 31, 2022
a2d4748
Merge branch 'master' into users/DaniilShmelev/download-python
DaniilShmelev Jan 31, 2022
8207049
adjust error messages
DaniilShmelev Jan 31, 2022
2d72a98
add input to allow unstable versions
DaniilShmelev Feb 1, 2022
f54dffd
fix ubuntu test
DaniilShmelev Feb 1, 2022
1569a3a
add test for unstable version download
DaniilShmelev Feb 1, 2022
e1601a2
add comments
DaniilShmelev Feb 1, 2022
0c4754d
Merge branch 'master' into users/DaniilShmelev/download-python
DaniilShmelev Feb 1, 2022
66e9de1
adjust comments
DaniilShmelev Feb 1, 2022
9a26bd2
select arch based on input
DaniilShmelev Feb 2, 2022
03fc221
extract interfaces into separate file
DaniilShmelev Feb 3, 2022
a59284d
make allowUnstable optional
DaniilShmelev Feb 3, 2022
a2f726b
add a separate debug message for downloading
DaniilShmelev Feb 3, 2022
e4494de
explain what the registry is more clearly
DaniilShmelev Feb 3, 2022
2500f6e
Merge branch 'master' into users/DaniilShmelev/download-python
DaniilShmelev Feb 3, 2022
4a82224
Merge branch 'master' into users/DaniilShmelev/download-python
DaniilShmelev Feb 4, 2022
ad0fc70
Merge branch 'master' into users/DaniilShmelev/download-python
DaniilShmelev Feb 7, 2022
d12956d
bump task version
DaniilShmelev Feb 7, 2022
cdfa18a
bump task version due to a new sprint
DaniilShmelev Feb 14, 2022
bb04973
fix allowUnstable default value
DaniilShmelev Feb 14, 2022
3c03c27
Merge branch 'master' into users/DaniilShmelev/download-python
DaniilShmelev Feb 16, 2022
8370035
Merge branch 'master' into users/DaniilShmelev/download-python
DaniilShmelev Mar 4, 2022
2eb8330
format osutil to unify code style
DaniilShmelev Mar 5, 2022
8ee219e
bump task version
DaniilShmelev Mar 5, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"loc.group.displayName.advanced": "Advanced",
"loc.input.label.versionSpec": "Version spec",
"loc.input.help.versionSpec": "Version range or exact version of a Python version to use, using semver's version range syntax. [More information](https://go.microsoft.com/fwlink/?LinkID=2006180)",
"loc.input.label.allowUnstable": "Allow downloading unstable releases",
"loc.input.help.allowUnstable": "Allow downloading unstable python versions from registry.",
DaniilShmelev marked this conversation as resolved.
Show resolved Hide resolved
"loc.input.label.addToPath": "Add to PATH",
"loc.input.help.addToPath": "Whether to prepend the retrieved Python version to the PATH environment variable to make it available in subsequent tasks or scripts without using the output variable.",
"loc.input.label.architecture": "Architecture",
Expand All @@ -16,7 +18,8 @@
"loc.messages.PyPyNotFound": "Could not find an installation of PyPy%d in Agent.ToolsDirectory.",
"loc.messages.ToolNotFoundMicrosoftHosted": "If this is a Microsoft-hosted agent, check that this image supports side-by-side versions of %s at %s.",
"loc.messages.ToolNotFoundSelfHosted": "If this is a self-hosted agent, see how to configure side-by-side %s versions at %s.",
"loc.messages.VersionNotFound": "Version spec %s for architecture %s did not match any version in Agent.ToolsDirectory.",
"loc.messages.ExactVersionNotRecommended": "Specifying an exact version is not recommended on Microsoft-Hosted agents. Patch versions of Python can be replaced by new ones on Hosted agents without notice, which will cause builds to fail unexpectedly. It is recommended to specify only major or major and minor version (Example: `3` or `3.6`)",
"loc.messages.PythonVersionRetirement": "Python 3.5 has reached its end of life and will be removed from the ms-hosted images on January, 24. Please update Python version if you use ms-hosted agents. For more information about this - https://github.com/actions/virtual-environments"
"loc.messages.VersionNotFound": "Version spec %s for architecture %s did not match any version in Agent.ToolsDirectory or in python-versions registry (https://github.com/actions/python-versions).",
"loc.messages.PythonVersionRetirement": "Python 3.5 has reached its end of life and will be removed from the ms-hosted images on January, 24. Please update Python version if you use ms-hosted agents. For more information about this - https://github.com/actions/virtual-environments",
"loc.messages.DownloadNotFound": "Could not find Python matching spec %s in the python-versions registry. Beware that we don't support downloading python from registry on self-hosted agents.",
DaniilShmelev marked this conversation as resolved.
Show resolved Hide resolved
"loc.messages.DownloadFailed": "Failed to download Python from the registry. Error: %s."
}
6 changes: 6 additions & 0 deletions Tasks/UsePythonVersionV0/TaskParameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default interface TaskParameters {
versionSpec: string,
allowUnstable?: boolean,
addToPath: boolean,
architecture: string
}
56 changes: 54 additions & 2 deletions Tasks/UsePythonVersionV0/Tests/L0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,74 @@ describe('UsePythonVersion L0 Suite', function () {
assert(testRunner.succeeded, 'task should have succeeded');
});

it('downloads python from registry on Windows', function () {
const testFile = path.join(__dirname, 'L0DownloadsFromRegistryWindows.js');
const testRunner = new MockTestRunner(testFile);

testRunner.run();

const pythonDir = path.join('C', 'tools', 'Python', '3.10.1', 'x64');
const pythonBinDir = path.join(pythonDir, 'Scripts');
const pythonAppdataDir = path.join('testappdata', 'Python', 'Python310', 'Scripts');

assert(didSetVariable(testRunner, 'pythonLocation', pythonDir));
assert(didPrependPath(testRunner, pythonDir));
assert(didPrependPath(testRunner, pythonBinDir));
assert(didPrependPath(testRunner, pythonAppdataDir));
assert.strictEqual(testRunner.stderr.length, 0, 'should not have written to stderr');
assert(testRunner.succeeded, 'task should have succeeded');
});

it('downloads python from registry on Ubuntu', function () {
const testFile = path.join(__dirname, 'L0DownloadsFromRegistryUbuntu.js');
const testRunner = new MockTestRunner(testFile);

testRunner.run();

const pythonDir = path.join('opt', 'hostedtoolcache', 'Python', '3.10.1', 'x64');
const pythonBinDir = path.join(pythonDir, 'bin');

assert(didSetVariable(testRunner, 'pythonLocation', pythonDir));
assert(didPrependPath(testRunner, pythonDir));
assert(didPrependPath(testRunner, pythonBinDir));
assert.strictEqual(testRunner.stderr.length, 0, 'should not have written to stderr');
assert(testRunner.succeeded, 'task should have succeeded');
});

it('downloads unstable python from registry', function () {
const testFile = path.join(__dirname, 'L0DownloadsUnstable.js');
const testRunner = new MockTestRunner(testFile);

testRunner.run();

const pythonDir = path.join('opt', 'hostedtoolcache', 'Python', '3.11.1', 'x64');
const pythonBinDir = path.join(pythonDir, 'bin');

assert(didSetVariable(testRunner, 'pythonLocation', pythonDir));
assert(didPrependPath(testRunner, pythonDir));
assert(didPrependPath(testRunner, pythonBinDir));
assert.strictEqual(testRunner.stderr.length, 0, 'should not have written to stderr');
assert(testRunner.succeeded, 'task should have succeeded');
});

it('fails when version is not found', function () {
const testFile = path.join(__dirname, 'L0FailsWhenVersionIsMissing.js');
const testRunner = new MockTestRunner(testFile);

testRunner.run();

assert(testRunner.createdErrorIssue('loc_mock_DownloadFailed Error: loc_mock_DownloadNotFound 3.11.x'));

const errorMessage = [
'loc_mock_VersionNotFound 3.x x64',
'loc_mock_VersionNotFound 3.11.x x64',
'loc_mock_ListAvailableVersions $(Agent.ToolsDirectory)',
'2.6.0 (x86)',
'2.7.13 (x86)',
'2.6.0 (x64)',
'2.7.13 (x64)',
'loc_mock_ToolNotFoundMicrosoftHosted Python https://aka.ms/hosted-agent-software',
'loc_mock_ToolNotFoundSelfHosted Python https://go.microsoft.com/fwlink/?linkid=871498',
].join(os.EOL);
].join('\r\n');

assert(testRunner.createdErrorIssue(errorMessage));
assert(testRunner.failed, 'task should have failed');
Expand Down
104 changes: 104 additions & 0 deletions Tasks/UsePythonVersionV0/Tests/L0DownloadsFromRegistryUbuntu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as path from 'path';
import * as fs from 'fs';

import { TaskMockRunner } from 'azure-pipelines-task-lib/mock-run';

const taskPath = path.join(__dirname, '..', 'main.js');
const taskRunner = new TaskMockRunner(taskPath);

taskRunner.setInput('versionSpec', '3.10.x');
taskRunner.setInput('addToPath', 'true');
taskRunner.setInput('architecture', 'x64');

// `getVariable` is not supported by `TaskLibAnswers`
process.env['AGENT_TOOLSDIRECTORY'] = '$(Agent.ToolsDirectory)';

let pythonWasInstalled = false;

// Mock azure-pipelines-tool-lib
taskRunner.registerMock('azure-pipelines-tool-lib/tool', {
findLocalTool() {
if (!pythonWasInstalled) {
return null;
}

return path.join('opt', 'hostedtoolcache', 'Python', '3.10.1', 'x64');
},
findLocalToolVersions: () => ['2.6.0', '2.7.13'],
downloadTool: () => Promise.resolve('/home/python.tar.gz'),
extractTar: () => Promise.resolve('/home/extracted/python'),
extractZip() {
throw new Error('This should never be called');
},
});

taskRunner.registerMock('os', {
platform() {
return 'linux';
},
arch() {
return 'x64';
},
EOL: '\r\n'
});

// Can't mock process, so have to mock taskutil instead
enum Platform {
Windows,
MacOS,
Linux
}

taskRunner.registerMock('./taskutil', {
Platform,
getPlatform() {
return Platform.Linux;
}
});

const tl = require('azure-pipelines-task-lib/mock-task');
const tlClone = Object.assign({}, tl);
tlClone.exec = function(command, args, options) {
if (command !== 'bash' || args !== './setup.sh') {
throw new Error(`Invalid command and arguments: ${command} ${args}`);
}

if (options.cwd !== '/home/extracted/python') {
throw new Error(`Invalid python installer dir path: ${options.cwd}`);
}

pythonWasInstalled = true;
};
taskRunner.registerMock('azure-pipelines-task-lib/mock-task', tlClone);

// Test manifest contains stable python 3.10.1, so the task should find it
taskRunner.registerMock('typed-rest-client', {
RestClient: class {
get(_url: string) {
return Promise.resolve({
result: JSON.parse(fs.readFileSync(path.join(__dirname, 'data', 'versions-manifest.json')).toString())
});
}
}
});

const lsbPath = '/etc/lsb-release';
taskRunner.registerMock('fs', {
existsSync(filePath) {
if (filePath !== lsbPath) {
throw new Error(`Tried to check the wrong file for existance: ${filePath}`);
}

return true;
},

readFileSync(filePath) {
if (filePath !== lsbPath) {
throw new Error(`Tried to read the wrong file: ${filePath}`);
}

return 'DISTRIB_RELEASE=18.04';
}
});

taskRunner.run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as path from 'path';
import * as fs from 'fs';

import { TaskMockRunner } from 'azure-pipelines-task-lib/mock-run';

const taskPath = path.join(__dirname, '..', 'main.js');
const taskRunner = new TaskMockRunner(taskPath);

taskRunner.setInput('versionSpec', '3.10.x');
taskRunner.setInput('addToPath', 'true');
taskRunner.setInput('architecture', 'x64');

// `getVariable` is not supported by `TaskLibAnswers`
process.env['AGENT_TOOLSDIRECTORY'] = '$(Agent.ToolsDirectory)';
process.env['APPDATA'] = 'testappdata';

let pythonWasInstalled = false;

// Mock azure-pipelines-tool-lib
taskRunner.registerMock('azure-pipelines-tool-lib/tool', {
findLocalTool() {
if (!pythonWasInstalled) {
return null;
}

return path.join('C', 'tools', 'Python', '3.10.1', 'x64');
},
findLocalToolVersions: () => ['2.6.0', '2.7.13'],
downloadTool: () => Promise.resolve('C:/downloaded/python.zip'),
extractZip: () => Promise.resolve('C:/extracted/python'),
extractTar() {
throw new Error('This should never be called');
},
});

taskRunner.registerMock('os', {
platform() {
return 'win32';
},
arch() {
return 'x64';
},
EOL: '\r\n'
});

// Can't mock process, so have to mock taskutil instead
enum Platform {
Windows,
MacOS,
Linux
}

taskRunner.registerMock('./taskutil', {
Platform,
getPlatform() {
return Platform.Windows;
}
});

const tl = require('azure-pipelines-task-lib/mock-task');
const tlClone = Object.assign({}, tl);
tlClone.exec = function(command, args, options) {
if (command !== 'powershell' || args !== './setup.ps1') {
throw new Error(`Invalid command and arguments: ${command} ${args}`);
}

if (options.cwd !== 'C:/extracted/python') {
throw new Error(`Invalid python installer dir path: ${options.cwd}`);
}

pythonWasInstalled = true;
};
taskRunner.registerMock('azure-pipelines-task-lib/mock-task', tlClone);

// Test manifest contains stable python 3.10.1, so the task should find it
taskRunner.registerMock('typed-rest-client', {
RestClient: class {
get(_url: string) {
return Promise.resolve({
result: JSON.parse(fs.readFileSync(path.join(__dirname, 'data', 'versions-manifest.json')).toString())
});
}
}
});

taskRunner.run();
Loading