mirror of
https://github.com/actions/setup-python.git
synced 2024-12-22 05:32:09 -05:00
parent
3f824b7ca6
commit
0d5da6a89a
10 changed files with 2645 additions and 55 deletions
21
.github/workflows/e2e-tests.yml
vendored
21
.github/workflows/e2e-tests.yml
vendored
|
@ -79,3 +79,24 @@ jobs:
|
|||
run: python __tests__/verify-python.py 3.10
|
||||
- name: Run python-path sample 3.10
|
||||
run: pipx run --python '${{ steps.cp310.outputs.python-path }}' nox --version
|
||||
|
||||
- name: Run with setup-python ==3.8
|
||||
uses: ./
|
||||
with:
|
||||
python-version: '==3.8'
|
||||
- name: Verify ==3.8
|
||||
run: python __tests__/verify-python.py 3.8
|
||||
|
||||
- name: Run with setup-python <3.11
|
||||
uses: ./
|
||||
with:
|
||||
python-version: '<3.11'
|
||||
- name: Verify <3.11
|
||||
run: python __tests__/verify-python.py 3.10
|
||||
|
||||
- name: Run with setup-python >3.8
|
||||
uses: ./
|
||||
with:
|
||||
python-version: '>3.8'
|
||||
- name: Verify >3.8
|
||||
run: python __tests__/verify-python.py 3.11
|
||||
|
|
147
.github/workflows/test-python.yml
vendored
147
.github/workflows/test-python.yml
vendored
|
@ -86,7 +86,152 @@ jobs:
|
|||
id: setup-python
|
||||
uses: ./
|
||||
with:
|
||||
python-version-file: '.python-version'
|
||||
python-version-file: .python-version
|
||||
|
||||
- name: Check python-path
|
||||
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
||||
shell: bash
|
||||
|
||||
- name: Validate version
|
||||
run: |
|
||||
$pythonVersion = (python --version)
|
||||
if ("Python ${{ matrix.python }}" -ne "$pythonVersion"){
|
||||
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
|
||||
exit 1
|
||||
}
|
||||
$pythonVersion
|
||||
shell: pwsh
|
||||
|
||||
- name: Run simple code
|
||||
run: python -c 'import math; print(math.factorial(5))'
|
||||
|
||||
setup-versions-from-file-without-parameter:
|
||||
name: Setup ${{ matrix.python }} ${{ matrix.os }} version file without parameter
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-20.04, ubuntu-22.04]
|
||||
python: [3.5.4, 3.6.7, 3.7.5, 3.8.15, 3.9.13]
|
||||
exclude:
|
||||
- os: ubuntu-22.04
|
||||
python: 3.5.4
|
||||
- os: ubuntu-22.04
|
||||
python: 3.6.7
|
||||
- os: ubuntu-22.04
|
||||
python: 3.7.5
|
||||
- os: windows-latest
|
||||
python: 3.8.15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: build-version-file ${{ matrix.python }}
|
||||
run: echo ${{ matrix.python }} > .python-version
|
||||
|
||||
- name: setup-python ${{ matrix.python }}
|
||||
id: setup-python
|
||||
uses: ./
|
||||
|
||||
- name: Check python-path
|
||||
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
||||
shell: bash
|
||||
|
||||
- name: Validate version
|
||||
run: |
|
||||
$pythonVersion = (python --version)
|
||||
if ("Python ${{ matrix.python }}" -ne "$pythonVersion"){
|
||||
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
|
||||
exit 1
|
||||
}
|
||||
$pythonVersion
|
||||
shell: pwsh
|
||||
|
||||
- name: Run simple code
|
||||
run: python -c 'import math; print(math.factorial(5))'
|
||||
|
||||
setup-versions-from-standard-pyproject-file:
|
||||
name: Setup ${{ matrix.python }} ${{ matrix.os }} standard pyproject file
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-20.04, ubuntu-22.04]
|
||||
python: [3.5.4, 3.6.7, 3.7.5, 3.8.15, 3.9.13]
|
||||
exclude:
|
||||
- os: ubuntu-22.04
|
||||
python: 3.5.4
|
||||
- os: ubuntu-22.04
|
||||
python: 3.6.7
|
||||
- os: ubuntu-22.04
|
||||
python: 3.7.5
|
||||
- os: windows-latest
|
||||
python: 3.8.15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: build-version-file ${{ matrix.python }}
|
||||
run: |
|
||||
echo '[project]
|
||||
requires-python = "${{ matrix.python }}"
|
||||
' > pyproject.toml
|
||||
|
||||
- name: setup-python ${{ matrix.python }}
|
||||
id: setup-python
|
||||
uses: ./
|
||||
with:
|
||||
python-version-file: pyproject.toml
|
||||
|
||||
- name: Check python-path
|
||||
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
||||
shell: bash
|
||||
|
||||
- name: Validate version
|
||||
run: |
|
||||
$pythonVersion = (python --version)
|
||||
if ("Python ${{ matrix.python }}" -ne "$pythonVersion"){
|
||||
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
|
||||
exit 1
|
||||
}
|
||||
$pythonVersion
|
||||
shell: pwsh
|
||||
|
||||
- name: Run simple code
|
||||
run: python -c 'import math; print(math.factorial(5))'
|
||||
|
||||
setup-versions-from-poetry-pyproject-file:
|
||||
name: Setup ${{ matrix.python }} ${{ matrix.os }} poetry pyproject file
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-20.04, ubuntu-22.04]
|
||||
python: [3.5.4, 3.6.7, 3.7.5, 3.8.15, 3.9.13]
|
||||
exclude:
|
||||
- os: ubuntu-22.04
|
||||
python: 3.5.4
|
||||
- os: ubuntu-22.04
|
||||
python: 3.6.7
|
||||
- os: ubuntu-22.04
|
||||
python: 3.7.5
|
||||
- os: windows-latest
|
||||
python: 3.8.15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: build-version-file ${{ matrix.python }}
|
||||
run: |
|
||||
echo '[tool.poetry.dependencies]
|
||||
python = "${{ matrix.python }}"
|
||||
' > pyproject.toml
|
||||
|
||||
- name: setup-python ${{ matrix.python }}
|
||||
id: setup-python
|
||||
uses: ./
|
||||
with:
|
||||
python-version-file: pyproject.toml
|
||||
|
||||
- name: Check python-path
|
||||
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
||||
|
|
BIN
.licenses/npm/@iarna/toml.dep.yml
generated
Normal file
BIN
.licenses/npm/@iarna/toml.dep.yml
generated
Normal file
Binary file not shown.
|
@ -1,9 +1,17 @@
|
|||
import * as cache from '@actions/cache';
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import {
|
||||
validateVersion,
|
||||
validatePythonVersionFormatForPyPy,
|
||||
isCacheFeatureAvailable
|
||||
isCacheFeatureAvailable,
|
||||
getVersionInputFromFile,
|
||||
getVersionInputFromPlainFile,
|
||||
getVersionInputFromTomlFile
|
||||
} from '../src/utils';
|
||||
|
||||
jest.mock('@actions/cache');
|
||||
|
@ -73,3 +81,58 @@ describe('isCacheFeatureAvailable', () => {
|
|||
expect(isCacheFeatureAvailable()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
const tempDir = path.join(
|
||||
__dirname,
|
||||
'runner',
|
||||
path.join(Math.random().toString(36).substring(7)),
|
||||
'temp'
|
||||
);
|
||||
|
||||
describe('Version from file test', () => {
|
||||
it.each([getVersionInputFromPlainFile, getVersionInputFromFile])(
|
||||
'Version from plain file test',
|
||||
async _fn => {
|
||||
await io.mkdirP(tempDir);
|
||||
const pythonVersionFileName = 'python-version.file';
|
||||
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||
const pythonVersionFileContent = '3.7';
|
||||
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
||||
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersionFileContent]);
|
||||
}
|
||||
);
|
||||
it.each([getVersionInputFromTomlFile, getVersionInputFromFile])(
|
||||
'Version from standard pyproject.toml test',
|
||||
async _fn => {
|
||||
await io.mkdirP(tempDir);
|
||||
const pythonVersionFileName = 'pyproject.toml';
|
||||
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||
const pythonVersion = '>=3.7';
|
||||
const pythonVersionFileContent = `[project]\nrequires-python = "${pythonVersion}"`;
|
||||
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
||||
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
|
||||
}
|
||||
);
|
||||
it.each([getVersionInputFromTomlFile, getVersionInputFromFile])(
|
||||
'Version from poetry pyproject.toml test',
|
||||
async _fn => {
|
||||
await io.mkdirP(tempDir);
|
||||
const pythonVersionFileName = 'pyproject.toml';
|
||||
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||
const pythonVersion = '>=3.7';
|
||||
const pythonVersionFileContent = `[tool.poetry.dependencies]\npython = "${pythonVersion}"`;
|
||||
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
||||
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
|
||||
}
|
||||
);
|
||||
it.each([getVersionInputFromTomlFile, getVersionInputFromFile])(
|
||||
'Version undefined',
|
||||
async _fn => {
|
||||
await io.mkdirP(tempDir);
|
||||
const pythonVersionFileName = 'pyproject.toml';
|
||||
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||
fs.writeFileSync(pythonVersionFilePath, ``);
|
||||
expect(_fn(pythonVersionFilePath)).toEqual([]);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
2294
dist/setup/index.js
vendored
2294
dist/setup/index.js
vendored
File diff suppressed because it is too large
Load diff
|
@ -78,6 +78,17 @@ steps:
|
|||
|
||||
You can also use several types of ranges that are specified in [semver](https://github.com/npm/node-semver#ranges), for instance:
|
||||
|
||||
- **[ranges](https://github.com/npm/node-semver#ranges)** to download and set up the latest available version of Python satisfying a range:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '>=3.9 <3.10'
|
||||
- run: python my_script.py
|
||||
```
|
||||
|
||||
- **[hyphen ranges](https://github.com/npm/node-semver#hyphen-ranges-xyz---abc)** to download and set up the latest available version of Python (includes both pre-release and stable versions):
|
||||
|
||||
```yaml
|
||||
|
@ -251,6 +262,16 @@ steps:
|
|||
python-version-file: '.python-version' # Read python version from a file .python-version
|
||||
- run: python my_script.py
|
||||
```
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version-file: 'pyproject.toml' # Read python version from a file pyproject.toml
|
||||
- run: python my_script.py
|
||||
```
|
||||
|
||||
## Check latest version
|
||||
|
||||
The `check-latest` flag defaults to `false`. Use the default or set `check-latest` to `false` if you prefer stability and if you want to ensure a specific `Python or PyPy` version is always used.
|
||||
|
|
11
package-lock.json
generated
11
package-lock.json
generated
|
@ -16,6 +16,7 @@
|
|||
"@actions/http-client": "^1.0.11",
|
||||
"@actions/io": "^1.0.2",
|
||||
"@actions/tool-cache": "^1.5.5",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"semver": "^7.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -2048,6 +2049,11 @@
|
|||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@iarna/toml": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz",
|
||||
"integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="
|
||||
},
|
||||
"node_modules/@istanbuljs/load-nyc-config": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
||||
|
@ -8519,6 +8525,11 @@
|
|||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"dev": true
|
||||
},
|
||||
"@iarna/toml": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz",
|
||||
"integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="
|
||||
},
|
||||
"@istanbuljs/load-nyc-config": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"@actions/http-client": "^1.0.11",
|
||||
"@actions/io": "^1.0.2",
|
||||
"@actions/tool-cache": "^1.5.5",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"semver": "^7.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -5,7 +5,13 @@ import * as path from 'path';
|
|||
import * as os from 'os';
|
||||
import fs from 'fs';
|
||||
import {getCacheDistributor} from './cache-distributions/cache-factory';
|
||||
import {isCacheFeatureAvailable, logWarning, IS_MAC} from './utils';
|
||||
import {
|
||||
isCacheFeatureAvailable,
|
||||
logWarning,
|
||||
IS_MAC,
|
||||
getVersionInputFromFile,
|
||||
getVersionInputFromPlainFile
|
||||
} from './utils';
|
||||
|
||||
function isPyPyVersion(versionSpec: string) {
|
||||
return versionSpec.startsWith('pypy');
|
||||
|
@ -22,43 +28,46 @@ async function cacheDependencies(cache: string, pythonVersion: string) {
|
|||
await cacheDistributor.restoreCache();
|
||||
}
|
||||
|
||||
function resolveVersionInput() {
|
||||
const versions = core.getMultilineInput('python-version');
|
||||
let versionFile = core.getInput('python-version-file');
|
||||
|
||||
if (versions.length && versionFile) {
|
||||
core.warning(
|
||||
'Both python-version and python-version-file inputs are specified, only python-version will be used.'
|
||||
function resolveVersionInputFromDefaultFile(): string[] {
|
||||
const couples: [string, (versionFile: string) => string[]][] = [
|
||||
['.python-version', getVersionInputFromPlainFile]
|
||||
];
|
||||
for (const [versionFile, _fn] of couples) {
|
||||
logWarning(
|
||||
`Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find '${versionFile}' file.`
|
||||
);
|
||||
if (fs.existsSync(versionFile)) {
|
||||
return _fn(versionFile);
|
||||
} else {
|
||||
logWarning(`${versionFile} doesn't exist.`);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function resolveVersionInput() {
|
||||
let versions = core.getMultilineInput('python-version');
|
||||
const versionFile = core.getInput('python-version-file');
|
||||
|
||||
if (versions.length) {
|
||||
return versions;
|
||||
}
|
||||
|
||||
if (versionFile) {
|
||||
if (!fs.existsSync(versionFile)) {
|
||||
throw new Error(
|
||||
`The specified python version file at: ${versionFile} doesn't exist.`
|
||||
if (versionFile) {
|
||||
core.warning(
|
||||
'Both python-version and python-version-file inputs are specified, only python-version will be used.'
|
||||
);
|
||||
}
|
||||
const version = fs.readFileSync(versionFile, 'utf8');
|
||||
core.info(`Resolved ${versionFile} as ${version}`);
|
||||
return [version];
|
||||
} else {
|
||||
if (versionFile) {
|
||||
if (!fs.existsSync(versionFile)) {
|
||||
throw new Error(
|
||||
`The specified python version file at: ${versionFile} doesn't exist.`
|
||||
);
|
||||
}
|
||||
versions = getVersionInputFromFile(versionFile);
|
||||
} else {
|
||||
versions = resolveVersionInputFromDefaultFile();
|
||||
}
|
||||
}
|
||||
|
||||
logWarning(
|
||||
"Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find '.python-version' file."
|
||||
);
|
||||
versionFile = '.python-version';
|
||||
if (fs.existsSync(versionFile)) {
|
||||
const version = fs.readFileSync(versionFile, 'utf8');
|
||||
core.info(`Resolved ${versionFile} as ${version}`);
|
||||
return [version];
|
||||
}
|
||||
|
||||
logWarning(`${versionFile} doesn't exist.`);
|
||||
|
||||
return versions;
|
||||
}
|
||||
|
||||
|
|
71
src/utils.ts
71
src/utils.ts
|
@ -4,6 +4,7 @@ import * as core from '@actions/core';
|
|||
import fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
import * as toml from '@iarna/toml';
|
||||
import * as exec from '@actions/exec';
|
||||
|
||||
export const IS_WINDOWS = process.platform === 'win32';
|
||||
|
@ -181,3 +182,73 @@ export async function getOSInfo() {
|
|||
return osInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a value from an object by following the keys path provided.
|
||||
* If the value is present, it is returned. Otherwise undefined is returned.
|
||||
*/
|
||||
function extractValue(obj: any, keys: string[]): string | undefined {
|
||||
if (keys.length > 0) {
|
||||
const value = obj[keys[0]];
|
||||
if (keys.length > 1 && value !== undefined) {
|
||||
return extractValue(value, keys.slice(1));
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Python version extracted from the TOML file.
|
||||
* If the `project` key is present at the root level, the version is assumed to
|
||||
* be specified according to PEP 621 in `project.requires-python`.
|
||||
* Otherwise, if the `tool` key is present at the root level, the version is
|
||||
* assumed to be specified using poetry under `tool.poetry.dependencies.python`.
|
||||
* If none is present, returns an empty list.
|
||||
*/
|
||||
export function getVersionInputFromTomlFile(versionFile: string): string[] {
|
||||
core.debug(`Trying to resolve version form ${versionFile}`);
|
||||
|
||||
const pyprojectFile = fs.readFileSync(versionFile, 'utf8');
|
||||
const pyprojectConfig = toml.parse(pyprojectFile);
|
||||
let keys = [];
|
||||
|
||||
if ('project' in pyprojectConfig) {
|
||||
// standard project metadata (PEP 621)
|
||||
keys = ['project', 'requires-python'];
|
||||
} else {
|
||||
// python poetry
|
||||
keys = ['tool', 'poetry', 'dependencies', 'python'];
|
||||
}
|
||||
const versions = [];
|
||||
const version = extractValue(pyprojectConfig, keys);
|
||||
if (version !== undefined) {
|
||||
versions.push(version);
|
||||
}
|
||||
|
||||
core.info(`Extracted ${versions} from ${versionFile}`);
|
||||
return Array.from(versions, version => version.split(',').join(' '));
|
||||
}
|
||||
|
||||
/**
|
||||
* Python version extracted from a plain text file.
|
||||
*/
|
||||
export function getVersionInputFromPlainFile(versionFile: string): string[] {
|
||||
core.debug(`Trying to resolve version form ${versionFile}`);
|
||||
const version = fs.readFileSync(versionFile, 'utf8');
|
||||
core.info(`Resolved ${versionFile} as ${version}`);
|
||||
return [version];
|
||||
}
|
||||
|
||||
/**
|
||||
* Python version extracted from a plain or TOML file.
|
||||
*/
|
||||
export function getVersionInputFromFile(versionFile: string): string[] {
|
||||
if (versionFile.endsWith('.toml')) {
|
||||
return getVersionInputFromTomlFile(versionFile);
|
||||
} else {
|
||||
return getVersionInputFromPlainFile(versionFile);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue