mirror of
https://github.com/actions/setup-python.git
synced 2025-06-24 12:41:09 +02:00
Compare commits
29 Commits
v4.6.0
...
releases/v
Author | SHA1 | Date | |
---|---|---|---|
0ae58361cd | |||
b64ffcaf5b | |||
8d2896179a | |||
7bc6abb01e | |||
e8111cec9d | |||
a00ea43da6 | |||
8635b1ccc5 | |||
f6cc428f53 | |||
5f2af211d6 | |||
3467d92d48 | |||
65d7f2d534 | |||
f97b83114c | |||
61a6322f88 | |||
ea5b57fccc | |||
014d32a830 | |||
c16c4b8d18 | |||
0d5da6a89a | |||
3f824b7ca6 | |||
bd6b4b6205 | |||
0cbcb9a3d7 | |||
669664dac1 | |||
9cbf792a3c | |||
e732e40717 | |||
a0d867bfa9 | |||
d99bed8b21 | |||
f71253fee3 | |||
b51c97f6e3 | |||
db9987b4c1 | |||
6db7460265 |
@ -7,7 +7,7 @@ module.exports = {
|
|||||||
'eslint-config-prettier'
|
'eslint-config-prettier'
|
||||||
],
|
],
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
plugins: ['@typescript-eslint', 'eslint-plugin-jest'],
|
plugins: ['@typescript-eslint', 'eslint-plugin-node', 'eslint-plugin-jest'],
|
||||||
rules: {
|
rules: {
|
||||||
'@typescript-eslint/no-require-imports': 'error',
|
'@typescript-eslint/no-require-imports': 'error',
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
@ -28,7 +28,8 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
'no-control-regex': 'off',
|
'no-control-regex': 'off',
|
||||||
'no-constant-condition': ['error', {checkLoops: false}]
|
'no-constant-condition': ['error', {checkLoops: false}],
|
||||||
|
'node/no-extraneous-import': 'error'
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
|
38
.github/workflows/e2e-cache.yml
vendored
38
.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,24 +41,32 @@ 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
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache: 'pipenv'
|
cache: 'pipenv'
|
||||||
- name: Install pipenv
|
- name: Install pipenv
|
||||||
run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python
|
run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python
|
||||||
- name: Install dependencies
|
- name: Prepare environment
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
mv ./__tests__/data/Pipfile.lock .
|
mv ./__tests__/data/Pipfile.lock .
|
||||||
mv ./__tests__/data/Pipfile .
|
mv ./__tests__/data/Pipfile .
|
||||||
|
mv ./__tests__/test-pipenv.py .
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: pwsh
|
||||||
|
if: steps.cache-pipenv.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
if ("${{ matrix.python-version }}" -Match "pypy") {
|
if ("${{ matrix.python-version }}" -Match "pypy") {
|
||||||
pipenv install --keep-outdated --python pypy
|
pipenv install --python pypy # --keep-outdated
|
||||||
} else {
|
} else {
|
||||||
pipenv install --keep-outdated --python ${{ matrix.python-version }}
|
pipenv install --python ${{ matrix.python-version }} # --keep-outdated
|
||||||
}
|
}
|
||||||
|
- name: Run Python Script
|
||||||
|
run: pipenv run python test-pipenv.py
|
||||||
|
|
||||||
python-poetry-dependencies-caching:
|
python-poetry-dependencies-caching:
|
||||||
name: Test poetry (Python ${{ matrix.python-version}}, ${{ matrix.os }})
|
name: Test poetry (Python ${{ matrix.python-version}}, ${{ matrix.os }})
|
||||||
@ -69,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
|
||||||
@ -91,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:
|
||||||
@ -110,8 +118,9 @@ 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
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
@ -119,13 +128,20 @@ jobs:
|
|||||||
cache-dependency-path: '**/pipenv-requirements.txt'
|
cache-dependency-path: '**/pipenv-requirements.txt'
|
||||||
- name: Install pipenv
|
- name: Install pipenv
|
||||||
run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python
|
run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python
|
||||||
- name: Install dependencies
|
- name: Prepare environment
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
mv ./__tests__/data/Pipfile.lock .
|
mv ./__tests__/data/Pipfile.lock .
|
||||||
mv ./__tests__/data/Pipfile .
|
mv ./__tests__/data/Pipfile .
|
||||||
|
mv ./__tests__/test-pipenv.py .
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: pwsh
|
||||||
|
if: steps.cache-pipenv.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
if ("${{ matrix.python-version }}" -Match "pypy") {
|
if ("${{ matrix.python-version }}" -Match "pypy") {
|
||||||
pipenv install --keep-outdated --python pypy
|
pipenv install --python pypy # --keep-outdated
|
||||||
} else {
|
} else {
|
||||||
pipenv install --keep-outdated --python ${{ matrix.python-version }}
|
pipenv install --python ${{ matrix.python-version }} # --keep-outdated
|
||||||
}
|
}
|
||||||
|
- name: Run Python Script
|
||||||
|
run: pipenv run python test-pipenv.py
|
||||||
|
23
.github/workflows/e2e-tests.yml
vendored
23
.github/workflows/e2e-tests.yml
vendored
@ -19,14 +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 2.7
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
python-version: 2.7
|
|
||||||
- name: Verify 2.7
|
|
||||||
run: python __tests__/verify-python.py 2.7
|
|
||||||
|
|
||||||
- name: Run with setup-python 3.5
|
- name: Run with setup-python 3.5
|
||||||
uses: ./
|
uses: ./
|
||||||
@ -86,3 +79,17 @@ jobs:
|
|||||||
run: python __tests__/verify-python.py 3.10
|
run: python __tests__/verify-python.py 3.10
|
||||||
- name: Run python-path sample 3.10
|
- name: Run python-path sample 3.10
|
||||||
run: pipx run --python '${{ steps.cp310.outputs.python-path }}' nox --version
|
run: pipx run --python '${{ steps.cp310.outputs.python-path }}' nox --version
|
||||||
|
|
||||||
|
- name: Run with setup-python ==3.8
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
python-version: '==3.8'
|
||||||
|
- name: Verify ==3.8
|
||||||
|
run: python __tests__/verify-python.py 3.8
|
||||||
|
|
||||||
|
- name: Run with setup-python <3.11
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
python-version: '<3.11'
|
||||||
|
- name: Verify <3.11
|
||||||
|
run: python __tests__/verify-python.py 3.10
|
||||||
|
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:
|
||||||
|
163
.github/workflows/test-python.yml
vendored
163
.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
|
||||||
@ -86,7 +86,152 @@ jobs:
|
|||||||
id: setup-python
|
id: setup-python
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
python-version-file: '.python-version'
|
python-version-file: .python-version
|
||||||
|
|
||||||
|
- name: Check python-path
|
||||||
|
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Validate version
|
||||||
|
run: |
|
||||||
|
$pythonVersion = (python --version)
|
||||||
|
if ("Python ${{ matrix.python }}" -ne "$pythonVersion"){
|
||||||
|
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$pythonVersion
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Run simple code
|
||||||
|
run: python -c 'import math; print(math.factorial(5))'
|
||||||
|
|
||||||
|
setup-versions-from-file-without-parameter:
|
||||||
|
name: Setup ${{ matrix.python }} ${{ matrix.os }} version file without parameter
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, windows-latest, ubuntu-20.04, ubuntu-22.04]
|
||||||
|
python: [3.5.4, 3.6.7, 3.7.5, 3.8.15, 3.9.13]
|
||||||
|
exclude:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
python: 3.5.4
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
python: 3.6.7
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
python: 3.7.5
|
||||||
|
- os: windows-latest
|
||||||
|
python: 3.8.15
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: build-version-file ${{ matrix.python }}
|
||||||
|
run: echo ${{ matrix.python }} > .python-version
|
||||||
|
|
||||||
|
- name: setup-python ${{ matrix.python }}
|
||||||
|
id: setup-python
|
||||||
|
uses: ./
|
||||||
|
|
||||||
|
- name: Check python-path
|
||||||
|
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Validate version
|
||||||
|
run: |
|
||||||
|
$pythonVersion = (python --version)
|
||||||
|
if ("Python ${{ matrix.python }}" -ne "$pythonVersion"){
|
||||||
|
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$pythonVersion
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Run simple code
|
||||||
|
run: python -c 'import math; print(math.factorial(5))'
|
||||||
|
|
||||||
|
setup-versions-from-standard-pyproject-file:
|
||||||
|
name: Setup ${{ matrix.python }} ${{ matrix.os }} standard pyproject file
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, windows-latest, ubuntu-20.04, ubuntu-22.04]
|
||||||
|
python: [3.5.4, 3.6.7, 3.7.5, 3.8.15, 3.9.13, '==3.10.10']
|
||||||
|
exclude:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
python: 3.5.4
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
python: 3.6.7
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
python: 3.7.5
|
||||||
|
- os: windows-latest
|
||||||
|
python: 3.8.15
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: build-version-file ${{ matrix.python }}
|
||||||
|
run: |
|
||||||
|
echo '[project]
|
||||||
|
requires-python = "${{ matrix.python }}"
|
||||||
|
' > pyproject.toml
|
||||||
|
|
||||||
|
- name: setup-python ${{ matrix.python }}
|
||||||
|
id: setup-python
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
python-version-file: pyproject.toml
|
||||||
|
|
||||||
|
- name: Check python-path
|
||||||
|
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Validate version
|
||||||
|
run: |
|
||||||
|
$pythonVersion = (python --version)
|
||||||
|
if ("Python ${{ matrix.python }}".replace("==", "") -ne "$pythonVersion"){
|
||||||
|
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$pythonVersion
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Run simple code
|
||||||
|
run: python -c 'import math; print(math.factorial(5))'
|
||||||
|
|
||||||
|
setup-versions-from-poetry-pyproject-file:
|
||||||
|
name: Setup ${{ matrix.python }} ${{ matrix.os }} poetry pyproject file
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, windows-latest, ubuntu-20.04, ubuntu-22.04]
|
||||||
|
python: [3.5.4, 3.6.7, 3.7.5, 3.8.15, 3.9.13]
|
||||||
|
exclude:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
python: 3.5.4
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
python: 3.6.7
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
python: 3.7.5
|
||||||
|
- os: windows-latest
|
||||||
|
python: 3.8.15
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: build-version-file ${{ matrix.python }}
|
||||||
|
run: |
|
||||||
|
echo '[tool.poetry.dependencies]
|
||||||
|
python = "${{ matrix.python }}"
|
||||||
|
' > pyproject.toml
|
||||||
|
|
||||||
|
- name: setup-python ${{ matrix.python }}
|
||||||
|
id: setup-python
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
python-version-file: pyproject.toml
|
||||||
|
|
||||||
- name: Check python-path
|
- name: Check python-path
|
||||||
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
||||||
@ -114,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
|
||||||
@ -148,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
|
||||||
@ -176,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
|
||||||
@ -206,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
|
||||||
@ -229,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:
|
||||||
@ -252,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: "@actions/http-client"
|
name: "@actions/http-client"
|
||||||
version: 2.0.1
|
version: 2.1.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions Http Client
|
summary: Actions Http Client
|
||||||
homepage: https://github.com/actions/http-client#readme
|
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
2
.licenses/npm/@azure/ms-rest-js.dep.yml
generated
2
.licenses/npm/@azure/ms-rest-js.dep.yml
generated
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@azure/ms-rest-js"
|
name: "@azure/ms-rest-js"
|
||||||
version: 2.6.6
|
version: 2.7.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Isomorphic client Runtime for Typescript/node.js/browser javascript client
|
summary: Isomorphic client Runtime for Typescript/node.js/browser javascript client
|
||||||
libraries generated using AutoRest
|
libraries generated using AutoRest
|
||||||
|
26
.licenses/npm/@iarna/toml.dep.yml
generated
Normal file
26
.licenses/npm/@iarna/toml.dep.yml
generated
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: "@iarna/toml"
|
||||||
|
version: 2.2.5
|
||||||
|
type: npm
|
||||||
|
summary: Better TOML parsing and stringifying all in that familiar JSON interface.
|
||||||
|
homepage: https://github.com/iarna/iarna-toml#readme
|
||||||
|
license: isc
|
||||||
|
licenses:
|
||||||
|
- sources: LICENSE
|
||||||
|
text: |+
|
||||||
|
Copyright (c) 2016, Rebecca Turner <me@re-becca.org>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
notices: []
|
||||||
|
...
|
34
.licenses/npm/ip-regex.dep.yml
generated
34
.licenses/npm/ip-regex.dep.yml
generated
@ -1,34 +0,0 @@
|
|||||||
---
|
|
||||||
name: ip-regex
|
|
||||||
version: 2.1.0
|
|
||||||
type: npm
|
|
||||||
summary: Regular expression for matching IP addresses (IPv4 & IPv6)
|
|
||||||
homepage: https://github.com/sindresorhus/ip-regex#readme
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: license
|
|
||||||
text: |
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
- sources: readme.md
|
|
||||||
text: MIT © [Sindre Sorhus](https://sindresorhus.com)
|
|
||||||
notices: []
|
|
43
.licenses/npm/psl.dep.yml
generated
43
.licenses/npm/psl.dep.yml
generated
@ -1,43 +0,0 @@
|
|||||||
---
|
|
||||||
name: psl
|
|
||||||
version: 1.8.0
|
|
||||||
type: npm
|
|
||||||
summary: Domain name parser based on the Public Suffix List
|
|
||||||
homepage: https://github.com/lupomontero/psl#readme
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2017 Lupo Montero lupomontero@gmail.com
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
- sources: README.md
|
|
||||||
text: |-
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2017 Lupo Montero <lupomontero@gmail.com>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
notices: []
|
|
34
.licenses/npm/punycode.dep.yml
generated
34
.licenses/npm/punycode.dep.yml
generated
@ -1,34 +0,0 @@
|
|||||||
---
|
|
||||||
name: punycode
|
|
||||||
version: 2.1.1
|
|
||||||
type: npm
|
|
||||||
summary: A robust Punycode converter that fully complies to RFC 3492 and RFC 5891,
|
|
||||||
and works on nearly all JavaScript platforms.
|
|
||||||
homepage: https://mths.be/punycode
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE-MIT.txt
|
|
||||||
text: |
|
|
||||||
Copyright Mathias Bynens <https://mathiasbynens.be/>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
- sources: README.md
|
|
||||||
text: Punycode.js is available under the [MIT](https://mths.be/mit) license.
|
|
||||||
notices: []
|
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: semver
|
name: semver
|
||||||
version: 7.3.8
|
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:
|
homepage:
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: semver
|
name: semver
|
||||||
version: 6.3.0
|
version: 7.5.2
|
||||||
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
|
23
.licenses/npm/tough-cookie.dep.yml
generated
23
.licenses/npm/tough-cookie.dep.yml
generated
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
name: tough-cookie
|
|
||||||
version: 3.0.1
|
|
||||||
type: npm
|
|
||||||
summary: RFC6265 Cookies and Cookie Jar for node.js
|
|
||||||
homepage: https://github.com/salesforce/tough-cookie
|
|
||||||
license: bsd-3-clause
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |
|
|
||||||
Copyright (c) 2015, Salesforce.com, Inc.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
notices: []
|
|
19
README.md
19
README.md
@ -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,12 +28,23 @@ 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'
|
||||||
- run: python my_script.py
|
- run: python my_script.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**GraalPy**
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 'graalpy-22.3'
|
||||||
|
- run: python my_script.py
|
||||||
|
```
|
||||||
|
|
||||||
The `python-version` input is optional. If not supplied, the action will try to resolve the version from the default `.python-version` file. If the `.python-version` file doesn't exist Python or PyPy version from the PATH will be used. The default version of Python or PyPy in PATH varies between runners and can be changed unexpectedly so we recommend always setting Python version explicitly using the `python-version` or `python-version-file` inputs.
|
The `python-version` input is optional. If not supplied, the action will try to resolve the version from the default `.python-version` file. If the `.python-version` file doesn't exist Python or PyPy version from the PATH will be used. The default version of Python or PyPy in PATH varies between runners and can be changed unexpectedly so we recommend always setting Python version explicitly using the `python-version` or `python-version-file` inputs.
|
||||||
|
|
||||||
The action will first check the local [tool cache](docs/advanced-usage.md#hosted-tool-cache) for a [semver](https://github.com/npm/node-semver#versions) match. If unable to find a specific version in the tool cache, the action will attempt to download a version of Python from [GitHub Releases](https://github.com/actions/python-versions/releases) and for PyPy from the official [PyPy's dist](https://downloads.python.org/pypy/).
|
The action will first check the local [tool cache](docs/advanced-usage.md#hosted-tool-cache) for a [semver](https://github.com/npm/node-semver#versions) match. If unable to find a specific version in the tool cache, the action will attempt to download a version of Python from [GitHub Releases](https://github.com/actions/python-versions/releases) and for PyPy from the official [PyPy's dist](https://downloads.python.org/pypy/).
|
||||||
@ -62,7 +73,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'
|
||||||
@ -82,7 +93,7 @@ See examples of using `cache` and `cache-dependency-path` for `pipenv` and `poet
|
|||||||
- [Check latest version](docs/advanced-usage.md#check-latest-version)
|
- [Check latest version](docs/advanced-usage.md#check-latest-version)
|
||||||
- [Caching packages](docs/advanced-usage.md#caching-packages)
|
- [Caching packages](docs/advanced-usage.md#caching-packages)
|
||||||
- [Outputs and environment variables](docs/advanced-usage.md#outputs-and-environment-variables)
|
- [Outputs and environment variables](docs/advanced-usage.md#outputs-and-environment-variables)
|
||||||
- [Available versions of Python and PyPy](docs/advanced-usage.md#available-versions-of-python-and-pypy)
|
- [Available versions of Python and PyPy](advanced-usage.md#available-versions-of-python-pypy-and-graalpy)
|
||||||
- [Hosted tool cache](docs/advanced-usage.md#hosted-tool-cache)
|
- [Hosted tool cache](docs/advanced-usage.md#hosted-tool-cache)
|
||||||
- [Using `setup-python` with a self-hosted runner](docs/advanced-usage.md#using-setup-python-with-a-self-hosted-runner)
|
- [Using `setup-python` with a self-hosted runner](docs/advanced-usage.md#using-setup-python-with-a-self-hosted-runner)
|
||||||
- [Using `setup-python` on GHES](docs/advanced-usage.md#using-setup-python-on-ghes)
|
- [Using `setup-python` on GHES](docs/advanced-usage.md#using-setup-python-on-ghes)
|
||||||
|
@ -8,7 +8,7 @@ import {State} from '../src/cache-distributions/cache-distributor';
|
|||||||
|
|
||||||
describe('restore-cache', () => {
|
describe('restore-cache', () => {
|
||||||
const pipFileLockHash =
|
const pipFileLockHash =
|
||||||
'a3bdcc71289e4979ca9e051810d81999cc99823109faf6912e17ff14c8e621a6';
|
'f8428d7cf00ea53a5c3702f0a9cb3cc467f76cd86a34723009350c4e4b32751a';
|
||||||
const requirementsHash =
|
const requirementsHash =
|
||||||
'd8110e0006d7fb5ee76365d565eef9d37df1d11598b912d3eb66d398d57a1121';
|
'd8110e0006d7fb5ee76365d565eef9d37df1d11598b912d3eb66d398d57a1121';
|
||||||
const requirementsLinuxHash =
|
const requirementsLinuxHash =
|
||||||
|
@ -4,8 +4,8 @@ verify_ssl = true
|
|||||||
name = "pypi"
|
name = "pypi"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
flake8 = "==4.0.1"
|
flake8 = "==6.0.0"
|
||||||
numpy = "==1.23.0"
|
numpy = "==1.25.1"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
80
__tests__/data/Pipfile.lock
generated
80
__tests__/data/Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "e9c37110984955621040e2dc8548c026eb8466c23db1b8e69430289b10be8938"
|
"sha256": "dcea65dabfe8442466b5e9280ecab72cfa7bf555791ee0ad55b6e7255dca1f43"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -18,62 +18,66 @@
|
|||||||
"default": {
|
"default": {
|
||||||
"flake8": {
|
"flake8": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d",
|
"sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7",
|
||||||
"sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"
|
"sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==4.0.1"
|
"version": "==6.0.0"
|
||||||
},
|
},
|
||||||
"mccabe": {
|
"mccabe": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
|
||||||
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
|
||||||
],
|
],
|
||||||
"version": "==0.6.1"
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==0.7.0"
|
||||||
},
|
},
|
||||||
"numpy": {
|
"numpy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:092f5e6025813e64ad6d1b52b519165d08c730d099c114a9247c9bb635a2a450",
|
"sha256:012097b5b0d00a11070e8f2e261128c44157a8689f7dedcf35576e525893f4fe",
|
||||||
"sha256:196cd074c3f97c4121601790955f915187736f9cf458d3ee1f1b46aff2b1ade0",
|
"sha256:0d3fe3dd0506a28493d82dc3cf254be8cd0d26f4008a417385cbf1ae95b54004",
|
||||||
"sha256:1c29b44905af288b3919803aceb6ec7fec77406d8b08aaa2e8b9e63d0fe2f160",
|
"sha256:0def91f8af6ec4bb94c370e38c575855bf1d0be8a8fbfba42ef9c073faf2cf19",
|
||||||
"sha256:2b2da66582f3a69c8ce25ed7921dcd8010d05e59ac8d89d126a299be60421171",
|
"sha256:1a180429394f81c7933634ae49b37b472d343cccb5bb0c4a575ac8bbc433722f",
|
||||||
"sha256:5043bcd71fcc458dfb8a0fc5509bbc979da0131b9d08e3d5f50fb0bbb36f169a",
|
"sha256:1d5d3c68e443c90b38fdf8ef40e60e2538a27548b39b12b73132456847f4b631",
|
||||||
"sha256:58bfd40eb478f54ff7a5710dd61c8097e169bc36cc68333d00a9bcd8def53b38",
|
"sha256:20e1266411120a4f16fad8efa8e0454d21d00b8c7cee5b5ccad7565d95eb42dd",
|
||||||
"sha256:79a506cacf2be3a74ead5467aee97b81fca00c9c4c8b3ba16dbab488cd99ba10",
|
"sha256:247d3ffdd7775bdf191f848be8d49100495114c82c2bd134e8d5d075fb386a1c",
|
||||||
"sha256:94b170b4fa0168cd6be4becf37cb5b127bd12a795123984385b8cd4aca9857e5",
|
"sha256:35a9527c977b924042170a0887de727cd84ff179e478481404c5dc66b4170009",
|
||||||
"sha256:97a76604d9b0e79f59baeca16593c711fddb44936e40310f78bfef79ee9a835f",
|
"sha256:38eb6548bb91c421261b4805dc44def9ca1a6eef6444ce35ad1669c0f1a3fc5d",
|
||||||
"sha256:98e8e0d8d69ff4d3fa63e6c61e8cfe2d03c29b16b58dbef1f9baa175bbed7860",
|
"sha256:3d7abcdd85aea3e6cdddb59af2350c7ab1ed764397f8eec97a038ad244d2d105",
|
||||||
"sha256:ac86f407873b952679f5f9e6c0612687e51547af0e14ddea1eedfcb22466babd",
|
"sha256:41a56b70e8139884eccb2f733c2f7378af06c82304959e174f8e7370af112e09",
|
||||||
"sha256:ae8adff4172692ce56233db04b7ce5792186f179c415c37d539c25de7298d25d",
|
"sha256:4a90725800caeaa160732d6b31f3f843ebd45d6b5f3eec9e8cc287e30f2805bf",
|
||||||
"sha256:bd3fa4fe2e38533d5336e1272fc4e765cabbbde144309ccee8675509d5cd7b05",
|
"sha256:6b82655dd8efeea69dbf85d00fca40013d7f503212bc5259056244961268b66e",
|
||||||
"sha256:d0d2094e8f4d760500394d77b383a1b06d3663e8892cdf5df3c592f55f3bff66",
|
"sha256:6c6c9261d21e617c6dc5eacba35cb68ec36bb72adcff0dee63f8fbc899362588",
|
||||||
"sha256:d54b3b828d618a19779a84c3ad952e96e2c2311b16384e973e671aa5be1f6187",
|
"sha256:77d339465dff3eb33c701430bcb9c325b60354698340229e1dff97745e6b3efa",
|
||||||
"sha256:d6ca8dabe696c2785d0c8c9b0d8a9b6e5fdbe4f922bde70d57fa1a2848134f95",
|
"sha256:791f409064d0a69dd20579345d852c59822c6aa087f23b07b1b4e28ff5880fcb",
|
||||||
"sha256:d8cc87bed09de55477dba9da370c1679bd534df9baa171dd01accbb09687dac3",
|
"sha256:9a3a9f3a61480cc086117b426a8bd86869c213fc4072e606f01c4e4b66eb92bf",
|
||||||
"sha256:f0f18804df7370571fb65db9b98bf1378172bd4e962482b857e612d1fec0f53e",
|
"sha256:c1516db588987450b85595586605742879e50dcce923e8973f79529651545b57",
|
||||||
"sha256:f1d88ef79e0a7fa631bb2c3dda1ea46b32b1fe614e10fedd611d3d5398447f2f",
|
"sha256:c40571fe966393b212689aa17e32ed905924120737194b5d5c1b20b9ed0fb171",
|
||||||
"sha256:f9c3fc2adf67762c9fe1849c859942d23f8d3e0bee7b5ed3d4a9c3eeb50a2f07",
|
"sha256:d412c1697c3853c6fc3cb9751b4915859c7afe6a277c2bf00acf287d56c4e625",
|
||||||
"sha256:fc431493df245f3c627c0c05c2bd134535e7929dbe2e602b80e42bf52ff760bc",
|
"sha256:d5154b1a25ec796b1aee12ac1b22f414f94752c5f94832f14d8d6c9ac40bcca6",
|
||||||
"sha256:fe8b9683eb26d2c4d5db32cd29b38fdcf8381324ab48313b5b69088e0e355379"
|
"sha256:d736b75c3f2cb96843a5c7f8d8ccc414768d34b0a75f466c05f3a739b406f10b",
|
||||||
|
"sha256:e8f6049c4878cb16960fbbfb22105e49d13d752d4d8371b55110941fb3b17800",
|
||||||
|
"sha256:f76aebc3358ade9eacf9bc2bb8ae589863a4f911611694103af05346637df1b7",
|
||||||
|
"sha256:fd67b306320dcadea700a8f79b9e671e607f8696e98ec255915c0c6d6b818503"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.23.0"
|
"version": "==1.25.1"
|
||||||
},
|
},
|
||||||
"pycodestyle": {
|
"pycodestyle": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20",
|
"sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053",
|
||||||
"sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"
|
"sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==2.8.0"
|
"version": "==2.10.0"
|
||||||
},
|
},
|
||||||
"pyflakes": {
|
"pyflakes": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c",
|
"sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf",
|
||||||
"sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"
|
"sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==2.4.0"
|
"version": "==3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {}
|
"develop": {}
|
||||||
|
5798
__tests__/data/graalpy.json
Normal file
5798
__tests__/data/graalpy.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,2 +1,2 @@
|
|||||||
numpy==1.22.3
|
numpy==1.22.3
|
||||||
pandas==1.4.2
|
flake8==6.0.0
|
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();
|
||||||
|
});
|
||||||
|
});
|
7
__tests__/test-pipenv.py
Normal file
7
__tests__/test-pipenv.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
a = np.array([2, 3, 4])
|
||||||
|
print(type(a))
|
||||||
|
|
||||||
|
b = np.array([1.2, 3.5, 5.1])
|
||||||
|
print(type(b))
|
@ -1,9 +1,18 @@
|
|||||||
import * as cache from '@actions/cache';
|
import * as cache from '@actions/cache';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
validateVersion,
|
validateVersion,
|
||||||
validatePythonVersionFormatForPyPy,
|
validatePythonVersionFormatForPyPy,
|
||||||
isCacheFeatureAvailable
|
isCacheFeatureAvailable,
|
||||||
|
getVersionInputFromFile,
|
||||||
|
getVersionInputFromPlainFile,
|
||||||
|
getVersionInputFromTomlFile,
|
||||||
|
getNextPageUrl
|
||||||
} from '../src/utils';
|
} from '../src/utils';
|
||||||
|
|
||||||
jest.mock('@actions/cache');
|
jest.mock('@actions/cache');
|
||||||
@ -73,3 +82,80 @@ describe('isCacheFeatureAvailable', () => {
|
|||||||
expect(isCacheFeatureAvailable()).toBe(true);
|
expect(isCacheFeatureAvailable()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tempDir = path.join(
|
||||||
|
__dirname,
|
||||||
|
'runner',
|
||||||
|
path.join(Math.random().toString(36).substring(7)),
|
||||||
|
'temp'
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('Version from file test', () => {
|
||||||
|
it.each([getVersionInputFromPlainFile, getVersionInputFromFile])(
|
||||||
|
'Version from plain file test',
|
||||||
|
async _fn => {
|
||||||
|
await io.mkdirP(tempDir);
|
||||||
|
const pythonVersionFileName = 'python-version.file';
|
||||||
|
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||||
|
const pythonVersionFileContent = '3.7';
|
||||||
|
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
||||||
|
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersionFileContent]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
it.each([getVersionInputFromTomlFile, getVersionInputFromFile])(
|
||||||
|
'Version from standard pyproject.toml test',
|
||||||
|
async _fn => {
|
||||||
|
await io.mkdirP(tempDir);
|
||||||
|
const pythonVersionFileName = 'pyproject.toml';
|
||||||
|
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||||
|
const pythonVersion = '>=3.7.0';
|
||||||
|
const pythonVersionFileContent = `[project]\nrequires-python = "${pythonVersion}"`;
|
||||||
|
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
||||||
|
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
it.each([getVersionInputFromTomlFile, getVersionInputFromFile])(
|
||||||
|
'Version from poetry pyproject.toml test',
|
||||||
|
async _fn => {
|
||||||
|
await io.mkdirP(tempDir);
|
||||||
|
const pythonVersionFileName = 'pyproject.toml';
|
||||||
|
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||||
|
const pythonVersion = '>=3.7.0';
|
||||||
|
const pythonVersionFileContent = `[tool.poetry.dependencies]\npython = "${pythonVersion}"`;
|
||||||
|
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
|
||||||
|
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
it.each([getVersionInputFromTomlFile, getVersionInputFromFile])(
|
||||||
|
'Version undefined',
|
||||||
|
async _fn => {
|
||||||
|
await io.mkdirP(tempDir);
|
||||||
|
const pythonVersionFileName = 'pyproject.toml';
|
||||||
|
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
|
||||||
|
fs.writeFileSync(pythonVersionFilePath, ``);
|
||||||
|
expect(_fn(pythonVersionFilePath)).toEqual([]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
141
dist/cache-save/index.js
vendored
141
dist/cache-save/index.js
vendored
@ -3218,6 +3218,10 @@ function checkBypass(reqUrl) {
|
|||||||
if (!reqUrl.hostname) {
|
if (!reqUrl.hostname) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const reqHost = reqUrl.hostname;
|
||||||
|
if (isLoopbackAddress(reqHost)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '';
|
const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '';
|
||||||
if (!noProxy) {
|
if (!noProxy) {
|
||||||
return false;
|
return false;
|
||||||
@ -3243,13 +3247,24 @@ function checkBypass(reqUrl) {
|
|||||||
.split(',')
|
.split(',')
|
||||||
.map(x => x.trim().toUpperCase())
|
.map(x => x.trim().toUpperCase())
|
||||||
.filter(x => x)) {
|
.filter(x => x)) {
|
||||||
if (upperReqHosts.some(x => x === upperNoProxyItem)) {
|
if (upperNoProxyItem === '*' ||
|
||||||
|
upperReqHosts.some(x => x === upperNoProxyItem ||
|
||||||
|
x.endsWith(`.${upperNoProxyItem}`) ||
|
||||||
|
(upperNoProxyItem.startsWith('.') &&
|
||||||
|
x.endsWith(`${upperNoProxyItem}`)))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
exports.checkBypass = checkBypass;
|
exports.checkBypass = checkBypass;
|
||||||
|
function isLoopbackAddress(host) {
|
||||||
|
const hostLower = host.toLowerCase();
|
||||||
|
return (hostLower === 'localhost' ||
|
||||||
|
hostLower.startsWith('127.') ||
|
||||||
|
hostLower.startsWith('[::1]') ||
|
||||||
|
hostLower.startsWith('[0:0:0:0:0:0:0:1]'));
|
||||||
|
}
|
||||||
//# sourceMappingURL=proxy.js.map
|
//# sourceMappingURL=proxy.js.map
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
@ -3285,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
|
||||||
@ -3295,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.
|
||||||
|
|
||||||
@ -3304,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.
|
||||||
@ -3353,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
|
||||||
@ -3433,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"
|
||||||
@ -3442,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')
|
||||||
@ -3457,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')
|
||||||
@ -3478,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`
|
||||||
@ -3506,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]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3530,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
|
||||||
}
|
}
|
||||||
@ -3585,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)
|
||||||
@ -4030,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
|
||||||
@ -4046,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) {
|
||||||
@ -4170,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
|
||||||
@ -4180,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()
|
||||||
@ -4199,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(' ')
|
||||||
@ -4220,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+/)
|
||||||
@ -4320,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
|
||||||
@ -4361,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
|
||||||
@ -4420,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)
|
||||||
@ -4495,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])
|
||||||
@ -4821,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.
|
||||||
@ -4832,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) {
|
||||||
@ -6582,6 +6644,10 @@ function checkBypass(reqUrl) {
|
|||||||
if (!reqUrl.hostname) {
|
if (!reqUrl.hostname) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const reqHost = reqUrl.hostname;
|
||||||
|
if (isLoopbackAddress(reqHost)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '';
|
const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '';
|
||||||
if (!noProxy) {
|
if (!noProxy) {
|
||||||
return false;
|
return false;
|
||||||
@ -6607,13 +6673,24 @@ function checkBypass(reqUrl) {
|
|||||||
.split(',')
|
.split(',')
|
||||||
.map(x => x.trim().toUpperCase())
|
.map(x => x.trim().toUpperCase())
|
||||||
.filter(x => x)) {
|
.filter(x => x)) {
|
||||||
if (upperReqHosts.some(x => x === upperNoProxyItem)) {
|
if (upperNoProxyItem === '*' ||
|
||||||
|
upperReqHosts.some(x => x === upperNoProxyItem ||
|
||||||
|
x.endsWith(`.${upperNoProxyItem}`) ||
|
||||||
|
(upperNoProxyItem.startsWith('.') &&
|
||||||
|
x.endsWith(`${upperNoProxyItem}`)))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
exports.checkBypass = checkBypass;
|
exports.checkBypass = checkBypass;
|
||||||
|
function isLoopbackAddress(host) {
|
||||||
|
const hostLower = host.toLowerCase();
|
||||||
|
return (hostLower === 'localhost' ||
|
||||||
|
hostLower.startsWith('127.') ||
|
||||||
|
hostLower.startsWith('[::1]') ||
|
||||||
|
hostLower.startsWith('[0:0:0:0:0:0:0:1]'));
|
||||||
|
}
|
||||||
//# sourceMappingURL=proxy.js.map
|
//# sourceMappingURL=proxy.js.map
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
@ -59012,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];
|
||||||
@ -59121,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];
|
||||||
|
3430
dist/setup/index.js
vendored
3430
dist/setup/index.js
vendored
File diff suppressed because it is too large
Load Diff
@ -11,9 +11,10 @@
|
|||||||
- [Outputs](advanced-usage.md#outputs)
|
- [Outputs](advanced-usage.md#outputs)
|
||||||
- [Environment variables](advanced-usage.md#environment-variables)
|
- [Environment variables](advanced-usage.md#environment-variables)
|
||||||
- [Using update-environment flag](advanced-usage.md#using-update-environment-flag)
|
- [Using update-environment flag](advanced-usage.md#using-update-environment-flag)
|
||||||
- [Available versions of Python and PyPy](advanced-usage.md#available-versions-of-python-and-pypy)
|
- [Available versions of Python and PyPy](advanced-usage.md#available-versions-of-python-pypy-and-graalpy)
|
||||||
- [Python](advanced-usage.md#python)
|
- [Python](advanced-usage.md#python)
|
||||||
- [PyPy](advanced-usage.md#pypy)
|
- [PyPy](advanced-usage.md#pypy)
|
||||||
|
- [GraalPy](advanced-usage.md#graalpy)
|
||||||
- [Hosted tool cache](advanced-usage.md#hosted-tool-cache)
|
- [Hosted tool cache](advanced-usage.md#hosted-tool-cache)
|
||||||
- [Using `setup-python` with a self-hosted runner](advanced-usage.md#using-setup-python-with-a-self-hosted-runner)
|
- [Using `setup-python` with a self-hosted runner](advanced-usage.md#using-setup-python-with-a-self-hosted-runner)
|
||||||
- [Windows](advanced-usage.md#windows)
|
- [Windows](advanced-usage.md#windows)
|
||||||
@ -30,7 +31,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 +45,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 +59,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 +70,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'
|
||||||
@ -78,11 +79,22 @@ steps:
|
|||||||
|
|
||||||
You can also use several types of ranges that are specified in [semver](https://github.com/npm/node-semver#ranges), for instance:
|
You can also use several types of ranges that are specified in [semver](https://github.com/npm/node-semver#ranges), for instance:
|
||||||
|
|
||||||
|
- **[ranges](https://github.com/npm/node-semver#ranges)** to download and set up the latest available version of Python satisfying a range:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '>=3.9 <3.10'
|
||||||
|
- run: python my_script.py
|
||||||
|
```
|
||||||
|
|
||||||
- **[hyphen ranges](https://github.com/npm/node-semver#hyphen-ranges-xyz---abc)** to download and set up the latest available version of Python (includes both pre-release and stable versions):
|
- **[hyphen ranges](https://github.com/npm/node-semver#hyphen-ranges-xyz---abc)** to download and set up the latest available version of Python (includes both pre-release and stable versions):
|
||||||
|
|
||||||
```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'
|
||||||
@ -93,7 +105,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'
|
||||||
@ -106,7 +118,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
|
||||||
@ -126,7 +138,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 }}
|
||||||
@ -144,7 +156,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: |
|
||||||
@ -161,7 +173,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: |
|
||||||
@ -178,7 +190,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: |
|
||||||
@ -200,10 +212,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:
|
||||||
@ -221,14 +233,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:
|
||||||
@ -245,12 +257,22 @@ 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
|
||||||
- run: python my_script.py
|
- run: python my_script.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version-file: 'pyproject.toml' # Read python version from a file pyproject.toml
|
||||||
|
- run: python my_script.py
|
||||||
|
```
|
||||||
|
|
||||||
## Check latest version
|
## Check latest version
|
||||||
|
|
||||||
The `check-latest` flag defaults to `false`. Use the default or set `check-latest` to `false` if you prefer stability and if you want to ensure a specific `Python or PyPy` version is always used.
|
The `check-latest` flag defaults to `false`. Use the default or set `check-latest` to `false` if you prefer stability and if you want to ensure a specific `Python or PyPy` version is always used.
|
||||||
@ -259,7 +281,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'
|
||||||
@ -274,7 +296,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'
|
||||||
@ -287,7 +309,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
|
||||||
@ -301,7 +323,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'
|
||||||
@ -316,7 +338,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'
|
||||||
@ -328,7 +350,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'
|
||||||
@ -343,7 +365,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'
|
||||||
@ -366,7 +388,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:
|
||||||
@ -383,7 +405,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:
|
||||||
@ -399,13 +421,13 @@ 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:
|
||||||
python-version: "3.8.0"
|
python-version: "3.8.0"
|
||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
- run: echo '${{ steps.cp310.outputs.cache-hit }}' # true if cache-hit occured on the primary key
|
- run: echo '${{ steps.cp310.outputs.cache-hit }}' # true if cache-hit occurred on the primary key
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
@ -417,7 +439,7 @@ These environment variables become available after setup-python action execution
|
|||||||
| pythonLocation |Contains the absolute path to the folder where the requested version of Python or PyPy is installed|
|
| pythonLocation |Contains the absolute path to the folder where the requested version of Python or PyPy is installed|
|
||||||
| Python_ROOT_DIR | https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython |
|
| Python_ROOT_DIR | https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython |
|
||||||
| Python2_ROOT_DIR |https://cmake.org/cmake/help/latest/module/FindPython2.html#module:FindPython2|
|
| Python2_ROOT_DIR |https://cmake.org/cmake/help/latest/module/FindPython2.html#module:FindPython2|
|
||||||
| Python3_ROOT_DIR |https://cmake.org/cmake/help/latest/module/FindPython2.html#module:FindPython3|
|
| Python3_ROOT_DIR |https://cmake.org/cmake/help/latest/module/FindPython3.html#module:FindPython3|
|
||||||
|
|
||||||
## Using `update-environment` flag
|
## Using `update-environment` flag
|
||||||
|
|
||||||
@ -430,7 +452,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:
|
||||||
@ -438,7 +460,7 @@ Such a requirement on side-effect could be because you don't want your composite
|
|||||||
update-environment: false
|
update-environment: false
|
||||||
- run: ${{ steps.cp310.outputs.python-path }} my_script.py
|
- run: ${{ steps.cp310.outputs.python-path }} my_script.py
|
||||||
```
|
```
|
||||||
## Available versions of Python and PyPy
|
## Available versions of Python, PyPy and GraalPy
|
||||||
### Python
|
### Python
|
||||||
|
|
||||||
`setup-python` is able to configure **Python** from two sources:
|
`setup-python` is able to configure **Python** from two sources:
|
||||||
@ -469,6 +491,11 @@ Such a requirement on side-effect could be because you don't want your composite
|
|||||||
- PyPy < 7.3.3 are not available to install on-flight.
|
- PyPy < 7.3.3 are not available to install on-flight.
|
||||||
- If some versions are not available, you can open an issue in https://foss.heptapod.net/pypy/pypy/
|
- If some versions are not available, you can open an issue in https://foss.heptapod.net/pypy/pypy/
|
||||||
|
|
||||||
|
### GraalPy
|
||||||
|
|
||||||
|
`setup-python` is able to download GraalPy versions from the [official GraalPy repository](https://github.com/oracle/graalpython).
|
||||||
|
- All available versions that we can download are listed in [releases](https://github.com/oracle/graalpython/releases).
|
||||||
|
|
||||||
## Hosted tool cache
|
## Hosted tool cache
|
||||||
|
|
||||||
GitHub hosted runners have a tool cache that comes with a few versions of Python + PyPy already installed. This tool cache helps speed up runs and tool setup by not requiring any new downloads. There is an environment variable called `RUNNER_TOOL_CACHE` on each runner that describes the location of the tool cache with Python and PyPy installed. `setup-python` works by taking a specific version of Python or PyPy from this tool cache and adding it to PATH.
|
GitHub hosted runners have a tool cache that comes with a few versions of Python + PyPy already installed. This tool cache helps speed up runs and tool setup by not requiring any new downloads. There is an environment variable called `RUNNER_TOOL_CACHE` on each runner that describes the location of the tool cache with Python and PyPy installed. `setup-python` works by taking a specific version of Python or PyPy from this tool cache and adding it to PATH.
|
||||||
@ -590,10 +617,11 @@ 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 }}"
|
||||||
|
allow-prereleases: true
|
||||||
- run: pipx run nox --error-on-missing-interpreters -s tests-${{ matrix.python_version }}
|
- run: pipx run nox --error-on-missing-interpreters -s tests-${{ matrix.python_version }}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
7345
package-lock.json
generated
7345
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@ -29,25 +29,28 @@
|
|||||||
"@actions/core": "^1.10.0",
|
"@actions/core": "^1.10.0",
|
||||||
"@actions/exec": "^1.1.0",
|
"@actions/exec": "^1.1.0",
|
||||||
"@actions/glob": "^0.2.0",
|
"@actions/glob": "^0.2.0",
|
||||||
|
"@actions/http-client": "^1.0.11",
|
||||||
"@actions/io": "^1.0.2",
|
"@actions/io": "^1.0.2",
|
||||||
"@actions/tool-cache": "^1.5.5",
|
"@actions/tool-cache": "^1.5.5",
|
||||||
"semver": "^7.1.3"
|
"@iarna/toml": "^2.2.5",
|
||||||
|
"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",
|
||||||
"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
|
||||||
|
@ -11,7 +11,7 @@ import * as tc from '@actions/tool-cache';
|
|||||||
|
|
||||||
// Python has "scripts" or "bin" directories where command-line tools that come with packages are installed.
|
// Python has "scripts" or "bin" directories where command-line tools that come with packages are installed.
|
||||||
// This is where pip is, along with anything that pip installs.
|
// This is where pip is, along with anything that pip installs.
|
||||||
// There is a seperate directory for `pip install --user`.
|
// There is a separate directory for `pip install --user`.
|
||||||
//
|
//
|
||||||
// For reference, these directories are as follows:
|
// For reference, these directories are as follows:
|
||||||
// macOS / Linux:
|
// macOS / Linux:
|
||||||
|
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,16 +1,27 @@
|
|||||||
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';
|
||||||
import {getCacheDistributor} from './cache-distributions/cache-factory';
|
import {getCacheDistributor} from './cache-distributions/cache-factory';
|
||||||
import {isCacheFeatureAvailable, logWarning, IS_MAC} from './utils';
|
import {
|
||||||
|
isCacheFeatureAvailable,
|
||||||
|
logWarning,
|
||||||
|
IS_MAC,
|
||||||
|
getVersionInputFromFile,
|
||||||
|
getVersionInputFromPlainFile
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
function isPyPyVersion(versionSpec: string) {
|
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;
|
||||||
@ -22,43 +33,46 @@ async function cacheDependencies(cache: string, pythonVersion: string) {
|
|||||||
await cacheDistributor.restoreCache();
|
await cacheDistributor.restoreCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveVersionInput() {
|
function resolveVersionInputFromDefaultFile(): string[] {
|
||||||
const versions = core.getMultilineInput('python-version');
|
const couples: [string, (versionFile: string) => string[]][] = [
|
||||||
let versionFile = core.getInput('python-version-file');
|
['.python-version', getVersionInputFromPlainFile]
|
||||||
|
];
|
||||||
if (versions.length && versionFile) {
|
for (const [versionFile, _fn] of couples) {
|
||||||
core.warning(
|
logWarning(
|
||||||
'Both python-version and python-version-file inputs are specified, only python-version will be used.'
|
`Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find '${versionFile}' file.`
|
||||||
);
|
);
|
||||||
|
if (fs.existsSync(versionFile)) {
|
||||||
|
return _fn(versionFile);
|
||||||
|
} else {
|
||||||
|
logWarning(`${versionFile} doesn't exist.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveVersionInput() {
|
||||||
|
let versions = core.getMultilineInput('python-version');
|
||||||
|
const versionFile = core.getInput('python-version-file');
|
||||||
|
|
||||||
if (versions.length) {
|
if (versions.length) {
|
||||||
return versions;
|
if (versionFile) {
|
||||||
}
|
core.warning(
|
||||||
|
'Both python-version and python-version-file inputs are specified, only python-version will be used.'
|
||||||
if (versionFile) {
|
|
||||||
if (!fs.existsSync(versionFile)) {
|
|
||||||
throw new Error(
|
|
||||||
`The specified python version file at: ${versionFile} doesn't exist.`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const version = fs.readFileSync(versionFile, 'utf8');
|
} else {
|
||||||
core.info(`Resolved ${versionFile} as ${version}`);
|
if (versionFile) {
|
||||||
return [version];
|
if (!fs.existsSync(versionFile)) {
|
||||||
|
throw new Error(
|
||||||
|
`The specified python version file at: ${versionFile} doesn't exist.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
versions = getVersionInputFromFile(versionFile);
|
||||||
|
} else {
|
||||||
|
versions = resolveVersionInputFromDefaultFile();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logWarning(
|
|
||||||
"Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find '.python-version' file."
|
|
||||||
);
|
|
||||||
versionFile = '.python-version';
|
|
||||||
if (fs.existsSync(versionFile)) {
|
|
||||||
const version = fs.readFileSync(versionFile, 'utf8');
|
|
||||||
core.info(`Resolved ${versionFile} as ${version}`);
|
|
||||||
return [version];
|
|
||||||
}
|
|
||||||
|
|
||||||
logWarning(`${versionFile} doesn't exist.`);
|
|
||||||
|
|
||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +111,22 @@ 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')) {
|
||||||
|
core.warning(
|
||||||
|
'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(
|
||||||
version,
|
version,
|
||||||
arch,
|
arch,
|
||||||
|
129
src/utils.ts
129
src/utils.ts
@ -4,7 +4,9 @@ import * as core from '@actions/core';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
|
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';
|
||||||
@ -28,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.
|
||||||
@ -89,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
|
||||||
*/
|
*/
|
||||||
@ -181,3 +193,118 @@ export async function getOSInfo() {
|
|||||||
return osInfo;
|
return osInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a value from an object by following the keys path provided.
|
||||||
|
* If the value is present, it is returned. Otherwise undefined is returned.
|
||||||
|
*/
|
||||||
|
function extractValue(obj: any, keys: string[]): string | undefined {
|
||||||
|
if (keys.length > 0) {
|
||||||
|
const value = obj[keys[0]];
|
||||||
|
if (keys.length > 1 && value !== undefined) {
|
||||||
|
return extractValue(value, keys.slice(1));
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Python version extracted from the TOML file.
|
||||||
|
* If the `project` key is present at the root level, the version is assumed to
|
||||||
|
* be specified according to PEP 621 in `project.requires-python`.
|
||||||
|
* Otherwise, if the `tool` key is present at the root level, the version is
|
||||||
|
* assumed to be specified using poetry under `tool.poetry.dependencies.python`.
|
||||||
|
* If none is present, returns an empty list.
|
||||||
|
*/
|
||||||
|
export function getVersionInputFromTomlFile(versionFile: string): string[] {
|
||||||
|
core.debug(`Trying to resolve version form ${versionFile}`);
|
||||||
|
|
||||||
|
const pyprojectFile = fs.readFileSync(versionFile, 'utf8');
|
||||||
|
const pyprojectConfig = toml.parse(pyprojectFile);
|
||||||
|
let keys = [];
|
||||||
|
|
||||||
|
if ('project' in pyprojectConfig) {
|
||||||
|
// standard project metadata (PEP 621)
|
||||||
|
keys = ['project', 'requires-python'];
|
||||||
|
} else {
|
||||||
|
// python poetry
|
||||||
|
keys = ['tool', 'poetry', 'dependencies', 'python'];
|
||||||
|
}
|
||||||
|
const versions = [];
|
||||||
|
const version = extractValue(pyprojectConfig, keys);
|
||||||
|
if (version !== undefined) {
|
||||||
|
versions.push(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`Extracted ${versions} from ${versionFile}`);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Python version extracted from a plain text file.
|
||||||
|
*/
|
||||||
|
export function getVersionInputFromPlainFile(versionFile: string): string[] {
|
||||||
|
core.debug(`Trying to resolve version form ${versionFile}`);
|
||||||
|
const version = fs.readFileSync(versionFile, 'utf8').trim();
|
||||||
|
core.info(`Resolved ${versionFile} as ${version}`);
|
||||||
|
return [version];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Python version extracted from a plain or TOML file.
|
||||||
|
*/
|
||||||
|
export function getVersionInputFromFile(versionFile: string): string[] {
|
||||||
|
if (versionFile.endsWith('.toml')) {
|
||||||
|
return getVersionInputFromTomlFile(versionFile);
|
||||||
|
} else {
|
||||||
|
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