mirror of
https://github.com/actions/setup-python.git
synced 2025-04-20 03:53:30 +00:00
resolved comments, update readme, add e2e tests.
This commit is contained in:
parent
739154f76b
commit
3d613a97df
47
.github/workflows/test-pypy.yml
vendored
Normal file
47
.github/workflows/test-pypy.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
name: Validate PyPy e2e
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
schedule:
|
||||
- cron: 0 0 * * *
|
||||
|
||||
jobs:
|
||||
setup-pypy:
|
||||
name: Setup PyPy ${{ matrix.pypy }} ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-18.04, ubuntu-20.04]
|
||||
pypy:
|
||||
- 'pypy-2.7'
|
||||
- 'pypy-3.6'
|
||||
- 'pypy-3.7'
|
||||
- 'pypy-2.7-v7.3.2'
|
||||
- 'pypy-3.6-v7.3.2'
|
||||
- 'pypy-3.7-v7.3.2'
|
||||
- 'pypy-3.6-v7.3.x'
|
||||
- 'pypy-3.7-v7.x'
|
||||
- 'pypy-3.6-v7.3.3rc1'
|
||||
- 'pypy-3.7-nightly'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: setup-python ${{ matrix.pypy }}
|
||||
uses: ./
|
||||
with:
|
||||
python-version: ${{ matrix.pypy }}
|
||||
|
||||
- name: PyPy and Python version
|
||||
run: python --version
|
||||
|
||||
- name: Run simple code
|
||||
run: python -c 'import math; print(math.factorial(5))'
|
@ -1,4 +1,4 @@
|
||||
name: Validate 'setup-python'
|
||||
name: Validate Python e2e
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@ -18,7 +18,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04]
|
||||
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@ -38,7 +38,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04]
|
||||
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04]
|
||||
python: [3.5.4, 3.6.7, 3.7.5, 3.8.1]
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -68,7 +68,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04]
|
||||
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@ -91,13 +91,13 @@ jobs:
|
||||
- name: Run simple code
|
||||
run: python -c 'import math; print(math.factorial(5))'
|
||||
|
||||
setup-pypy:
|
||||
setup-pypy-legacy-way:
|
||||
name: Setup PyPy ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04]
|
||||
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
57
README.md
57
README.md
@ -17,6 +17,7 @@ This action sets up a Python environment for use in actions by:
|
||||
- Allows for pinning to a specific patch version of Python without the worry of it ever being removed or changed.
|
||||
- Automatic setup and download of Python packages if using a self-hosted runner.
|
||||
- Support for pre-release versions of Python.
|
||||
- Support for installation any version of PyPy on-flight
|
||||
|
||||
# Usage
|
||||
|
||||
@ -40,7 +41,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [ '2.x', '3.x', 'pypy2', 'pypy3' ]
|
||||
python-version: [ '2.x', '3.x', 'pypy-2.7', 'pypy-3.6', 'pypy-3.7' ]
|
||||
name: Python ${{ matrix.python-version }} sample
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -60,7 +61,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
python-version: [2.7, 3.6, 3.7, 3.8, pypy2, pypy3]
|
||||
python-version: [2.7, 3.6, 3.7, 3.8, pypy-2.7, pypy-3.6]
|
||||
exclude:
|
||||
- os: macos-latest
|
||||
python-version: 3.8
|
||||
@ -91,7 +92,6 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- run: python my_script.py
|
||||
|
||||
```
|
||||
|
||||
Download and set up an accurate pre-release version of Python:
|
||||
@ -114,6 +114,27 @@ steps:
|
||||
- run: python my_script.py
|
||||
```
|
||||
|
||||
Download and set up PyPy:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version:
|
||||
- pypy-3.6 # the latest available version of PyPy
|
||||
- pypy-3.7 # the latest available version of PyPy
|
||||
- pypy-3.7-v7.3.3 # Python 3.7 and PyPy 7.3.3
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- run: python my_script.py
|
||||
```
|
||||
More details on PyPy syntax and examples of using preview / nightly versions of PyPy can be found in [Available versions of PyPy](#available-versions-of-pypy) section.
|
||||
|
||||
# Getting started with Python + Actions
|
||||
|
||||
Check out our detailed guide on using [Python with GitHub Actions](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-python-with-github-actions).
|
||||
@ -129,7 +150,21 @@ Check out our detailed guide on using [Python with GitHub Actions](https://help.
|
||||
- If the exact patch version doesn't matter to you, specifying just the major and minor version will get you the latest preinstalled patch version. In the previous example, the version spec `3.8` will use the `3.8.2` Python version found in the cache.
|
||||
- Downloadable Python versions from GitHub Releases ([actions/python-versions](https://github.com/actions/python-versions/releases)).
|
||||
- All available versions are listed in the [version-manifest.json](https://github.com/actions/python-versions/blob/main/versions-manifest.json) file.
|
||||
- If there is a specific version of Python that is not available, you can open an issue here.
|
||||
- If there is a specific version of Python that is not available, you can open an issue here
|
||||
|
||||
# Available versions of PyPy
|
||||
|
||||
`setup-python` is able to configure PyPy from two sources:
|
||||
|
||||
- Preinstalled versions of PyPy in the tools cache on GitHub-hosted runners
|
||||
- For detailed information regarding the available versions of PyPy that are installed see [Supported software](https://docs.github.com/en/actions/reference/specifications-for-github-hosted-runners#supported-software).
|
||||
- For the latest PyPy release, all version of Python are cached.
|
||||
- Cache is updated with 1-2 weeks delay. if you specify PyPy as `pypy-3.6`, the version from cache will be used although a new version is available. If you need to start using the recently released version right after release, you should specify exact PyPy version `pypy-3.6-v7.3.3`.
|
||||
|
||||
- Downloadable PyPy versions from [official PyPy site](https://downloads.python.org/pypy/).
|
||||
- All available versions are listed in the [versions.json](https://downloads.python.org/pypy/versions.json) file.
|
||||
- PyPy < 7.3.3 are not available to install on-flight.
|
||||
- If some versions are not available, you can open an issue in https://foss.heptapod.net/pypy/pypy/
|
||||
|
||||
# Hosted Tool Cache
|
||||
|
||||
@ -156,6 +191,20 @@ You should specify only a major and minor version if you are okay with the most
|
||||
- The patch version that will be preinstalled, will generally be the latest and every time there is a new patch released, the older version that is preinstalled will be replaced.
|
||||
- Using the most recent patch version will result in a very quick setup since no downloads will be required since a locally installed version Python on the runner will be used.
|
||||
|
||||
# Specifying a PyPy version
|
||||
The version of PyPy should be specified in the format `pypy-<python_version>[-v<pypy_version>]`.
|
||||
Parameter `<pypy_version>` is optional and can be skipped. The latest version will be used in this case.
|
||||
|
||||
```
|
||||
pypy-3.6 # the latest available version of PyPy
|
||||
pypy-3.7 # the latest available version of PyPy
|
||||
pypy-2.7 # the latest available version of PyPy
|
||||
pypy-3.7-v7.3.3 # Python 3.7 and PyPy 7.3.3
|
||||
pypy-3.7-v7.x # Python 3.7 and the latest available PyPy 7.x
|
||||
pypy-3.7-v7.3.3rc1 # Python 3.7 and preview version of PyPy
|
||||
pypy-3.7-nightly # Python 3.7 and nightly PyPy
|
||||
```
|
||||
|
||||
# Using `setup-python` with a self hosted runner
|
||||
|
||||
Python distributions are only available for the same [environments](https://github.com/actions/virtual-environments#available-environments) that GitHub Actions hosted environments are available for. If you are using an unsupported version of Ubuntu such as `19.04` or another Linux distribution such as Fedora, `setup-python` will not work. If you have a supported self-hosted runner and you would like to use `setup-python`, there are a few extra things you need to make sure are set up so that new versions of Python can be downloaded and configured on your runner.
|
||||
|
42
dist/index.js
vendored
42
dist/index.js
vendored
@ -1154,7 +1154,8 @@ function findPyPyToolCache(pythonVersion, pypyVersion, architecture) {
|
||||
function parsePyPyVersion(versionSpec) {
|
||||
const versions = versionSpec.split('-').filter(item => !!item);
|
||||
if (versions.length < 2) {
|
||||
throw new Error("Invalid 'version' property for PyPy. PyPy version should be specified as 'pypy-<python-version>'. See readme for more examples.");
|
||||
core.setFailed("Invalid 'version' property for PyPy. PyPy version should be specified as 'pypy-<python-version>'. See README for examples and documentation.");
|
||||
process.exit();
|
||||
}
|
||||
const pythonVersion = versions[1];
|
||||
let pypyVersion;
|
||||
@ -1164,6 +1165,10 @@ function parsePyPyVersion(versionSpec) {
|
||||
else {
|
||||
pypyVersion = 'x';
|
||||
}
|
||||
if (!utils_1.validateVersion(pythonVersion) || !utils_1.validateVersion(pypyVersion)) {
|
||||
core.setFailed("Invalid 'version' property for PyPy. Both Python version and PyPy versions should satisfy SemVer notation. See README for examples and documentation.");
|
||||
process.exit();
|
||||
}
|
||||
return {
|
||||
pypyVersion: pypyVersion,
|
||||
pythonVersion: pythonVersion
|
||||
@ -2316,6 +2321,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = __importStar(__webpack_require__(747));
|
||||
const path = __importStar(__webpack_require__(622));
|
||||
const semver = __importStar(__webpack_require__(876));
|
||||
exports.IS_WINDOWS = process.platform === 'win32';
|
||||
exports.IS_LINUX = process.platform === 'linux';
|
||||
/** create Symlinks for downloaded PyPy
|
||||
@ -2334,6 +2340,14 @@ function createSymlinkInFolder(folderPath, sourceName, targetName, setExecutable
|
||||
}
|
||||
}
|
||||
exports.createSymlinkInFolder = createSymlinkInFolder;
|
||||
function validateVersion(version) {
|
||||
return isNightlyKeyword(version) || Boolean(semver.validRange(version));
|
||||
}
|
||||
exports.validateVersion = validateVersion;
|
||||
function isNightlyKeyword(pypyVersion) {
|
||||
return pypyVersion === 'nightly';
|
||||
}
|
||||
exports.isNightlyKeyword = isNightlyKeyword;
|
||||
|
||||
|
||||
/***/ }),
|
||||
@ -2766,9 +2780,14 @@ function installPyPy(pypyVersion, pythonVersion, architecture) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let downloadDir;
|
||||
const releases = yield getAvailablePyPyVersions();
|
||||
if (!releases || releases.length === 0) {
|
||||
core.setFailed('No release was found in PyPy version.json');
|
||||
process.exit();
|
||||
}
|
||||
const releaseData = findRelease(releases, pythonVersion, pypyVersion, architecture);
|
||||
if (!releaseData || !releaseData.foundAsset) {
|
||||
throw new Error(`PyPy version ${pythonVersion} (${pypyVersion}) with arch ${architecture} not found`);
|
||||
core.setFailed(`PyPy version ${pythonVersion} (${pypyVersion}) with arch ${architecture} not found`);
|
||||
process.exit();
|
||||
}
|
||||
const { foundAsset, resolvedPythonVersion, resolvedPyPyVersion } = releaseData;
|
||||
let downloadUrl = `${foundAsset.download_url}`;
|
||||
@ -2786,7 +2805,7 @@ function installPyPy(pypyVersion, pythonVersion, architecture) {
|
||||
const archiveName = fs.readdirSync(downloadDir)[0];
|
||||
const toolDir = path.join(downloadDir, archiveName);
|
||||
let installDir = toolDir;
|
||||
if (!isNightlyKeyword(resolvedPyPyVersion)) {
|
||||
if (!utils_1.isNightlyKeyword(resolvedPyPyVersion)) {
|
||||
installDir = yield tc.cacheDir(toolDir, 'PyPy', resolvedPythonVersion, architecture);
|
||||
}
|
||||
writeExactPyPyVersionFile(installDir, resolvedPyPyVersion);
|
||||
@ -2803,7 +2822,8 @@ function getAvailablePyPyVersions() {
|
||||
const http = new httpm.HttpClient('tool-cache');
|
||||
const response = yield http.getJson(url);
|
||||
if (!response.result) {
|
||||
throw new Error(`Unable to retrieve the list of available PyPy versions from '${url}'`);
|
||||
core.setFailed(`Unable to retrieve the list of available PyPy versions from '${url}'`);
|
||||
process.exit();
|
||||
}
|
||||
return response.result;
|
||||
});
|
||||
@ -2830,12 +2850,13 @@ function installPip(pythonLocation) {
|
||||
}
|
||||
function findRelease(releases, pythonVersion, pypyVersion, architecture) {
|
||||
const filterReleases = releases.filter(item => {
|
||||
const isPythonVersionSatisfies = semver.satisfies(semver.coerce(item.python_version), pythonVersion);
|
||||
const isPyPyNightly = isNightlyKeyword(pypyVersion) && isNightlyKeyword(item.pypy_version);
|
||||
const isPyPyVersionSatisfies = isPyPyNightly ||
|
||||
const isPythonVersionSatisfied = semver.satisfies(semver.coerce(item.python_version), pythonVersion);
|
||||
const isPyPyNightly = utils_1.isNightlyKeyword(pypyVersion) && utils_1.isNightlyKeyword(item.pypy_version);
|
||||
const isPyPyVersionSatisfied = isPyPyNightly ||
|
||||
semver.satisfies(pypyVersionToSemantic(item.pypy_version), pypyVersion);
|
||||
const isArchExists = item.files.some(file => file.arch === architecture && file.platform === process.platform);
|
||||
return isPythonVersionSatisfies && isPyPyVersionSatisfies && isArchExists;
|
||||
const isArchPresent = item.files &&
|
||||
item.files.some(file => file.arch === architecture && file.platform === process.platform);
|
||||
return isPythonVersionSatisfied && isPyPyVersionSatisfied && isArchPresent;
|
||||
});
|
||||
if (filterReleases.length === 0) {
|
||||
return null;
|
||||
@ -2884,9 +2905,6 @@ function getPyPyBinaryPath(installDir) {
|
||||
return utils_1.IS_WINDOWS ? installDir : _binDir;
|
||||
}
|
||||
exports.getPyPyBinaryPath = getPyPyBinaryPath;
|
||||
function isNightlyKeyword(pypyVersion) {
|
||||
return pypyVersion === 'nightly';
|
||||
}
|
||||
function pypyVersionToSemantic(versionSpec) {
|
||||
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g;
|
||||
return versionSpec.replace(prereleaseVersion, '$1-$2.$3');
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as path from 'path';
|
||||
import * as pypyInstall from './install-pypy';
|
||||
import {IS_WINDOWS} from './utils';
|
||||
import {IS_WINDOWS, validateVersion} from './utils';
|
||||
|
||||
import * as semver from 'semver';
|
||||
import * as core from '@actions/core';
|
||||
@ -93,10 +93,12 @@ function parsePyPyVersion(versionSpec: string): IPyPyVersionSpec {
|
||||
const versions = versionSpec.split('-').filter(item => !!item);
|
||||
|
||||
if (versions.length < 2) {
|
||||
throw new Error(
|
||||
"Invalid 'version' property for PyPy. PyPy version should be specified as 'pypy-<python-version>'. See readme for more examples."
|
||||
core.setFailed(
|
||||
"Invalid 'version' property for PyPy. PyPy version should be specified as 'pypy-<python-version>'. See README for examples and documentation."
|
||||
);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
const pythonVersion = versions[1];
|
||||
let pypyVersion: string;
|
||||
if (versions.length > 2) {
|
||||
@ -105,6 +107,13 @@ function parsePyPyVersion(versionSpec: string): IPyPyVersionSpec {
|
||||
pypyVersion = 'x';
|
||||
}
|
||||
|
||||
if (!validateVersion(pythonVersion) || !validateVersion(pypyVersion)) {
|
||||
core.setFailed(
|
||||
"Invalid 'version' property for PyPy. Both Python version and PyPy versions should satisfy SemVer notation. See README for examples and documentation."
|
||||
);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
return {
|
||||
pypyVersion: pypyVersion,
|
||||
pythonVersion: pythonVersion
|
||||
|
@ -6,7 +6,12 @@ import * as httpm from '@actions/http-client';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import {IS_WINDOWS, IPyPyManifestRelease, createSymlinkInFolder} from './utils';
|
||||
import {
|
||||
IS_WINDOWS,
|
||||
IPyPyManifestRelease,
|
||||
createSymlinkInFolder,
|
||||
isNightlyKeyword
|
||||
} from './utils';
|
||||
|
||||
const PYPY_VERSION_FILE = 'PYPY_VERSION';
|
||||
|
||||
@ -18,6 +23,11 @@ export async function installPyPy(
|
||||
let downloadDir;
|
||||
|
||||
const releases = await getAvailablePyPyVersions();
|
||||
if (!releases || releases.length === 0) {
|
||||
core.setFailed('No release was found in PyPy version.json');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
const releaseData = findRelease(
|
||||
releases,
|
||||
pythonVersion,
|
||||
@ -26,9 +36,10 @@ export async function installPyPy(
|
||||
);
|
||||
|
||||
if (!releaseData || !releaseData.foundAsset) {
|
||||
throw new Error(
|
||||
core.setFailed(
|
||||
`PyPy version ${pythonVersion} (${pypyVersion}) with arch ${architecture} not found`
|
||||
);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
const {foundAsset, resolvedPythonVersion, resolvedPyPyVersion} = releaseData;
|
||||
@ -74,9 +85,10 @@ async function getAvailablePyPyVersions() {
|
||||
|
||||
const response = await http.getJson<IPyPyManifestRelease[]>(url);
|
||||
if (!response.result) {
|
||||
throw new Error(
|
||||
core.setFailed(
|
||||
`Unable to retrieve the list of available PyPy versions from '${url}'`
|
||||
);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
return response.result;
|
||||
@ -124,19 +136,21 @@ function findRelease(
|
||||
architecture: string
|
||||
) {
|
||||
const filterReleases = releases.filter(item => {
|
||||
const isPythonVersionSatisfies = semver.satisfies(
|
||||
const isPythonVersionSatisfied = semver.satisfies(
|
||||
semver.coerce(item.python_version)!,
|
||||
pythonVersion
|
||||
);
|
||||
const isPyPyNightly =
|
||||
isNightlyKeyword(pypyVersion) && isNightlyKeyword(item.pypy_version);
|
||||
const isPyPyVersionSatisfies =
|
||||
const isPyPyVersionSatisfied =
|
||||
isPyPyNightly ||
|
||||
semver.satisfies(pypyVersionToSemantic(item.pypy_version), pypyVersion);
|
||||
const isArchExists = item.files.some(
|
||||
const isArchPresent =
|
||||
item.files &&
|
||||
item.files.some(
|
||||
file => file.arch === architecture && file.platform === process.platform
|
||||
);
|
||||
return isPythonVersionSatisfies && isPyPyVersionSatisfies && isArchExists;
|
||||
return isPythonVersionSatisfied && isPyPyVersionSatisfied && isArchPresent;
|
||||
});
|
||||
|
||||
if (filterReleases.length === 0) {
|
||||
@ -206,10 +220,6 @@ export function getPyPyBinaryPath(installDir: string) {
|
||||
return IS_WINDOWS ? installDir : _binDir;
|
||||
}
|
||||
|
||||
function isNightlyKeyword(pypyVersion: string) {
|
||||
return pypyVersion === 'nightly';
|
||||
}
|
||||
|
||||
export function pypyVersionToSemantic(versionSpec: string) {
|
||||
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g;
|
||||
return versionSpec.replace(prereleaseVersion, '$1-$2.$3');
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
|
||||
export const IS_WINDOWS = process.platform === 'win32';
|
||||
export const IS_LINUX = process.platform === 'linux';
|
||||
@ -40,3 +41,11 @@ export function createSymlinkInFolder(
|
||||
fs.chmodSync(targetPath, '755');
|
||||
}
|
||||
}
|
||||
|
||||
export function validateVersion(version: string) {
|
||||
return isNightlyKeyword(version) || Boolean(semver.validRange(version));
|
||||
}
|
||||
|
||||
export function isNightlyKeyword(pypyVersion: string) {
|
||||
return pypyVersion === 'nightly';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user