Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions Tasks/UsePythonVersionV0/archutil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export type Arch = 'x86' | 'x64' | 'arm' | 'arm64';

export function resolveArchitecture(input?: string): Arch {
if (input && ['x86', 'x64', 'arm', 'arm64'].includes(input)) {
return input as Arch;
}

const envArch = (process.env['AGENT_OSARCH'] || '').toLowerCase();
if (envArch.includes('arm64') || envArch.includes('aarch64')) return 'arm64';
if (envArch.startsWith('arm')) return 'arm';
if (envArch.includes('x86')) return 'x86';

const nodeArch = (process.arch || '').toLowerCase();
if (nodeArch === 'arm64' || nodeArch === 'aarch64') return 'arm64';
if (nodeArch.startsWith('arm')) return 'arm';
if (nodeArch === 'ia32' || nodeArch === 'x86') return 'x86';
return 'x64';
}
31 changes: 27 additions & 4 deletions Tasks/UsePythonVersionV0/installpythonversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as rest from 'typed-rest-client';
import * as task from 'azure-pipelines-task-lib/task';
import * as tool from 'azure-pipelines-tool-lib/tool';
import * as osutil from './osutil';

import * as fs from 'fs';
import { TaskParameters, PythonRelease, PythonFileInfo } from './interfaces';

