Skip to content

Commit 5ccb29d

Browse files
Install multiple python versions (#567)
1 parent c3e0339 commit 5ccb29d

File tree

5 files changed

+190
-57
lines changed

5 files changed

+190
-57
lines changed

.github/workflows/test-pypy.yml

+42
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,46 @@ jobs:
124124
EXECUTABLE=${EXECUTABLE/-/} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name
125125
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
126126
${EXECUTABLE} --version
127+
shell: bash
128+
129+
setup-pypy-multiple-versions:
130+
runs-on: ${{ matrix.os }}
131+
strategy:
132+
fail-fast: false
133+
matrix:
134+
os: [ubuntu-latest, windows-latest, macos-latest]
135+
steps:
136+
- uses: actions/checkout@v3
137+
- name: Setup PyPy and check latest
138+
uses: ./
139+
with:
140+
python-version: |
141+
pypy-3.7-v7.3.x
142+
pypy3.8
143+
check-latest: true
144+
- name: PyPy and Python version
145+
run: python --version
146+
147+
- name: Run simple code
148+
run: python -c 'import math; print(math.factorial(5))'
149+
150+
- name: Assert PyPy is running
151+
run: |
152+
import platform
153+
assert platform.python_implementation().lower() == "pypy"
154+
shell: python
155+
156+
- name: Assert expected binaries (or symlinks) are present
157+
run: |
158+
EXECUTABLE="pypy-3.7-v7.3.x"
159+
EXECUTABLE=${EXECUTABLE/-/} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name
160+
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
161+
${EXECUTABLE} --version
162+
shell: bash
163+
- name: Assert expected binaries (or symlinks) are present
164+
run: |
165+
EXECUTABLE='pypy3.8'
166+
EXECUTABLE=${EXECUTABLE/pypy-/pypy} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name
167+
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
168+
${EXECUTABLE} --version
127169
shell: bash

.github/workflows/test-python.yml

+29-2
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,35 @@ jobs:
191191
- name: Validate version
192192
run: |
193193
$pythonVersion = (python --version)
194-
if ("$pythonVersion" -NotMatch "${{ matrix.python }}"){
195-
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
194+
if ("$pythonVersion" -NotMatch "${{ matrix.python-version }}"){
195+
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python-version }}"
196+
exit 1
197+
}
198+
$pythonVersion
199+
shell: pwsh
200+
201+
setup-python-multiple-python-versions:
202+
runs-on: ${{ matrix.os }}
203+
strategy:
204+
fail-fast: false
205+
matrix:
206+
os: [ubuntu-latest, windows-latest, macos-latest]
207+
steps:
208+
- uses: actions/checkout@v3
209+
- name: Setup Python and check latest
210+
uses: ./
211+
with:
212+
python-version: |
213+
3.7
214+
3.8
215+
3.9
216+
3.10
217+
check-latest: true
218+
- name: Validate version
219+
run: |
220+
$pythonVersion = (python --version)
221+
if ("$pythonVersion" -NotMatch "3.10"){
222+
Write-Host "The current version is $pythonVersion; expected version is 3.10"
196223
exit 1
197224
}
198225
$pythonVersion

dist/setup/index.js

