mirror of
https://github.com/actions/setup-python.git
synced 2025-06-24 04:38:00 +02:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
d27e3f3d7c | |||
dec86ecf4e | |||
4dcd67678f | |||
8b89ef08a0 | |||
5ccb29d877 | |||
c3e033939c | |||
206e984b94 |
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@ -1 +1 @@
|
|||||||
* @actions/actions-service
|
* @actions/setup-actions-team
|
||||||
|
15
.github/workflows/basic-validation.yml
vendored
Normal file
15
.github/workflows/basic-validation.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
name: Basic validation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
jobs:
|
||||||
|
call-basic-validation:
|
||||||
|
name: Basic validation
|
||||||
|
uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@main
|
41
.github/workflows/check-dist.yml
vendored
41
.github/workflows/check-dist.yml
vendored
@ -1,8 +1,3 @@
|
|||||||
# `dist/index.js` is a special file in Actions.
|
|
||||||
# When you reference an action with `uses:` in a workflow,
|
|
||||||
# `index.js` is the code that will run.
|
|
||||||
# For our project, we generate this file through a build process from other source files.
|
|
||||||
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
|
|
||||||
name: Check dist/
|
name: Check dist/
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@ -17,36 +12,6 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-dist:
|
call-check-dist:
|
||||||
runs-on: ubuntu-latest
|
name: Check dist/
|
||||||
|
uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set Node.js 16.x
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 16.x
|
|
||||||
cache: npm
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Rebuild the dist/ directory
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
- name: Compare the expected and actual dist/ directories
|
|
||||||
run: |
|
|
||||||
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
|
|
||||||
echo "Detected uncommitted changes after build. See status below:"
|
|
||||||
git diff
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
id: diff
|
|
||||||
|
|
||||||
# If index.js was different than expected, upload the expected version as an artifact
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
|
||||||
with:
|
|
||||||
name: dist
|
|
||||||
path: dist/
|
|
44
.github/workflows/codeql-analysis.yml
vendored
44
.github/workflows/codeql-analysis.yml
vendored
@ -1,47 +1,13 @@
|
|||||||
name: "Code scanning - action"
|
name: CodeQL analysis
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ 'main' ]
|
branches: [ 'main' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '25 3 * * 5'
|
- cron: '0 3 * * 0'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
CodeQL-Build:
|
call-codeQL-analysis:
|
||||||
|
name: CodeQL analysis
|
||||||
strategy:
|
uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main
|
||||||
fail-fast: false
|
|
||||||
|
|
||||||
# CodeQL runs on ubuntu-latest and windows-latest
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v2
|
|
||||||
# Override language selection by uncommenting this and choosing your languages
|
|
||||||
# with:
|
|
||||||
# languages: go, javascript, csharp, python, cpp, java
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v2
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 https://git.io/JvXDl
|
|
||||||
|
|
||||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
|
||||||
# and modify them (or add more) to build your code if your project
|
|
||||||
# uses a compiled language
|
|
||||||
|
|
||||||
#- run: |
|
|
||||||
# make bootstrap
|
|
||||||
# make release
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v2
|
|
@ -1,4 +1,5 @@
|
|||||||
name: Main workflow
|
name: e2e tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@ -8,9 +9,10 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md'
|
- '**.md'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run:
|
test-setup-python:
|
||||||
name: Run
|
name: Test setup-python
|
||||||
runs-on: ${{ matrix.operating-system }}
|
runs-on: ${{ matrix.operating-system }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@ -19,21 +21,6 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set Node.js 16.x
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 16.x
|
|
||||||
cache: npm
|
|
||||||
|
|
||||||
- name: npm ci
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Lint
|
|
||||||
run: npm run format-check
|
|
||||||
|
|
||||||
- name: npm test
|
|
||||||
run: npm test
|
|
||||||
|
|
||||||
- name: Run with setup-python 2.7
|
- name: Run with setup-python 2.7
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@ -98,4 +85,4 @@ jobs:
|
|||||||
- name: Verify 3.10
|
- name: Verify 3.10
|
||||||
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
|
21
.github/workflows/licensed.yml
vendored
21
.github/workflows/licensed.yml
vendored
@ -9,21 +9,6 @@ on:
|
|||||||
- main
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
call-licensed:
|
||||||
runs-on: ubuntu-latest
|
name: Licensed
|
||||||
name: Check licenses
|
uses: actions/reusable-workflows/.github/workflows/licensed.yml@main
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Set Node.js 16.x
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 16.x
|
|
||||||
cache: npm
|
|
||||||
- run: npm ci
|
|
||||||
- name: Install licensed
|
|
||||||
run: |
|
|
||||||
cd $RUNNER_TEMP
|
|
||||||
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.4.4/licensed-3.4.4-linux-x64.tar.gz
|
|
||||||
sudo tar -xzf licensed.tar.gz
|
|
||||||
sudo mv licensed /usr/local/bin/licensed
|
|
||||||
- run: licensed status
|
|
@ -1,4 +1,5 @@
|
|||||||
name: Release new action version
|
name: Release new action version
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [released]
|
types: [released]
|
||||||
@ -24,4 +25,4 @@ jobs:
|
|||||||
uses: actions/publish-action@v0.2.1
|
uses: actions/publish-action@v0.2.1
|
||||||
with:
|
with:
|
||||||
source-tag: ${{ env.TAG_NAME }}
|
source-tag: ${{ env.TAG_NAME }}
|
||||||
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
|
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
|
43
.github/workflows/test-pypy.yml
vendored
43
.github/workflows/test-pypy.yml
vendored
@ -1,4 +1,5 @@
|
|||||||
name: Validate PyPy e2e
|
name: Validate PyPy e2e
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@ -123,4 +124,46 @@ jobs:
|
|||||||
EXECUTABLE=${EXECUTABLE/-/} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name
|
EXECUTABLE=${EXECUTABLE/-/} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name
|
||||||
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
|
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
|
||||||
${EXECUTABLE} --version
|
${EXECUTABLE} --version
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
setup-pypy-multiple-versions:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup PyPy and check latest
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
python-version: |
|
||||||
|
pypy-3.7-v7.3.x
|
||||||
|
pypy3.8
|
||||||
|
check-latest: true
|
||||||
|
- name: PyPy and Python version
|
||||||
|
run: python --version
|
||||||
|
|
||||||
|
- name: Run simple code
|
||||||
|
run: python -c 'import math; print(math.factorial(5))'
|
||||||
|
|
||||||
|
- name: Assert PyPy is running
|
||||||
|
run: |
|
||||||
|
import platform
|
||||||
|
assert platform.python_implementation().lower() == "pypy"
|
||||||
|
shell: python
|
||||||
|
|
||||||
|
- name: Assert expected binaries (or symlinks) are present
|
||||||
|
run: |
|
||||||
|
EXECUTABLE="pypy-3.7-v7.3.x"
|
||||||
|
EXECUTABLE=${EXECUTABLE/-/} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name
|
||||||
|
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
|
||||||
|
${EXECUTABLE} --version
|
||||||
|
shell: bash
|
||||||
|
- name: Assert expected binaries (or symlinks) are present
|
||||||
|
run: |
|
||||||
|
EXECUTABLE='pypy3.8'
|
||||||
|
EXECUTABLE=${EXECUTABLE/pypy-/pypy} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name
|
||||||
|
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
|
||||||
|
${EXECUTABLE} --version
|
||||||
shell: bash
|
shell: bash
|
32
.github/workflows/test-python.yml
vendored
32
.github/workflows/test-python.yml
vendored
@ -1,4 +1,5 @@
|
|||||||
name: Validate Python e2e
|
name: Validate Python e2e
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@ -190,8 +191,35 @@ jobs:
|
|||||||
- name: Validate version
|
- name: Validate version
|
||||||
run: |
|
run: |
|
||||||
$pythonVersion = (python --version)
|
$pythonVersion = (python --version)
|
||||||
if ("$pythonVersion" -NotMatch "${{ matrix.python }}"){
|
if ("$pythonVersion" -NotMatch "${{ matrix.python-version }}"){
|
||||||
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
|
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python-version }}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$pythonVersion
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
setup-python-multiple-python-versions:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup Python and check latest
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
python-version: |
|
||||||
|
3.7
|
||||||
|
3.8
|
||||||
|
3.9
|
||||||
|
3.10
|
||||||
|
check-latest: true
|
||||||
|
- name: Validate version
|
||||||
|
run: |
|
||||||
|
$pythonVersion = (python --version)
|
||||||
|
if ("$pythonVersion" -NotMatch "3.10"){
|
||||||
|
Write-Host "The current version is $pythonVersion; expected version is 3.10"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
$pythonVersion
|
$pythonVersion
|
||||||
|
@ -55,7 +55,7 @@ The action defaults to searching for a dependency file (`requirements.txt` for p
|
|||||||
|
|
||||||
- For `pip`, the action will cache the global cache directory
|
- For `pip`, the action will cache the global cache directory
|
||||||
- For `pipenv`, the action will cache virtualenv directory
|
- For `pipenv`, the action will cache virtualenv directory
|
||||||
- For `poetry`, the action will cache virtualenv directory
|
- For `poetry`, the action will cache virtualenv directories -- one for each poetry project found
|
||||||
|
|
||||||
**Caching pip dependencies:**
|
**Caching pip dependencies:**
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import * as path from 'path';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as cache from '@actions/cache';
|
import * as cache from '@actions/cache';
|
||||||
import * as exec from '@actions/exec';
|
import * as exec from '@actions/exec';
|
||||||
import * as io from '@actions/io';
|
import * as io from '@actions/io';
|
||||||
import {getCacheDistributor} from '../src/cache-distributions/cache-factory';
|
import {getCacheDistributor} from '../src/cache-distributions/cache-factory';
|
||||||
|
import {State} from '../src/cache-distributions/cache-distributor';
|
||||||
import * as utils from './../src/utils';
|
import * as utils from './../src/utils';
|
||||||
|
|
||||||
describe('restore-cache', () => {
|
describe('restore-cache', () => {
|
||||||
@ -13,7 +15,7 @@ describe('restore-cache', () => {
|
|||||||
const requirementsLinuxHash =
|
const requirementsLinuxHash =
|
||||||
'2d0ff7f46b0e120e3d3294db65768b474934242637b9899b873e6283dfd16d7c';
|
'2d0ff7f46b0e120e3d3294db65768b474934242637b9899b873e6283dfd16d7c';
|
||||||
const poetryLockHash =
|
const poetryLockHash =
|
||||||
'571bf984f8d210e6a97f854e479fdd4a2b5af67b5fdac109ec337a0ea16e7836';
|
'f24ea1ad73968e6c8d80c16a093ade72d9332c433aeef979a0dd943e6a99b2ab';
|
||||||
const poetryConfigOutput = `
|
const poetryConfigOutput = `
|
||||||
cache-dir = "/Users/patrick/Library/Caches/pypoetry"
|
cache-dir = "/Users/patrick/Library/Caches/pypoetry"
|
||||||
experimental.new-installer = false
|
experimental.new-installer = false
|
||||||
@ -27,7 +29,7 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py
|
|||||||
let infoSpy: jest.SpyInstance;
|
let infoSpy: jest.SpyInstance;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.SpyInstance;
|
||||||
let debugSpy: jest.SpyInstance;
|
let debugSpy: jest.SpyInstance;
|
||||||
let saveSatetSpy: jest.SpyInstance;
|
let saveStateSpy: jest.SpyInstance;
|
||||||
let getStateSpy: jest.SpyInstance;
|
let getStateSpy: jest.SpyInstance;
|
||||||
let setOutputSpy: jest.SpyInstance;
|
let setOutputSpy: jest.SpyInstance;
|
||||||
|
|
||||||
@ -52,8 +54,8 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py
|
|||||||
debugSpy = jest.spyOn(core, 'debug');
|
debugSpy = jest.spyOn(core, 'debug');
|
||||||
debugSpy.mockImplementation(input => undefined);
|
debugSpy.mockImplementation(input => undefined);
|
||||||
|
|
||||||
saveSatetSpy = jest.spyOn(core, 'saveState');
|
saveStateSpy = jest.spyOn(core, 'saveState');
|
||||||
saveSatetSpy.mockImplementation(input => undefined);
|
saveStateSpy.mockImplementation(input => undefined);
|
||||||
|
|
||||||
getStateSpy = jest.spyOn(core, 'getState');
|
getStateSpy = jest.spyOn(core, 'getState');
|
||||||
getStateSpy.mockImplementation(input => undefined);
|
getStateSpy.mockImplementation(input => undefined);
|
||||||
@ -100,21 +102,68 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py
|
|||||||
|
|
||||||
describe('Restore dependencies', () => {
|
describe('Restore dependencies', () => {
|
||||||
it.each([
|
it.each([
|
||||||
['pip', '3.8.12', undefined, requirementsHash],
|
[
|
||||||
['pip', '3.8.12', '**/requirements-linux.txt', requirementsLinuxHash],
|
'pip',
|
||||||
|
'3.8.12',
|
||||||
|
'__tests__/data/**/requirements.txt',
|
||||||
|
requirementsHash,
|
||||||
|
undefined
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'pip',
|
||||||
|
'3.8.12',
|
||||||
|
'__tests__/data/**/requirements-linux.txt',
|
||||||
|
requirementsLinuxHash,
|
||||||
|
undefined
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'pip',
|
'pip',
|
||||||
'3.8.12',
|
'3.8.12',
|
||||||
'__tests__/data/requirements-linux.txt',
|
'__tests__/data/requirements-linux.txt',
|
||||||
requirementsLinuxHash
|
requirementsLinuxHash,
|
||||||
|
undefined
|
||||||
],
|
],
|
||||||
['pip', '3.8.12', '__tests__/data/requirements.txt', requirementsHash],
|
[
|
||||||
['pipenv', '3.9.1', undefined, pipFileLockHash],
|
'pip',
|
||||||
['pipenv', '3.9.12', '__tests__/data/requirements.txt', requirementsHash],
|
'3.8.12',
|
||||||
['poetry', '3.9.1', undefined, poetryLockHash]
|
'__tests__/data/requirements.txt',
|
||||||
|
requirementsHash,
|
||||||
|
undefined
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'pipenv',
|
||||||
|
'3.9.1',
|
||||||
|
'__tests__/data/**/Pipfile.lock',
|
||||||
|
pipFileLockHash,
|
||||||
|
undefined
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'pipenv',
|
||||||
|
'3.9.12',
|
||||||
|
'__tests__/data/requirements.txt',
|
||||||
|
requirementsHash,
|
||||||
|
undefined
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'poetry',
|
||||||
|
'3.9.1',
|
||||||
|
'__tests__/data/**/poetry.lock',
|
||||||
|
poetryLockHash,
|
||||||
|
[
|
||||||
|
'/Users/patrick/Library/Caches/pypoetry/virtualenvs',
|
||||||
|
path.join(__dirname, 'data', 'inner', '.venv'),
|
||||||
|
path.join(__dirname, 'data', '.venv')
|
||||||
|
]
|
||||||
|
]
|
||||||
])(
|
])(
|
||||||
'restored dependencies for %s by primaryKey',
|
'restored dependencies for %s by primaryKey',
|
||||||
async (packageManager, pythonVersion, dependencyFile, fileHash) => {
|
async (
|
||||||
|
packageManager,
|
||||||
|
pythonVersion,
|
||||||
|
dependencyFile,
|
||||||
|
fileHash,
|
||||||
|
cachePaths
|
||||||
|
) => {
|
||||||
const cacheDistributor = getCacheDistributor(
|
const cacheDistributor = getCacheDistributor(
|
||||||
packageManager,
|
packageManager,
|
||||||
pythonVersion,
|
pythonVersion,
|
||||||
@ -123,10 +172,21 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py
|
|||||||
|
|
||||||
await cacheDistributor.restoreCache();
|
await cacheDistributor.restoreCache();
|
||||||
|
|
||||||
|
if (cachePaths !== undefined) {
|
||||||
|
expect(saveStateSpy).toHaveBeenCalledWith(
|
||||||
|
State.CACHE_PATHS,
|
||||||
|
cachePaths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (process.platform === 'linux' && packageManager === 'pip') {
|
if (process.platform === 'linux' && packageManager === 'pip') {
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
`Cache restored from key: setup-python-${process.env['RUNNER_OS']}-20.04-Ubuntu-python-${pythonVersion}-${packageManager}-${fileHash}`
|
`Cache restored from key: setup-python-${process.env['RUNNER_OS']}-20.04-Ubuntu-python-${pythonVersion}-${packageManager}-${fileHash}`
|
||||||
);
|
);
|
||||||
|
} else if (packageManager === 'poetry') {
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache restored from key: setup-python-${process.env['RUNNER_OS']}-python-${pythonVersion}-${packageManager}-v2-${fileHash}`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
`Cache restored from key: setup-python-${process.env['RUNNER_OS']}-python-${pythonVersion}-${packageManager}-${fileHash}`
|
`Cache restored from key: setup-python-${process.env['RUNNER_OS']}-python-${pythonVersion}-${packageManager}-${fileHash}`
|
||||||
@ -164,8 +224,13 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py
|
|||||||
|
|
||||||
describe('Dependencies changed', () => {
|
describe('Dependencies changed', () => {
|
||||||
it.each([
|
it.each([
|
||||||
['pip', '3.8.12', undefined, pipFileLockHash],
|
['pip', '3.8.12', '__tests__/data/**/requirements.txt', pipFileLockHash],
|
||||||
['pip', '3.8.12', '**/requirements-linux.txt', pipFileLockHash],
|
[
|
||||||
|
'pip',
|
||||||
|
'3.8.12',
|
||||||
|
'__tests__/data/**/requirements-linux.txt',
|
||||||
|
pipFileLockHash
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'pip',
|
'pip',
|
||||||
'3.8.12',
|
'3.8.12',
|
||||||
@ -173,9 +238,9 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py
|
|||||||
pipFileLockHash
|
pipFileLockHash
|
||||||
],
|
],
|
||||||
['pip', '3.8.12', '__tests__/data/requirements.txt', pipFileLockHash],
|
['pip', '3.8.12', '__tests__/data/requirements.txt', pipFileLockHash],
|
||||||
['pipenv', '3.9.1', undefined, requirementsHash],
|
['pipenv', '3.9.1', '__tests__/data/**/Pipfile.lock', requirementsHash],
|
||||||
['pipenv', '3.9.12', '__tests__/data/requirements.txt', requirementsHash],
|
['pipenv', '3.9.12', '__tests__/data/requirements.txt', requirementsHash],
|
||||||
['poetry', '3.9.1', undefined, requirementsHash]
|
['poetry', '3.9.1', '__tests__/data/**/poetry.lock', requirementsHash]
|
||||||
])(
|
])(
|
||||||
'restored dependencies for %s by primaryKey',
|
'restored dependencies for %s by primaryKey',
|
||||||
async (packageManager, pythonVersion, dependencyFile, fileHash) => {
|
async (packageManager, pythonVersion, dependencyFile, fileHash) => {
|
||||||
|
@ -18,7 +18,7 @@ describe('run', () => {
|
|||||||
let infoSpy: jest.SpyInstance;
|
let infoSpy: jest.SpyInstance;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.SpyInstance;
|
||||||
let debugSpy: jest.SpyInstance;
|
let debugSpy: jest.SpyInstance;
|
||||||
let saveSatetSpy: jest.SpyInstance;
|
let saveStateSpy: jest.SpyInstance;
|
||||||
let getStateSpy: jest.SpyInstance;
|
let getStateSpy: jest.SpyInstance;
|
||||||
let getInputSpy: jest.SpyInstance;
|
let getInputSpy: jest.SpyInstance;
|
||||||
let setFailedSpy: jest.SpyInstance;
|
let setFailedSpy: jest.SpyInstance;
|
||||||
@ -43,8 +43,8 @@ describe('run', () => {
|
|||||||
debugSpy = jest.spyOn(core, 'debug');
|
debugSpy = jest.spyOn(core, 'debug');
|
||||||
debugSpy.mockImplementation(input => undefined);
|
debugSpy.mockImplementation(input => undefined);
|
||||||
|
|
||||||
saveSatetSpy = jest.spyOn(core, 'saveState');
|
saveStateSpy = jest.spyOn(core, 'saveState');
|
||||||
saveSatetSpy.mockImplementation(input => undefined);
|
saveStateSpy.mockImplementation(input => undefined);
|
||||||
|
|
||||||
getStateSpy = jest.spyOn(core, 'getState');
|
getStateSpy = jest.spyOn(core, 'getState');
|
||||||
getStateSpy.mockImplementation(input => {
|
getStateSpy.mockImplementation(input => {
|
||||||
|
1
__tests__/data/inner/poetry.lock
generated
Symbolic link
1
__tests__/data/inner/poetry.lock
generated
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../poetry.lock
|
1
__tests__/data/inner/pyproject.toml
Symbolic link
1
__tests__/data/inner/pyproject.toml
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../pyproject.toml
|
@ -42,14 +42,13 @@ describe('validateVersion', () => {
|
|||||||
describe('isCacheFeatureAvailable', () => {
|
describe('isCacheFeatureAvailable', () => {
|
||||||
it('isCacheFeatureAvailable disabled on GHES', () => {
|
it('isCacheFeatureAvailable disabled on GHES', () => {
|
||||||
jest.spyOn(cache, 'isFeatureAvailable').mockImplementation(() => false);
|
jest.spyOn(cache, 'isFeatureAvailable').mockImplementation(() => false);
|
||||||
|
const infoMock = jest.spyOn(core, 'warning');
|
||||||
|
const message =
|
||||||
|
'Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.';
|
||||||
try {
|
try {
|
||||||
process.env['GITHUB_SERVER_URL'] = 'http://example.com';
|
process.env['GITHUB_SERVER_URL'] = 'http://example.com';
|
||||||
isCacheFeatureAvailable();
|
expect(isCacheFeatureAvailable()).toBeFalsy();
|
||||||
} catch (error) {
|
expect(infoMock).toHaveBeenCalledWith(message);
|
||||||
expect(error).toHaveProperty(
|
|
||||||
'message',
|
|
||||||
'Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.'
|
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
delete process.env['GITHUB_SERVER_URL'];
|
delete process.env['GITHUB_SERVER_URL'];
|
||||||
}
|
}
|
||||||
|
4
dist/cache-save/index.js
vendored
4
dist/cache-save/index.js
vendored
@ -59711,6 +59711,9 @@ class CacheDistributor {
|
|||||||
this.cacheDependencyPath = cacheDependencyPath;
|
this.cacheDependencyPath = cacheDependencyPath;
|
||||||
this.CACHE_KEY_PREFIX = 'setup-python';
|
this.CACHE_KEY_PREFIX = 'setup-python';
|
||||||
}
|
}
|
||||||
|
handleLoadedCache() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () { });
|
||||||
|
}
|
||||||
restoreCache() {
|
restoreCache() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const { primaryKey, restoreKey } = yield this.computeKeys();
|
const { primaryKey, restoreKey } = yield this.computeKeys();
|
||||||
@ -59723,6 +59726,7 @@ class CacheDistributor {
|
|||||||
core.saveState(State.CACHE_PATHS, cachePath);
|
core.saveState(State.CACHE_PATHS, cachePath);
|
||||||
core.saveState(State.STATE_CACHE_PRIMARY_KEY, primaryKey);
|
core.saveState(State.STATE_CACHE_PRIMARY_KEY, primaryKey);
|
||||||
const matchedKey = yield cache.restoreCache(cachePath, primaryKey, restoreKey);
|
const matchedKey = yield cache.restoreCache(cachePath, primaryKey, restoreKey);
|
||||||
|
yield this.handleLoadedCache();
|
||||||
this.handleMatchResult(matchedKey, primaryKey);
|
this.handleMatchResult(matchedKey, primaryKey);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
149
dist/setup/index.js
vendored
149
dist/setup/index.js
vendored
@ -65787,6 +65787,9 @@ class CacheDistributor {
|
|||||||
this.cacheDependencyPath = cacheDependencyPath;
|
this.cacheDependencyPath = cacheDependencyPath;
|
||||||
this.CACHE_KEY_PREFIX = 'setup-python';
|
this.CACHE_KEY_PREFIX = 'setup-python';
|
||||||
}
|
}
|
||||||
|
handleLoadedCache() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () { });
|
||||||
|
}
|
||||||
restoreCache() {
|
restoreCache() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const { primaryKey, restoreKey } = yield this.computeKeys();
|
const { primaryKey, restoreKey } = yield this.computeKeys();
|
||||||
@ -65799,6 +65802,7 @@ class CacheDistributor {
|
|||||||
core.saveState(State.CACHE_PATHS, cachePath);
|
core.saveState(State.CACHE_PATHS, cachePath);
|
||||||
core.saveState(State.STATE_CACHE_PRIMARY_KEY, primaryKey);
|
core.saveState(State.STATE_CACHE_PRIMARY_KEY, primaryKey);
|
||||||
const matchedKey = yield cache.restoreCache(cachePath, primaryKey, restoreKey);
|
const matchedKey = yield cache.restoreCache(cachePath, primaryKey, restoreKey);
|
||||||
|
yield this.handleLoadedCache();
|
||||||
this.handleMatchResult(matchedKey, primaryKey);
|
this.handleMatchResult(matchedKey, primaryKey);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -66078,6 +66082,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
||||||
|
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
||||||
|
var m = o[Symbol.asyncIterator], i;
|
||||||
|
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
||||||
|
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
||||||
|
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
||||||
|
};
|
||||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
@ -66090,38 +66101,48 @@ const core = __importStar(__nccwpck_require__(2186));
|
|||||||
const cache_distributor_1 = __importDefault(__nccwpck_require__(8953));
|
const cache_distributor_1 = __importDefault(__nccwpck_require__(8953));
|
||||||
const utils_1 = __nccwpck_require__(1314);
|
const utils_1 = __nccwpck_require__(1314);
|
||||||
class PoetryCache extends cache_distributor_1.default {
|
class PoetryCache extends cache_distributor_1.default {
|
||||||
constructor(pythonVersion, patterns = '**/poetry.lock') {
|
constructor(pythonVersion, patterns = '**/poetry.lock', poetryProjects = new Set()) {
|
||||||
super('poetry', patterns);
|
super('poetry', patterns);
|
||||||
this.pythonVersion = pythonVersion;
|
this.pythonVersion = pythonVersion;
|
||||||
this.patterns = patterns;
|
this.patterns = patterns;
|
||||||
|
this.poetryProjects = poetryProjects;
|
||||||
}
|
}
|
||||||
getCacheGlobalDirectories() {
|
getCacheGlobalDirectories() {
|
||||||
|
var e_1, _a;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const poetryConfig = yield this.getPoetryConfiguration();
|
// Same virtualenvs path may appear for different projects, hence we use a Set
|
||||||
const cacheDir = poetryConfig['cache-dir'];
|
const paths = new Set();
|
||||||
const virtualenvsPath = poetryConfig['virtualenvs.path'].replace('{cache-dir}', cacheDir);
|
const globber = yield glob.create(this.patterns);
|
||||||
const paths = [virtualenvsPath];
|
try {
|
||||||
if (poetryConfig['virtualenvs.in-project'] === true) {
|
for (var _b = __asyncValues(globber.globGenerator()), _c; _c = yield _b.next(), !_c.done;) {
|
||||||
paths.push(path.join(process.cwd(), '.venv'));
|
const file = _c.value;
|
||||||
}
|
const basedir = path.dirname(file);
|
||||||
const pythonLocation = yield io.which('python');
|
core.debug(`Processing Poetry project at ${basedir}`);
|
||||||
if (pythonLocation) {
|
this.poetryProjects.add(basedir);
|
||||||
core.debug(`pythonLocation is ${pythonLocation}`);
|
const poetryConfig = yield this.getPoetryConfiguration(basedir);
|
||||||
const { exitCode, stderr } = yield exec.getExecOutput(`poetry env use ${pythonLocation}`, undefined, { ignoreReturnCode: true });
|
const cacheDir = poetryConfig['cache-dir'];
|
||||||
if (exitCode) {
|
const virtualenvsPath = poetryConfig['virtualenvs.path'].replace('{cache-dir}', cacheDir);
|
||||||
utils_1.logWarning(stderr);
|
paths.add(virtualenvsPath);
|
||||||
|
if (poetryConfig['virtualenvs.in-project']) {
|
||||||
|
paths.add(path.join(basedir, '.venv'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||||
utils_1.logWarning('python binaries were not found in PATH');
|
finally {
|
||||||
|
try {
|
||||||
|
if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
|
||||||
|
}
|
||||||
|
finally { if (e_1) throw e_1.error; }
|
||||||
}
|
}
|
||||||
return paths;
|
return [...paths];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
computeKeys() {
|
computeKeys() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const hash = yield glob.hashFiles(this.patterns);
|
const hash = yield glob.hashFiles(this.patterns);
|
||||||
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
// "v2" is here to invalidate old caches of this cache distributor, which were created broken:
|
||||||
|
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}-v2-${hash}`;
|
||||||
const restoreKey = undefined;
|
const restoreKey = undefined;
|
||||||
return {
|
return {
|
||||||
primaryKey,
|
primaryKey,
|
||||||
@ -66129,12 +66150,33 @@ class PoetryCache extends cache_distributor_1.default {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getPoetryConfiguration() {
|
handleLoadedCache() {
|
||||||
|
const _super = Object.create(null, {
|
||||||
|
handleLoadedCache: { get: () => super.handleLoadedCache }
|
||||||
|
});
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const { stdout, stderr, exitCode } = yield exec.getExecOutput('poetry', [
|
yield _super.handleLoadedCache.call(this);
|
||||||
'config',
|
// After the cache is loaded -- make sure virtualenvs use the correct Python version (the one that we have just installed).
|
||||||
'--list'
|
// This will handle invalid caches, recreating virtualenvs if necessary.
|
||||||
]);
|
const pythonLocation = yield io.which('python');
|
||||||
|
if (pythonLocation) {
|
||||||
|
core.debug(`pythonLocation is ${pythonLocation}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
utils_1.logWarning('python binaries were not found in PATH');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const poetryProject of this.poetryProjects) {
|
||||||
|
const { exitCode, stderr } = yield exec.getExecOutput('poetry', ['env', 'use', pythonLocation], { ignoreReturnCode: true, cwd: poetryProject });
|
||||||
|
if (exitCode) {
|
||||||
|
utils_1.logWarning(stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getPoetryConfiguration(basedir) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const { stdout, stderr, exitCode } = yield exec.getExecOutput('poetry', ['config', '--list'], { cwd: basedir });
|
||||||
if (exitCode && stderr) {
|
if (exitCode && stderr) {
|
||||||
throw new Error('Could not get cache folder path for poetry package manager');
|
throw new Error('Could not get cache folder path for poetry package manager');
|
||||||
}
|
}
|
||||||
@ -66867,31 +66909,31 @@ function cacheDependencies(cache, pythonVersion) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
function resolveVersionInput() {
|
function resolveVersionInput() {
|
||||||
let version = core.getInput('python-version');
|
let versions = core.getMultilineInput('python-version');
|
||||||
let versionFile = core.getInput('python-version-file');
|
let versionFile = core.getInput('python-version-file');
|
||||||
if (version && versionFile) {
|
if (versions.length && versionFile) {
|
||||||
core.warning('Both python-version and python-version-file inputs are specified, only python-version will be used.');
|
core.warning('Both python-version and python-version-file inputs are specified, only python-version will be used.');
|
||||||
}
|
}
|
||||||
if (version) {
|
if (versions.length) {
|
||||||
return version;
|
return versions;
|
||||||
}
|
}
|
||||||
if (versionFile) {
|
if (versionFile) {
|
||||||
if (!fs_1.default.existsSync(versionFile)) {
|
if (!fs_1.default.existsSync(versionFile)) {
|
||||||
throw new Error(`The specified python version file at: ${versionFile} doesn't exist.`);
|
throw new Error(`The specified python version file at: ${versionFile} doesn't exist.`);
|
||||||
}
|
}
|
||||||
version = fs_1.default.readFileSync(versionFile, 'utf8');
|
const version = fs_1.default.readFileSync(versionFile, 'utf8');
|
||||||
core.info(`Resolved ${versionFile} as ${version}`);
|
core.info(`Resolved ${versionFile} as ${version}`);
|
||||||
return version;
|
return [version];
|
||||||
}
|
}
|
||||||
utils_1.logWarning("Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find '.python-version' file.");
|
utils_1.logWarning("Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find '.python-version' file.");
|
||||||
versionFile = '.python-version';
|
versionFile = '.python-version';
|
||||||
if (fs_1.default.existsSync(versionFile)) {
|
if (fs_1.default.existsSync(versionFile)) {
|
||||||
version = fs_1.default.readFileSync(versionFile, 'utf8');
|
const version = fs_1.default.readFileSync(versionFile, 'utf8');
|
||||||
core.info(`Resolved ${versionFile} as ${version}`);
|
core.info(`Resolved ${versionFile} as ${version}`);
|
||||||
return version;
|
return [version];
|
||||||
}
|
}
|
||||||
utils_1.logWarning(`${versionFile} doesn't exist.`);
|
utils_1.logWarning(`${versionFile} doesn't exist.`);
|
||||||
return version;
|
return versions;
|
||||||
}
|
}
|
||||||
function run() {
|
function run() {
|
||||||
var _a;
|
var _a;
|
||||||
@ -66904,22 +66946,26 @@ function run() {
|
|||||||
}
|
}
|
||||||
core.debug(`Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}`);
|
core.debug(`Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}`);
|
||||||
try {
|
try {
|
||||||
const version = resolveVersionInput();
|
const versions = resolveVersionInput();
|
||||||
const checkLatest = core.getBooleanInput('check-latest');
|
const checkLatest = core.getBooleanInput('check-latest');
|
||||||
if (version) {
|
if (versions.length) {
|
||||||
let pythonVersion;
|
let pythonVersion = '';
|
||||||
const arch = core.getInput('architecture') || os.arch();
|
const arch = core.getInput('architecture') || os.arch();
|
||||||
const updateEnvironment = core.getBooleanInput('update-environment');
|
const updateEnvironment = core.getBooleanInput('update-environment');
|
||||||
if (isPyPyVersion(version)) {
|
core.startGroup('Installed versions');
|
||||||
const installed = yield finderPyPy.findPyPyVersion(version, arch, updateEnvironment, checkLatest);
|
for (const version of versions) {
|
||||||
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
|
if (isPyPyVersion(version)) {
|
||||||
core.info(`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`);
|
const installed = yield finderPyPy.findPyPyVersion(version, arch, updateEnvironment, checkLatest);
|
||||||
}
|
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
|
||||||
else {
|
core.info(`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`);
|
||||||
const installed = yield finder.useCpythonVersion(version, arch, updateEnvironment, checkLatest);
|
}
|
||||||
pythonVersion = installed.version;
|
else {
|
||||||
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
|
const installed = yield finder.useCpythonVersion(version, arch, updateEnvironment, checkLatest);
|
||||||
|
pythonVersion = installed.version;
|
||||||
|
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
core.endGroup();
|
||||||
const cache = core.getInput('cache');
|
const cache = core.getInput('cache');
|
||||||
if (cache && utils_1.isCacheFeatureAvailable()) {
|
if (cache && utils_1.isCacheFeatureAvailable()) {
|
||||||
yield cacheDependencies(cache, pythonVersion);
|
yield cacheDependencies(cache, pythonVersion);
|
||||||
@ -67057,16 +67103,15 @@ function isGhes() {
|
|||||||
}
|
}
|
||||||
exports.isGhes = isGhes;
|
exports.isGhes = isGhes;
|
||||||
function isCacheFeatureAvailable() {
|
function isCacheFeatureAvailable() {
|
||||||
if (!cache.isFeatureAvailable()) {
|
if (cache.isFeatureAvailable()) {
|
||||||
if (isGhes()) {
|
return true;
|
||||||
throw new Error('Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.');
|
}
|
||||||
}
|
if (isGhes()) {
|
||||||
else {
|
core.warning('Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.');
|
||||||
core.warning('The runner was not able to contact the cache service. Caching will be skipped');
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
core.warning('The runner was not able to contact the cache service. Caching will be skipped');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
exports.isCacheFeatureAvailable = isCacheFeatureAvailable;
|
exports.isCacheFeatureAvailable = isCacheFeatureAvailable;
|
||||||
function logWarning(message) {
|
function logWarning(message) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
- [Using the python-version input](advanced-usage.md#using-the-python-version-input)
|
- [Using the python-version input](advanced-usage.md#using-the-python-version-input)
|
||||||
- [Specifying a Python version](advanced-usage.md#specifying-a-python-version)
|
- [Specifying a Python version](advanced-usage.md#specifying-a-python-version)
|
||||||
- [Specifying a PyPy version](advanced-usage.md#specifying-a-pypy-version)
|
- [Specifying a PyPy version](advanced-usage.md#specifying-a-pypy-version)
|
||||||
|
- [Specifying multiple Python and PyPy versions](advanced-usage.md#specifying-multiple-python/pypy-version)
|
||||||
- [Matrix Testing](advanced-usage.md#matrix-testing)
|
- [Matrix Testing](advanced-usage.md#matrix-testing)
|
||||||
- [Using the python-version-file input](advanced-usage.md#using-the-python-version-file-input)
|
- [Using the python-version-file input](advanced-usage.md#using-the-python-version-file-input)
|
||||||
- [Check latest version](advanced-usage.md#check-latest-version)
|
- [Check latest version](advanced-usage.md#check-latest-version)
|
||||||
@ -132,6 +133,62 @@ jobs:
|
|||||||
```
|
```
|
||||||
More details on PyPy syntax can be found in the [Available versions of PyPy](#pypy) section.
|
More details on PyPy syntax can be found in the [Available versions of PyPy](#pypy) section.
|
||||||
|
|
||||||
|
### Specifying multiple Python/PyPy version
|
||||||
|
The python-version input can get multiple python/pypy versions. The last specified version will be used as a default one.
|
||||||
|
|
||||||
|
Download and set up multiple Python versions:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: |
|
||||||
|
3.8
|
||||||
|
3.9
|
||||||
|
3.10
|
||||||
|
- run: python my_script.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Download and set up multiple PyPy versions:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: |
|
||||||
|
pypy-3.7-v7.3.x
|
||||||
|
pypy3.9-nightly
|
||||||
|
pypy3.8
|
||||||
|
- run: python my_script.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Download and set up multiple Python/PyPy versions:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: |
|
||||||
|
3.8
|
||||||
|
3.9
|
||||||
|
pypy3.9-nightly
|
||||||
|
pypy3.8
|
||||||
|
3.10
|
||||||
|
- run: python my_script.py
|
||||||
|
```
|
||||||
|
|
||||||
### Matrix Testing
|
### Matrix Testing
|
||||||
|
|
||||||
Using `setup-python` it's possible to use [matrix syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix) to install several versions of Python or PyPy:
|
Using `setup-python` it's possible to use [matrix syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix) to install several versions of Python or PyPy:
|
||||||
@ -202,7 +259,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@v3
|
||||||
- uses: actions/setup-python@v3
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.7'
|
python-version: '3.7'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
10335
package-lock.json
generated
10335
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,7 @@
|
|||||||
"build": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/cache-save src/cache-save.ts",
|
"build": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/cache-save src/cache-save.ts",
|
||||||
"format": "prettier --write \"{,!(node_modules)/**/}*.ts\"",
|
"format": "prettier --write \"{,!(node_modules)/**/}*.ts\"",
|
||||||
"format-check": "prettier --check \"{,!(node_modules)/**/}*.ts\"",
|
"format-check": "prettier --check \"{,!(node_modules)/**/}*.ts\"",
|
||||||
|
"lint": "echo \"Fake command that does nothing. It is used in reusable workflows\"",
|
||||||
"release": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/cache-save src/cache-save.ts && git add -f dist/",
|
"release": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/cache-save src/cache-save.ts && git add -f dist/",
|
||||||
"test": "jest --coverage"
|
"test": "jest --coverage"
|
||||||
},
|
},
|
||||||
|
@ -19,6 +19,7 @@ abstract class CacheDistributor {
|
|||||||
primaryKey: string;
|
primaryKey: string;
|
||||||
restoreKey: string[] | undefined;
|
restoreKey: string[] | undefined;
|
||||||
}>;
|
}>;
|
||||||
|
protected async handleLoadedCache() {}
|
||||||
|
|
||||||
public async restoreCache() {
|
public async restoreCache() {
|
||||||
const {primaryKey, restoreKey} = await this.computeKeys();
|
const {primaryKey, restoreKey} = await this.computeKeys();
|
||||||
@ -41,6 +42,8 @@ abstract class CacheDistributor {
|
|||||||
restoreKey
|
restoreKey
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await this.handleLoadedCache();
|
||||||
|
|
||||||
this.handleMatchResult(matchedKey, primaryKey);
|
this.handleMatchResult(matchedKey, primaryKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,52 +10,44 @@ import {logWarning} from '../utils';
|
|||||||
class PoetryCache extends CacheDistributor {
|
class PoetryCache extends CacheDistributor {
|
||||||
constructor(
|
constructor(
|
||||||
private pythonVersion: string,
|
private pythonVersion: string,
|
||||||
protected patterns: string = '**/poetry.lock'
|
protected patterns: string = '**/poetry.lock',
|
||||||
|
protected poetryProjects: Set<string> = new Set<string>()
|
||||||
) {
|
) {
|
||||||
super('poetry', patterns);
|
super('poetry', patterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getCacheGlobalDirectories() {
|
protected async getCacheGlobalDirectories() {
|
||||||
const poetryConfig = await this.getPoetryConfiguration();
|
// Same virtualenvs path may appear for different projects, hence we use a Set
|
||||||
|
const paths = new Set<string>();
|
||||||
|
const globber = await glob.create(this.patterns);
|
||||||
|
|
||||||
const cacheDir = poetryConfig['cache-dir'];
|
for await (const file of globber.globGenerator()) {
|
||||||
const virtualenvsPath = poetryConfig['virtualenvs.path'].replace(
|
const basedir = path.dirname(file);
|
||||||
'{cache-dir}',
|
core.debug(`Processing Poetry project at ${basedir}`);
|
||||||
cacheDir
|
this.poetryProjects.add(basedir);
|
||||||
);
|
|
||||||
|
|
||||||
const paths = [virtualenvsPath];
|
const poetryConfig = await this.getPoetryConfiguration(basedir);
|
||||||
|
|
||||||
if (poetryConfig['virtualenvs.in-project'] === true) {
|
const cacheDir = poetryConfig['cache-dir'];
|
||||||
paths.push(path.join(process.cwd(), '.venv'));
|
const virtualenvsPath = poetryConfig['virtualenvs.path'].replace(
|
||||||
}
|
'{cache-dir}',
|
||||||
|
cacheDir
|
||||||
const pythonLocation = await io.which('python');
|
|
||||||
|
|
||||||
if (pythonLocation) {
|
|
||||||
core.debug(`pythonLocation is ${pythonLocation}`);
|
|
||||||
const {
|
|
||||||
exitCode,
|
|
||||||
stderr
|
|
||||||
} = await exec.getExecOutput(
|
|
||||||
`poetry env use ${pythonLocation}`,
|
|
||||||
undefined,
|
|
||||||
{ignoreReturnCode: true}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (exitCode) {
|
paths.add(virtualenvsPath);
|
||||||
logWarning(stderr);
|
|
||||||
|
if (poetryConfig['virtualenvs.in-project']) {
|
||||||
|
paths.add(path.join(basedir, '.venv'));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logWarning('python binaries were not found in PATH');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return paths;
|
return [...paths];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async computeKeys() {
|
protected async computeKeys() {
|
||||||
const hash = await glob.hashFiles(this.patterns);
|
const hash = await glob.hashFiles(this.patterns);
|
||||||
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
// "v2" is here to invalidate old caches of this cache distributor, which were created broken:
|
||||||
|
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}-v2-${hash}`;
|
||||||
const restoreKey = undefined;
|
const restoreKey = undefined;
|
||||||
return {
|
return {
|
||||||
primaryKey,
|
primaryKey,
|
||||||
@ -63,11 +55,39 @@ class PoetryCache extends CacheDistributor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPoetryConfiguration() {
|
protected async handleLoadedCache() {
|
||||||
const {stdout, stderr, exitCode} = await exec.getExecOutput('poetry', [
|
await super.handleLoadedCache();
|
||||||
'config',
|
|
||||||
'--list'
|
// After the cache is loaded -- make sure virtualenvs use the correct Python version (the one that we have just installed).
|
||||||
]);
|
// This will handle invalid caches, recreating virtualenvs if necessary.
|
||||||
|
|
||||||
|
const pythonLocation = await io.which('python');
|
||||||
|
if (pythonLocation) {
|
||||||
|
core.debug(`pythonLocation is ${pythonLocation}`);
|
||||||
|
} else {
|
||||||
|
logWarning('python binaries were not found in PATH');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const poetryProject of this.poetryProjects) {
|
||||||
|
const {exitCode, stderr} = await exec.getExecOutput(
|
||||||
|
'poetry',
|
||||||
|
['env', 'use', pythonLocation],
|
||||||
|
{ignoreReturnCode: true, cwd: poetryProject}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (exitCode) {
|
||||||
|
logWarning(stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getPoetryConfiguration(basedir: string) {
|
||||||
|
const {stdout, stderr, exitCode} = await exec.getExecOutput(
|
||||||
|
'poetry',
|
||||||
|
['config', '--list'],
|
||||||
|
{cwd: basedir}
|
||||||
|
);
|
||||||
|
|
||||||
if (exitCode && stderr) {
|
if (exitCode && stderr) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -22,18 +22,18 @@ async function cacheDependencies(cache: string, pythonVersion: string) {
|
|||||||
await cacheDistributor.restoreCache();
|
await cacheDistributor.restoreCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveVersionInput(): string {
|
function resolveVersionInput() {
|
||||||
let version = core.getInput('python-version');
|
let versions = core.getMultilineInput('python-version');
|
||||||
let versionFile = core.getInput('python-version-file');
|
let versionFile = core.getInput('python-version-file');
|
||||||
|
|
||||||
if (version && versionFile) {
|
if (versions.length && versionFile) {
|
||||||
core.warning(
|
core.warning(
|
||||||
'Both python-version and python-version-file inputs are specified, only python-version will be used.'
|
'Both python-version and python-version-file inputs are specified, only python-version will be used.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version) {
|
if (versions.length) {
|
||||||
return version;
|
return versions;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (versionFile) {
|
if (versionFile) {
|
||||||
@ -42,9 +42,9 @@ function resolveVersionInput(): string {
|
|||||||
`The specified python version file at: ${versionFile} doesn't exist.`
|
`The specified python version file at: ${versionFile} doesn't exist.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
version = fs.readFileSync(versionFile, 'utf8');
|
const version = fs.readFileSync(versionFile, 'utf8');
|
||||||
core.info(`Resolved ${versionFile} as ${version}`);
|
core.info(`Resolved ${versionFile} as ${version}`);
|
||||||
return version;
|
return [version];
|
||||||
}
|
}
|
||||||
|
|
||||||
logWarning(
|
logWarning(
|
||||||
@ -52,14 +52,14 @@ function resolveVersionInput(): string {
|
|||||||
);
|
);
|
||||||
versionFile = '.python-version';
|
versionFile = '.python-version';
|
||||||
if (fs.existsSync(versionFile)) {
|
if (fs.existsSync(versionFile)) {
|
||||||
version = fs.readFileSync(versionFile, 'utf8');
|
const version = fs.readFileSync(versionFile, 'utf8');
|
||||||
core.info(`Resolved ${versionFile} as ${version}`);
|
core.info(`Resolved ${versionFile} as ${version}`);
|
||||||
return version;
|
return [version];
|
||||||
}
|
}
|
||||||
|
|
||||||
logWarning(`${versionFile} doesn't exist.`);
|
logWarning(`${versionFile} doesn't exist.`);
|
||||||
|
|
||||||
return version;
|
return versions;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
@ -75,35 +75,38 @@ async function run() {
|
|||||||
`Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}`
|
`Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}`
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
const version = resolveVersionInput();
|
const versions = resolveVersionInput();
|
||||||
const checkLatest = core.getBooleanInput('check-latest');
|
const checkLatest = core.getBooleanInput('check-latest');
|
||||||
|
|
||||||
if (version) {
|
if (versions.length) {
|
||||||
let pythonVersion: string;
|
let pythonVersion = '';
|
||||||
const arch: string = core.getInput('architecture') || os.arch();
|
const arch: string = core.getInput('architecture') || os.arch();
|
||||||
const updateEnvironment = core.getBooleanInput('update-environment');
|
const updateEnvironment = core.getBooleanInput('update-environment');
|
||||||
if (isPyPyVersion(version)) {
|
core.startGroup('Installed versions');
|
||||||
const installed = await finderPyPy.findPyPyVersion(
|
for (const version of versions) {
|
||||||
version,
|
if (isPyPyVersion(version)) {
|
||||||
arch,
|
const installed = await finderPyPy.findPyPyVersion(
|
||||||
updateEnvironment,
|
version,
|
||||||
checkLatest
|
arch,
|
||||||
);
|
updateEnvironment,
|
||||||
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
|
checkLatest
|
||||||
core.info(
|
);
|
||||||
`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`
|
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
|
||||||
);
|
core.info(
|
||||||
} else {
|
`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`
|
||||||
const installed = await finder.useCpythonVersion(
|
);
|
||||||
version,
|
} else {
|
||||||
arch,
|
const installed = await finder.useCpythonVersion(
|
||||||
updateEnvironment,
|
version,
|
||||||
checkLatest
|
arch,
|
||||||
);
|
updateEnvironment,
|
||||||
pythonVersion = installed.version;
|
checkLatest
|
||||||
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
|
);
|
||||||
|
pythonVersion = installed.version;
|
||||||
|
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
core.endGroup();
|
||||||
const cache = core.getInput('cache');
|
const cache = core.getInput('cache');
|
||||||
if (cache && isCacheFeatureAvailable()) {
|
if (cache && isCacheFeatureAvailable()) {
|
||||||
await cacheDependencies(cache, pythonVersion);
|
await cacheDependencies(cache, pythonVersion);
|
||||||
|
22
src/utils.ts
22
src/utils.ts
@ -105,21 +105,21 @@ export function isGhes(): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isCacheFeatureAvailable(): boolean {
|
export function isCacheFeatureAvailable(): boolean {
|
||||||
if (!cache.isFeatureAvailable()) {
|
if (cache.isFeatureAvailable()) {
|
||||||
if (isGhes()) {
|
return true;
|
||||||
throw new Error(
|
}
|
||||||
'Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.'
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
core.warning(
|
|
||||||
'The runner was not able to contact the cache service. Caching will be skipped'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (isGhes()) {
|
||||||
|
core.warning(
|
||||||
|
'Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.'
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
core.warning(
|
||||||
|
'The runner was not able to contact the cache service. Caching will be skipped'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logWarning(message: string): void {
|
export function logWarning(message: string): void {
|
||||||
|
Reference in New Issue
Block a user