const MANIFEST_URL = 'https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json';
Expand All @@ -22,6 +22,12 @@ export async function installPythonVersion(versionSpec: string, parameters: Task

task.debug(`Extracted python archive to ${pythonInstallerDir}; running installation script`);

const pythonLibDir =
[path.join(pythonInstallerDir, 'python', 'lib'),
path.join(pythonInstallerDir, 'lib'),
path.join(pythonInstallerDir, 'python', 'lib64')]
.find(p => fs.existsSync(p)) || path.join(pythonInstallerDir, 'python', 'lib');

const installerScriptOptions = {
cwd: pythonInstallerDir,
windowsHide: true
Expand All @@ -30,7 +36,17 @@ export async function installPythonVersion(versionSpec: string, parameters: Task
if (os.platform() === 'win32') {
return task.exec('powershell', './setup.ps1', installerScriptOptions);
} else {
return task.exec('bash', './setup.sh', installerScriptOptions);
//return task.exec('bash', './setup.sh', installerScriptOptions);
const linuxOpts = {
...installerScriptOptions,
env: {
...process.env,
LD_LIBRARY_PATH: `${pythonLibDir}:${process.env.LD_LIBRARY_PATH || ''}`
}
};
task.debug(`Using LD_LIBRARY_PATH=${linuxOpts.env!.LD_LIBRARY_PATH}`);
return task.exec('bash', './setup.sh', linuxOpts);

}
}

Expand All @@ -44,7 +60,7 @@ export async function installPythonVersion(versionSpec: string, parameters: Task
*/
async function downloadPythonVersion(versionSpec: string, parameters: TaskParameters): Promise<string> {
const auth = `token ${parameters.githubToken}`;
const additionalHeaders = {};
const additionalHeaders: Record<string, string> = {};
if (parameters.githubToken) {
additionalHeaders['Authorization'] = auth;
} else {
Expand All @@ -71,7 +87,14 @@ async function downloadPythonVersion(versionSpec: string, parameters: TaskParame

task.debug(`Found matching file for system: ${matchingPythonFile.filename}`);

const pythonArchivePath: string = await tool.downloadTool(matchingPythonFile.download_url, matchingPythonFile.filename, null, additionalHeaders);
// const pythonArchivePath: string = await tool.downloadTool(matchingPythonFile.download_url, matchingPythonFile.filename, null, additionalHeaders);

const pythonArchivePath: string = await tool.downloadTool(
matchingPythonFile.download_url,
matchingPythonFile.filename,
undefined,
additionalHeaders
);

task.debug(`Downloaded python archive to ${pythonArchivePath}`);

Expand Down
5 changes: 3 additions & 2 deletions Tasks/UsePythonVersionV0/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"minimumAgentVersion": "2.182.1",
"version": {
"Major": 0,
"Minor": 263,
"Minor": 265,
"Patch": 0
},
"demands": [],
Expand Down Expand Up @@ -78,7 +78,8 @@
"groupName": "advanced",
"options": {
"x86": "x86",
"x64": "x64"
"x64": "x64",
"arm64": "arm64"
}
}
],
Expand Down
5 changes: 3 additions & 2 deletions Tasks/UsePythonVersionV0/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"minimumAgentVersion": "2.182.1",
"version": {
"Major": 0,
"Minor": 263,
"Minor": 265,
"Patch": 0
},
"demands": [],
Expand Down Expand Up @@ -78,7 +78,8 @@
"groupName": "advanced",
"options": {
"x86": "x86",
"x64": "x64"
"x64": "x64",
"arm64": "arm64"
}
}
],
Expand Down
52 changes: 44 additions & 8 deletions Tasks/UsePythonVersionV0/usepythonversion.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as os from 'os';
import * as path from 'path';

import * as fs from 'fs';
import * as semver from 'semver';

import * as task from 'azure-pipelines-task-lib/task';
Expand All @@ -12,6 +12,7 @@ import * as toolUtil from './toolutil';

import { desugarDevVersion, pythonVersionToSemantic, isExactVersion } from './versionspec';
import { TaskParameters } from './interfaces';
import { resolveArchitecture } from './archutil';

// Python has "scripts" or "bin" directories where command-line tools that come with packages are installed.
// This is where pip is, along with anything that pip installs.
Expand Down Expand Up @@ -94,31 +95,50 @@ async function useCpythonVersion(parameters: Readonly<TaskParameters>, platform:
const semanticVersionSpec = pythonVersionToSemantic(desugaredVersionSpec);
task.debug(`Semantic version spec of ${parameters.versionSpec} is ${semanticVersionSpec}`);

const effectiveArch = resolveArchitecture(parameters.architecture);
task.debug(`Effective architecture resolved to: ${effectiveArch}`);

// Throw warning if Python version is 3.5
if (semver.satisfies(semver.coerce(parameters.versionSpec), "3.5.*")) {
task.warning(task.loc('PythonVersionRetirement'));

// if (semver.satisfies(semver.coerce(parameters.versionSpec), "3.5.*")) {
// task.warning(task.loc('PythonVersionRetirement'));
// }

{
const coerced = semver.coerce(parameters.versionSpec);
if (coerced && semver.satisfies(coerced, "3.5.*")) {
task.warning(task.loc('PythonVersionRetirement'));
}
}


if (isExactVersion(semanticVersionSpec)) {
task.warning(task.loc('ExactVersionNotRecommended'));
}

let installDir: string | null = tool.findLocalTool('Python', semanticVersionSpec, parameters.architecture);
//let installDir: string | null = tool.findLocalTool('Python', semanticVersionSpec, parameters.architecture);
let installDir: string | null = tool.findLocalTool('Python', semanticVersionSpec, effectiveArch);
// Python version not found in local cache, try to download and install

if (!installDir) {
task.debug(`Could not find a local python installation matching ${semanticVersionSpec}.`);
if (!parameters.disableDownloadFromRegistry) {
try {
task.debug('Trying to download python from registry.');
await installPythonVersion(semanticVersionSpec, parameters);
installDir = tool.findLocalTool('Python', semanticVersionSpec, parameters.architecture);
// await installPythonVersion(semanticVersionSpec, parameters);
// installDir = tool.findLocalTool('Python', semanticVersionSpec, parameters.architecture);
await installPythonVersion(semanticVersionSpec, { ...parameters, architecture: effectiveArch });
installDir = tool.findLocalTool('Python', semanticVersionSpec, effectiveArch);

if (installDir) {
task.debug(`Successfully installed python from registry to ${installDir}.`);
}
} catch (err) {

} catch (err: unknown) {
task.error(task.loc('DownloadFailed', err.toString()));
throw err;
}

}
}

Expand All @@ -133,17 +153,33 @@ async function useCpythonVersion(parameters: Readonly<TaskParameters>, platform:
.map(s => `${s} (x64)`)
.join(os.EOL);


const arm64Versions = tool.findLocalToolVersions('Python', 'arm64')
.map(s => `${s} (arm64)`)
.join(os.EOL);
throw new Error([
task.loc('VersionNotFound', parameters.versionSpec, parameters.architecture),
task.loc('VersionNotFound', parameters.versionSpec, effectiveArch),
task.loc('ListAvailableVersions', task.getVariable('Agent.ToolsDirectory')),
x86Versions,
x64Versions,
arm64Versions,
task.loc('ToolNotFoundMicrosoftHosted', 'Python', 'https://aka.ms/hosted-agent-software'),
task.loc('ToolNotFoundSelfHosted', 'Python', 'https://go.microsoft.com/fwlink/?linkid=871498')
].join(os.EOL));
}

task.setVariable('pythonLocation', installDir);
//task.setVariable('LD_LIBRARY_PATH', `${path.join(installDir, 'lib')}:${process.env.LD_LIBRARY_PATH || ''}`, false, true);

const libCandidates = [
path.join(installDir, 'lib'),
path.join(installDir, 'python', 'lib'),
path.join(installDir, 'lib64')
];
const libDir = libCandidates.find(p => fs.existsSync(p)) || libCandidates[0];

task.setVariable('LD_LIBRARY_PATH', `${libDir}:${process.env.LD_LIBRARY_PATH || ''}`, false, true);

if (parameters.addToPath) {
toolUtil.prependPathSafe(installDir);
toolUtil.prependPathSafe(binDir(installDir, platform))
Expand Down
Loading