mirror of
https://github.com/actions/setup-python.git
synced 2025-06-25 05:01:09 +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]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
python-version: ['3.9', 'pypy-3.7-v7.x']
|
python-version: ['3.9', 'pypy-3.7-v7.x']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@ -41,7 +41,7 @@ jobs:
|
|||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
python-version: ['3.9', 'pypy-3.9-v7.x']
|
python-version: ['3.9', 'pypy-3.9-v7.x']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
id: cache-pipenv
|
id: cache-pipenv
|
||||||
uses: ./
|
uses: ./
|
||||||
@ -77,7 +77,7 @@ jobs:
|
|||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
python-version: ['3.9', 'pypy-3.8']
|
python-version: ['3.9', 'pypy-3.8']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install poetry
|
- name: Install poetry
|
||||||
run: pipx install poetry
|
run: pipx install poetry
|
||||||
- name: Init pyproject.toml
|
- name: Init pyproject.toml
|
||||||
@ -99,7 +99,7 @@ jobs:
|
|||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
python-version: ['3.9', 'pypy-3.7-v7.x']
|
python-version: ['3.9', 'pypy-3.7-v7.x']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@ -118,7 +118,7 @@ jobs:
|
|||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
python-version: ['3.9', 'pypy-3.9-v7.x']
|
python-version: ['3.9', 'pypy-3.9-v7.x']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
id: cache-pipenv
|
id: cache-pipenv
|
||||||
uses: ./
|
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]
|
operating-system: [ubuntu-20.04, windows-latest]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run with setup-python 3.5
|
- name: Run with setup-python 3.5
|
||||||
uses: ./
|
uses: ./
|
||||||
@ -93,10 +93,3 @@ jobs:
|
|||||||
python-version: '<3.11'
|
python-version: '<3.11'
|
||||||
- name: Verify <3.11
|
- name: Verify <3.11
|
||||||
run: python __tests__/verify-python.py 3.10
|
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:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: setup-python ${{ matrix.pypy }}
|
- name: setup-python ${{ matrix.pypy }}
|
||||||
id: setup-python
|
id: setup-python
|
||||||
@ -78,7 +78,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: setup-python ${{ matrix.pypy }}
|
- name: setup-python ${{ matrix.pypy }}
|
||||||
id: setup-python
|
id: setup-python
|
||||||
@ -100,7 +100,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup PyPy and check latest
|
- name: Setup PyPy and check latest
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@ -133,7 +133,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup PyPy and check latest
|
- name: Setup PyPy and check latest
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
|
26
.github/workflows/test-python.yml
vendored
26
.github/workflows/test-python.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
|||||||
python: 3.8.15
|
python: 3.8.15
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: setup-python ${{ matrix.python }}
|
- name: setup-python ${{ matrix.python }}
|
||||||
id: setup-python
|
id: setup-python
|
||||||
@ -77,7 +77,7 @@ jobs:
|
|||||||
python: 3.8.15
|
python: 3.8.15
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: build-version-file ${{ matrix.python }}
|
- name: build-version-file ${{ matrix.python }}
|
||||||
run: echo ${{ matrix.python }} > .python-version
|
run: echo ${{ matrix.python }} > .python-version
|
||||||
@ -124,7 +124,7 @@ jobs:
|
|||||||
python: 3.8.15
|
python: 3.8.15
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: build-version-file ${{ matrix.python }}
|
- name: build-version-file ${{ matrix.python }}
|
||||||
run: echo ${{ matrix.python }} > .python-version
|
run: echo ${{ matrix.python }} > .python-version
|
||||||
@ -157,7 +157,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, windows-latest, ubuntu-20.04, ubuntu-22.04]
|
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:
|
exclude:
|
||||||
- os: ubuntu-22.04
|
- os: ubuntu-22.04
|
||||||
python: 3.5.4
|
python: 3.5.4
|
||||||
@ -169,7 +169,7 @@ jobs:
|
|||||||
python: 3.8.15
|
python: 3.8.15
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: build-version-file ${{ matrix.python }}
|
- name: build-version-file ${{ matrix.python }}
|
||||||
run: |
|
run: |
|
||||||
@ -190,7 +190,7 @@ jobs:
|
|||||||
- name: Validate version
|
- name: Validate version
|
||||||
run: |
|
run: |
|
||||||
$pythonVersion = (python --version)
|
$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 }}"
|
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@ -219,7 +219,7 @@ jobs:
|
|||||||
python: 3.8.15
|
python: 3.8.15
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: build-version-file ${{ matrix.python }}
|
- name: build-version-file ${{ matrix.python }}
|
||||||
run: |
|
run: |
|
||||||
@ -259,7 +259,7 @@ jobs:
|
|||||||
os: [macos-latest, windows-latest, ubuntu-20.04]
|
os: [macos-latest, windows-latest, ubuntu-20.04]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: setup-python 3.9.0-beta.4
|
- name: setup-python 3.9.0-beta.4
|
||||||
id: setup-python
|
id: setup-python
|
||||||
@ -293,7 +293,7 @@ jobs:
|
|||||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: setup-python 3.9-dev
|
- name: setup-python 3.9-dev
|
||||||
id: setup-python
|
id: setup-python
|
||||||
@ -321,7 +321,7 @@ jobs:
|
|||||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: setup-python 3.12
|
- name: setup-python 3.12
|
||||||
id: setup-python
|
id: setup-python
|
||||||
@ -351,7 +351,7 @@ jobs:
|
|||||||
python: ['3.7', '3.8', '3.9', '3.10']
|
python: ['3.7', '3.8', '3.9', '3.10']
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: setup-python ${{ matrix.python }}
|
- name: setup-python ${{ matrix.python }}
|
||||||
id: setup-python
|
id: setup-python
|
||||||
@ -374,7 +374,7 @@ jobs:
|
|||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
python-version: ['3.8', '3.9', '3.10']
|
python-version: ['3.8', '3.9', '3.10']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Python and check latest
|
- name: Setup Python and check latest
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@ -397,7 +397,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Python and check latest
|
- name: Setup Python and check latest
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: semver
|
name: semver
|
||||||
version: 6.3.0
|
version: 6.3.1
|
||||||
type: npm
|
type: npm
|
||||||
summary: The semantic version parser used by npm.
|
summary: The semantic version parser used by npm.
|
||||||
homepage: https://github.com/npm/node-semver#readme
|
homepage:
|
||||||
license: isc
|
license: isc
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
@ -18,7 +18,7 @@ See [action.yml](action.yml)
|
|||||||
**Python**
|
**Python**
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
@ -28,7 +28,7 @@ steps:
|
|||||||
**PyPy**
|
**PyPy**
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 'pypy3.9'
|
python-version: 'pypy3.9'
|
||||||
@ -62,7 +62,7 @@ The action defaults to searching for a dependency file (`requirements.txt` or `p
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
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,
|
isCacheFeatureAvailable,
|
||||||
getVersionInputFromFile,
|
getVersionInputFromFile,
|
||||||
getVersionInputFromPlainFile,
|
getVersionInputFromPlainFile,
|
||||||
getVersionInputFromTomlFile
|
getVersionInputFromTomlFile,
|
||||||
|
getNextPageUrl
|
||||||
} from '../src/utils';
|
} from '../src/utils';
|
||||||
|
|
||||||
jest.mock('@actions/cache');
|
jest.mock('@actions/cache');
|
||||||
@ -107,7 +108,7 @@ describe('Version from file test', () => {
|
|||||||
await io.mkdirP(tempDir);
|
await io.mkdirP(tempDir);
|
||||||
const pythonVersionFileName = 'pyproject.toml';
|
const pythonVersionFileName = 'pyproject.toml';
|
||||||
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||||
const pythonVersion = '>=3.7';
|
const pythonVersion = '>=3.7.0';
|
||||||
const pythonVersionFileContent = `[project]\nrequires-python = "${pythonVersion}"`;
|
const pythonVersionFileContent = `[project]\nrequires-python = "${pythonVersion}"`;
|
||||||
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
||||||
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
|
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
|
||||||
@ -119,7 +120,7 @@ describe('Version from file test', () => {
|
|||||||
await io.mkdirP(tempDir);
|
await io.mkdirP(tempDir);
|
||||||
const pythonVersionFileName = 'pyproject.toml';
|
const pythonVersionFileName = 'pyproject.toml';
|
||||||
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||||
const pythonVersion = '>=3.7';
|
const pythonVersion = '>=3.7.0';
|
||||||
const pythonVersionFileContent = `[tool.poetry.dependencies]\npython = "${pythonVersion}"`;
|
const pythonVersionFileContent = `[tool.poetry.dependencies]\npython = "${pythonVersion}"`;
|
||||||
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
||||||
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
|
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.
|
// Max safe segment length for coercion.
|
||||||
var MAX_SAFE_COMPONENT_LENGTH = 16
|
var MAX_SAFE_COMPONENT_LENGTH = 16
|
||||||
|
|
||||||
|
var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
|
||||||
|
|
||||||
// The actual regexps go on exports.re
|
// The actual regexps go on exports.re
|
||||||
var re = exports.re = []
|
var re = exports.re = []
|
||||||
|
var safeRe = exports.safeRe = []
|
||||||
var src = exports.src = []
|
var src = exports.src = []
|
||||||
var t = exports.tokens = {}
|
var t = exports.tokens = {}
|
||||||
var R = 0
|
var R = 0
|
||||||
@ -3310,6 +3313,31 @@ function tok (n) {
|
|||||||
t[n] = R++
|
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,
|
// The following Regular Expressions can be used for tokenizing,
|
||||||
// validating, and parsing SemVer version strings.
|
// validating, and parsing SemVer version strings.
|
||||||
|
|
||||||
@ -3319,14 +3347,14 @@ function tok (n) {
|
|||||||
tok('NUMERICIDENTIFIER')
|
tok('NUMERICIDENTIFIER')
|
||||||
src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*'
|
src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*'
|
||||||
tok('NUMERICIDENTIFIERLOOSE')
|
tok('NUMERICIDENTIFIERLOOSE')
|
||||||
src[t.NUMERICIDENTIFIERLOOSE] = '[0-9]+'
|
src[t.NUMERICIDENTIFIERLOOSE] = '\\d+'
|
||||||
|
|
||||||
// ## Non-numeric Identifier
|
// ## Non-numeric Identifier
|
||||||
// Zero or more digits, followed by a letter or hyphen, and then zero or
|
// Zero or more digits, followed by a letter or hyphen, and then zero or
|
||||||
// more letters, digits, or hyphens.
|
// more letters, digits, or hyphens.
|
||||||
|
|
||||||
tok('NONNUMERICIDENTIFIER')
|
tok('NONNUMERICIDENTIFIER')
|
||||||
src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'
|
src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-]' + LETTERDASHNUMBER + '*'
|
||||||
|
|
||||||
// ## Main Version
|
// ## Main Version
|
||||||
// Three dot-separated numeric identifiers.
|
// Three dot-separated numeric identifiers.
|
||||||
@ -3368,7 +3396,7 @@ src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] +
|
|||||||
// Any combination of digits, letters, or hyphens.
|
// Any combination of digits, letters, or hyphens.
|
||||||
|
|
||||||
tok('BUILDIDENTIFIER')
|
tok('BUILDIDENTIFIER')
|
||||||
src[t.BUILDIDENTIFIER] = '[0-9A-Za-z-]+'
|
src[t.BUILDIDENTIFIER] = LETTERDASHNUMBER + '+'
|
||||||
|
|
||||||
// ## Build Metadata
|
// ## Build Metadata
|
||||||
// Plus sign, followed by one or more period-separated build metadata
|
// Plus sign, followed by one or more period-separated build metadata
|
||||||
@ -3448,6 +3476,7 @@ src[t.COERCE] = '(^|[^\\d])' +
|
|||||||
'(?:$|[^\\d])'
|
'(?:$|[^\\d])'
|
||||||
tok('COERCERTL')
|
tok('COERCERTL')
|
||||||
re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g')
|
re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g')
|
||||||
|
safeRe[t.COERCERTL] = new RegExp(makeSafeRe(src[t.COERCE]), 'g')
|
||||||
|
|
||||||
// Tilde ranges.
|
// Tilde ranges.
|
||||||
// Meaning is "reasonably at or greater than"
|
// Meaning is "reasonably at or greater than"
|
||||||
@ -3457,6 +3486,7 @@ src[t.LONETILDE] = '(?:~>?)'
|
|||||||
tok('TILDETRIM')
|
tok('TILDETRIM')
|
||||||
src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+'
|
src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+'
|
||||||
re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g')
|
re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g')
|
||||||
|
safeRe[t.TILDETRIM] = new RegExp(makeSafeRe(src[t.TILDETRIM]), 'g')
|
||||||
var tildeTrimReplace = '$1~'
|
var tildeTrimReplace = '$1~'
|
||||||
|
|
||||||
tok('TILDE')
|
tok('TILDE')
|
||||||
@ -3472,6 +3502,7 @@ src[t.LONECARET] = '(?:\\^)'
|
|||||||
tok('CARETTRIM')
|
tok('CARETTRIM')
|
||||||
src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+'
|
src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+'
|
||||||
re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g')
|
re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g')
|
||||||
|
safeRe[t.CARETTRIM] = new RegExp(makeSafeRe(src[t.CARETTRIM]), 'g')
|
||||||
var caretTrimReplace = '$1^'
|
var caretTrimReplace = '$1^'
|
||||||
|
|
||||||
tok('CARET')
|
tok('CARET')
|
||||||
@ -3493,6 +3524,7 @@ src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] +
|
|||||||
|
|
||||||
// this one has to use the /g flag
|
// this one has to use the /g flag
|
||||||
re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g')
|
re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g')
|
||||||
|
safeRe[t.COMPARATORTRIM] = new RegExp(makeSafeRe(src[t.COMPARATORTRIM]), 'g')
|
||||||
var comparatorTrimReplace = '$1$2$3'
|
var comparatorTrimReplace = '$1$2$3'
|
||||||
|
|
||||||
// Something like `1.2.3 - 1.2.4`
|
// Something like `1.2.3 - 1.2.4`
|
||||||
@ -3521,6 +3553,14 @@ for (var i = 0; i < R; i++) {
|
|||||||
debug(i, src[i])
|
debug(i, src[i])
|
||||||
if (!re[i]) {
|
if (!re[i]) {
|
||||||
re[i] = new RegExp(src[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
|
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)) {
|
if (!r.test(version)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -3600,7 +3640,7 @@ function SemVer (version, options) {
|
|||||||
this.options = options
|
this.options = options
|
||||||
this.loose = !!options.loose
|
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) {
|
if (!m) {
|
||||||
throw new TypeError('Invalid Version: ' + version)
|
throw new TypeError('Invalid Version: ' + version)
|
||||||
@ -4045,6 +4085,7 @@ function Comparator (comp, options) {
|
|||||||
return new Comparator(comp, options)
|
return new Comparator(comp, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
comp = comp.trim().split(/\s+/).join(' ')
|
||||||
debug('comparator', comp, options)
|
debug('comparator', comp, options)
|
||||||
this.options = options
|
this.options = options
|
||||||
this.loose = !!options.loose
|
this.loose = !!options.loose
|
||||||
@ -4061,7 +4102,7 @@ function Comparator (comp, options) {
|
|||||||
|
|
||||||
var ANY = {}
|
var ANY = {}
|
||||||
Comparator.prototype.parse = function (comp) {
|
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)
|
var m = comp.match(r)
|
||||||
|
|
||||||
if (!m) {
|
if (!m) {
|
||||||
@ -4185,9 +4226,16 @@ function Range (range, options) {
|
|||||||
this.loose = !!options.loose
|
this.loose = !!options.loose
|
||||||
this.includePrerelease = !!options.includePrerelease
|
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.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())
|
return this.parseRange(range.trim())
|
||||||
}, this).filter(function (c) {
|
}, this).filter(function (c) {
|
||||||
// throw out any that are not relevant for whatever reason
|
// throw out any that are not relevant for whatever reason
|
||||||
@ -4195,7 +4243,7 @@ function Range (range, options) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!this.set.length) {
|
if (!this.set.length) {
|
||||||
throw new TypeError('Invalid SemVer Range: ' + range)
|
throw new TypeError('Invalid SemVer Range: ' + this.raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.format()
|
this.format()
|
||||||
@ -4214,20 +4262,19 @@ Range.prototype.toString = function () {
|
|||||||
|
|
||||||
Range.prototype.parseRange = function (range) {
|
Range.prototype.parseRange = function (range) {
|
||||||
var loose = this.options.loose
|
var loose = this.options.loose
|
||||||
range = range.trim()
|
|
||||||
// `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
|
// `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)
|
range = range.replace(hr, hyphenReplace)
|
||||||
debug('hyphen replace', range)
|
debug('hyphen replace', range)
|
||||||
// `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
|
// `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
|
||||||
range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)
|
range = range.replace(safeRe[t.COMPARATORTRIM], comparatorTrimReplace)
|
||||||
debug('comparator trim', range, re[t.COMPARATORTRIM])
|
debug('comparator trim', range, safeRe[t.COMPARATORTRIM])
|
||||||
|
|
||||||
// `~ 1.2.3` => `~1.2.3`
|
// `~ 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`
|
// `^ 1.2.3` => `^1.2.3`
|
||||||
range = range.replace(re[t.CARETTRIM], caretTrimReplace)
|
range = range.replace(safeRe[t.CARETTRIM], caretTrimReplace)
|
||||||
|
|
||||||
// normalize spaces
|
// normalize spaces
|
||||||
range = range.split(/\s+/).join(' ')
|
range = range.split(/\s+/).join(' ')
|
||||||
@ -4235,7 +4282,7 @@ Range.prototype.parseRange = function (range) {
|
|||||||
// At this point, the range is completely trimmed and
|
// At this point, the range is completely trimmed and
|
||||||
// ready to be split into comparators.
|
// 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) {
|
var set = range.split(' ').map(function (comp) {
|
||||||
return parseComparator(comp, this.options)
|
return parseComparator(comp, this.options)
|
||||||
}, this).join(' ').split(/\s+/)
|
}, this).join(' ').split(/\s+/)
|
||||||
@ -4335,7 +4382,7 @@ function replaceTildes (comp, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function replaceTilde (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) {
|
return comp.replace(r, function (_, M, m, p, pr) {
|
||||||
debug('tilde', comp, _, M, m, p, pr)
|
debug('tilde', comp, _, M, m, p, pr)
|
||||||
var ret
|
var ret
|
||||||
@ -4376,7 +4423,7 @@ function replaceCarets (comp, options) {
|
|||||||
|
|
||||||
function replaceCaret (comp, options) {
|
function replaceCaret (comp, options) {
|
||||||
debug('caret', 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) {
|
return comp.replace(r, function (_, M, m, p, pr) {
|
||||||
debug('caret', comp, _, M, m, p, pr)
|
debug('caret', comp, _, M, m, p, pr)
|
||||||
var ret
|
var ret
|
||||||
@ -4435,7 +4482,7 @@ function replaceXRanges (comp, options) {
|
|||||||
|
|
||||||
function replaceXRange (comp, options) {
|
function replaceXRange (comp, options) {
|
||||||
comp = comp.trim()
|
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) {
|
return comp.replace(r, function (ret, gtlt, M, m, p, pr) {
|
||||||
debug('xRange', comp, ret, gtlt, M, m, p, pr)
|
debug('xRange', comp, ret, gtlt, M, m, p, pr)
|
||||||
var xM = isX(M)
|
var xM = isX(M)
|
||||||
@ -4510,7 +4557,7 @@ function replaceXRange (comp, options) {
|
|||||||
function replaceStars (comp, options) {
|
function replaceStars (comp, options) {
|
||||||
debug('replaceStars', comp, options)
|
debug('replaceStars', comp, options)
|
||||||
// Looseness is ignored here. star is always as loose as it gets!
|
// 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])
|
// This function is passed to string.replace(re[t.HYPHENRANGE])
|
||||||
@ -4836,7 +4883,7 @@ function coerce (version, options) {
|
|||||||
|
|
||||||
var match = null
|
var match = null
|
||||||
if (!options.rtl) {
|
if (!options.rtl) {
|
||||||
match = version.match(re[t.COERCE])
|
match = version.match(safeRe[t.COERCE])
|
||||||
} else {
|
} else {
|
||||||
// Find the right-most coercible string that does not share
|
// Find the right-most coercible string that does not share
|
||||||
// a terminus with a more left-ward coercible string.
|
// 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
|
// 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.
|
// coercible string can be more right-ward without the same terminus.
|
||||||
var next
|
var next
|
||||||
while ((next = re[t.COERCERTL].exec(version)) &&
|
while ((next = safeRe[t.COERCERTL].exec(version)) &&
|
||||||
(!match || match.index + match[0].length !== version.length)
|
(!match || match.index + match[0].length !== version.length)
|
||||||
) {
|
) {
|
||||||
if (!match ||
|
if (!match ||
|
||||||
next.index + next[0].length !== match.index + match[0].length) {
|
next.index + next[0].length !== match.index + match[0].length) {
|
||||||
match = next
|
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
|
// leave it in a clean state
|
||||||
re[t.COERCERTL].lastIndex = -1
|
safeRe[t.COERCERTL].lastIndex = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match === null) {
|
if (match === null) {
|
||||||
@ -59042,7 +59089,11 @@ module.exports = v4;
|
|||||||
|
|
||||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
if (k2 === undefined) k2 = k;
|
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) {
|
}) : (function(o, m, k, k2) {
|
||||||
if (k2 === undefined) k2 = k;
|
if (k2 === undefined) k2 = k;
|
||||||
o[k2] = m[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) {
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
if (k2 === undefined) k2 = k;
|
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) {
|
}) : (function(o, m, k, k2) {
|
||||||
if (k2 === undefined) k2 = k;
|
if (k2 === undefined) k2 = k;
|
||||||
o[k2] = m[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
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.7.5'
|
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
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.7'
|
python-version: '3.7'
|
||||||
@ -58,7 +58,7 @@ You can specify the version with **prerelease tag** to download and set up an ac
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.12.0-alpha.1'
|
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
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.12-dev'
|
python-version: '3.12-dev'
|
||||||
@ -82,7 +82,7 @@ You can also use several types of ranges that are specified in [semver](https://
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '>=3.9 <3.10'
|
python-version: '>=3.9 <3.10'
|
||||||
@ -93,7 +93,7 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.12.0-alpha - 3.12.0'
|
python-version: '3.12.0-alpha - 3.12.0'
|
||||||
@ -104,7 +104,7 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
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.
|
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
|
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.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
|
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' # the latest available version of PyPy that supports Python 3.7
|
||||||
- 'pypy3.7-v7.3.3' # Python 3.7 and PyPy 7.3.3
|
- 'pypy3.7-v7.3.3' # Python 3.7 and PyPy 7.3.3
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
@ -155,7 +155,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: |
|
python-version: |
|
||||||
@ -172,7 +172,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: |
|
python-version: |
|
||||||
@ -189,7 +189,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: |
|
python-version: |
|
||||||
@ -211,10 +211,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
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
|
name: Python ${{ matrix.python-version }} sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
@ -232,14 +232,14 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
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:
|
exclude:
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
python-version: '3.8'
|
python-version: '3.8'
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: '3.6'
|
python-version: '3.6'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
@ -256,7 +256,7 @@ jobs:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version-file: '.python-version' # Read python version from a file .python-version
|
python-version-file: '.python-version' # Read python version from a file .python-version
|
||||||
@ -265,7 +265,7 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version-file: 'pyproject.toml' # Read python version from a file pyproject.toml
|
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
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.7'
|
python-version: '3.7'
|
||||||
@ -295,7 +295,7 @@ steps:
|
|||||||
**Caching pipenv dependencies:**
|
**Caching pipenv dependencies:**
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
@ -308,7 +308,7 @@ steps:
|
|||||||
**Caching poetry dependencies:**
|
**Caching poetry dependencies:**
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install poetry
|
- name: Install poetry
|
||||||
run: pipx install poetry
|
run: pipx install poetry
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
@ -322,7 +322,7 @@ steps:
|
|||||||
**Using a list of file paths to cache dependencies**
|
**Using a list of file paths to cache dependencies**
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
@ -337,7 +337,7 @@ steps:
|
|||||||
**Using wildcard patterns to cache dependencies**
|
**Using wildcard patterns to cache dependencies**
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
@ -349,7 +349,7 @@ steps:
|
|||||||
**Using a list of wildcard patterns to cache dependencies**
|
**Using a list of wildcard patterns to cache dependencies**
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
@ -364,7 +364,7 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
@ -387,7 +387,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
id: cp310
|
id: cp310
|
||||||
with:
|
with:
|
||||||
@ -404,7 +404,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
id: cp310
|
id: cp310
|
||||||
with:
|
with:
|
||||||
@ -420,7 +420,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
id: cp310
|
id: cp310
|
||||||
with:
|
with:
|
||||||
@ -451,7 +451,7 @@ Such a requirement on side-effect could be because you don't want your composite
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
id: cp310
|
id: cp310
|
||||||
with:
|
with:
|
||||||
@ -611,7 +611,7 @@ jobs:
|
|||||||
python_version: ["3.11", "3.12"]
|
python_version: ["3.11", "3.12"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "${{ matrix.python_version }}"
|
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"
|
"semver": "^7.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^29.5.6",
|
||||||
"@types/node": "^16.11.25",
|
"@types/node": "^16.11.25",
|
||||||
"@types/semver": "^7.1.0",
|
"@types/semver": "^7.1.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||||
"@typescript-eslint/parser": "^5.54.0",
|
"@typescript-eslint/parser": "^5.54.0",
|
||||||
"@vercel/ncc": "^0.33.4",
|
"@vercel/ncc": "^0.38.0",
|
||||||
"eslint": "^8.35.0",
|
"eslint": "^8.35.0",
|
||||||
"eslint-config-prettier": "^8.6.0",
|
"eslint-config-prettier": "^8.6.0",
|
||||||
"eslint-plugin-jest": "^27.2.1",
|
"eslint-plugin-jest": "^27.2.1",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"husky": "^7.0.2",
|
"husky": "^7.0.2",
|
||||||
"jest": "^27.2.5",
|
"jest": "^29.7.0",
|
||||||
"jest-circus": "^27.2.5",
|
"jest-circus": "^29.7.0",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.4",
|
||||||
"ts-jest": "^27.0.5",
|
"ts-jest": "^29.1.1",
|
||||||
"typescript": "^4.2.3"
|
"typescript": "^4.2.3"
|
||||||
},
|
},
|
||||||
"husky": {
|
"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,
|
getPyPyVersionFromPath,
|
||||||
readExactPyPyVersionFile,
|
readExactPyPyVersionFile,
|
||||||
validatePythonVersionFormatForPyPy,
|
validatePythonVersionFormatForPyPy,
|
||||||
IPyPyManifestRelease
|
IPyPyManifestRelease,
|
||||||
|
getBinaryDirectory
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
@ -82,7 +83,7 @@ export async function findPyPyVersion(
|
|||||||
IS_WINDOWS ? installDir : _binDir,
|
IS_WINDOWS ? installDir : _binDir,
|
||||||
`python${binaryExtension}`
|
`python${binaryExtension}`
|
||||||
);
|
);
|
||||||
const pythonLocation = pypyInstall.getPyPyBinaryPath(installDir);
|
const pythonLocation = getBinaryDirectory(installDir);
|
||||||
if (updateEnvironment) {
|
if (updateEnvironment) {
|
||||||
core.exportVariable('pythonLocation', installDir);
|
core.exportVariable('pythonLocation', installDir);
|
||||||
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
|
// 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,
|
IPyPyManifestRelease,
|
||||||
createSymlinkInFolder,
|
createSymlinkInFolder,
|
||||||
isNightlyKeyword,
|
isNightlyKeyword,
|
||||||
writeExactPyPyVersionFile
|
writeExactPyPyVersionFile,
|
||||||
|
getBinaryDirectory
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
export async function installPyPy(
|
export async function installPyPy(
|
||||||
@ -94,7 +95,7 @@ export async function installPyPy(
|
|||||||
|
|
||||||
writeExactPyPyVersionFile(installDir, resolvedPyPyVersion);
|
writeExactPyPyVersionFile(installDir, resolvedPyPyVersion);
|
||||||
|
|
||||||
const binaryPath = getPyPyBinaryPath(installDir);
|
const binaryPath = getBinaryDirectory(installDir);
|
||||||
await createPyPySymlink(binaryPath, resolvedPythonVersion);
|
await createPyPySymlink(binaryPath, resolvedPythonVersion);
|
||||||
await installPip(binaryPath);
|
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) {
|
export function pypyVersionToSemantic(versionSpec: string) {
|
||||||
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g;
|
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g;
|
||||||
return versionSpec.replace(prereleaseVersion, '$1-$2.$3');
|
return versionSpec.replace(prereleaseVersion, '$1-$2.$3');
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as finder from './find-python';
|
import * as finder from './find-python';
|
||||||
import * as finderPyPy from './find-pypy';
|
import * as finderPyPy from './find-pypy';
|
||||||
|
import * as finderGraalPy from './find-graalpy';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
@ -17,6 +18,10 @@ function isPyPyVersion(versionSpec: string) {
|
|||||||
return versionSpec.startsWith('pypy');
|
return versionSpec.startsWith('pypy');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isGraalPyVersion(versionSpec: string) {
|
||||||
|
return versionSpec.startsWith('graalpy');
|
||||||
|
}
|
||||||
|
|
||||||
async function cacheDependencies(cache: string, pythonVersion: string) {
|
async function cacheDependencies(cache: string, pythonVersion: string) {
|
||||||
const cacheDependencyPath =
|
const cacheDependencyPath =
|
||||||
core.getInput('cache-dependency-path') || undefined;
|
core.getInput('cache-dependency-path') || undefined;
|
||||||
@ -106,10 +111,20 @@ async function run() {
|
|||||||
core.info(
|
core.info(
|
||||||
`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`
|
`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 {
|
} else {
|
||||||
if (version.startsWith('2')) {
|
if (version.startsWith('2')) {
|
||||||
core.warning(
|
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(
|
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 semver from 'semver';
|
||||||
import * as toml from '@iarna/toml';
|
import * as toml from '@iarna/toml';
|
||||||
import * as exec from '@actions/exec';
|
import * as exec from '@actions/exec';
|
||||||
|
import * as ifm from '@actions/http-client/interfaces';
|
||||||
|
|
||||||
export const IS_WINDOWS = process.platform === 'win32';
|
export const IS_WINDOWS = process.platform === 'win32';
|
||||||
export const IS_LINUX = process.platform === 'linux';
|
export const IS_LINUX = process.platform === 'linux';
|
||||||
@ -29,6 +30,16 @@ export interface IPyPyManifestRelease {
|
|||||||
files: IPyPyManifestAsset[];
|
files: IPyPyManifestAsset[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IGraalPyManifestAsset {
|
||||||
|
name: string;
|
||||||
|
browser_download_url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGraalPyManifestRelease {
|
||||||
|
tag_name: string;
|
||||||
|
assets: IGraalPyManifestAsset[];
|
||||||
|
}
|
||||||
|
|
||||||
/** create Symlinks for downloaded PyPy
|
/** create Symlinks for downloaded PyPy
|
||||||
* It should be executed only for downloaded versions in runtime, because
|
* It should be executed only for downloaded versions in runtime, because
|
||||||
* toolcache versions have this setup.
|
* 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
|
* "3.x" or "3" are not supported
|
||||||
* because it could cause ambiguity when both PyPy version and Python version are not precise
|
* 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}`);
|
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[] {
|
export function getVersionInputFromPlainFile(versionFile: string): string[] {
|
||||||
core.debug(`Trying to resolve version form ${versionFile}`);
|
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}`);
|
core.info(`Resolved ${versionFile} as ${version}`);
|
||||||
return [version];
|
return [version];
|
||||||
}
|
}
|
||||||
@ -252,3 +277,34 @@ export function getVersionInputFromFile(versionFile: string): string[] {
|
|||||||
return getVersionInputFromPlainFile(versionFile);
|
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