Skip to content

Commit 2f06e9d

Browse files
Add check-latest functionality (actions#406)
1 parent 49a521f commit 2f06e9d

14 files changed

+440
-82
lines changed

.github/workflows/test-pypy.yml

+33
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,36 @@ jobs:
9191

9292
- name: Run simple code
9393
run: ${{ steps.setup-python.outputs.python-path }} -c 'import math; print(math.factorial(5))'
94+
95+
check-latest:
96+
runs-on: ${{ matrix.os }}
97+
strategy:
98+
fail-fast: false
99+
matrix:
100+
os: [ubuntu-latest, windows-latest, macos-latest]
101+
steps:
102+
- uses: actions/checkout@v3
103+
- name: Setup PyPy and check latest
104+
uses: ./
105+
with:
106+
python-version: 'pypy-3.7-v7.3.x'
107+
check-latest: true
108+
- name: PyPy and Python version
109+
run: python --version
110+
111+
- name: Run simple code
112+
run: python -c 'import math; print(math.factorial(5))'
113+
114+
- name: Assert PyPy is running
115+
run: |
116+
import platform
117+
assert platform.python_implementation().lower() == "pypy"
118+
shell: python
119+
120+
- name: Assert expected binaries (or symlinks) are present
121+
run: |
122+
EXECUTABLE="pypy-3.7-v7.3.x"
123+
EXECUTABLE=${EXECUTABLE/-/} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name
124+
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
125+
${EXECUTABLE} --version
126+
shell: bash

.github/workflows/test-python.yml

+24
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,27 @@ jobs:
172172

173173
- name: Run simple code
174174
run: ${{ steps.setup-python.outputs.python-path }} -c 'import math; print(math.factorial(5))'
175+
176+
check-latest:
177+
runs-on: ${{ matrix.os }}
178+
strategy:
179+
fail-fast: false
180+
matrix:
181+
os: [ubuntu-latest, windows-latest, macos-latest]
182+
python-version: ["3.8", "3.9", "3.10"]
183+
steps:
184+
- uses: actions/checkout@v3
185+
- name: Setup Python and check latest
186+
uses: ./
187+
with:
188+
python-version: ${{ matrix.python-version }}
189+
check-latest: true
190+
- name: Validate version
191+
run: |
192+
$pythonVersion = (python --version)
193+
if ("$pythonVersion" -NotMatch "${{ matrix.python }}"){
194+
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
195+
exit 1
196+
}
197+
$pythonVersion
198+
shell: pwsh

.licenses/npm/@actions/http-client.dep.yml

-32
This file was deleted.

README.md

+18
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,24 @@ pypy3.7-nightly or pypy-3.7-nightly # Python 3.7 and nightly PyPy
259259
260260
Note: `pypy2` and `pypy3` have been removed in v3. Use the format above instead.
261261
262+
# Check latest version
263+
264+
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/PyPy` version is always used.
265+
266+
If `check-latest` is set to `true`, the action first checks if the cached version is the latest one. If the locally cached version is not the most up-to-date, a `Python/PyPy` version will then be downloaded. Set `check-latest` to `true` if you want the most up-to-date `Python/PyPy` version to always be used.
267+
268+
> Setting `check-latest` to `true` has performance implications as downloading `Python/PyPy` versions is slower than using cached versions.
269+
270+
```yaml
271+
steps:
272+
- uses: actions/checkout@v3
273+
- uses: actions/setup-python@v3
274+
with:
275+
python-version: '3.7'
276+
check-latest: true
277+
- run: python my_script.py
278+
```
279+
262280
# Caching packages dependencies
263281

264282
The action has built-in functionality for caching and restoring dependencies. It uses [actions/cache](https://github.com/actions/toolkit/tree/main/packages/cache) under the hood for caching dependencies but requires less configuration settings. Supported package managers are `pip`, `pipenv` and `poetry`. The `cache` input is optional, and caching is turned off by default.

__tests__/find-pypy.test.ts

+110-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import * as finder from '../src/find-pypy';
1414
import {
1515
IPyPyManifestRelease,
1616
IS_WINDOWS,
17-
validateVersion,
1817
getPyPyVersionFromPath
1918
} from '../src/utils';
2019

@@ -82,6 +81,12 @@ describe('findPyPyToolCache', () => {
8281
const pypyPath = path.join('PyPy', actualPythonVersion, architecture);
8382
let tcFind: jest.SpyInstance;
8483
let spyReadExactPyPyVersion: jest.SpyInstance;
84+
let infoSpy: jest.SpyInstance;
85+
let warningSpy: jest.SpyInstance;
86+
let debugSpy: jest.SpyInstance;
87+
let addPathSpy: jest.SpyInstance;
88+
let exportVariableSpy: jest.SpyInstance;
89+
let setOutputSpy: jest.SpyInstance;
8590

8691
beforeEach(() => {
8792
tcFind = jest.spyOn(tc, 'find');
@@ -94,6 +99,24 @@ describe('findPyPyToolCache', () => {
9499

95100
spyReadExactPyPyVersion = jest.spyOn(utils, 'readExactPyPyVersionFile');
96101
spyReadExactPyPyVersion.mockImplementation(() => actualPyPyVersion);
102+
103+
infoSpy = jest.spyOn(core, 'info');
104+
infoSpy.mockImplementation(() => null);
105+
106+
warningSpy = jest.spyOn(core, 'warning');
107+
warningSpy.mockImplementation(() => null);
108+
109+
debugSpy = jest.spyOn(core, 'debug');
110+
debugSpy.mockImplementation(() => null);
111+
112+
addPathSpy = jest.spyOn(core, 'addPath');
113+
addPathSpy.mockImplementation(() => null);
114+
115+
exportVariableSpy = jest.spyOn(core, 'exportVariable');
116+
exportVariableSpy.mockImplementation(() => null);
117+
118+
setOutputSpy = jest.spyOn(core, 'setOutput');
119+
setOutputSpy.mockImplementation(() => null);
97120
});
98121

99122
afterEach(() => {
@@ -136,6 +159,13 @@ describe('findPyPyToolCache', () => {
136159
});
137160

138161
describe('findPyPyVersion', () => {
162+
let getBooleanInputSpy: jest.SpyInstance;
163+
let warningSpy: jest.SpyInstance;
164+
let debugSpy: jest.SpyInstance;
165+
let infoSpy: jest.SpyInstance;
166+
let addPathSpy: jest.SpyInstance;
167+
let exportVariableSpy: jest.SpyInstance;
168+
let setOutputSpy: jest.SpyInstance;
139169
let tcFind: jest.SpyInstance;
140170
let spyExtractZip: jest.SpyInstance;
141171
let spyExtractTar: jest.SpyInstance;
@@ -154,6 +184,27 @@ describe('findPyPyVersion', () => {
154184
const env = process.env;
155185

156186
beforeEach(() => {
187+
getBooleanInputSpy = jest.spyOn(core, 'getBooleanInput');
188+
getBooleanInputSpy.mockImplementation(() => false);
189+
190+
infoSpy = jest.spyOn(core, 'info');
191+
infoSpy.mockImplementation(() => {});
192+
193+
warningSpy = jest.spyOn(core, 'warning');
194+
warningSpy.mockImplementation(() => null);
195+
196+
debugSpy = jest.spyOn(core, 'debug');
197+
debugSpy.mockImplementation(() => null);
198+
199+
addPathSpy = jest.spyOn(core, 'addPath');
200+
addPathSpy.mockImplementation(() => null);
201+
202+
exportVariableSpy = jest.spyOn(core, 'exportVariable');
203+
exportVariableSpy.mockImplementation(() => null);
204+
205+
setOutputSpy = jest.spyOn(core, 'setOutput');
206+
setOutputSpy.mockImplementation(() => null);
207+
157208
jest.resetModules();
158209
process.env = {...env};
159210
tcFind = jest.spyOn(tc, 'find');
@@ -222,7 +273,7 @@ describe('findPyPyVersion', () => {
222273

223274
it('found PyPy in toolcache', async () => {
224275
await expect(
225-
finder.findPyPyVersion('pypy-3.6-v7.3.x', architecture, true)
276+
finder.findPyPyVersion('pypy-3.6-v7.3.x', architecture, true, false)
226277
).resolves.toEqual({
227278
resolvedPythonVersion: '3.6.12',
228279
resolvedPyPyVersion: '7.3.3'
@@ -240,13 +291,13 @@ describe('findPyPyVersion', () => {
240291

241292
it('throw on invalid input format', async () => {
242293
await expect(
243-
finder.findPyPyVersion('pypy3.7-v7.3.x', architecture, true)
294+
finder.findPyPyVersion('pypy3.7-v7.3.x', architecture, true, false)
244295
).rejects.toThrow();
245296
});
246297

247298
it('throw on invalid input format pypy3.7-7.3.x', async () => {
248299
await expect(
249-
finder.findPyPyVersion('pypy3.7-v7.3.x', architecture, true)
300+
finder.findPyPyVersion('pypy3.7-v7.3.x', architecture, true, false)
250301
).rejects.toThrow();
251302
});
252303

@@ -258,7 +309,7 @@ describe('findPyPyVersion', () => {
258309
spyChmodSync = jest.spyOn(fs, 'chmodSync');
259310
spyChmodSync.mockImplementation(() => undefined);
260311
await expect(
261-
finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture, true)
312+
finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture, true, false)
262313
).resolves.toEqual({
263314
resolvedPythonVersion: '3.7.9',
264315
resolvedPyPyVersion: '7.3.3'
@@ -282,7 +333,7 @@ describe('findPyPyVersion', () => {
282333
spyChmodSync = jest.spyOn(fs, 'chmodSync');
283334
spyChmodSync.mockImplementation(() => undefined);
284335
await expect(
285-
finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture, false)
336+
finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture, false, false)
286337
).resolves.toEqual({
287338
resolvedPythonVersion: '3.7.9',
288339
resolvedPyPyVersion: '7.3.3'
@@ -293,9 +344,61 @@ describe('findPyPyVersion', () => {
293344

294345
it('throw if release is not found', async () => {
295346
await expect(
296-
finder.findPyPyVersion('pypy-3.7-v7.5.x', architecture, true)
347+
finder.findPyPyVersion('pypy-3.7-v7.5.x', architecture, true, false)
297348
).rejects.toThrowError(
298349
`PyPy version 3.7 (v7.5.x) with arch ${architecture} not found`
299350
);
300351
});
352+
353+
it('check-latest enabled version found and used from toolcache', async () => {
354+
await expect(
355+
finder.findPyPyVersion('pypy-3.6-v7.3.x', architecture, false, true)
356+
).resolves.toEqual({
357+
resolvedPythonVersion: '3.6.12',
358+
resolvedPyPyVersion: '7.3.3'
359+
});
360+
361+
expect(infoSpy).toHaveBeenCalledWith(
362+
'Resolved as PyPy 7.3.3 with Python (3.6.12)'
363+
);
364+
});
365+
366+
it('check-latest enabled version found and install successfully', async () => {
367+
spyCacheDir = jest.spyOn(tc, 'cacheDir');
368+
spyCacheDir.mockImplementation(() =>
369+
path.join(toolDir, 'PyPy', '3.7.7', architecture)
370+
);
371+
spyChmodSync = jest.spyOn(fs, 'chmodSync');
372+
spyChmodSync.mockImplementation(() => undefined);
373+
await expect(
374+
finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture, false, true)
375+
).resolves.toEqual({
376+
resolvedPythonVersion: '3.7.9',
377+
resolvedPyPyVersion: '7.3.3'
378+
});
379+
expect(infoSpy).toHaveBeenCalledWith(
380+
'Resolved as PyPy 7.3.3 with Python (3.7.9)'
381+
);
382+
});
383+
384+
it('check-latest enabled version is not found and used from toolcache', async () => {
385+
tcFind.mockImplementationOnce((tool: string, version: string) => {
386+
const semverRange = new semver.Range(version);
387+
let pypyPath = '';
388+
if (semver.satisfies('3.8.8', semverRange)) {
389+
pypyPath = path.join(toolDir, 'PyPy', '3.8.8', architecture);
390+
}
391+
return pypyPath;
392+
});
393+
await expect(
394+
finder.findPyPyVersion('pypy-3.8-v7.3.x', architecture, false, true)
395+
).resolves.toEqual({
396+
resolvedPythonVersion: '3.8.8',
397+
resolvedPyPyVersion: '7.3.3'
398+
});
399+
400+
expect(infoSpy).toHaveBeenCalledWith(
401+
'Failed to resolve PyPy v7.3.x with Python (3.8) from manifest'
402+
);
403+
});
301404
});

0 commit comments

Comments
 (0)