+25-21
Original file line numberDiff line numberDiff line change
@@ -66867,31 +66867,31 @@ function cacheDependencies(cache, pythonVersion) {
6686766867
});
6686866868
}
6686966869
function resolveVersionInput() {
66870-
let version = core.getInput('python-version');
66870+
let versions = core.getMultilineInput('python-version');
6687166871
let versionFile = core.getInput('python-version-file');
66872-
if (version && versionFile) {
66872+
if (versions.length && versionFile) {
6687366873
core.warning('Both python-version and python-version-file inputs are specified, only python-version will be used.');
6687466874
}
66875-
if (version) {
66876-
return version;
66875+
if (versions.length) {
66876+
return versions;
6687766877
}
6687866878
if (versionFile) {
6687966879
if (!fs_1.default.existsSync(versionFile)) {
6688066880
throw new Error(`The specified python version file at: ${versionFile} doesn't exist.`);
6688166881
}
66882-
version = fs_1.default.readFileSync(versionFile, 'utf8');
66882+
const version = fs_1.default.readFileSync(versionFile, 'utf8');
6688366883
core.info(`Resolved ${versionFile} as ${version}`);
66884-
return version;
66884+
return [version];
6688566885
}
6688666886
utils_1.logWarning("Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find '.python-version' file.");
6688766887
versionFile = '.python-version';
6688866888
if (fs_1.default.existsSync(versionFile)) {
66889-
version = fs_1.default.readFileSync(versionFile, 'utf8');
66889+
const version = fs_1.default.readFileSync(versionFile, 'utf8');
6689066890
core.info(`Resolved ${versionFile} as ${version}`);
66891-
return version;
66891+
return [version];
6689266892
}
6689366893
utils_1.logWarning(`${versionFile} doesn't exist.`);
66894-
return version;
66894+
return versions;
6689566895
}
6689666896
function run() {
6689766897
var _a;
@@ -66904,22 +66904,26 @@ function run() {
6690466904
}
6690566905
core.debug(`Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}`);
6690666906
try {
66907-
const version = resolveVersionInput();
66907+
const versions = resolveVersionInput();
6690866908
const checkLatest = core.getBooleanInput('check-latest');
66909-
if (version) {
66910-
let pythonVersion;
66909+
if (versions.length) {
66910+
let pythonVersion = '';
6691166911
const arch = core.getInput('architecture') || os.arch();
6691266912
const updateEnvironment = core.getBooleanInput('update-environment');
66913-
if (isPyPyVersion(version)) {
66914-
const installed = yield finderPyPy.findPyPyVersion(version, arch, updateEnvironment, checkLatest);
66915-
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
66916-
core.info(`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`);
66917-
}
66918-
else {
66919-
const installed = yield finder.useCpythonVersion(version, arch, updateEnvironment, checkLatest);
66920-
pythonVersion = installed.version;
66921-
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
66913+
core.startGroup('Installed versions');
66914+
for (const version of versions) {
66915+
if (isPyPyVersion(version)) {
66916+
const installed = yield finderPyPy.findPyPyVersion(version, arch, updateEnvironment, checkLatest);
66917+
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
66918+
core.info(`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`);
66919+
}
66920+
else {
66921+
const installed = yield finder.useCpythonVersion(version, arch, updateEnvironment, checkLatest);
66922+
pythonVersion = installed.version;
66923+
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
66924+
}
6692266925
}
66926+
core.endGroup();
6692366927
const cache = core.getInput('cache');
6692466928
if (cache && utils_1.isCacheFeatureAvailable()) {
6692566929
yield cacheDependencies(cache, pythonVersion);

docs/advanced-usage.md

+57
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
- [Using the python-version input](advanced-usage.md#using-the-python-version-input)
33
- [Specifying a Python version](advanced-usage.md#specifying-a-python-version)
44
- [Specifying a PyPy version](advanced-usage.md#specifying-a-pypy-version)
5+
- [Specifying multiple Python and PyPy versions](advanced-usage.md#specifying-multiple-python/pypy-version)
56
- [Matrix Testing](advanced-usage.md#matrix-testing)
67
- [Using the python-version-file input](advanced-usage.md#using-the-python-version-file-input)
78
- [Check latest version](advanced-usage.md#check-latest-version)
@@ -132,6 +133,62 @@ jobs:
132133
```
133134
More details on PyPy syntax can be found in the [Available versions of PyPy](#pypy) section.
134135

136+
### Specifying multiple Python/PyPy version
137+
The python-version input can get multiple python/pypy versions. The last specified version will be used as a default one.
138+
139+
Download and set up multiple Python versions:
140+
141+
```yaml
142+
jobs:
143+
build:
144+
runs-on: ubuntu-latest
145+
steps:
146+
- uses: actions/checkout@v3
147+
- uses: actions/setup-python@v4
148+
with:
149+
python-version: |
150+
3.8
151+
3.9
152+
3.10
153+
- run: python my_script.py
154+
```
155+
156+
Download and set up multiple PyPy versions:
157+
158+
```yaml
159+
jobs:
160+
build:
161+
runs-on: ubuntu-latest
162+
steps:
163+
- uses: actions/checkout@v3
164+
- uses: actions/setup-python@v4
165+
with:
166+
python-version: |
167+
pypy-3.7-v7.3.x
168+
pypy3.9-nightly
169+
pypy3.8
170+
- run: python my_script.py
171+
```
172+
173+
Download and set up multiple Python/PyPy versions:
174+
175+
```yaml
176+
jobs:
177+
build:
178+
runs-on: ubuntu-latest
179+
steps:
180+
- uses: actions/checkout@v3
181+
- uses: actions/setup-python@v4
182+
with:
183+
python-version: |
184+
3.8
185+
3.9
186+
pypy3.9-nightly
187+
pypy3.8
188+
3.10
189+
- run: python my_script.py
190+
```
191+
135192
### Matrix Testing
136193
137194
Using `setup-python` it's possible to use [matrix syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix) to install several versions of Python or PyPy:

src/setup-python.ts

+37-34
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,18 @@ async function cacheDependencies(cache: string, pythonVersion: string) {
2222
await cacheDistributor.restoreCache();
2323
}
2424

25-
function resolveVersionInput(): string {
26-
let version = core.getInput('python-version');
25+
function resolveVersionInput() {
26+
let versions = core.getMultilineInput('python-version');
2727
let versionFile = core.getInput('python-version-file');
2828

29-
if (version && versionFile) {
29+
if (versions.length && versionFile) {
3030
core.warning(
3131
'Both python-version and python-version-file inputs are specified, only python-version will be used.'
3232
);
3333
}
3434

35-
if (version) {
36-
return version;
35+
if (versions.length) {
36+
return versions;
3737
}
3838

3939
if (versionFile) {
@@ -42,24 +42,24 @@ function resolveVersionInput(): string {
4242
`The specified python version file at: ${versionFile} doesn't exist.`
4343
);
4444
}
45-
version = fs.readFileSync(versionFile, 'utf8');
45+
const version = fs.readFileSync(versionFile, 'utf8');
4646
core.info(`Resolved ${versionFile} as ${version}`);
47-
return version;
47+
return [version];
4848
}
4949

5050
logWarning(
5151
"Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find '.python-version' file."
5252
);
5353
versionFile = '.python-version';
5454
if (fs.existsSync(versionFile)) {
55-
version = fs.readFileSync(versionFile, 'utf8');
55+
const version = fs.readFileSync(versionFile, 'utf8');
5656
core.info(`Resolved ${versionFile} as ${version}`);
57-
return version;
57+
return [version];
5858
}
5959

6060
logWarning(`${versionFile} doesn't exist.`);
6161

62-
return version;
62+
return versions;
6363
}
6464

6565
async function run() {
@@ -75,35 +75,38 @@ async function run() {
7575
`Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}`
7676
);
7777
try {
78-
const version = resolveVersionInput();
78+
const versions = resolveVersionInput();
7979
const checkLatest = core.getBooleanInput('check-latest');
8080

81-
if (version) {
82-
let pythonVersion: string;
81+
if (versions.length) {
82+
let pythonVersion = '';
8383
const arch: string = core.getInput('architecture') || os.arch();
8484
const updateEnvironment = core.getBooleanInput('update-environment');
85-
if (isPyPyVersion(version)) {
86-
const installed = await finderPyPy.findPyPyVersion(
87-
version,
88-
arch,
89-
updateEnvironment,
90-
checkLatest
91-
);
92-
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
93-
core.info(
94-
`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`
95-
);
96-
} else {
97-
const installed = await finder.useCpythonVersion(
98-
version,
99-
arch,
100-
updateEnvironment,
101-
checkLatest
102-
);
103-
pythonVersion = installed.version;
104-
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
85+
core.startGroup('Installed versions');
86+
for (const version of versions) {
87+
if (isPyPyVersion(version)) {
88+
const installed = await finderPyPy.findPyPyVersion(
89+
version,
90+
arch,
91+
updateEnvironment,
92+
checkLatest
93+
);
94+
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
95+
core.info(
96+
`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`
97+
);
98+
} else {
99+
const installed = await finder.useCpythonVersion(
100+
version,
101+
arch,
102+
updateEnvironment,
103+
checkLatest
104+
);
105+
pythonVersion = installed.version;
106+
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
107+
}
105108
}
106-
109+
core.endGroup();
107110
const cache = core.getInput('cache');
108111
if (cache && isCacheFeatureAvailable()) {
109112
await cacheDependencies(cache, pythonVersion);

0 commit comments

Comments
 (0)