mirror of
https://github.com/actions/setup-python.git
synced 2025-06-24 20:51:08 +02:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
b64ffcaf5b | |||
8d2896179a | |||
7bc6abb01e | |||
e8111cec9d | |||
a00ea43da6 | |||
8635b1ccc5 | |||
f6cc428f53 | |||
5f2af211d6 | |||
3467d92d48 | |||
65d7f2d534 | |||
f97b83114c |
10
.github/workflows/e2e-cache.yml
vendored
10
.github/workflows/e2e-cache.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
python-version: ['3.9', 'pypy-3.7-v7.x']
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Python
|
||||
uses: ./
|
||||
with:
|
||||
@ -41,7 +41,7 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
python-version: ['3.9', 'pypy-3.9-v7.x']
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Python
|
||||
id: cache-pipenv
|
||||
uses: ./
|
||||
@ -77,7 +77,7 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
python-version: ['3.9', 'pypy-3.8']
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install poetry
|
||||
run: pipx install poetry
|
||||
- name: Init pyproject.toml
|
||||
@ -99,7 +99,7 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
python-version: ['3.9', 'pypy-3.7-v7.x']
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Python
|
||||
uses: ./
|
||||
with:
|
||||
@ -118,7 +118,7 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
python-version: ['3.9', 'pypy-3.9-v7.x']
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Python
|
||||
id: cache-pipenv
|
||||
uses: ./
|
||||
|
9
.github/workflows/e2e-tests.yml
vendored
9
.github/workflows/e2e-tests.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
operating-system: [ubuntu-20.04, windows-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run with setup-python 3.5
|
||||
uses: ./
|
||||
@ -93,10 +93,3 @@ jobs:
|
||||
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
|
||||
|
116
.github/workflows/test-graalpy.yml
vendored
Normal file
116
.github/workflows/test-graalpy.yml
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
name: Validate GraalPy e2e
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
setup-graalpy:
|
||||
name: Setup GraalPy ${{ matrix.graalpy }} ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-20.04, ubuntu-latest]
|
||||
graalpy:
|
||||
- 'graalpy-23.0'
|
||||
- 'graalpy-22.3'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-python ${{ matrix.graalpy }}
|
||||
id: setup-python
|
||||
uses: ./
|
||||
with:
|
||||
python-version: ${{ matrix.graalpy }}
|
||||
|
||||
- name: Check python-path
|
||||
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
||||
shell: bash
|
||||
|
||||
- name: GraalPy and Python version
|
||||
run: python --version
|
||||
|
||||
- name: Run simple code
|
||||
run: python -c 'import math; print(math.factorial(5))'
|
||||
|
||||
- name: Assert GraalPy is running
|
||||
run: |
|
||||
import platform
|
||||
assert platform.python_implementation().lower() == "graalvm"
|
||||
shell: python
|
||||
|
||||
- name: Assert expected binaries (or symlinks) are present
|
||||
run: |
|
||||
EXECUTABLE=${{ matrix.graalpy }}
|
||||
EXECUTABLE=${EXECUTABLE/graalpy-/graalpy} # remove the first '-' in "graalpy-X.Y" -> "graalpyX.Y" to match executable name
|
||||
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
|
||||
${EXECUTABLE} --version
|
||||
shell: bash
|
||||
|
||||
setup-graalpy-noenv:
|
||||
name: Setup GraalPy ${{ matrix.graalpy }} ${{ matrix.os }} (noenv)
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-20.04, ubuntu-latest]
|
||||
graalpy: ['graalpy23.0', 'graalpy22.3']
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-python ${{ matrix.graalpy }}
|
||||
id: setup-python
|
||||
uses: ./
|
||||
with:
|
||||
python-version: ${{ matrix.graalpy }}
|
||||
update-environment: false
|
||||
|
||||
- name: GraalPy and Python version
|
||||
run: ${{ steps.setup-python.outputs.python-path }} --version
|
||||
|
||||
- name: Run simple code
|
||||
run: ${{ steps.setup-python.outputs.python-path }} -c 'import math; print(math.factorial(5))'
|
||||
|
||||
check-latest:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup GraalPy and check latest
|
||||
uses: ./
|
||||
id: graalpy
|
||||
with:
|
||||
python-version: 'graalpy-23.x'
|
||||
check-latest: true
|
||||
- name: GraalPy and Python version
|
||||
run: python --version
|
||||
|
||||
- name: Run simple code
|
||||
run: python -c 'import math; print(math.factorial(5))'
|
||||
|
||||
- name: Assert GraalPy is running
|
||||
run: |
|
||||
import platform
|
||||
assert platform.python_implementation().lower() == "graalvm"
|
||||
shell: python
|
||||
|
||||
- name: Assert expected binaries (or symlinks) are present
|
||||
run: |
|
||||
EXECUTABLE='${{ steps.graalpy.outputs.python-version }}'
|
||||
EXECUTABLE="${EXECUTABLE%.*}"
|
||||
${EXECUTABLE} --version
|
||||
shell: bash
|
8
.github/workflows/test-pypy.yml
vendored
8
.github/workflows/test-pypy.yml
vendored
@ -35,7 +35,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-python ${{ matrix.pypy }}
|
||||
id: setup-python
|
||||
@ -78,7 +78,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-python ${{ matrix.pypy }}
|
||||
id: setup-python
|
||||
@ -100,7 +100,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup PyPy and check latest
|
||||
uses: ./
|
||||
with:
|
||||
@ -133,7 +133,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup PyPy and check latest
|
||||
uses: ./
|
||||
with:
|
||||
|
26
.github/workflows/test-python.yml
vendored
26
.github/workflows/test-python.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
python: 3.8.15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-python ${{ matrix.python }}
|
||||
id: setup-python
|
||||
@ -77,7 +77,7 @@ jobs:
|
||||
python: 3.8.15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: build-version-file ${{ matrix.python }}
|
||||
run: echo ${{ matrix.python }} > .python-version
|
||||
@ -124,7 +124,7 @@ jobs:
|
||||
python: 3.8.15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: build-version-file ${{ matrix.python }}
|
||||
run: echo ${{ matrix.python }} > .python-version
|
||||
@ -157,7 +157,7 @@ jobs:
|
||||
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]
|
||||
python: [3.5.4, 3.6.7, 3.7.5, 3.8.15, 3.9.13, '==3.10.10']
|
||||
exclude:
|
||||
- os: ubuntu-22.04
|
||||
python: 3.5.4
|
||||
@ -169,7 +169,7 @@ jobs:
|
||||
python: 3.8.15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: build-version-file ${{ matrix.python }}
|
||||
run: |
|
||||
@ -190,7 +190,7 @@ jobs:
|
||||
- name: Validate version
|
||||
run: |
|
||||
$pythonVersion = (python --version)
|
||||
if ("Python ${{ matrix.python }}" -ne "$pythonVersion"){
|
||||
if ("Python ${{ matrix.python }}".replace("==", "") -ne "$pythonVersion"){
|
||||
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
|
||||
exit 1
|
||||
}
|
||||
@ -219,7 +219,7 @@ jobs:
|
||||
python: 3.8.15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: build-version-file ${{ matrix.python }}
|
||||
run: |
|
||||
@ -259,7 +259,7 @@ jobs:
|
||||
os: [macos-latest, windows-latest, ubuntu-20.04]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-python 3.9.0-beta.4
|
||||
id: setup-python
|
||||
@ -293,7 +293,7 @@ jobs:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-python 3.9-dev
|
||||
id: setup-python
|
||||
@ -321,7 +321,7 @@ jobs:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-python 3.12
|
||||
id: setup-python
|
||||
@ -351,7 +351,7 @@ jobs:
|
||||
python: ['3.7', '3.8', '3.9', '3.10']
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: setup-python ${{ matrix.python }}
|
||||
id: setup-python
|
||||
@ -374,7 +374,7 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
python-version: ['3.8', '3.9', '3.10']
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Python and check latest
|
||||
uses: ./
|
||||
with:
|
||||
@ -397,7 +397,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Python and check latest
|
||||
uses: ./
|
||||
with:
|
||||
|
@ -1,9 +1,9 @@
|
||||
---
|
||||
name: semver
|
||||
version: 6.3.0
|
||||
version: 6.3.1
|
||||
type: npm
|
||||
summary: The semantic version parser used by npm.
|
||||
homepage: https://github.com/npm/node-semver#readme
|
||||
homepage:
|
||||
license: isc
|
||||
licenses:
|
||||
- sources: LICENSE
|
@ -18,7 +18,7 @@ See [action.yml](action.yml)
|
||||
**Python**
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
@ -28,7 +28,7 @@ steps:
|
||||
**PyPy**
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 'pypy3.9'
|
||||
@ -62,7 +62,7 @@ The action defaults to searching for a dependency file (`requirements.txt` or `p
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9'
|
||||
|
5798
__tests__/data/graalpy.json
Normal file
5798
__tests__/data/graalpy.json
Normal file
File diff suppressed because it is too large
Load Diff
378
__tests__/find-graalpy.test.ts
Normal file
378
__tests__/find-graalpy.test.ts
Normal file
@ -0,0 +1,378 @@
|
||||
import fs from 'fs';
|
||||
|
||||
import {HttpClient} from '@actions/http-client';
|
||||
import * as ifm from '@actions/http-client/interfaces';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as core from '@actions/core';
|
||||
|
||||
import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
|
||||
import * as finder from '../src/find-graalpy';
|
||||
import {IGraalPyManifestRelease, IS_WINDOWS} from '../src/utils';
|
||||
|
||||
import manifestData from './data/graalpy.json';
|
||||
|
||||
const architecture = 'x64';
|
||||
|
||||
const toolDir = path.join(__dirname, 'runner', 'tools');
|
||||
const tempDir = path.join(__dirname, 'runner', 'temp');
|
||||
|
||||
/* GraalPy doesn't have a windows release yet */
|
||||
const describeSkipOnWindows = IS_WINDOWS ? describe.skip : describe;
|
||||
|
||||
describe('parseGraalPyVersion', () => {
|
||||
it.each([
|
||||
['graalpy-23', '23'],
|
||||
['graalpy-23.0', '23.0'],
|
||||
['graalpy23.0', '23.0']
|
||||
])('%s -> %s', (input, expected) => {
|
||||
expect(finder.parseGraalPyVersion(input)).toEqual(expected);
|
||||
});
|
||||
|
||||
it.each(['', 'graalpy-', 'graalpy', 'p', 'notgraalpy-'])(
|
||||
'throw on invalid input "%s"',
|
||||
input => {
|
||||
expect(() => finder.parseGraalPyVersion(input)).toThrow(
|
||||
"Invalid 'version' property for GraalPy. GraalPy version should be specified as 'graalpy<python-version>' or 'graalpy-<python-version>'. See README for examples and documentation."
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('findGraalPyToolCache', () => {
|
||||
const actualGraalPyVersion = '23.0.0';
|
||||
const graalpyPath = path.join('GraalPy', actualGraalPyVersion, architecture);
|
||||
let tcFind: jest.SpyInstance;
|
||||
let infoSpy: jest.SpyInstance;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let debugSpy: jest.SpyInstance;
|
||||
let addPathSpy: jest.SpyInstance;
|
||||
let exportVariableSpy: jest.SpyInstance;
|
||||
let setOutputSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
tcFind = jest.spyOn(tc, 'find');
|
||||
tcFind.mockImplementation((toolname: string, pythonVersion: string) => {
|
||||
const semverVersion = new semver.Range(pythonVersion);
|
||||
return semver.satisfies(actualGraalPyVersion, semverVersion)
|
||||
? graalpyPath
|
||||
: '';
|
||||
});
|
||||
|
||||
infoSpy = jest.spyOn(core, 'info');
|
||||
infoSpy.mockImplementation(() => null);
|
||||
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
warningSpy.mockImplementation(() => null);
|
||||
|
||||
debugSpy = jest.spyOn(core, 'debug');
|
||||
debugSpy.mockImplementation(() => null);
|
||||
|
||||
addPathSpy = jest.spyOn(core, 'addPath');
|
||||
addPathSpy.mockImplementation(() => null);
|
||||
|
||||
exportVariableSpy = jest.spyOn(core, 'exportVariable');
|
||||
exportVariableSpy.mockImplementation(() => null);
|
||||
|
||||
setOutputSpy = jest.spyOn(core, 'setOutput');
|
||||
setOutputSpy.mockImplementation(() => null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.clearAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('GraalPy exists on the path and versions are satisfied', () => {
|
||||
expect(finder.findGraalPyToolCache('23.0.0', architecture)).toEqual({
|
||||
installDir: graalpyPath,
|
||||
resolvedGraalPyVersion: actualGraalPyVersion
|
||||
});
|
||||
});
|
||||
|
||||
it('GraalPy exists on the path and versions are satisfied with semver', () => {
|
||||
expect(finder.findGraalPyToolCache('23.0', architecture)).toEqual({
|
||||
installDir: graalpyPath,
|
||||
resolvedGraalPyVersion: actualGraalPyVersion
|
||||
});
|
||||
});
|
||||
|
||||
it("GraalPy exists on the path, but version doesn't match", () => {
|
||||
expect(finder.findGraalPyToolCache('22.3', architecture)).toEqual({
|
||||
installDir: '',
|
||||
resolvedGraalPyVersion: ''
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describeSkipOnWindows('findGraalPyVersion', () => {
|
||||
let getBooleanInputSpy: jest.SpyInstance;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let debugSpy: jest.SpyInstance;
|
||||
let infoSpy: jest.SpyInstance;
|
||||
let addPathSpy: jest.SpyInstance;
|
||||
let exportVariableSpy: jest.SpyInstance;
|
||||
let setOutputSpy: jest.SpyInstance;
|
||||
let tcFind: jest.SpyInstance;
|
||||
let spyExtractZip: jest.SpyInstance;
|
||||
let spyExtractTar: jest.SpyInstance;
|
||||
let spyHttpClient: jest.SpyInstance;
|
||||
let spyExistsSync: jest.SpyInstance;
|
||||
let spyExec: jest.SpyInstance;
|
||||
let spySymlinkSync: jest.SpyInstance;
|
||||
let spyDownloadTool: jest.SpyInstance;
|
||||
let spyFsReadDir: jest.SpyInstance;
|
||||
let spyCacheDir: jest.SpyInstance;
|
||||
let spyChmodSync: jest.SpyInstance;
|
||||
let spyCoreAddPath: jest.SpyInstance;
|
||||
let spyCoreExportVariable: jest.SpyInstance;
|
||||
const env = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
getBooleanInputSpy = jest.spyOn(core, 'getBooleanInput');
|
||||
getBooleanInputSpy.mockImplementation(() => false);
|
||||
|
||||
infoSpy = jest.spyOn(core, 'info');
|
||||
infoSpy.mockImplementation(() => {});
|
||||
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
warningSpy.mockImplementation(() => null);
|
||||
|
||||
debugSpy = jest.spyOn(core, 'debug');
|
||||
debugSpy.mockImplementation(() => null);
|
||||
|
||||
addPathSpy = jest.spyOn(core, 'addPath');
|
||||
addPathSpy.mockImplementation(() => null);
|
||||
|
||||
exportVariableSpy = jest.spyOn(core, 'exportVariable');
|
||||
exportVariableSpy.mockImplementation(() => null);
|
||||
|
||||
setOutputSpy = jest.spyOn(core, 'setOutput');
|
||||
setOutputSpy.mockImplementation(() => null);
|
||||
|
||||
jest.resetModules();
|
||||
process.env = {...env};
|
||||
tcFind = jest.spyOn(tc, 'find');
|
||||
tcFind.mockImplementation((tool: string, version: string) => {
|
||||
const semverRange = new semver.Range(version);
|
||||
let graalpyPath = '';
|
||||
if (semver.satisfies('23.0.0', semverRange)) {
|
||||
graalpyPath = path.join(toolDir, 'GraalPy', '23.0.0', architecture);
|
||||
}
|
||||
return graalpyPath;
|
||||
});
|
||||
|
||||
spyDownloadTool = jest.spyOn(tc, 'downloadTool');
|
||||
spyDownloadTool.mockImplementation(() => path.join(tempDir, 'GraalPy'));
|
||||
|
||||
spyExtractZip = jest.spyOn(tc, 'extractZip');
|
||||
spyExtractZip.mockImplementation(() => tempDir);
|
||||
|
||||
spyExtractTar = jest.spyOn(tc, 'extractTar');
|
||||
spyExtractTar.mockImplementation(() => tempDir);
|
||||
|
||||
spyFsReadDir = jest.spyOn(fs, 'readdirSync');
|
||||
spyFsReadDir.mockImplementation((directory: string) => ['GraalPyTest']);
|
||||
|
||||
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
||||
spyHttpClient.mockImplementation(
|
||||
async (): Promise<ifm.ITypedResponse<IGraalPyManifestRelease[]>> => {
|
||||
const result = JSON.stringify(manifestData);
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {},
|
||||
result: JSON.parse(result) as IGraalPyManifestRelease[]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
spyExec = jest.spyOn(exec, 'exec');
|
||||
spyExec.mockImplementation(() => undefined);
|
||||
|
||||
spySymlinkSync = jest.spyOn(fs, 'symlinkSync');
|
||||
spySymlinkSync.mockImplementation(() => undefined);
|
||||
|
||||
spyExistsSync = jest.spyOn(fs, 'existsSync');
|
||||
spyExistsSync.mockReturnValue(true);
|
||||
|
||||
spyCoreAddPath = jest.spyOn(core, 'addPath');
|
||||
|
||||
spyCoreExportVariable = jest.spyOn(core, 'exportVariable');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.clearAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
process.env = env;
|
||||
});
|
||||
|
||||
it('found GraalPy in toolcache', async () => {
|
||||
await expect(
|
||||
finder.findGraalPyVersion(
|
||||
'graalpy-23.0',
|
||||
architecture,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
)
|
||||
).resolves.toEqual('23.0.0');
|
||||
expect(spyCoreAddPath).toHaveBeenCalled();
|
||||
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
||||
'pythonLocation',
|
||||
expect.anything()
|
||||
);
|
||||
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
||||
'PKG_CONFIG_PATH',
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
||||
it('throw on invalid input format', async () => {
|
||||
await expect(
|
||||
finder.findGraalPyVersion('graalpy-x23', architecture, true, false, false)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('found and install successfully', async () => {
|
||||
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||
spyCacheDir.mockImplementation(() =>
|
||||
path.join(toolDir, 'GraalPy', '23.0.0', architecture)
|
||||
);
|
||||
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||
spyChmodSync.mockImplementation(() => undefined);
|
||||
await expect(
|
||||
finder.findGraalPyVersion(
|
||||
'graalpy-23.0.0',
|
||||
architecture,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
)
|
||||
).resolves.toEqual('23.0.0');
|
||||
expect(spyCoreAddPath).toHaveBeenCalled();
|
||||
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
||||
'pythonLocation',
|
||||
expect.anything()
|
||||
);
|
||||
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
||||
'PKG_CONFIG_PATH',
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
||||
it('found and install successfully without environment update', async () => {
|
||||
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||
spyCacheDir.mockImplementation(() =>
|
||||
path.join(toolDir, 'GraalPy', '23.0.0', architecture)
|
||||
);
|
||||
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||
spyChmodSync.mockImplementation(() => undefined);
|
||||
await expect(
|
||||
finder.findGraalPyVersion(
|
||||
'graalpy-23.0.0',
|
||||
architecture,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
).resolves.toEqual('23.0.0');
|
||||
expect(spyCoreAddPath).not.toHaveBeenCalled();
|
||||
expect(spyCoreExportVariable).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('throw if release is not found', async () => {
|
||||
await expect(
|
||||
finder.findGraalPyVersion(
|
||||
'graalpy-19.0.0',
|
||||
architecture,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
)
|
||||
).rejects.toThrow(
|
||||
`GraalPy version 19.0.0 with arch ${architecture} not found`
|
||||
);
|
||||
});
|
||||
|
||||
it('check-latest enabled version found and used from toolcache', async () => {
|
||||
await expect(
|
||||
finder.findGraalPyVersion(
|
||||
'graalpy-23.0.0',
|
||||
architecture,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
)
|
||||
).resolves.toEqual('23.0.0');
|
||||
|
||||
expect(infoSpy).toHaveBeenCalledWith('Resolved as GraalPy 23.0.0');
|
||||
});
|
||||
|
||||
it('check-latest enabled version found and install successfully', async () => {
|
||||
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||
spyCacheDir.mockImplementation(() =>
|
||||
path.join(toolDir, 'GraalPy', '23.0.0', architecture)
|
||||
);
|
||||
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||
spyChmodSync.mockImplementation(() => undefined);
|
||||
await expect(
|
||||
finder.findGraalPyVersion(
|
||||
'graalpy-23.0.0',
|
||||
architecture,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
)
|
||||
).resolves.toEqual('23.0.0');
|
||||
expect(infoSpy).toHaveBeenCalledWith('Resolved as GraalPy 23.0.0');
|
||||
});
|
||||
|
||||
it('check-latest enabled version is not found and used from toolcache', async () => {
|
||||
tcFind.mockImplementationOnce((tool: string, version: string) => {
|
||||
const semverRange = new semver.Range(version);
|
||||
let graalpyPath = '';
|
||||
if (semver.satisfies('22.3.4', semverRange)) {
|
||||
graalpyPath = path.join(toolDir, 'GraalPy', '22.3.4', architecture);
|
||||
}
|
||||
return graalpyPath;
|
||||
});
|
||||
await expect(
|
||||
finder.findGraalPyVersion(
|
||||
'graalpy-22.3.4',
|
||||
architecture,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
)
|
||||
).resolves.toEqual('22.3.4');
|
||||
|
||||
expect(infoSpy).toHaveBeenCalledWith(
|
||||
'Failed to resolve GraalPy 22.3.4 from manifest'
|
||||
);
|
||||
});
|
||||
|
||||
it('found and install successfully, pre-release fallback', async () => {
|
||||
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||
spyCacheDir.mockImplementation(() =>
|
||||
path.join(toolDir, 'GraalPy', '23.1', architecture)
|
||||
);
|
||||
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||
spyChmodSync.mockImplementation(() => undefined);
|
||||
await expect(
|
||||
finder.findGraalPyVersion(
|
||||
'graalpy23.1',
|
||||
architecture,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
).rejects.toThrow();
|
||||
await expect(
|
||||
finder.findGraalPyVersion('graalpy23.1', architecture, false, false, true)
|
||||
).resolves.toEqual('23.1.0-a.1');
|
||||
});
|
||||
});
|
256
__tests__/install-graalpy.test.ts
Normal file
256
__tests__/install-graalpy.test.ts
Normal file
@ -0,0 +1,256 @@
|
||||
import fs from 'fs';
|
||||
|
||||
import {HttpClient} from '@actions/http-client';
|
||||
import * as ifm from '@actions/http-client/interfaces';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as core from '@actions/core';
|
||||
import * as path from 'path';
|
||||
|
||||
import * as installer from '../src/install-graalpy';
|
||||
import {
|
||||
IGraalPyManifestRelease,
|
||||
IGraalPyManifestAsset,
|
||||
IS_WINDOWS
|
||||
} from '../src/utils';
|
||||
|
||||
import manifestData from './data/graalpy.json';
|
||||
|
||||
const architecture = 'x64';
|
||||
|
||||
const toolDir = path.join(__dirname, 'runner', 'tools');
|
||||
const tempDir = path.join(__dirname, 'runner', 'temp');
|
||||
|
||||
/* GraalPy doesn't have a windows release yet */
|
||||
const describeSkipOnWindows = IS_WINDOWS ? describe.skip : describe;
|
||||
|
||||
describe('graalpyVersionToSemantic', () => {
|
||||
it.each([
|
||||
['23.0.0a1', '23.0.0a1'],
|
||||
['23.0.0', '23.0.0'],
|
||||
['23.0.x', '23.0.x'],
|
||||
['23.x', '23.x']
|
||||
])('%s -> %s', (input, expected) => {
|
||||
expect(installer.graalPyTagToVersion(input)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describeSkipOnWindows('findRelease', () => {
|
||||
const result = JSON.stringify(manifestData);
|
||||
const releases = JSON.parse(result) as IGraalPyManifestRelease[];
|
||||
const extension = 'tar.gz';
|
||||
const arch = installer.toGraalPyArchitecture(architecture);
|
||||
const platform = installer.toGraalPyPlatform(process.platform);
|
||||
const extensionName = `${platform}-${arch}.${extension}`;
|
||||
const files: IGraalPyManifestAsset = {
|
||||
name: `graalpython-23.0.0-${extensionName}`,
|
||||
browser_download_url: `https://github.com/oracle/graalpython/releases/download/graal-23.0.0/graalpython-23.0.0-${extensionName}`
|
||||
};
|
||||
const filesRC1: IGraalPyManifestAsset = {
|
||||
name: `graalpython-23.1.0a1-${extensionName}`,
|
||||
browser_download_url: `https://github.com/oracle/graalpython/releases/download/graal-23.1.0a1/graalpython-23.1.0a1-${extensionName}`
|
||||
};
|
||||
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let debugSpy: jest.SpyInstance;
|
||||
let infoSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
infoSpy = jest.spyOn(core, 'info');
|
||||
infoSpy.mockImplementation(() => {});
|
||||
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
warningSpy.mockImplementation(() => null);
|
||||
|
||||
debugSpy = jest.spyOn(core, 'debug');
|
||||
debugSpy.mockImplementation(() => null);
|
||||
});
|
||||
|
||||
it("GraalPy version doesn't match", () => {
|
||||
const graalpyVersion = '12.0.0';
|
||||
expect(
|
||||
installer.findRelease(releases, graalpyVersion, architecture, false)
|
||||
).toEqual(null);
|
||||
});
|
||||
|
||||
it('GraalPy version matches', () => {
|
||||
const graalpyVersion = '23.0.0';
|
||||
expect(
|
||||
installer.findRelease(releases, graalpyVersion, architecture, false)
|
||||
).toMatchObject({
|
||||
foundAsset: files,
|
||||
resolvedGraalPyVersion: graalpyVersion
|
||||
});
|
||||
});
|
||||
|
||||
it('Preview version of GraalPy is found', () => {
|
||||
const graalpyVersion = installer.graalPyTagToVersion('vm-23.1.0a1');
|
||||
expect(
|
||||
installer.findRelease(releases, graalpyVersion, architecture, false)
|
||||
).toMatchObject({
|
||||
foundAsset: {
|
||||
name: `graalpython-23.1.0a1-${extensionName}`,
|
||||
browser_download_url: `https://github.com/oracle/graalpython/releases/download/graal-23.1.0a1/graalpython-23.1.0a1-${extensionName}`
|
||||
},
|
||||
resolvedGraalPyVersion: '23.1.0-a.1'
|
||||
});
|
||||
});
|
||||
|
||||
it('Latest GraalPy is found', () => {
|
||||
const graalpyVersion = 'x';
|
||||
expect(
|
||||
installer.findRelease(releases, graalpyVersion, architecture, false)
|
||||
).toMatchObject({
|
||||
foundAsset: files,
|
||||
resolvedGraalPyVersion: '23.0.0'
|
||||
});
|
||||
});
|
||||
|
||||
it('GraalPy version matches semver (pre-release)', () => {
|
||||
const graalpyVersion = '23.1.x';
|
||||
expect(
|
||||
installer.findRelease(releases, graalpyVersion, architecture, false)
|
||||
).toBeNull();
|
||||
expect(
|
||||
installer.findRelease(releases, graalpyVersion, architecture, true)
|
||||
).toMatchObject({
|
||||
foundAsset: filesRC1,
|
||||
resolvedGraalPyVersion: '23.1.0-a.1'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describeSkipOnWindows('installGraalPy', () => {
|
||||
let tcFind: jest.SpyInstance;
|
||||
let warningSpy: jest.SpyInstance;
|
||||
let debugSpy: jest.SpyInstance;
|
||||
let infoSpy: jest.SpyInstance;
|
||||
let spyExtractZip: jest.SpyInstance;
|
||||
let spyExtractTar: jest.SpyInstance;
|
||||
let spyFsReadDir: jest.SpyInstance;
|
||||
let spyFsWriteFile: jest.SpyInstance;
|
||||
let spyHttpClient: jest.SpyInstance;
|
||||
let spyExistsSync: jest.SpyInstance;
|
||||
let spyExec: jest.SpyInstance;
|
||||
let spySymlinkSync: jest.SpyInstance;
|
||||
let spyDownloadTool: jest.SpyInstance;
|
||||
let spyCacheDir: jest.SpyInstance;
|
||||
let spyChmodSync: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
tcFind = jest.spyOn(tc, 'find');
|
||||
tcFind.mockImplementation(() =>
|
||||
path.join('GraalPy', '3.6.12', architecture)
|
||||
);
|
||||
|
||||
spyDownloadTool = jest.spyOn(tc, 'downloadTool');
|
||||
spyDownloadTool.mockImplementation(() => path.join(tempDir, 'GraalPy'));
|
||||
|
||||
spyExtractZip = jest.spyOn(tc, 'extractZip');
|
||||
spyExtractZip.mockImplementation(() => tempDir);
|
||||
|
||||
spyExtractTar = jest.spyOn(tc, 'extractTar');
|
||||
spyExtractTar.mockImplementation(() => tempDir);
|
||||
|
||||
infoSpy = jest.spyOn(core, 'info');
|
||||
infoSpy.mockImplementation(() => {});
|
||||
|
||||
warningSpy = jest.spyOn(core, 'warning');
|
||||
warningSpy.mockImplementation(() => null);
|
||||
|
||||
debugSpy = jest.spyOn(core, 'debug');
|
||||
debugSpy.mockImplementation(() => null);
|
||||
|
||||
spyFsReadDir = jest.spyOn(fs, 'readdirSync');
|
||||
spyFsReadDir.mockImplementation(() => ['GraalPyTest']);
|
||||
|
||||
spyFsWriteFile = jest.spyOn(fs, 'writeFileSync');
|
||||
spyFsWriteFile.mockImplementation(() => undefined);
|
||||
|
||||
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
||||
spyHttpClient.mockImplementation(
|
||||
async (): Promise<ifm.ITypedResponse<IGraalPyManifestRelease[]>> => {
|
||||
const result = JSON.stringify(manifestData);
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {},
|
||||
result: JSON.parse(result) as IGraalPyManifestRelease[]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
spyExec = jest.spyOn(exec, 'exec');
|
||||
spyExec.mockImplementation(() => undefined);
|
||||
|
||||
spySymlinkSync = jest.spyOn(fs, 'symlinkSync');
|
||||
spySymlinkSync.mockImplementation(() => undefined);
|
||||
|
||||
spyExistsSync = jest.spyOn(fs, 'existsSync');
|
||||
spyExistsSync.mockImplementation(() => false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
jest.clearAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('throw if release is not found', async () => {
|
||||
await expect(
|
||||
installer.installGraalPy('7.3.3', architecture, false, undefined)
|
||||
).rejects.toThrow(
|
||||
`GraalPy version 7.3.3 with arch ${architecture} not found`
|
||||
);
|
||||
|
||||
expect(spyHttpClient).toHaveBeenCalled();
|
||||
expect(spyDownloadTool).not.toHaveBeenCalled();
|
||||
expect(spyExec).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('found and install GraalPy', async () => {
|
||||
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||
spyCacheDir.mockImplementation(() =>
|
||||
path.join(toolDir, 'GraalPy', '21.3.0', architecture)
|
||||
);
|
||||
|
||||
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||
spyChmodSync.mockImplementation(() => undefined);
|
||||
|
||||
await expect(
|
||||
installer.installGraalPy('21.x', architecture, false, undefined)
|
||||
).resolves.toEqual({
|
||||
installDir: path.join(toolDir, 'GraalPy', '21.3.0', architecture),
|
||||
resolvedGraalPyVersion: '21.3.0'
|
||||
});
|
||||
|
||||
expect(spyHttpClient).toHaveBeenCalled();
|
||||
expect(spyDownloadTool).toHaveBeenCalled();
|
||||
expect(spyCacheDir).toHaveBeenCalled();
|
||||
expect(spyExec).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('found and install GraalPy, pre-release fallback', async () => {
|
||||
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||
spyCacheDir.mockImplementation(() =>
|
||||
path.join(toolDir, 'GraalPy', '23.1.0', architecture)
|
||||
);
|
||||
|
||||
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||
spyChmodSync.mockImplementation(() => undefined);
|
||||
|
||||
await expect(
|
||||
installer.installGraalPy('23.1.x', architecture, false, undefined)
|
||||
).rejects.toThrow();
|
||||
await expect(
|
||||
installer.installGraalPy('23.1.x', architecture, true, undefined)
|
||||
).resolves.toEqual({
|
||||
installDir: path.join(toolDir, 'GraalPy', '23.1.0', architecture),
|
||||
resolvedGraalPyVersion: '23.1.0-a.1'
|
||||
});
|
||||
|
||||
expect(spyHttpClient).toHaveBeenCalled();
|
||||
expect(spyDownloadTool).toHaveBeenCalled();
|
||||
expect(spyCacheDir).toHaveBeenCalled();
|
||||
expect(spyExec).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -11,7 +11,8 @@ import {
|
||||
isCacheFeatureAvailable,
|
||||
getVersionInputFromFile,
|
||||
getVersionInputFromPlainFile,
|
||||
getVersionInputFromTomlFile
|
||||
getVersionInputFromTomlFile,
|
||||
getNextPageUrl
|
||||
} from '../src/utils';
|
||||
|
||||
jest.mock('@actions/cache');
|
||||
@ -107,7 +108,7 @@ describe('Version from file test', () => {
|
||||
await io.mkdirP(tempDir);
|
||||
const pythonVersionFileName = 'pyproject.toml';
|
||||
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||
const pythonVersion = '>=3.7';
|
||||
const pythonVersion = '>=3.7.0';
|
||||
const pythonVersionFileContent = `[project]\nrequires-python = "${pythonVersion}"`;
|
||||
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
||||
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
|
||||
@ -119,7 +120,7 @@ describe('Version from file test', () => {
|
||||
await io.mkdirP(tempDir);
|
||||
const pythonVersionFileName = 'pyproject.toml';
|
||||
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||
const pythonVersion = '>=3.7';
|
||||
const pythonVersion = '>=3.7.0';
|
||||
const pythonVersionFileContent = `[tool.poetry.dependencies]\npython = "${pythonVersion}"`;
|
||||
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
||||
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
|
||||
@ -136,3 +137,25 @@ describe('Version from file test', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('getNextPageUrl', () => {
|
||||
it('GitHub API pagination next page is parsed correctly', () => {
|
||||
function generateResponse(link: string) {
|
||||
return {
|
||||
statusCode: 200,
|
||||
result: null,
|
||||
headers: {
|
||||
link: link
|
||||
}
|
||||
};
|
||||
}
|
||||
const page1Links =
|
||||
'<https://api.github.com/repositories/129883600/releases?page=2>; rel="next", <https://api.github.com/repositories/129883600/releases?page=3>; rel="last"';
|
||||
expect(getNextPageUrl(generateResponse(page1Links))).toStrictEqual(
|
||||
'https://api.github.com/repositories/129883600/releases?page=2'
|
||||
);
|
||||
const page2Links =
|
||||
'<https://api.github.com/repositories/129883600/releases?page=1>; rel="prev", <https://api.github.com/repositories/129883600/releases?page=1>; rel="first"';
|
||||
expect(getNextPageUrl(generateResponse(page2Links))).toBeNull();
|
||||
});
|
||||
});
|
||||
|
107
dist/cache-save/index.js
vendored
107
dist/cache-save/index.js
vendored
@ -3300,8 +3300,11 @@ var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
|
||||
// Max safe segment length for coercion.
|
||||
var MAX_SAFE_COMPONENT_LENGTH = 16
|
||||
|
||||
var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
|
||||
|
||||
// The actual regexps go on exports.re
|
||||
var re = exports.re = []
|
||||
var safeRe = exports.safeRe = []
|
||||
var src = exports.src = []
|
||||
var t = exports.tokens = {}
|
||||
var R = 0
|
||||
@ -3310,6 +3313,31 @@ function tok (n) {
|
||||
t[n] = R++
|
||||
}
|
||||
|
||||
var LETTERDASHNUMBER = '[a-zA-Z0-9-]'
|
||||
|
||||
// Replace some greedy regex tokens to prevent regex dos issues. These regex are
|
||||
// used internally via the safeRe object since all inputs in this library get
|
||||
// normalized first to trim and collapse all extra whitespace. The original
|
||||
// regexes are exported for userland consumption and lower level usage. A
|
||||
// future breaking change could export the safer regex only with a note that
|
||||
// all input should have extra whitespace removed.
|
||||
var safeRegexReplacements = [
|
||||
['\\s', 1],
|
||||
['\\d', MAX_LENGTH],
|
||||
[LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH],
|
||||
]
|
||||
|
||||
function makeSafeRe (value) {
|
||||
for (var i = 0; i < safeRegexReplacements.length; i++) {
|
||||
var token = safeRegexReplacements[i][0]
|
||||
var max = safeRegexReplacements[i][1]
|
||||
value = value
|
||||
.split(token + '*').join(token + '{0,' + max + '}')
|
||||
.split(token + '+').join(token + '{1,' + max + '}')
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// The following Regular Expressions can be used for tokenizing,
|
||||
// validating, and parsing SemVer version strings.
|
||||
|
||||
@ -3319,14 +3347,14 @@ function tok (n) {
|
||||
tok('NUMERICIDENTIFIER')
|
||||
src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*'
|
||||
tok('NUMERICIDENTIFIERLOOSE')
|
||||
src[t.NUMERICIDENTIFIERLOOSE] = '[0-9]+'
|
||||
src[t.NUMERICIDENTIFIERLOOSE] = '\\d+'
|
||||
|
||||
// ## Non-numeric Identifier
|
||||
// Zero or more digits, followed by a letter or hyphen, and then zero or
|
||||
// more letters, digits, or hyphens.
|
||||
|
||||
tok('NONNUMERICIDENTIFIER')
|
||||
src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'
|
||||
src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-]' + LETTERDASHNUMBER + '*'
|
||||
|
||||
// ## Main Version
|
||||
// Three dot-separated numeric identifiers.
|
||||
@ -3368,7 +3396,7 @@ src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] +
|
||||
// Any combination of digits, letters, or hyphens.
|
||||
|
||||
tok('BUILDIDENTIFIER')
|
||||
src[t.BUILDIDENTIFIER] = '[0-9A-Za-z-]+'
|
||||
src[t.BUILDIDENTIFIER] = LETTERDASHNUMBER + '+'
|
||||
|
||||
// ## Build Metadata
|
||||
// Plus sign, followed by one or more period-separated build metadata
|
||||
@ -3448,6 +3476,7 @@ src[t.COERCE] = '(^|[^\\d])' +
|
||||
'(?:$|[^\\d])'
|
||||
tok('COERCERTL')
|
||||
re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g')
|
||||
safeRe[t.COERCERTL] = new RegExp(makeSafeRe(src[t.COERCE]), 'g')
|
||||
|
||||
// Tilde ranges.
|
||||
// Meaning is "reasonably at or greater than"
|
||||
@ -3457,6 +3486,7 @@ src[t.LONETILDE] = '(?:~>?)'
|
||||
tok('TILDETRIM')
|
||||
src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+'
|
||||
re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g')
|
||||
safeRe[t.TILDETRIM] = new RegExp(makeSafeRe(src[t.TILDETRIM]), 'g')
|
||||
var tildeTrimReplace = '$1~'
|
||||
|
||||
tok('TILDE')
|
||||
@ -3472,6 +3502,7 @@ src[t.LONECARET] = '(?:\\^)'
|
||||
tok('CARETTRIM')
|
||||
src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+'
|
||||
re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g')
|
||||
safeRe[t.CARETTRIM] = new RegExp(makeSafeRe(src[t.CARETTRIM]), 'g')
|
||||
var caretTrimReplace = '$1^'
|
||||
|
||||
tok('CARET')
|
||||
@ -3493,6 +3524,7 @@ src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] +
|
||||
|
||||
// this one has to use the /g flag
|
||||
re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g')
|
||||
safeRe[t.COMPARATORTRIM] = new RegExp(makeSafeRe(src[t.COMPARATORTRIM]), 'g')
|
||||
var comparatorTrimReplace = '$1$2$3'
|
||||
|
||||
// Something like `1.2.3 - 1.2.4`
|
||||
@ -3521,6 +3553,14 @@ for (var i = 0; i < R; i++) {
|
||||
debug(i, src[i])
|
||||
if (!re[i]) {
|
||||
re[i] = new RegExp(src[i])
|
||||
|
||||
// Replace all greedy whitespace to prevent regex dos issues. These regex are
|
||||
// used internally via the safeRe object since all inputs in this library get
|
||||
// normalized first to trim and collapse all extra whitespace. The original
|
||||
// regexes are exported for userland consumption and lower level usage. A
|
||||
// future breaking change could export the safer regex only with a note that
|
||||
// all input should have extra whitespace removed.
|
||||
safeRe[i] = new RegExp(makeSafeRe(src[i]))
|
||||
}
|
||||
}
|
||||
|
||||
@ -3545,7 +3585,7 @@ function parse (version, options) {
|
||||
return null
|
||||
}
|
||||
|
||||
var r = options.loose ? re[t.LOOSE] : re[t.FULL]
|
||||
var r = options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL]
|
||||
if (!r.test(version)) {
|
||||
return null
|
||||
}
|
||||
@ -3600,7 +3640,7 @@ function SemVer (version, options) {
|
||||
this.options = options
|
||||
this.loose = !!options.loose
|
||||
|
||||
var m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL])
|
||||
var m = version.trim().match(options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL])
|
||||
|
||||
if (!m) {
|
||||
throw new TypeError('Invalid Version: ' + version)
|
||||
@ -4045,6 +4085,7 @@ function Comparator (comp, options) {
|
||||
return new Comparator(comp, options)
|
||||
}
|
||||
|
||||
comp = comp.trim().split(/\s+/).join(' ')
|
||||
debug('comparator', comp, options)
|
||||
this.options = options
|
||||
this.loose = !!options.loose
|
||||
@ -4061,7 +4102,7 @@ function Comparator (comp, options) {
|
||||
|
||||
var ANY = {}
|
||||
Comparator.prototype.parse = function (comp) {
|
||||
var r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
|
||||
var r = this.options.loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR]
|
||||
var m = comp.match(r)
|
||||
|
||||
if (!m) {
|
||||
@ -4185,9 +4226,16 @@ function Range (range, options) {
|
||||
this.loose = !!options.loose
|
||||
this.includePrerelease = !!options.includePrerelease
|
||||
|
||||
// First, split based on boolean or ||
|
||||
// First reduce all whitespace as much as possible so we do not have to rely
|
||||
// on potentially slow regexes like \s*. This is then stored and used for
|
||||
// future error messages as well.
|
||||
this.raw = range
|
||||
this.set = range.split(/\s*\|\|\s*/).map(function (range) {
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.join(' ')
|
||||
|
||||
// First, split based on boolean or ||
|
||||
this.set = this.raw.split('||').map(function (range) {
|
||||
return this.parseRange(range.trim())
|
||||
}, this).filter(function (c) {
|
||||
// throw out any that are not relevant for whatever reason
|
||||
@ -4195,7 +4243,7 @@ function Range (range, options) {
|
||||
})
|
||||
|
||||
if (!this.set.length) {
|
||||
throw new TypeError('Invalid SemVer Range: ' + range)
|
||||
throw new TypeError('Invalid SemVer Range: ' + this.raw)
|
||||
}
|
||||
|
||||
this.format()
|
||||
@ -4214,20 +4262,19 @@ Range.prototype.toString = function () {
|
||||
|
||||
Range.prototype.parseRange = function (range) {
|
||||
var loose = this.options.loose
|
||||
range = range.trim()
|
||||
// `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
|
||||
var hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]
|
||||
var hr = loose ? safeRe[t.HYPHENRANGELOOSE] : safeRe[t.HYPHENRANGE]
|
||||
range = range.replace(hr, hyphenReplace)
|
||||
debug('hyphen replace', range)
|
||||
// `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
|
||||
range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)
|
||||
debug('comparator trim', range, re[t.COMPARATORTRIM])
|
||||
range = range.replace(safeRe[t.COMPARATORTRIM], comparatorTrimReplace)
|
||||
debug('comparator trim', range, safeRe[t.COMPARATORTRIM])
|
||||
|
||||
// `~ 1.2.3` => `~1.2.3`
|
||||
range = range.replace(re[t.TILDETRIM], tildeTrimReplace)
|
||||
range = range.replace(safeRe[t.TILDETRIM], tildeTrimReplace)
|
||||
|
||||
// `^ 1.2.3` => `^1.2.3`
|
||||
range = range.replace(re[t.CARETTRIM], caretTrimReplace)
|
||||
range = range.replace(safeRe[t.CARETTRIM], caretTrimReplace)
|
||||
|
||||
// normalize spaces
|
||||
range = range.split(/\s+/).join(' ')
|
||||
@ -4235,7 +4282,7 @@ Range.prototype.parseRange = function (range) {
|
||||
// At this point, the range is completely trimmed and
|
||||
// ready to be split into comparators.
|
||||
|
||||
var compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
|
||||
var compRe = loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR]
|
||||
var set = range.split(' ').map(function (comp) {
|
||||
return parseComparator(comp, this.options)
|
||||
}, this).join(' ').split(/\s+/)
|
||||
@ -4335,7 +4382,7 @@ function replaceTildes (comp, options) {
|
||||
}
|
||||
|
||||
function replaceTilde (comp, options) {
|
||||
var r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]
|
||||
var r = options.loose ? safeRe[t.TILDELOOSE] : safeRe[t.TILDE]
|
||||
return comp.replace(r, function (_, M, m, p, pr) {
|
||||
debug('tilde', comp, _, M, m, p, pr)
|
||||
var ret
|
||||
@ -4376,7 +4423,7 @@ function replaceCarets (comp, options) {
|
||||
|
||||
function replaceCaret (comp, options) {
|
||||
debug('caret', comp, options)
|
||||
var r = options.loose ? re[t.CARETLOOSE] : re[t.CARET]
|
||||
var r = options.loose ? safeRe[t.CARETLOOSE] : safeRe[t.CARET]
|
||||
return comp.replace(r, function (_, M, m, p, pr) {
|
||||
debug('caret', comp, _, M, m, p, pr)
|
||||
var ret
|
||||
@ -4435,7 +4482,7 @@ function replaceXRanges (comp, options) {
|
||||
|
||||
function replaceXRange (comp, options) {
|
||||
comp = comp.trim()
|
||||
var r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE]
|
||||
var r = options.loose ? safeRe[t.XRANGELOOSE] : safeRe[t.XRANGE]
|
||||
return comp.replace(r, function (ret, gtlt, M, m, p, pr) {
|
||||
debug('xRange', comp, ret, gtlt, M, m, p, pr)
|
||||
var xM = isX(M)
|
||||
@ -4510,7 +4557,7 @@ function replaceXRange (comp, options) {
|
||||
function replaceStars (comp, options) {
|
||||
debug('replaceStars', comp, options)
|
||||
// Looseness is ignored here. star is always as loose as it gets!
|
||||
return comp.trim().replace(re[t.STAR], '')
|
||||
return comp.trim().replace(safeRe[t.STAR], '')
|
||||
}
|
||||
|
||||
// This function is passed to string.replace(re[t.HYPHENRANGE])
|
||||
@ -4836,7 +4883,7 @@ function coerce (version, options) {
|
||||
|
||||
var match = null
|
||||
if (!options.rtl) {
|
||||
match = version.match(re[t.COERCE])
|
||||
match = version.match(safeRe[t.COERCE])
|
||||
} else {
|
||||
// Find the right-most coercible string that does not share
|
||||
// a terminus with a more left-ward coercible string.
|
||||
@ -4847,17 +4894,17 @@ function coerce (version, options) {
|
||||
// Stop when we get a match that ends at the string end, since no
|
||||
// coercible string can be more right-ward without the same terminus.
|
||||
var next
|
||||
while ((next = re[t.COERCERTL].exec(version)) &&
|
||||
while ((next = safeRe[t.COERCERTL].exec(version)) &&
|
||||
(!match || match.index + match[0].length !== version.length)
|
||||
) {
|
||||
if (!match ||
|
||||
next.index + next[0].length !== match.index + match[0].length) {
|
||||
match = next
|
||||
}
|
||||
re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length
|
||||
safeRe[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length
|
||||
}
|
||||
// leave it in a clean state
|
||||
re[t.COERCERTL].lastIndex = -1
|
||||
safeRe[t.COERCERTL].lastIndex = -1
|
||||
}
|
||||
|
||||
if (match === null) {
|
||||
@ -59042,7 +59089,11 @@ module.exports = v4;
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
@ -59151,7 +59202,11 @@ exports.CACHE_DEPENDENCY_BACKUP_PATH = '**/pyproject.toml';
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
|
747
dist/setup/index.js
vendored
747
dist/setup/index.js
vendored
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,7 @@ If there is a specific version of Python that you need and you don't want to wor
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.7.5'
|
||||
@ -44,7 +44,7 @@ You can specify **only a major and minor version** if you are okay with the most
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.7'
|
||||
@ -58,7 +58,7 @@ You can specify the version with **prerelease tag** to download and set up an ac
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.12.0-alpha.1'
|
||||
@ -69,7 +69,7 @@ It's also possible to use **x.y-dev syntax** to download and set up the latest p
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.12-dev'
|
||||
@ -82,7 +82,7 @@ You can also use several types of ranges that are specified in [semver](https://
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '>=3.9 <3.10'
|
||||
@ -93,7 +93,7 @@ steps:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.12.0-alpha - 3.12.0'
|
||||
@ -104,7 +104,7 @@ steps:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
@ -117,7 +117,7 @@ The version of PyPy should be specified in the format `pypy<python_version>[-v<p
|
||||
The `-v<pypy_version>` parameter is optional and can be skipped. The latest PyPy version will be used in this case.
|
||||
|
||||
```
|
||||
pypy3.8 or pypy-3.8 # the latest available version of PyPy that supports Python 3.8
|
||||
pypy3.9 or pypy-3.9 # the latest available version of PyPy that supports Python 3.9
|
||||
pypy2.7 or pypy-2.7 # the latest available version of PyPy that supports Python 2.7
|
||||
pypy3.7-v7.3.3 or pypy-3.7-v7.3.3 # Python 3.7 and PyPy 7.3.3
|
||||
pypy3.7-v7.x or pypy-3.7-v7.x # Python 3.7 and the latest available PyPy 7.x
|
||||
@ -137,7 +137,7 @@ jobs:
|
||||
- 'pypy3.7' # the latest available version of PyPy that supports Python 3.7
|
||||
- 'pypy3.7-v7.3.3' # Python 3.7 and PyPy 7.3.3
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
@ -155,7 +155,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: |
|
||||
@ -172,7 +172,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: |
|
||||
@ -189,7 +189,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: |
|
||||
@ -211,10 +211,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [ '2.x', '3.x', 'pypy2.7', 'pypy3.7', 'pypy3.8' ]
|
||||
python-version: [ '2.x', '3.x', 'pypy2.7', 'pypy3.8', 'pypy3.9' ]
|
||||
name: Python ${{ matrix.python-version }} sample
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
@ -232,14 +232,14 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
python-version: ['2.7', '3.7', '3.8', '3.9', '3.10', 'pypy2.7', 'pypy3.8']
|
||||
python-version: ['2.7', '3.7', '3.8', '3.9', '3.10', 'pypy2.7', 'pypy3.9']
|
||||
exclude:
|
||||
- os: macos-latest
|
||||
python-version: '3.8'
|
||||
- os: windows-latest
|
||||
python-version: '3.6'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
@ -256,7 +256,7 @@ jobs:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version-file: '.python-version' # Read python version from a file .python-version
|
||||
@ -265,7 +265,7 @@ steps:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version-file: 'pyproject.toml' # Read python version from a file pyproject.toml
|
||||
@ -280,7 +280,7 @@ If `check-latest` is set to `true`, the action first checks if the cached versio
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.7'
|
||||
@ -295,7 +295,7 @@ steps:
|
||||
**Caching pipenv dependencies:**
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9'
|
||||
@ -308,7 +308,7 @@ steps:
|
||||
**Caching poetry dependencies:**
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install poetry
|
||||
run: pipx install poetry
|
||||
- uses: actions/setup-python@v4
|
||||
@ -322,7 +322,7 @@ steps:
|
||||
**Using a list of file paths to cache dependencies**
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9'
|
||||
@ -337,7 +337,7 @@ steps:
|
||||
**Using wildcard patterns to cache dependencies**
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9'
|
||||
@ -349,7 +349,7 @@ steps:
|
||||
**Using a list of wildcard patterns to cache dependencies**
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
@ -364,7 +364,7 @@ steps:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
@ -387,7 +387,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
id: cp310
|
||||
with:
|
||||
@ -404,7 +404,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
id: cp310
|
||||
with:
|
||||
@ -420,7 +420,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
id: cp310
|
||||
with:
|
||||
@ -451,7 +451,7 @@ Such a requirement on side-effect could be because you don't want your composite
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
id: cp310
|
||||
with:
|
||||
@ -611,7 +611,7 @@ jobs:
|
||||
python_version: ["3.11", "3.12"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "${{ matrix.python_version }}"
|
||||
|
7134
package-lock.json
generated
7134
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -36,21 +36,21 @@
|
||||
"semver": "^7.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/jest": "^29.5.6",
|
||||
"@types/node": "^16.11.25",
|
||||
"@types/semver": "^7.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||
"@typescript-eslint/parser": "^5.54.0",
|
||||
"@vercel/ncc": "^0.33.4",
|
||||
"@vercel/ncc": "^0.38.0",
|
||||
"eslint": "^8.35.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-jest": "^27.2.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"husky": "^7.0.2",
|
||||
"jest": "^27.2.5",
|
||||
"jest-circus": "^27.2.5",
|
||||
"jest": "^29.7.0",
|
||||
"jest-circus": "^29.7.0",
|
||||
"prettier": "^2.8.4",
|
||||
"ts-jest": "^27.0.5",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"husky": {
|
||||
|
146
src/find-graalpy.ts
Normal file
146
src/find-graalpy.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import * as path from 'path';
|
||||
import * as graalpyInstall from './install-graalpy';
|
||||
import {
|
||||
IS_WINDOWS,
|
||||
validateVersion,
|
||||
IGraalPyManifestRelease,
|
||||
getBinaryDirectory
|
||||
} from './utils';
|
||||
|
||||
import * as semver from 'semver';
|
||||
import * as core from '@actions/core';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
|
||||
export async function findGraalPyVersion(
|
||||
versionSpec: string,
|
||||
architecture: string,
|
||||
updateEnvironment: boolean,
|
||||
checkLatest: boolean,
|
||||
allowPreReleases: boolean
|
||||
): Promise<string> {
|
||||
let resolvedGraalPyVersion = '';
|
||||
let installDir: string | null;
|
||||
let releases: IGraalPyManifestRelease[] | undefined;
|
||||
|
||||
let graalpyVersionSpec = parseGraalPyVersion(versionSpec);
|
||||
|
||||
if (checkLatest) {
|
||||
releases = await graalpyInstall.getAvailableGraalPyVersions();
|
||||
if (releases && releases.length > 0) {
|
||||
const releaseData = graalpyInstall.findRelease(
|
||||
releases,
|
||||
graalpyVersionSpec,
|
||||
architecture,
|
||||
false
|
||||
);
|
||||
|
||||
if (releaseData) {
|
||||
core.info(`Resolved as GraalPy ${releaseData.resolvedGraalPyVersion}`);
|
||||
graalpyVersionSpec = releaseData.resolvedGraalPyVersion;
|
||||
} else {
|
||||
core.info(
|
||||
`Failed to resolve GraalPy ${graalpyVersionSpec} from manifest`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
({installDir, resolvedGraalPyVersion} = findGraalPyToolCache(
|
||||
graalpyVersionSpec,
|
||||
architecture
|
||||
));
|
||||
|
||||
if (!installDir) {
|
||||
({installDir, resolvedGraalPyVersion} = await graalpyInstall.installGraalPy(
|
||||
graalpyVersionSpec,
|
||||
architecture,
|
||||
allowPreReleases,
|
||||
releases
|
||||
));
|
||||
}
|
||||
|
||||
const pipDir = IS_WINDOWS ? 'Scripts' : 'bin';
|
||||
const _binDir = path.join(installDir, pipDir);
|
||||
const binaryExtension = IS_WINDOWS ? '.exe' : '';
|
||||
const pythonPath = path.join(
|
||||
IS_WINDOWS ? installDir : _binDir,
|
||||
`python${binaryExtension}`
|
||||
);
|
||||
const pythonLocation = getBinaryDirectory(installDir);
|
||||
if (updateEnvironment) {
|
||||
core.exportVariable('pythonLocation', installDir);
|
||||
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
|
||||
core.exportVariable('Python_ROOT_DIR', installDir);
|
||||
// https://cmake.org/cmake/help/latest/module/FindPython2.html#module:FindPython2
|
||||
core.exportVariable('Python2_ROOT_DIR', installDir);
|
||||
// https://cmake.org/cmake/help/latest/module/FindPython3.html#module:FindPython3
|
||||
core.exportVariable('Python3_ROOT_DIR', installDir);
|
||||
core.exportVariable('PKG_CONFIG_PATH', pythonLocation + '/lib/pkgconfig');
|
||||
core.addPath(pythonLocation);
|
||||
core.addPath(_binDir);
|
||||
}
|
||||
core.setOutput('python-version', 'graalpy' + resolvedGraalPyVersion);
|
||||
core.setOutput('python-path', pythonPath);
|
||||
|
||||
return resolvedGraalPyVersion;
|
||||
}
|
||||
|
||||
export function findGraalPyToolCache(
|
||||
graalpyVersion: string,
|
||||
architecture: string
|
||||
) {
|
||||
let resolvedGraalPyVersion = '';
|
||||
let installDir: string | null = tc.find(
|
||||
'GraalPy',
|
||||
graalpyVersion,
|
||||
architecture
|
||||
);
|
||||
|
||||
if (installDir) {
|
||||
// 'tc.find' finds tool based on Python version but we also need to check
|
||||
// whether GraalPy version satisfies requested version.
|
||||
resolvedGraalPyVersion = path.basename(path.dirname(installDir));
|
||||
|
||||
const isGraalPyVersionSatisfies = semver.satisfies(
|
||||
resolvedGraalPyVersion,
|
||||
graalpyVersion
|
||||
);
|
||||
if (!isGraalPyVersionSatisfies) {
|
||||
installDir = null;
|
||||
resolvedGraalPyVersion = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (!installDir) {
|
||||
core.info(
|
||||
`GraalPy version ${graalpyVersion} was not found in the local cache`
|
||||
);
|
||||
}
|
||||
|
||||
return {installDir, resolvedGraalPyVersion};
|
||||
}
|
||||
|
||||
export function parseGraalPyVersion(versionSpec: string): string {
|
||||
const versions = versionSpec.split('-').filter(item => !!item);
|
||||
|
||||
if (/^(graalpy)(.+)/.test(versions[0])) {
|
||||
const version = versions[0].replace('graalpy', '');
|
||||
versions.splice(0, 1, 'graalpy', version);
|
||||
}
|
||||
|
||||
if (versions.length < 2 || versions[0] != 'graalpy') {
|
||||
throw new Error(
|
||||
"Invalid 'version' property for GraalPy. GraalPy version should be specified as 'graalpy<python-version>' or 'graalpy-<python-version>'. See README for examples and documentation."
|
||||
);
|
||||
}
|
||||
|
||||
const pythonVersion = versions[1];
|
||||
|
||||
if (!validateVersion(pythonVersion)) {
|
||||
throw new Error(
|
||||
"Invalid 'version' property for GraalPy. GraalPy versions should satisfy SemVer notation. See README for examples and documentation."
|
||||
);
|
||||
}
|
||||
|
||||
return pythonVersion;
|
||||
}
|
@ -7,7 +7,8 @@ import {
|
||||
getPyPyVersionFromPath,
|
||||
readExactPyPyVersionFile,
|
||||
validatePythonVersionFormatForPyPy,
|
||||
IPyPyManifestRelease
|
||||
IPyPyManifestRelease,
|
||||
getBinaryDirectory
|
||||
} from './utils';
|
||||
|
||||
import * as semver from 'semver';
|
||||
@ -82,7 +83,7 @@ export async function findPyPyVersion(
|
||||
IS_WINDOWS ? installDir : _binDir,
|
||||
`python${binaryExtension}`
|
||||
);
|
||||
const pythonLocation = pypyInstall.getPyPyBinaryPath(installDir);
|
||||
const pythonLocation = getBinaryDirectory(installDir);
|
||||
if (updateEnvironment) {
|
||||
core.exportVariable('pythonLocation', installDir);
|
||||
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
|
||||
|
262
src/install-graalpy.ts
Normal file
262
src/install-graalpy.ts
Normal file
@ -0,0 +1,262 @@
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as core from '@actions/core';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as semver from 'semver';
|
||||
import * as httpm from '@actions/http-client';
|
||||
import * as ifm from '@actions/http-client/interfaces';
|
||||
import * as exec from '@actions/exec';
|
||||
import fs from 'fs';
|
||||
|
||||
import {
|
||||
IS_WINDOWS,
|
||||
IGraalPyManifestRelease,
|
||||
createSymlinkInFolder,
|
||||
isNightlyKeyword,
|
||||
getBinaryDirectory,
|
||||
getNextPageUrl
|
||||
} from './utils';
|
||||
|
||||
const TOKEN = core.getInput('token');
|
||||
const AUTH = !TOKEN ? undefined : `token ${TOKEN}`;
|
||||
|
||||
export async function installGraalPy(
|
||||
graalpyVersion: string,
|
||||
architecture: string,
|
||||
allowPreReleases: boolean,
|
||||
releases: IGraalPyManifestRelease[] | undefined
|
||||
) {
|
||||
let downloadDir;
|
||||
|
||||
releases = releases ?? (await getAvailableGraalPyVersions());
|
||||
|
||||
if (!releases || !releases.length) {
|
||||
throw new Error('No release was found in GraalPy version.json');
|
||||
}
|
||||
|
||||
let releaseData = findRelease(releases, graalpyVersion, architecture, false);
|
||||
|
||||
if (allowPreReleases && (!releaseData || !releaseData.foundAsset)) {
|
||||
// check for pre-release
|
||||
core.info(
|
||||
[
|
||||
`Stable GraalPy version ${graalpyVersion} with arch ${architecture} not found`,
|
||||
`Trying pre-release versions`
|
||||
].join(os.EOL)
|
||||
);
|
||||
releaseData = findRelease(releases, graalpyVersion, architecture, true);
|
||||
}
|
||||
|
||||
if (!releaseData || !releaseData.foundAsset) {
|
||||
throw new Error(
|
||||
`GraalPy version ${graalpyVersion} with arch ${architecture} not found`
|
||||
);
|
||||
}
|
||||
|
||||
const {foundAsset, resolvedGraalPyVersion} = releaseData;
|
||||
const downloadUrl = `${foundAsset.browser_download_url}`;
|
||||
|
||||
core.info(`Downloading GraalPy from "${downloadUrl}" ...`);
|
||||
|
||||
try {
|
||||
const graalpyPath = await tc.downloadTool(downloadUrl, undefined, AUTH);
|
||||
|
||||
core.info('Extracting downloaded archive...');
|
||||
downloadDir = await tc.extractTar(graalpyPath);
|
||||
|
||||
// root folder in archive can have unpredictable name so just take the first folder
|
||||
// downloadDir is unique folder under TEMP and can't contain any other folders
|
||||
const archiveName = fs.readdirSync(downloadDir)[0];
|
||||
|
||||
const toolDir = path.join(downloadDir, archiveName);
|
||||
let installDir = toolDir;
|
||||
if (!isNightlyKeyword(resolvedGraalPyVersion)) {
|
||||
installDir = await tc.cacheDir(
|
||||
toolDir,
|
||||
'GraalPy',
|
||||
resolvedGraalPyVersion,
|
||||
architecture
|
||||
);
|
||||
}
|
||||
|
||||
const binaryPath = getBinaryDirectory(installDir);
|
||||
await createGraalPySymlink(binaryPath, resolvedGraalPyVersion);
|
||||
await installPip(binaryPath);
|
||||
|
||||
return {installDir, resolvedGraalPyVersion};
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
// Rate limit?
|
||||
if (
|
||||
err instanceof tc.HTTPError &&
|
||||
(err.httpStatusCode === 403 || err.httpStatusCode === 429)
|
||||
) {
|
||||
core.info(
|
||||
`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`
|
||||
);
|
||||
} else {
|
||||
core.info(err.message);
|
||||
}
|
||||
if (err.stack !== undefined) {
|
||||
core.debug(err.stack);
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAvailableGraalPyVersions() {
|
||||
const http: httpm.HttpClient = new httpm.HttpClient('tool-cache');
|
||||
|
||||
const headers: ifm.IHeaders = {};
|
||||
if (AUTH) {
|
||||
headers.authorization = AUTH;
|
||||
}
|
||||
|
||||
let url: string | null =
|
||||
'https://api.github.com/repos/oracle/graalpython/releases';
|
||||
const result: IGraalPyManifestRelease[] = [];
|
||||
do {
|
||||
const response: ifm.ITypedResponse<IGraalPyManifestRelease[]> =
|
||||
await http.getJson(url, headers);
|
||||
if (!response.result) {
|
||||
throw new Error(
|
||||
`Unable to retrieve the list of available GraalPy versions from '${url}'`
|
||||
);
|
||||
}
|
||||
result.push(...response.result);
|
||||
url = getNextPageUrl(response);
|
||||
} while (url);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function createGraalPySymlink(
|
||||
graalpyBinaryPath: string,
|
||||
graalpyVersion: string
|
||||
) {
|
||||
const version = semver.coerce(graalpyVersion)!;
|
||||
const pythonBinaryPostfix = semver.major(version);
|
||||
const pythonMinor = semver.minor(version);
|
||||
const graalpyMajorMinorBinaryPostfix = `${pythonBinaryPostfix}.${pythonMinor}`;
|
||||
const binaryExtension = IS_WINDOWS ? '.exe' : '';
|
||||
|
||||
core.info('Creating symlinks...');
|
||||
createSymlinkInFolder(
|
||||
graalpyBinaryPath,
|
||||
`graalpy${binaryExtension}`,
|
||||
`python${pythonBinaryPostfix}${binaryExtension}`,
|
||||
true
|
||||
);
|
||||
|
||||
createSymlinkInFolder(
|
||||
graalpyBinaryPath,
|
||||
`graalpy${binaryExtension}`,
|
||||
`python${binaryExtension}`,
|
||||
true
|
||||
);
|
||||
|
||||
createSymlinkInFolder(
|
||||
graalpyBinaryPath,
|
||||
`graalpy${binaryExtension}`,
|
||||
`graalpy${graalpyMajorMinorBinaryPostfix}${binaryExtension}`,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
async function installPip(pythonLocation: string) {
|
||||
core.info(
|
||||
"Installing pip (GraalPy doesn't update pip because it uses a patched version of pip)"
|
||||
);
|
||||
const pythonBinary = path.join(pythonLocation, 'python');
|
||||
await exec.exec(`${pythonBinary} -m ensurepip --default-pip`);
|
||||
}
|
||||
|
||||
export function graalPyTagToVersion(tag: string) {
|
||||
const versionPattern = /.*-(\d+\.\d+\.\d+(?:\.\d+)?)((?:a|b|rc))?(\d*)?/;
|
||||
const match = tag.match(versionPattern);
|
||||
if (match && match[2]) {
|
||||
return `${match[1]}-${match[2]}.${match[3]}`;
|
||||
} else if (match) {
|
||||
return match[1];
|
||||
} else {
|
||||
return tag.replace(/.*-/, '');
|
||||
}
|
||||
}
|
||||
|
||||
export function findRelease(
|
||||
releases: IGraalPyManifestRelease[],
|
||||
graalpyVersion: string,
|
||||
architecture: string,
|
||||
includePrerelease: boolean
|
||||
) {
|
||||
const options = {includePrerelease: includePrerelease};
|
||||
const filterReleases = releases.filter(item => {
|
||||
const isVersionSatisfied = semver.satisfies(
|
||||
graalPyTagToVersion(item.tag_name),
|
||||
graalpyVersion,
|
||||
options
|
||||
);
|
||||
return (
|
||||
isVersionSatisfied && !!findAsset(item, architecture, process.platform)
|
||||
);
|
||||
});
|
||||
|
||||
if (!filterReleases.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sortedReleases = filterReleases.sort((previous, current) =>
|
||||
semver.compare(
|
||||
semver.coerce(graalPyTagToVersion(current.tag_name))!,
|
||||
semver.coerce(graalPyTagToVersion(previous.tag_name))!
|
||||
)
|
||||
);
|
||||
|
||||
const foundRelease = sortedReleases[0];
|
||||
const foundAsset = findAsset(foundRelease, architecture, process.platform);
|
||||
|
||||
return {
|
||||
foundAsset,
|
||||
resolvedGraalPyVersion: graalPyTagToVersion(foundRelease.tag_name)
|
||||
};
|
||||
}
|
||||
|
||||
export function toGraalPyPlatform(platform: string) {
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
return 'windows';
|
||||
case 'darwin':
|
||||
return 'macos';
|
||||
}
|
||||
return platform;
|
||||
}
|
||||
|
||||
export function toGraalPyArchitecture(architecture: string) {
|
||||
switch (architecture) {
|
||||
case 'x64':
|
||||
return 'amd64';
|
||||
case 'arm64':
|
||||
return 'aarch64';
|
||||
}
|
||||
return architecture;
|
||||
}
|
||||
|
||||
export function findAsset(
|
||||
item: IGraalPyManifestRelease,
|
||||
architecture: string,
|
||||
platform: string
|
||||
) {
|
||||
const graalpyArch = toGraalPyArchitecture(architecture);
|
||||
const graalpyPlatform = toGraalPyPlatform(platform);
|
||||
const found = item.assets.filter(
|
||||
file =>
|
||||
file.name.startsWith('graalpy') &&
|
||||
file.name.endsWith(`-${graalpyPlatform}-${graalpyArch}.tar.gz`)
|
||||
);
|
||||
/*
|
||||
In the future there could be more variants of GraalPy for a single release. Pick the shortest name, that one is the most likely to be the primary variant.
|
||||
*/
|
||||
found.sort((f1, f2) => f1.name.length - f2.name.length);
|
||||
return found[0];
|
||||
}
|
@ -13,7 +13,8 @@ import {
|
||||
IPyPyManifestRelease,
|
||||
createSymlinkInFolder,
|
||||
isNightlyKeyword,
|
||||
writeExactPyPyVersionFile
|
||||
writeExactPyPyVersionFile,
|
||||
getBinaryDirectory
|
||||
} from './utils';
|
||||
|
||||
export async function installPyPy(
|
||||
@ -94,7 +95,7 @@ export async function installPyPy(
|
||||
|
||||
writeExactPyPyVersionFile(installDir, resolvedPyPyVersion);
|
||||
|
||||
const binaryPath = getPyPyBinaryPath(installDir);
|
||||
const binaryPath = getBinaryDirectory(installDir);
|
||||
await createPyPySymlink(binaryPath, resolvedPythonVersion);
|
||||
await installPip(binaryPath);
|
||||
|
||||
@ -237,15 +238,6 @@ export function findRelease(
|
||||
};
|
||||
}
|
||||
|
||||
/** Get PyPy binary location from the tool of installation directory
|
||||
* - On Linux and macOS, the Python interpreter is in 'bin'.
|
||||
* - On Windows, it is in the installation root.
|
||||
*/
|
||||
export function getPyPyBinaryPath(installDir: string) {
|
||||
const _binDir = path.join(installDir, 'bin');
|
||||
return IS_WINDOWS ? installDir : _binDir;
|
||||
}
|
||||
|
||||
export function pypyVersionToSemantic(versionSpec: string) {
|
||||
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g;
|
||||
return versionSpec.replace(prereleaseVersion, '$1-$2.$3');
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as finder from './find-python';
|
||||
import * as finderPyPy from './find-pypy';
|
||||
import * as finderGraalPy from './find-graalpy';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import fs from 'fs';
|
||||
@ -17,6 +18,10 @@ function isPyPyVersion(versionSpec: string) {
|
||||
return versionSpec.startsWith('pypy');
|
||||
}
|
||||
|
||||
function isGraalPyVersion(versionSpec: string) {
|
||||
return versionSpec.startsWith('graalpy');
|
||||
}
|
||||
|
||||
async function cacheDependencies(cache: string, pythonVersion: string) {
|
||||
const cacheDependencyPath =
|
||||
core.getInput('cache-dependency-path') || undefined;
|
||||
@ -106,10 +111,20 @@ async function run() {
|
||||
core.info(
|
||||
`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`
|
||||
);
|
||||
} else if (isGraalPyVersion(version)) {
|
||||
const installed = await finderGraalPy.findGraalPyVersion(
|
||||
version,
|
||||
arch,
|
||||
updateEnvironment,
|
||||
checkLatest,
|
||||
allowPreReleases
|
||||
);
|
||||
pythonVersion = `${installed}`;
|
||||
core.info(`Successfully set up GraalPy ${installed}`);
|
||||
} else {
|
||||
if (version.startsWith('2')) {
|
||||
core.warning(
|
||||
'The support for python 2.7 will be removed on June 19. Related issue: https://github.com/actions/setup-python/issues/672'
|
||||
'The support for python 2.7 was removed on June 19, 2023. Related issue: https://github.com/actions/setup-python/issues/672'
|
||||
);
|
||||
}
|
||||
const installed = await finder.useCpythonVersion(
|
||||
|
62
src/utils.ts
62
src/utils.ts
@ -6,6 +6,7 @@ import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
import * as toml from '@iarna/toml';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as ifm from '@actions/http-client/interfaces';
|
||||
|
||||
export const IS_WINDOWS = process.platform === 'win32';
|
||||
export const IS_LINUX = process.platform === 'linux';
|
||||
@ -29,6 +30,16 @@ export interface IPyPyManifestRelease {
|
||||
files: IPyPyManifestAsset[];
|
||||
}
|
||||
|
||||
export interface IGraalPyManifestAsset {
|
||||
name: string;
|
||||
browser_download_url: string;
|
||||
}
|
||||
|
||||
export interface IGraalPyManifestRelease {
|
||||
tag_name: string;
|
||||
assets: IGraalPyManifestAsset[];
|
||||
}
|
||||
|
||||
/** create Symlinks for downloaded PyPy
|
||||
* It should be executed only for downloaded versions in runtime, because
|
||||
* toolcache versions have this setup.
|
||||
@ -90,7 +101,7 @@ export function writeExactPyPyVersionFile(
|
||||
}
|
||||
|
||||
/**
|
||||
* Python version should be specified explicitly like "x.y" (2.7, 3.6, 3.7)
|
||||
* Python version should be specified explicitly like "x.y" (3.10, 3.11, etc)
|
||||
* "3.x" or "3" are not supported
|
||||
* because it could cause ambiguity when both PyPy version and Python version are not precise
|
||||
*/
|
||||
@ -229,7 +240,21 @@ export function getVersionInputFromTomlFile(versionFile: string): string[] {
|
||||
}
|
||||
|
||||
core.info(`Extracted ${versions} from ${versionFile}`);
|
||||
return Array.from(versions, version => version.split(',').join(' '));
|
||||
const rawVersions = Array.from(versions, version =>
|
||||
version.split(',').join(' ')
|
||||
);
|
||||
const validatedVersions = rawVersions
|
||||
.map(item => semver.validRange(item, true))
|
||||
.filter((versionRange, index) => {
|
||||
if (!versionRange) {
|
||||
core.debug(
|
||||
`The version ${rawVersions[index]} is not valid SemVer range`
|
||||
);
|
||||
}
|
||||
|
||||
return !!versionRange;
|
||||
}) as string[];
|
||||
return validatedVersions;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,7 +262,7 @@ export function getVersionInputFromTomlFile(versionFile: string): string[] {
|
||||
*/
|
||||
export function getVersionInputFromPlainFile(versionFile: string): string[] {
|
||||
core.debug(`Trying to resolve version form ${versionFile}`);
|
||||
const version = fs.readFileSync(versionFile, 'utf8');
|
||||
const version = fs.readFileSync(versionFile, 'utf8').trim();
|
||||
core.info(`Resolved ${versionFile} as ${version}`);
|
||||
return [version];
|
||||
}
|
||||
@ -252,3 +277,34 @@ export function getVersionInputFromFile(versionFile: string): string[] {
|
||||
return getVersionInputFromPlainFile(versionFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the directory containing interpreter binary from installation directory of PyPy or GraalPy
|
||||
* - On Linux and macOS, the Python interpreter is in 'bin'.
|
||||
* - On Windows, it is in the installation root.
|
||||
*/
|
||||
export function getBinaryDirectory(installDir: string) {
|
||||
return IS_WINDOWS ? installDir : path.join(installDir, 'bin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract next page URL from a HTTP response "link" header. Such headers are used in GitHub APIs.
|
||||
*/
|
||||
export function getNextPageUrl<T>(response: ifm.ITypedResponse<T>) {
|
||||
const responseHeaders = <ifm.IHeaders>response.headers;
|
||||
const linkHeader = responseHeaders.link;
|
||||
if (typeof linkHeader === 'string') {
|
||||
for (const link of linkHeader.split(/\s*,\s*/)) {
|
||||
const match = link.match(/<([^>]+)>(.*)/);
|
||||
if (match) {
|
||||
const url = match[1];
|
||||
for (const param of match[2].split(/\s*;\s*/)) {
|
||||
if (param.match(/rel="?next"?/)) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
Reference in New Issue
Block a user