Compare commits

...
Author SHA1 Message Date
Shubham Tiwari e86dffea3d Update utils.ts 2022-04-01 16:04:52 +05:30
Shubham TiwariandBrian Cristante 05fb98de9a Cache on ghes (#363)
* initial changes

* updated version

* format check

* refactored code

* updated test cases

* Update src/utils.ts

Co-authored-by: Brian Cristante <33549821+brcrista@users.noreply.github.com>

* Update utils.ts

* Update utils.test.ts

* review comments

* dist update

* Review comment

* update version

* updated version

Co-authored-by: Brian Cristante <33549821+brcrista@users.noreply.github.com>
2022-03-31 15:11:27 -04:00
Brian Cristante 6c566026c0 Merge pull request #281 from patrick91/feature/poetry-caching
Add poetry caching support
2022-03-31 13:27:09 -04:00
Brian Cristante 9516be869f Merge pull request #358 from actions/brcrista/codeql-main
Run CodeQL only on pushes to main
2022-03-31 10:54:02 -04:00
Patrick Arminio 7aa3e95001 Update dist 2022-03-30 16:47:06 -05:00
Patrick ArminioandBrian Cristante 6c31eb3fc7 Use \n instead of os.EOL
Co-authored-by: Brian Cristante <33549821+brcrista@users.noreply.github.com>
2022-03-30 22:41:41 +01:00
Patrick Arminio 7a37d78806 Update dist 2022-03-30 10:07:42 -05:00
Patrick Arminio e4be7a40b0 Initialise pyproject.toml 2022-03-28 13:42:47 -05:00
Patrick Arminio aac0ef93ba Build and format 2022-03-25 22:54:10 -05:00
Patrick Arminio b0c8e3dab7 Remove console.log 2022-03-25 22:53:31 -05:00
Patrick Arminio 0ebf7997c4 Remove unused file 2022-03-25 22:53:31 -05:00
Patrick Arminio 415c3568c9 Reduce test matrix 2022-03-25 22:53:31 -05:00
Patrick Arminio 42ed863652 Parse values from poetry 2022-03-25 22:53:31 -05:00
Patrick Arminio c275cf49c7 Release 2022-03-25 22:53:30 -05:00
Patrick Arminio 7c950ebdca Add more tests 2022-03-25 22:53:30 -05:00
Patrick Arminio 1259541ec8 Add tests 2022-03-25 22:53:30 -05:00
Patrick Arminio 95824fd246 Get poetry cache implementation when requested 2022-03-25 22:53:30 -05:00
Patrick Arminio 59cb3f4919 Add untest poetry cache implementation 2022-03-25 22:53:30 -05:00
Patrick Arminio 18c67b44e4 Initial preparation to add support for poetry 2022-03-25 22:53:30 -05:00
Brian Cristante bbebfafece Run CodeQL only on pushes to main 2022-03-24 09:48:15 -04:00
Brian Cristante 6c048c7558 Merge pull request #357 from actions/dependabot/npm_and_yarn/minimist-1.2.6
Bump minimist from 1.2.5 to 1.2.6
2022-03-24 09:34:43 -04:00
dependabot[bot] 0ff64a7f0e Bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-24 11:25:49 +00:00
Brian Cristante 792c23f43e Merge pull request #356 from cclauss/patch-2
README.md: Upgrade to v3
2022-03-17 09:45:50 -04:00
Christian Clauss 8c1441c007 README.md: Upgrade to v3
* https://github.com/actions/checkout/releases
* https://github.com/actions/setup-python/releases
2022-03-17 07:40:10 +01:00
Dmitry Shibanov 0ebf233433 Remove legacy PyPy input (#342) 2022-02-28 10:19:48 +03:00
Dmitry Shibanov 665cd78205 Update lockfileversion (#341) 2022-02-25 14:12:36 +03:00
Dmitry Shibanov 93cb78f17b Update to node16 (#340) 2022-02-25 12:31:18 +03:00
Dmitry Shibanov 7f80679172 Add fix for Windows caching of pip (#332) 2022-02-04 14:00:41 +03:00
Dmitry Shibanov dc9de69ff3 Update node-fetch from 2.6.6 to 2.6.7 (#327) 2022-02-01 14:17:23 +03:00
Hugo van Kemenade ba33a692f1 Include Python version in pip cache key (#303) 2022-01-31 13:42:08 +03:00
Dmitry Shibanov 156361d073 Fix ci for pipenv and reduce test matrix (#323) 2022-01-26 18:54:54 +03:00
Christian Clauss 9a115684c9 README.md: Bring Python versions up to date (#256) 2021-12-30 11:55:42 +03:00
Lenin 3a40ba0199 Fix small typo in README (comma missing) (#277) 2021-12-29 16:01:07 +03:00
killuazhu fa17801fa7 Fix typo in README.md (#224) 2021-12-29 15:30:46 +03:00
Dmitry Shibanov 6277dd1255 Fix build command (#306) 2021-12-21 15:44:58 +03:00
Dmitry Shibanov 2b732b899c Removing a non-existent command from the documentation (#293) 2021-12-13 18:51:02 +03:00
Nikita Bykov f382193329 Update @actions/cache version to 1.0.8 (#283) 2021-11-29 12:19:53 +03:00
Dmitry Shibanov 3ef38b826b Create ADR for integrating cache functionality to setup-python action (#247) 2021-11-18 13:29:22 +03:00
Dmitry Shibanov 0066b88440 Add a workflow to release a new version of the action (#274) 2021-11-17 15:52:32 +03:00
Dmitry Shibanov 280924fbef Implementation of python's caching (#266) 2021-11-17 13:31:22 +03:00
Micael Jarniac 52636cf49a Fix consistency in README (#250) 2021-11-12 18:22:20 +03:00
Nikita Bykov ca5c59064f Update version for setup-python in package.json (#264) 2021-11-09 17:07:47 +03:00
Nikita Bykov feeaa3ba49 Add issue and pull request templates (#258) 2021-10-20 12:22:43 +03:00
Dmitry Shibanov 002b1e4044 Change python version for pypy in workflow files (#259) 2021-10-19 20:24:02 +03:00
MaksimZhukov d12543aa2a Merge pull request #252 from dmitry-shibanov/v-dmshib/update-dependencies
Update dependencies
2021-10-14 13:15:23 +03:00
Dmitry Shibanov 4aa563ba8c update dependencies 2021-10-13 15:18:42 +03:00
Konrad Pabjan 70e2063f71 Update test-python.yml (#246) 2021-09-29 11:05:36 -04:00
Brian Cristante 2d803e7fea Create check-dist.yml (#236)
* Add check-dist.yml

* Fix triggers in licensed.yml
2021-08-25 11:47:15 -04:00
Konrad PabjanandBrian Cristante 43c2dc443f Recommendations for Python on GHES (#219)
* Recommendations for Python on GHES

* Update README.md

Co-authored-by: Brian Cristante <33549821+brcrista@users.noreply.github.com>

Co-authored-by: Brian Cristante <33549821+brcrista@users.noreply.github.com>
2021-06-24 22:50:36 +02:00
Alena Sviridenko dc73133d4d Fix PyPy installation on Windows to adopt new parameters format (#201)
* test for pypy new version notation

* formatting

* uncommented condition

* test

* added pypy to test matrix

* test

* test

* restored all tests

* removed logs, added multiarch support for toolcache

* reduced test matrix

* removed extra condition about arch
2021-04-12 13:59:38 -04:00
Robin Neatherway a1121449a2 Add on: pull_request trigger to CodeQL workflow (#180)
From February 2021, in order to provide feedback on pull requests, Code Scanning workflows must be configured with both `push` and `pull_request` triggers. This is because Code Scanning compares the results from a pull request against the results for the base branch to tell you only what has changed between the two.

Early in the beta period we supported displaying results on pull requests for workflows with only `push` triggers, but have discontinued support as this proved to be less robust.

See https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#scanning-pull-requests for more information on how best to configure your Code Scanning workflows.
2021-01-15 12:20:02 +01:00
Adam J. Stewart 66319ca9fa Use quotes around Python versions in README (#175) 2021-01-04 11:14:24 +01:00
Dmitry Shibanov 3105fb18c0 fix is_windows (#172) 2020-12-18 15:05:24 +01:00
95 changed files with 144303 additions and 10749 deletions
+22 -28
View File
@@ -1,44 +1,38 @@
--- ---
name: Bug report name: Bug report
about: Create a report to help us improve about: Create a bug report
title: '' title: ''
labels: bug labels: bug, needs triage
assignees: '' assignees: ''
--- ---
**Describe the bug** <!--- Please direct any generic questions related to actions to our support community forum at https://github.community/c/code-to-cloud/github-actions/41 --->
<!--- Before opening up a new bug report, please make sure to check for similar existing issues -->
**Description:**
A clear and concise description of what the bug is. A clear and concise description of what the bug is.
**Which version of the action are you using?** **Action version:**
Specify the action version
- [ ] `v1` **Platform:**
- [ ] `v2` - [ ] Ubuntu
- [ ] Some other tag (such as `v2.0.1` or `master`) - [ ] macOS
**Environment**
- [ ] self-hosted
- [ ] Linux
- [ ] Windows - [ ] Windows
- [ ] Mac
If applicable, please specify if you're using a container **Runner type:**
- [ ] Hosted
- [ ] Self-hosted
**Python Versions** **Tools version:**
Please list all of the effected versions of Python (`3.8.2`, etc.) <!--- Please list all of the affected Python versions (`3.8.2`, etc.)-->
**To Reproduce** **Repro steps:**
Steps to reproduce the behavior: A description with steps to reproduce the issue. If your have a public example or repo to share, please provide the link.
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Run/Repo Url** **Expected behavior:**
If applicable, and if your repo/run is public, please include a URL so it is easier for us to investigate. A description of what you expected to happen.
**Screenshots** **Actual behavior:**
If applicable, add screenshots to help explain your problem. A description of what is actually happening.
**Additional context**
Add any other context about the problem here.
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
+18
View File
@@ -0,0 +1,18 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature request, needs triage
assignees: ''
---
<!--- Please direct any generic questions related to actions to our support community forum at https://github.community/c/code-to-cloud/github-actions/41 --->
<!--- Before opening up a new feature request, please make sure to check for similar existing issues and pull requests -->
**Description:**
Describe your proposal.
**Justification:**
Justification or a use case for your proposal.
**Are you willing to submit a PR?**
<!--- We accept contributions! -->
+9
View File
@@ -0,0 +1,9 @@
**Description:**
Describe your changes.
**Related issue:**
Add link to the related issue.
**Check list:**
- [ ] Mark if documentation changes are required.
- [ ] Mark if tests were added or updated to cover the changes.
+51
View File
@@ -0,0 +1,51 @@
# `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/
on:
push:
branches:
- main
paths-ignore:
- '**.md'
pull_request:
paths-ignore:
- '**.md'
workflow_dispatch:
jobs:
check-dist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set Node.js 16.x
uses: actions/setup-node@v1
with:
node-version: 16.x
- 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@v2
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
with:
name: dist
path: dist/
+2
View File
@@ -2,6 +2,8 @@ name: "Code scanning - action"
on: on:
push: push:
branches: [ 'main' ]
pull_request:
schedule: schedule:
- cron: '25 3 * * 5' - cron: '25 3 * * 5'
+115
View File
@@ -0,0 +1,115 @@
name: e2e-cache
on:
pull_request:
paths-ignore:
- '**.md'
push:
branches:
- main
- releases/*
paths-ignore:
- '**.md'
permissions:
contents: read
jobs:
python-pip-dependencies-caching:
name: Test pip (Python ${{ matrix.python-version}}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', 'pypy-3.7-v7.x']
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: ./
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: pip install numpy pandas requests
python-pipenv-dependencies-caching:
name: Test pipenv (Python ${{ matrix.python-version}}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', 'pypy-3.7-v7.x']
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: ./
with:
python-version: ${{ matrix.python-version }}
cache: 'pipenv'
- name: Install pipenv
run: pipx install pipenv
- name: Install dependencies
run: pipenv install numpy
python-poetry-dependencies-caching:
name: Test poetry (Python ${{ matrix.python-version}}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', 'pypy-3.7-v7.x']
steps:
- uses: actions/checkout@v2
- name: Install poetry
run: pipx install poetry
- name: Setup Python
uses: ./
with:
python-version: ${{ matrix.python-version }}
cache: 'poetry'
- name: Init pyproject.toml
run: poetry init -n
- name: Install dependencies
run: poetry add flake8
python-pip-dependencies-caching-path:
name: Test pip (Python ${{ matrix.python-version}}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', 'pypy-3.7-v7.x']
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: ./
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: __tests__/data/requirements.txt
- name: Install dependencies
run: pip install numpy pandas requests
python-pipenv-dependencies-caching-path:
name: Test pipenv (Python ${{ matrix.python-version}}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', 'pypy-3.7-v7.x']
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: ./
with:
python-version: ${{ matrix.python-version }}
cache: 'pipenv'
cache-dependency-path: '**/requirements-linux.txt'
- name: Install pipenv
run: pipx install pipenv
- name: Install dependencies
run: pipenv install numpy
+11 -3
View File
@@ -1,8 +1,12 @@
name: Licensed name: Licensed
on: on:
push: {branches: main} push:
pull_request: {branches: main} branches:
- main
pull_request:
branches:
- main
jobs: jobs:
test: test:
@@ -10,11 +14,15 @@ jobs:
name: Check licenses name: Check licenses
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set Node.js 16.x
uses: actions/setup-node@v2
with:
node-version: 16.x
- run: npm ci - run: npm ci
- name: Install licensed - name: Install licensed
run: | run: |
cd $RUNNER_TEMP cd $RUNNER_TEMP
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/2.12.2/licensed-2.12.2-linux-x64.tar.gz 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 tar -xzf licensed.tar.gz
sudo mv licensed /usr/local/bin/licensed sudo mv licensed /usr/local/bin/licensed
- run: licensed status - run: licensed status
@@ -0,0 +1,27 @@
name: Release new action version
on:
release:
types: [released]
workflow_dispatch:
inputs:
TAG_NAME:
description: 'Tag name that the major tag will point to'
required: true
env:
TAG_NAME: ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }}
permissions:
contents: write
jobs:
update_tag:
name: Update the major tag to include the ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} changes
environment:
name: releaseNewActionVersion
runs-on: ubuntu-latest
steps:
- name: Update the ${{ env.TAG_NAME }} tag
uses: actions/publish-action@v0.1.0
with:
source-tag: ${{ env.TAG_NAME }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
+6 -7
View File
@@ -18,17 +18,16 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-latest, windows-latest, ubuntu-18.04, ubuntu-20.04] os: [macos-latest, windows-latest, ubuntu-18.04, ubuntu-latest]
pypy: pypy:
- 'pypy-2.7' - 'pypy-2.7'
- 'pypy-3.6'
- 'pypy-3.7' - 'pypy-3.7'
- 'pypy-2.7-v7.3.2' - 'pypy-2.7-v7.3.4'
- 'pypy-3.6-v7.3.2' - 'pypy-3.7-v7.3.5'
- 'pypy-3.7-v7.3.2' - 'pypy-3.7-v7.3.4'
- 'pypy-3.6-v7.3.x' - 'pypy-3.7-v7.3.x'
- 'pypy-3.7-v7.x' - 'pypy-3.7-v7.x'
- 'pypy-3.6-v7.3.3rc1' - 'pypy-2.7-v7.3.4rc1'
- 'pypy-3.7-nightly' - 'pypy-3.7-nightly'
steps: steps:
+3 -23
View File
@@ -18,7 +18,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04] os: [macos-latest, windows-latest, ubuntu-18.04, ubuntu-20.04]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -38,7 +38,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04] os: [macos-latest, windows-latest, ubuntu-18.04, ubuntu-20.04]
python: [3.5.4, 3.6.7, 3.7.5, 3.8.1] python: [3.5.4, 3.6.7, 3.7.5, 3.8.1]
steps: steps:
- name: Checkout - name: Checkout
@@ -68,7 +68,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04] os: [macos-latest, windows-latest, ubuntu-18.04, ubuntu-20.04]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -91,23 +91,3 @@ jobs:
- name: Run simple code - name: Run simple code
run: python -c 'import math; print(math.factorial(5))' run: python -c 'import math; print(math.factorial(5))'
setup-pypy-legacy:
name: Setup PyPy ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: setup-python pypy3
uses: ./
with:
python-version: 'pypy3'
- name: setup-python pypy2
uses: ./
with:
python-version: 'pypy2'
+4 -4
View File
@@ -19,13 +19,13 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set Node.js 12.x - name: Set Node.js 16.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 16.x
- name: npm install - name: npm ci
run: npm install run: npm ci
- name: Lint - name: Lint
run: npm run format-check run: npm run format-check
+1
View File
@@ -9,6 +9,7 @@ allowed:
- mit - mit
- cc0-1.0 - cc0-1.0
- unlicense - unlicense
- 0bsd
reviewed: reviewed:
npm: npm:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+121 -32
View File
@@ -1,4 +1,4 @@
# setup-python V2 # setup-python V3
<p align="left"> <p align="left">
<a href="https://github.com/actions/setup-python"><img alt="GitHub Actions status" src="https://github.com/actions/setup-python/workflows/Main%20workflow/badge.svg"></a> <a href="https://github.com/actions/setup-python"><img alt="GitHub Actions status" src="https://github.com/actions/setup-python/workflows/Main%20workflow/badge.svg"></a>
@@ -9,6 +9,7 @@ This action sets up a Python environment for use in actions by:
- optionally installing and adding to PATH a version of Python that is already installed in the tools cache. - optionally installing and adding to PATH a version of Python that is already installed in the tools cache.
- downloading, installing and adding to PATH an available version of Python from GitHub Releases ([actions/python-versions](https://github.com/actions/python-versions/releases)) if a specific version is not available in the tools cache. - downloading, installing and adding to PATH an available version of Python from GitHub Releases ([actions/python-versions](https://github.com/actions/python-versions/releases)) if a specific version is not available in the tools cache.
- failing if a specific version of Python is not preinstalled or available for download. - failing if a specific version of Python is not preinstalled or available for download.
- optionally caching dependencies for pip, pipenv and poetry.
- registering problem matchers for error output. - registering problem matchers for error output.
# What's new # What's new
@@ -18,6 +19,7 @@ This action sets up a Python environment for use in actions by:
- Automatic setup and download of Python packages if using a self-hosted runner. - Automatic setup and download of Python packages if using a self-hosted runner.
- Support for pre-release versions of Python. - Support for pre-release versions of Python.
- Support for installing any version of PyPy on-flight - Support for installing any version of PyPy on-flight
- Support for built-in caching of pip, pipenv and poetry dependencies
# Usage # Usage
@@ -26,8 +28,8 @@ See [action.yml](action.yml)
Basic: Basic:
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-python@v2 - uses: actions/setup-python@v3
with: with:
python-version: '3.x' # Version range or exact version of a Python version to use, using SemVer's version range syntax python-version: '3.x' # Version range or exact version of a Python version to use, using SemVer's version range syntax
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
@@ -41,12 +43,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: [ '2.x', '3.x', 'pypy-2.7', 'pypy-3.6', 'pypy-3.7' ] python-version: [ '2.x', '3.x', 'pypy-2.7', 'pypy-3.7', 'pypy-3.8' ]
name: Python ${{ matrix.python-version }} sample name: Python ${{ matrix.python-version }} sample
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Setup python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v3
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
architecture: x64 architecture: x64
@@ -61,20 +63,20 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
python-version: [2.7, 3.6, 3.7, 3.8, pypy-2.7, pypy-3.6] python-version: ['2.7', '3.7', '3.8', '3.9', '3.10', 'pypy-2.7', 'pypy-3.8']
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@v2 - uses: actions/checkout@v3
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v3
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Display Python version - name: Display Python version
run: python -c "import sys; print(sys.version)" run: python --version
``` ```
Download and set up a version of Python that does not come preinstalled on an image: Download and set up a version of Python that does not come preinstalled on an image:
@@ -85,10 +87,10 @@ jobs:
strategy: strategy:
matrix: matrix:
# in this example, there is a newer version already installed, 3.7.7, so the older version will be downloaded # in this example, there is a newer version already installed, 3.7.7, so the older version will be downloaded
python-version: [3.5, 3.6, 3.7.4, 3.8] python-version: ['3.7.4', '3.8', '3.9', '3.10']
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-python@v2 - uses: actions/setup-python@v3
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- run: python my_script.py - run: python my_script.py
@@ -97,20 +99,20 @@ jobs:
Download and set up an accurate pre-release version of Python: Download and set up an accurate pre-release version of Python:
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-python@v2 - uses: actions/setup-python@v3
with: with:
python-version: '3.9.0-beta.4' python-version: '3.11.0-alpha.1'
- run: python my_script.py - run: python my_script.py
``` ```
Download and set up the latest available version of Python (includes both pre-release and stable versions): Download and set up the latest available version of Python (includes both pre-release and stable versions):
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-python@v2 - uses: actions/setup-python@v3
with: with:
python-version: '3.9.0-alpha - 3.9.0' # SemVer's version range syntax python-version: '3.11.0-alpha - 3.11.0' # SemVer's version range syntax
- run: python my_script.py - run: python my_script.py
``` ```
@@ -123,12 +125,12 @@ jobs:
strategy: strategy:
matrix: matrix:
python-version: python-version:
- pypy-3.6 # the latest available version of PyPy that supports Python 3.6 - 'pypy-3.7' # the latest available version of PyPy that supports Python 3.7
- pypy-3.7 # the latest available version of PyPy that supports Python 3.7 - 'pypy-3.7-v7.3.3' # Python 3.7 and PyPy 7.3.3
- pypy-3.7-v7.3.3 # Python 3.7 and PyPy 7.3.3 - 'pypy-3.8' # the latest available version of PyPy that supports Python 3.8
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-python@v2 - uses: actions/setup-python@v3
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- run: python my_script.py - run: python my_script.py
@@ -144,7 +146,7 @@ Check out our detailed guide on using [Python with GitHub Actions](https://help.
`setup-python` is able to configure Python from two sources: `setup-python` is able to configure Python from two sources:
- Preinstalled versions of Python in the tools cache on GitHub-hosted runners. - Preinstalled versions of Python in the tools cache on GitHub-hosted runners.
- For detailed information regarding the available versions of Python that are installed see [Supported software](https://docs.github.com/en/actions/reference/specifications-for-github-hosted-runners#supported-software). - For detailed information regarding the available versions of Python that are installed, see [Supported software](https://docs.github.com/en/actions/reference/specifications-for-github-hosted-runners#supported-software).
- For every minor version of Python, expect only the latest patch to be preinstalled. - For every minor version of Python, expect only the latest patch to be preinstalled.
- If `3.8.1` is installed for example, and `3.8.2` is released, expect `3.8.1` to be removed and replaced by `3.8.2` in the tools cache. - If `3.8.1` is installed for example, and `3.8.2` is released, expect `3.8.1` to be removed and replaced by `3.8.2` in the tools cache.
- If the exact patch version doesn't matter to you, specifying just the major and minor version will get you the latest preinstalled patch version. In the previous example, the version spec `3.8` will use the `3.8.2` Python version found in the cache. - If the exact patch version doesn't matter to you, specifying just the major and minor version will get you the latest preinstalled patch version. In the previous example, the version spec `3.8` will use the `3.8.2` Python version found in the cache.
@@ -157,9 +159,9 @@ Check out our detailed guide on using [Python with GitHub Actions](https://help.
`setup-python` is able to configure PyPy from two sources: `setup-python` is able to configure PyPy from two sources:
- Preinstalled versions of PyPy in the tools cache on GitHub-hosted runners - Preinstalled versions of PyPy in the tools cache on GitHub-hosted runners
- For detailed information regarding the available versions of PyPy that are installed see [Supported software](https://docs.github.com/en/actions/reference/specifications-for-github-hosted-runners#supported-software). - For detailed information regarding the available versions of PyPy that are installed, see [Supported software](https://docs.github.com/en/actions/reference/specifications-for-github-hosted-runners#supported-software).
- For the latest PyPy release, all versions of Python are cached. - For the latest PyPy release, all versions of Python are cached.
- Cache is updated with a 1-2 week delay. If you specify the PyPy version as `pypy-3.6`, the cached version will be used although a newer version is available. If you need to start using the recently released version right after release, you should specify the exact PyPy version using `pypy-3.6-v7.3.3`. - Cache is updated with a 1-2 week delay. If you specify the PyPy version as `pypy-3.7`, the cached version will be used although a newer version is available. If you need to start using the recently released version right after release, you should specify the exact PyPy version using `pypy-3.7-v7.3.3`.
- Downloadable PyPy versions from the [official PyPy site](https://downloads.python.org/pypy/). - Downloadable PyPy versions from the [official PyPy site](https://downloads.python.org/pypy/).
- All available versions that we can download are listed in [versions.json](https://downloads.python.org/pypy/versions.json) file. - All available versions that we can download are listed in [versions.json](https://downloads.python.org/pypy/versions.json) file.
@@ -196,8 +198,8 @@ The version of PyPy should be specified in the format `pypy-<python_version>[-v<
The `<pypy_version>` parameter is optional and can be skipped. The latest version will be used in this case. The `<pypy_version>` parameter is optional and can be skipped. The latest version will be used in this case.
``` ```
pypy-3.6 # the latest available version of PyPy that supports Python 3.6
pypy-3.7 # the latest available version of PyPy that supports Python 3.7 pypy-3.7 # the latest available version of PyPy that supports Python 3.7
pypy-3.8 # the latest available version of PyPy that supports Python 3.8
pypy-2.7 # the latest available version of PyPy that supports Python 2.7 pypy-2.7 # the latest available version of PyPy that supports Python 2.7
pypy-3.7-v7.3.3 # Python 3.7 and PyPy 7.3.3 pypy-3.7-v7.3.3 # Python 3.7 and PyPy 7.3.3
pypy-3.7-v7.x # Python 3.7 and the latest available PyPy 7.x pypy-3.7-v7.x # Python 3.7 and the latest available PyPy 7.x
@@ -205,6 +207,87 @@ pypy-3.7-v7.3.3rc1 # Python 3.7 and preview version of PyPy
pypy-3.7-nightly # Python 3.7 and nightly PyPy pypy-3.7-nightly # Python 3.7 and nightly PyPy
``` ```
# Caching packages dependencies
The action has built-in functionality for caching and restoring dependencies. It uses [actions/cache](https://github.com/actions/toolkit/tree/main/packages/cache) under the hood for caching dependencies but requires less configuration settings. Supported package managers are `pip`, `pipenv` and `poetry`. The `cache` input is optional, and caching is turned off by default.
The action defaults to searching for a dependency file (`requirements.txt` for pip, `Pipfile.lock` for pipenv or `poetry.lock` for poetry) in the repository, and uses its hash as a part of the cache key. Use `cache-dependency-path` for cases where multiple dependency files are used, they are located in different subdirectories or different files for the hash want to be used.
- For pip, the action will cache global cache directory
- For pipenv, the action will cache virtualenv directory
- For poetry, the action will cache virtualenv directory
**Please Note:** Restored cache will not be used if the requirements.txt file is not updated for a long time and a newer version of the dependency is available that can lead to an increase in total build time.
The requirements file format allows to specify dependency versions using logical operators (for example chardet>=3.0.4) or specify dependencies without any versions. In this case the pip install -r requirements.txt command will always try to install the latest available package version. To be sure that the cache will be used, please stick to a specific dependency version and update it manually if necessary.
**Caching pip dependencies:**
```yaml
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
with:
python-version: '3.9'
cache: 'pip'
- run: pip install -r requirements.txt
```
**Caching pipenv dependencies:**
```yaml
steps:
- uses: actions/checkout@v3
- name: Install pipenv
run: pipx install pipenv
- uses: actions/setup-python@v3
with:
python-version: '3.9'
cache: 'pipenv'
- run: pipenv install
```
**Caching poetry dependencies:**
```yaml
steps:
- uses: actions/checkout@v2
- name: Install poetry
run: pipx install poetry
- uses: actions/setup-python@v2
with:
python-version: '3.9'
cache: 'poetry'
- run: poetry install
- run: poetry run pytest
```
**Using wildcard patterns to cache dependencies**
```yaml
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
with:
python-version: '3.9'
cache: 'pip'
cache-dependency-path: '**/requirements-dev.txt'
- run: pip install -r subdirectory/requirements-dev.txt
```
**Using a list of file paths to cache dependencies**
```yaml
steps:
- uses: actions/checkout@v3
- name: Install pipenv
run: pipx install pipenv
- uses: actions/setup-python@v3
with:
python-version: '3.9'
cache: 'pipenv'
cache-dependency-path: |
server/app/Pipfile.lock
__test__/app/Pipfile.lock
- run: pipenv install
```
# Using `setup-python` with a self hosted runner # Using `setup-python` with a self hosted runner
Python distributions are only available for the same [environments](https://github.com/actions/virtual-environments#available-environments) that GitHub Actions hosted environments are available for. If you are using an unsupported version of Ubuntu such as `19.04` or another Linux distribution such as Fedora, `setup-python` will not work. If you have a supported self-hosted runner and you would like to use `setup-python`, there are a few extra things you need to make sure are set up so that new versions of Python can be downloaded and configured on your runner. Python distributions are only available for the same [environments](https://github.com/actions/virtual-environments#available-environments) that GitHub Actions hosted environments are available for. If you are using an unsupported version of Ubuntu such as `19.04` or another Linux distribution such as Fedora, `setup-python` will not work. If you have a supported self-hosted runner and you would like to use `setup-python`, there are a few extra things you need to make sure are set up so that new versions of Python can be downloaded and configured on your runner.
@@ -232,7 +315,7 @@ If you are experiencing problems while configuring Python on your self-hosted ru
- The user starting the runner is in the owning group, and the owning group has write permission. - The user starting the runner is in the owning group, and the owning group has write permission.
- All users have write permission. - All users have write permission.
- One quick way to grant access is to change the user and group of `/opt/hostedtoolcache` to be the same as the runners using `chown`. - One quick way to grant access is to change the user and group of `/opt/hostedtoolcache` to be the same as the runners using `chown`.
- `sudo chown runner-user:runner-group opt/hostedtoolcache/`. - `sudo chown runner-user:runner-group /opt/hostedtoolcache/`.
- If your runner is configured as a service and you run into problems, make sure the user that the service is running as is correct. For more information, you can [check the status of your self-hosted runner](https://help.github.com/en/actions/hosting-your-own-runners/configuring-the-self-hosted-runner-application-as-a-service#checking-the-status-of-the-service). - If your runner is configured as a service and you run into problems, make sure the user that the service is running as is correct. For more information, you can [check the status of your self-hosted runner](https://help.github.com/en/actions/hosting-your-own-runners/configuring-the-self-hosted-runner-application-as-a-service#checking-the-status-of-the-service).
### Mac ### Mac
@@ -247,6 +330,12 @@ If you are experiencing problems while configuring Python on your self-hosted ru
`setup-python` helps keep your dependencies explicit and ensures consistent behavior between different runners. If you use `python` in a shell on a GitHub hosted runner without `setup-python` it will default to whatever is in PATH. The default version of Python in PATH vary between runners and can change unexpectedly so we recommend you always use `setup-python`. `setup-python` helps keep your dependencies explicit and ensures consistent behavior between different runners. If you use `python` in a shell on a GitHub hosted runner without `setup-python` it will default to whatever is in PATH. The default version of Python in PATH vary between runners and can change unexpectedly so we recommend you always use `setup-python`.
# Using `setup-python` on GHES
`setup-python` comes pre-installed on the appliance with GHES if Actions is enabled. When dynamically downloading Python distributions, `setup-python` downloads distributions from [`actions/python-versions`](https://github.com/actions/python-versions) on github.com (outside of the appliance). These calls to `actions/python-versions` are made via unauthenticated requests, which are limited to [60 requests per hour per IP](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting). If more requests are made within the time frame, then you will start to see rate-limit errors during download that read `##[error]API rate limit exceeded for...`.
To avoid hitting rate-limit problems, we recommend [setting up your own runner tool cache](https://docs.github.com/en/enterprise-server@2.22/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access#about-the-included-setup-actions-and-the-runner-tool-cache).
# License # License
The scripts and documentation in this project are released under the [MIT License](LICENSE). The scripts and documentation in this project are released under the [MIT License](LICENSE).
+194
View File
@@ -0,0 +1,194 @@
import * as core from '@actions/core';
import * as cache from '@actions/cache';
import * as exec from '@actions/exec';
import {getCacheDistributor} from '../src/cache-distributions/cache-factory';
describe('restore-cache', () => {
const pipFileLockHash =
'67d817abcde9c72da0ed5b8f235647cb14638b9ff9d742b42e4406d2eb16fe3c';
const requirementsHash =
'd8110e0006d7fb5ee76365d565eef9d37df1d11598b912d3eb66d398d57a1121';
const requirementsLinuxHash =
'2d0ff7f46b0e120e3d3294db65768b474934242637b9899b873e6283dfd16d7c';
const poetryLockHash =
'571bf984f8d210e6a97f854e479fdd4a2b5af67b5fdac109ec337a0ea16e7836';
const poetryConfigOutput = `
cache-dir = "/Users/patrick/Library/Caches/pypoetry"
experimental.new-installer = false
installer.parallel = true
virtualenvs.create = true
virtualenvs.in-project = true
virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/pypoetry/virtualenvs
`;
// core spy
let infoSpy: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
let debugSpy: jest.SpyInstance;
let saveSatetSpy: jest.SpyInstance;
let getStateSpy: jest.SpyInstance;
// cache spy
let restoreCacheSpy: jest.SpyInstance;
// exec spy
let getExecOutputSpy: jest.SpyInstance;
beforeEach(() => {
process.env['RUNNER_OS'] = process.env['RUNNER_OS'] ?? 'linux';
infoSpy = jest.spyOn(core, 'info');
infoSpy.mockImplementation(input => undefined);
warningSpy = jest.spyOn(core, 'warning');
warningSpy.mockImplementation(input => undefined);
debugSpy = jest.spyOn(core, 'debug');
debugSpy.mockImplementation(input => undefined);
saveSatetSpy = jest.spyOn(core, 'saveState');
saveSatetSpy.mockImplementation(input => undefined);
getStateSpy = jest.spyOn(core, 'getState');
getStateSpy.mockImplementation(input => undefined);
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
getExecOutputSpy.mockImplementation((input: string) => {
if (input.includes('pip')) {
return {stdout: 'pip', stderr: '', exitCode: 0};
}
if (input.includes('poetry')) {
return {stdout: poetryConfigOutput, stderr: '', exitCode: 0};
}
return {stdout: '', stderr: 'Error occured', exitCode: 2};
});
restoreCacheSpy = jest.spyOn(cache, 'restoreCache');
restoreCacheSpy.mockImplementation(
(cachePaths: string[], primaryKey: string, restoreKey?: string) => {
return primaryKey;
}
);
});
describe('Validate provided package manager', () => {
it.each(['npm', 'pip2', 'pip21', 'pip21.3', 'pipenv32'])(
'Throw an error because %s is not supported',
async packageManager => {
expect(() =>
getCacheDistributor(packageManager, '3.8.12', undefined)
).toThrowError(`Caching for '${packageManager}' is not supported`);
}
);
});
describe('Restore dependencies', () => {
it.each([
['pip', '3.8.12', undefined, requirementsHash],
['pip', '3.8.12', '**/requirements-linux.txt', requirementsLinuxHash],
[
'pip',
'3.8.12',
'__tests__/data/requirements-linux.txt',
requirementsLinuxHash
],
['pip', '3.8.12', '__tests__/data/requirements.txt', requirementsHash],
['pipenv', '3.9.1', undefined, pipFileLockHash],
['pipenv', '3.9.12', '__tests__/data/requirements.txt', requirementsHash],
['poetry', '3.9.1', undefined, poetryLockHash]
])(
'restored dependencies for %s by primaryKey',
async (packageManager, pythonVersion, dependencyFile, fileHash) => {
const cacheDistributor = await getCacheDistributor(
packageManager,
pythonVersion,
dependencyFile
);
await cacheDistributor.restoreCache();
expect(infoSpy).toHaveBeenCalledWith(
`Cache restored from key: setup-python-${process.env['RUNNER_OS']}-python-${pythonVersion}-${packageManager}-${fileHash}`
);
},
30000
);
it.each([
['pip', '3.8.12', 'requirements-linux.txt', 'requirements-linux.txt'],
['pip', '3.8.12', 'requirements.txt', 'requirements.txt'],
['pipenv', '3.9.12', 'requirements.txt', 'requirements.txt']
])(
'Should throw an error because dependency file is not found',
async (
packageManager,
pythonVersion,
dependencyFile,
cacheDependencyPath
) => {
const cacheDistributor = await getCacheDistributor(
packageManager,
pythonVersion,
dependencyFile
);
await expect(cacheDistributor.restoreCache()).rejects.toThrowError(
`No file in ${process.cwd()} matched to [${cacheDependencyPath
.split('\n')
.join(',')}], make sure you have checked out the target repository`
);
}
);
});
describe('Dependencies changed', () => {
it.each([
['pip', '3.8.12', undefined, pipFileLockHash],
['pip', '3.8.12', '**/requirements-linux.txt', pipFileLockHash],
[
'pip',
'3.8.12',
'__tests__/data/requirements-linux.txt',
pipFileLockHash
],
['pip', '3.8.12', '__tests__/data/requirements.txt', pipFileLockHash],
['pipenv', '3.9.1', undefined, requirementsHash],
['pipenv', '3.9.12', '__tests__/data/requirements.txt', requirementsHash],
['poetry', '3.9.1', undefined, requirementsHash]
])(
'restored dependencies for %s by primaryKey',
async (packageManager, pythonVersion, dependencyFile, fileHash) => {
restoreCacheSpy.mockImplementation(
(cachePaths: string[], primaryKey: string, restoreKey?: string) => {
return primaryKey !== fileHash && restoreKey ? pipFileLockHash : '';
}
);
const cacheDistributor = await getCacheDistributor(
packageManager,
pythonVersion,
dependencyFile
);
await cacheDistributor.restoreCache();
let result = '';
switch (packageManager) {
case 'pip':
result = `Cache restored from key: ${fileHash}`;
break;
case 'pipenv':
result = 'pipenv cache is not found';
break;
case 'poetry':
result = 'poetry cache is not found';
break;
}
expect(infoSpy).toHaveBeenCalledWith(result);
}
);
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});
});
+222
View File
@@ -0,0 +1,222 @@
import * as core from '@actions/core';
import * as cache from '@actions/cache';
import * as exec from '@actions/exec';
import {run} from '../src/cache-save';
import {State} from '../src/cache-distributions/cache-distributor';
describe('run', () => {
const pipFileLockHash =
'67d817abcde9c72da0ed5b8f235647cb14638b9ff9d742b42e4406d2eb16fe3c';
const requirementsHash =
'd8110e0006d7fb5ee76365d565eef9d37df1d11598b912d3eb66d398d57a1121';
const requirementsLinuxHash =
'2d0ff7f46b0e120e3d3294db65768b474934242637b9899b873e6283dfd16d7c';
const poetryLockHash =
'571bf984f8d210e6a97f854e479fdd4a2b5af67b5fdac109ec337a0ea16e7836';
// core spy
let infoSpy: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
let debugSpy: jest.SpyInstance;
let saveSatetSpy: jest.SpyInstance;
let getStateSpy: jest.SpyInstance;
let getInputSpy: jest.SpyInstance;
let setFailedSpy: jest.SpyInstance;
// cache spy
let saveCacheSpy: jest.SpyInstance;
// exec spy
let getExecOutputSpy: jest.SpyInstance;
let inputs = {} as any;
beforeEach(() => {
process.env['RUNNER_OS'] = process.env['RUNNER_OS'] ?? 'linux';
infoSpy = jest.spyOn(core, 'info');
infoSpy.mockImplementation(input => undefined);
warningSpy = jest.spyOn(core, 'warning');
warningSpy.mockImplementation(input => undefined);
debugSpy = jest.spyOn(core, 'debug');
debugSpy.mockImplementation(input => undefined);
saveSatetSpy = jest.spyOn(core, 'saveState');
saveSatetSpy.mockImplementation(input => undefined);
getStateSpy = jest.spyOn(core, 'getState');
getStateSpy.mockImplementation(input => {
if (input === State.CACHE_PATHS) {
return JSON.stringify([__dirname]);
}
return requirementsHash;
});
setFailedSpy = jest.spyOn(core, 'setFailed');
getInputSpy = jest.spyOn(core, 'getInput');
getInputSpy.mockImplementation(input => inputs[input]);
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
getExecOutputSpy.mockImplementation((input: string) => {
if (input.includes('pip')) {
return {stdout: 'pip', stderr: '', exitCode: 0};
}
return {stdout: '', stderr: 'Error occured', exitCode: 2};
});
saveCacheSpy = jest.spyOn(cache, 'saveCache');
saveCacheSpy.mockImplementation(() => undefined);
});
describe('Package manager validation', () => {
it('Package manager is not provided, skip caching', async () => {
inputs['cache'] = '';
await run();
expect(getInputSpy).toHaveBeenCalled();
expect(infoSpy).not.toHaveBeenCalled();
expect(saveCacheSpy).not.toHaveBeenCalled();
expect(setFailedSpy).not.toHaveBeenCalled();
});
});
describe('Validate unchanged cache is not saved', () => {
it('should not save cache for pip', async () => {
inputs['cache'] = 'pip';
await run();
expect(getInputSpy).toHaveBeenCalled();
expect(debugSpy).toHaveBeenCalledWith(
`paths for caching are ${__dirname}`
);
expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${requirementsHash}, not saving cache.`
);
expect(setFailedSpy).not.toHaveBeenCalled();
});
it('should not save cache for pipenv', async () => {
inputs['cache'] = 'pipenv';
await run();
expect(getInputSpy).toHaveBeenCalled();
expect(debugSpy).toHaveBeenCalledWith(
`paths for caching are ${__dirname}`
);
expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${requirementsHash}, not saving cache.`
);
expect(setFailedSpy).not.toHaveBeenCalled();
});
it('should not save cache for pipenv', async () => {
inputs['cache'] = 'pipenv';
await run();
expect(getInputSpy).toHaveBeenCalled();
expect(debugSpy).toHaveBeenCalledWith(
`paths for caching are ${__dirname}`
);
expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${requirementsHash}, not saving cache.`
);
expect(setFailedSpy).not.toHaveBeenCalled();
});
});
describe('action saves the cache', () => {
it('saves cache from pip', async () => {
inputs['cache'] = 'pip';
getStateSpy.mockImplementation((name: string) => {
if (name === State.CACHE_MATCHED_KEY) {
return requirementsHash;
} else if (name === State.CACHE_PATHS) {
return JSON.stringify([__dirname]);
} else {
return pipFileLockHash;
}
});
await run();
expect(getInputSpy).toHaveBeenCalled();
expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).not.toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${requirementsHash}, not saving cache.`
);
expect(saveCacheSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenLastCalledWith(
`Cache saved with the key: ${pipFileLockHash}`
);
expect(setFailedSpy).not.toHaveBeenCalled();
});
it('saves cache from pipenv', async () => {
inputs['cache'] = 'pipenv';
getStateSpy.mockImplementation((name: string) => {
if (name === State.CACHE_MATCHED_KEY) {
return pipFileLockHash;
} else if (name === State.CACHE_PATHS) {
return JSON.stringify([__dirname]);
} else {
return requirementsHash;
}
});
await run();
expect(getInputSpy).toHaveBeenCalled();
expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).not.toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${pipFileLockHash}, not saving cache.`
);
expect(saveCacheSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenLastCalledWith(
`Cache saved with the key: ${requirementsHash}`
);
expect(setFailedSpy).not.toHaveBeenCalled();
});
it('saves cache from poetry', async () => {
inputs['cache'] = 'poetry';
getStateSpy.mockImplementation((name: string) => {
if (name === State.CACHE_MATCHED_KEY) {
return poetryLockHash;
} else if (name === State.CACHE_PATHS) {
return JSON.stringify([__dirname]);
} else {
return requirementsHash;
}
});
await run();
expect(getInputSpy).toHaveBeenCalled();
expect(getStateSpy).toHaveBeenCalledTimes(3);
expect(infoSpy).not.toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${poetryLockHash}, not saving cache.`
);
expect(saveCacheSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenLastCalledWith(
`Cache saved with the key: ${requirementsHash}`
);
expect(setFailedSpy).not.toHaveBeenCalled();
});
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
inputs = {};
});
});
+276
View File
@@ -0,0 +1,276 @@
{
"_meta": {
"hash": {
"sha256": "408f110354c997d8df1e17841d5335ae690f5b25f8c78040d62257f9535c6005"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"altgraph": {
"hashes": [
"sha256:743628f2ac6a7c26f5d9223c91ed8ecbba535f506f4b6f558885a8a56a105857",
"sha256:ebf2269361b47d97b3b88e696439f6e4cbc607c17c51feb1754f90fb79839158"
],
"version": "==0.17.2"
},
"certifi": {
"hashes": [
"sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3",
"sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"
],
"index": "pypi",
"version": "==2020.6.20"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"index": "pypi",
"version": "==3.0.4"
},
"docutils": {
"hashes": [
"sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
"sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"
],
"index": "pypi",
"version": "==0.16"
},
"future": {
"hashes": [
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
"version": "==0.18.2"
},
"idna": {
"hashes": [
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
],
"index": "pypi",
"version": "==2.9"
},
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
"index": "pypi",
"version": "==1.1.0"
},
"kivy": {
"hashes": [
"sha256:090d3ded9835a17477cd93fbdaf0a7c42ff2218981cf198ded5ad8795bc74391",
"sha256:11e85eaf6efbfa2362a3334ffdad179a1b0ca8d255cca79eaa6a2765560d4982",
"sha256:1a1ff32f8a95f1e175198cbab81fcd2596783b180d4eafe63e87d171aa7fdb5e",
"sha256:1d28b198a64c30db8d94a0488e85f3037af60d514ab0d7ad5ab45add3ab77090",
"sha256:4a5480cbf837d3780c77a4f61b32b56d22ae9f03845e7a89dd3eaef1ae5fd037",
"sha256:4d0e596f74271e901b551f77661dde238df4765484fce9f5d1c72e8022984e84",
"sha256:5c3d0f2749522d62e9cce09cd54b2d823bf1b6b644ff1f627be49de6f3e3cba0",
"sha256:815a5c0b3b72fcd81ca7b2aa0744087163ed03e4cf9ab4e7c9733cea99fc1571",
"sha256:8819a27a09871af451760cb69486ced52e830c8a0a37480f22ef5e692f12c05b",
"sha256:a687602d90c4629dd036f577ca39acb76ba581370f9d915f3cab99be818ba8ad",
"sha256:b7ef6aad43a86d8df3fb865db864e354f2155a748019f8517f69f65c1a29cb64",
"sha256:b85ccf165050cbf2ee8447671eebbc222b369b40f0e0038dd9547d49a5e37373",
"sha256:c36652caa7f6c327dee834cfc699d5962d346b7a53e54bd81abc17c314226d89",
"sha256:ece170514db3f49844a41e4c910ad9ce9bc46da6f47a49158e11266bdcc6e479",
"sha256:f3bea6e4a21991827885d04127fc6d09a0e974ecfa12da7bf5faae93562ea102",
"sha256:f835462dd9aa491272552ef079b948a088598e2e95d68bb1d885d2c3f3d4e2c3"
],
"index": "pypi",
"version": "==1.11.1"
},
"kivy-deps-angle": {
"hashes": [
"sha256:50605fdd4c9fdbe9f717069734a598a9aba0afe5d3f0412afbe2ecff0326e92d",
"sha256:64ac7f33c000585dc30194e604aed925972c6b7c3848b5c3b073ae916fb0b55c",
"sha256:99c40d53582a958748e251dfbd61aa67fb85963e27529ca08a21f2f5eeed04e1",
"sha256:a2cea09e8a5e899629466403fbd540459f1cdef8d08c6c479b6607b95309be02",
"sha256:b167e19b3eea55a9a8c606a607bb909ec1bedda88deee40347c780b310155a79",
"sha256:b9d07976b0bf6bac724a42aa8ed5a8c7caa95609046db30c8f15bb731f8e4d36",
"sha256:bb4d53f15a093214adbbe205c108ede5cc0f6af6eff104c1b8c468ddaaf6400a",
"sha256:d0e7b7b9eb9669837a5d70808a7ea45f2b61961b56f9f69a233bad6bd36ce260"
],
"index": "pypi",
"version": "==0.3.0"
},
"kivy-deps.glew": {
"hashes": [
"sha256:09f72ee5ef33ff273332e2a229dc97d650d29818a0189339421949e4e0f63d93",
"sha256:1e28e40017af9d081fc0fc95b4fadaf31d15e9f63478dcee1c4257d67079894e",
"sha256:45aa7f0e8d9bcf5fc1810c9c38bc20edf7dee61df81ecf62102e0f84153f924a",
"sha256:6bb435620c3187d2c61054adb9ec277ed487256b457a0a7b1491bc0cb7247e18",
"sha256:92e72fa2c425887987d1aa861c99537033dc20d68ae1c54864871f0401682586",
"sha256:ab81783a82bef88a8d2bcf8a93bc21df6b8b0db6ee551eb802727d18f9074b17",
"sha256:c843104690c0c8f3a58105c53c57f31506f6f90562c18de00bd19317cc1045a7",
"sha256:cf351aad171796f8051af8e49ec430a9aa128d8557d8643e73f2bb1e5f9c2dab",
"sha256:ee8ab67abb2c98d84feede657cae472e7723e529af07394244bdd33caafb1a38",
"sha256:ef1116d99bd9cc737cb8c0e13e676955c17d6e4d6d1af5cfccef089a430071bb"
],
"index": "pypi",
"version": "==0.1.12"
},
"kivy-deps.gstreamer": {
"hashes": [
"sha256:0d9598d2d31c0e780adf4b767fa3a691123621fd0ffef94b83cf82c2da84341b",
"sha256:309eca64dee5939f16f8465e5cbb08bdde7c90ded1af6a00690c7e928326af79",
"sha256:3d53d2c84c0a997c4cac6c239b1e0a6486e533836321003dc365ec42b97a664b",
"sha256:4d996377111e854b3dea90846f9b2f98766a44529fd8b72125e18c552381d928",
"sha256:4f2ddd61d185310258d338ae80a646df7822efdd7d67e57f49dc7b87555c5d7e",
"sha256:6fa9f76afe600baa221abee31ce7dc63e653d0affe0f6c558bfc4f35af96396f",
"sha256:739cd331b9f33a822d700273674a79a3157054e9358a01a0d553f094a5f4a8c9",
"sha256:c29cfc63fe70a58dad889e631f1ba4711c9ea80103f2b2b8d670a97f093076c8",
"sha256:c4709765e2b17c6c96b46a92207b0457def147544d825654077603eaf0d424de"
],
"index": "pypi",
"version": "==0.1.17"
},
"kivy-deps.sdl2": {
"hashes": [
"sha256:053f26e8c05d5545bdbc7eeb8c450b8e4410ee355792e9345af536110fe247e2",
"sha256:1b987bdd4fbbcb31baf0d7fc9584ad99912179b8968311bb7e30fbeb14e98e0d",
"sha256:228128cdd8112dc7505ac43027a770476e9ef282e0b84ca68037133cd025960b",
"sha256:2c2fd5a12a7a9afe3bb962b273561099a180edae91bb9c8f8386b72253fcae4a",
"sha256:5ce23f1a3286d6288751a12b0eaefd02f947ea101bb807e9781b964e496fc3f3",
"sha256:7928746eaed51944c10d1bb36fcefebe3d1aff1b97ba32359c2c97ba74707e1b",
"sha256:9270fa8ed5130074b167a7a3a9c85efc3cfe3c04584ab084cb6ae9e4edfa8168",
"sha256:92ed97d3247bc8ce98f336cbc940bb889310199326e9ccf251c49ae7e4b80de8",
"sha256:96e1fa89fd8b5351f2d3c26bbffd50df8d554b03fba4025ecc941d773d241698",
"sha256:c3ace0ddde0e59cdcaf260eda1daa0c05ca9bf8cd0c4ea404539de25a5dcaec7"
],
"index": "pypi",
"version": "==0.1.22"
},
"kivy-garden": {
"hashes": [
"sha256:9b7d9de5efacbcd0c4b3dd873b30622a86093c9965aa47b523c7a32f3eb34610",
"sha256:c256f42788421273a08fbb0a228f0fb0e80dd86b629fb8c0920507f645be6c72"
],
"index": "pypi",
"version": "==0.1.4"
},
"packaging": {
"hashes": [
"sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7",
"sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"
],
"index": "pypi",
"version": "==21.0"
},
"pdf2image": {
"hashes": [
"sha256:a0d9906f5507192210a8d5d7ead63145e9dec4bccc4564b1fb644e923913c31c"
],
"index": "pypi",
"version": "==1.12.1"
},
"pefile": {
"hashes": [
"sha256:344a49e40a94e10849f0fe34dddc80f773a12b40675bf2f7be4b8be578bdd94a"
],
"markers": "python_full_version >= '3.6.0'",
"version": "==2021.9.3"
},
"pillow": {
"hashes": [
"sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f",
"sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8",
"sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad",
"sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f",
"sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae",
"sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d",
"sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5",
"sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b",
"sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8",
"sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233",
"sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6",
"sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727",
"sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f",
"sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38",
"sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4",
"sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626",
"sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d",
"sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6",
"sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6",
"sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63",
"sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f",
"sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41",
"sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1",
"sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117",
"sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d",
"sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9",
"sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a",
"sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce"
],
"index": "pypi",
"version": "==7.2"
},
"pygments": {
"hashes": [
"sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44",
"sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"
],
"index": "pypi",
"version": "==2.6.1"
},
"pyinstaller": {
"hashes": [
"sha256:3730fa80d088f8bb7084d32480eb87cbb4ddb64123363763cf8f2a1378c1c4b7"
],
"index": "pypi",
"version": "==3.6"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
"version": "==2.4.7"
},
"pywin32-ctypes": {
"hashes": [
"sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942",
"sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"
],
"version": "==0.2.0"
},
"requests": {
"hashes": [
"sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
"sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
],
"index": "pypi",
"version": "==2.24.0"
},
"urllib3": {
"hashes": [
"sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
"sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
],
"index": "pypi",
"version": "==1.25.9"
},
"xlrd": {
"hashes": [
"sha256:546eb36cee8db40c3eaa46c351e67ffee6eeb5fa2650b71bc4c758a29a1b29b2",
"sha256:e551fb498759fa3a5384a94ccd4c3c02eb7c00ea424426e212ac0c57be9dfbde"
],
"index": "pypi",
"version": "==1.2.0"
}
},
"develop": {}
}
+413
View File
@@ -0,0 +1,413 @@
[[package]]
name = "altgraph"
version = "0.17.2"
description = "Python graph (network) package"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "certifi"
version = "2020.6.20"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "chardet"
version = "3.0.4"
description = "Universal encoding detector for Python 2 and 3"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "dis3"
version = "0.1.3"
description = "Python 2.7 backport of the \"dis\" module from Python 3.5+"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "docutils"
version = "0.16"
description = "Docutils -- Python Documentation Utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "future"
version = "0.18.2"
description = "Clean single-source support for Python 3 and 2"
category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "idna"
version = "2.9"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "itsdangerous"
version = "1.1.0"
description = "Various helpers to pass data to untrusted environments and back."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "kivy"
version = "1.11.1"
description = "A software library for rapid development of hardware-accelerated multitouch applications."
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
docutils = "*"
Kivy-Garden = ">=0.1.4"
pygments = "*"
[package.extras]
tuio = ["oscpy"]
[[package]]
name = "kivy-deps.angle"
version = "0.3.0"
description = "Repackaged binary dependency of Kivy."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "kivy-deps.glew"
version = "0.1.12"
description = "Repackaged binary dependency of Kivy."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "kivy-deps.gstreamer"
version = "0.1.17"
description = "Repackaged binary dependency of Kivy."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "kivy-deps.sdl2"
version = "0.1.22"
description = "Repackaged binary dependency of Kivy."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "kivy-garden"
version = "0.1.4"
description = "Garden tool for kivy flowers."
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
requests = "*"
[[package]]
name = "packaging"
version = "21.0"
description = "Core utilities for Python packages"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2"
[[package]]
name = "pdf2image"
version = "1.12.1"
description = "A wrapper around the pdftoppm and pdftocairo command line tools to convert PDF to a PIL Image list."
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
pillow = "*"
[[package]]
name = "pefile"
version = "2021.9.3"
description = "Python PE parsing module"
category = "main"
optional = false
python-versions = ">=3.6.0"
[package.dependencies]
future = "*"
[[package]]
name = "pillow"
version = "7.2.0"
description = "Python Imaging Library (Fork)"
category = "main"
optional = false
python-versions = ">=3.5"
[[package]]
name = "pygments"
version = "2.6.1"
description = "Pygments is a syntax highlighting package written in Python."
category = "main"
optional = false
python-versions = ">=3.5"
[[package]]
name = "pyinstaller"
version = "3.6"
description = "PyInstaller bundles a Python application and all its dependencies into a single package."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.dependencies]
altgraph = "*"
dis3 = "*"
[[package]]
name = "pyparsing"
version = "2.4.7"
description = "Python parsing module"
category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "pywin32-ctypes"
version = "0.2.0"
description = ""
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "requests"
version = "2.24.0"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.dependencies]
certifi = ">=2017.4.17"
chardet = ">=3.0.2,<4"
idna = ">=2.5,<3"
urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26"
[package.extras]
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
[[package]]
name = "urllib3"
version = "1.25.9"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
[package.extras]
brotli = ["brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "xlrd"
version = "1.2.0"
description = "Library for developers to extract data from Microsoft Excel (tm) spreadsheet files"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "b3d607363c6daf2b5448aa5ee676cff28606af6200d8e9b42e89937a190a3d46"
[metadata.files]
altgraph = [
{file = "altgraph-0.17.2-py2.py3-none-any.whl", hash = "sha256:743628f2ac6a7c26f5d9223c91ed8ecbba535f506f4b6f558885a8a56a105857"},
{file = "altgraph-0.17.2.tar.gz", hash = "sha256:ebf2269361b47d97b3b88e696439f6e4cbc607c17c51feb1754f90fb79839158"},
]
certifi = [
{file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"},
{file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"},
]
chardet = [
{file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
{file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
]
dis3 = [
{file = "dis3-0.1.3-py2-none-any.whl", hash = "sha256:61f7720dd0d8749d23fda3d7227ce74d73da11c2fade993a67ab2f9852451b14"},
{file = "dis3-0.1.3-py3-none-any.whl", hash = "sha256:30b6412d33d738663e8ded781b138f4b01116437f0872aa56aa3adba6aeff218"},
{file = "dis3-0.1.3.tar.gz", hash = "sha256:9259b881fc1df02ed12ac25f82d4a85b44241854330b1a651e40e0c675cb2d1e"},
]
docutils = [
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
{file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
]
future = [
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
]
idna = [
{file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"},
{file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"},
]
itsdangerous = [
{file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"},
{file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"},
]
kivy = [
{file = "Kivy-1.11.1-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:11e85eaf6efbfa2362a3334ffdad179a1b0ca8d255cca79eaa6a2765560d4982"},
{file = "Kivy-1.11.1-cp27-cp27m-win32.whl", hash = "sha256:5c3d0f2749522d62e9cce09cd54b2d823bf1b6b644ff1f627be49de6f3e3cba0"},
{file = "Kivy-1.11.1-cp27-cp27m-win_amd64.whl", hash = "sha256:f835462dd9aa491272552ef079b948a088598e2e95d68bb1d885d2c3f3d4e2c3"},
{file = "Kivy-1.11.1-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:090d3ded9835a17477cd93fbdaf0a7c42ff2218981cf198ded5ad8795bc74391"},
{file = "Kivy-1.11.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:b85ccf165050cbf2ee8447671eebbc222b369b40f0e0038dd9547d49a5e37373"},
{file = "Kivy-1.11.1-cp35-cp35m-win32.whl", hash = "sha256:4a5480cbf837d3780c77a4f61b32b56d22ae9f03845e7a89dd3eaef1ae5fd037"},
{file = "Kivy-1.11.1-cp35-cp35m-win_amd64.whl", hash = "sha256:a687602d90c4629dd036f577ca39acb76ba581370f9d915f3cab99be818ba8ad"},
{file = "Kivy-1.11.1-cp36-cp36m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c36652caa7f6c327dee834cfc699d5962d346b7a53e54bd81abc17c314226d89"},
{file = "Kivy-1.11.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:b7ef6aad43a86d8df3fb865db864e354f2155a748019f8517f69f65c1a29cb64"},
{file = "Kivy-1.11.1-cp36-cp36m-win32.whl", hash = "sha256:f3bea6e4a21991827885d04127fc6d09a0e974ecfa12da7bf5faae93562ea102"},
{file = "Kivy-1.11.1-cp36-cp36m-win_amd64.whl", hash = "sha256:ece170514db3f49844a41e4c910ad9ce9bc46da6f47a49158e11266bdcc6e479"},
{file = "Kivy-1.11.1-cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:8819a27a09871af451760cb69486ced52e830c8a0a37480f22ef5e692f12c05b"},
{file = "Kivy-1.11.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:1a1ff32f8a95f1e175198cbab81fcd2596783b180d4eafe63e87d171aa7fdb5e"},
{file = "Kivy-1.11.1-cp37-cp37m-win32.whl", hash = "sha256:815a5c0b3b72fcd81ca7b2aa0744087163ed03e4cf9ab4e7c9733cea99fc1571"},
{file = "Kivy-1.11.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1d28b198a64c30db8d94a0488e85f3037af60d514ab0d7ad5ab45add3ab77090"},
{file = "Kivy-1.11.1.tar.gz", hash = "sha256:4d0e596f74271e901b551f77661dde238df4765484fce9f5d1c72e8022984e84"},
]
"kivy-deps.angle" = [
{file = "kivy_deps.angle-0.3.0-cp310-cp310-win32.whl", hash = "sha256:7b56477c726e361592f794d49b2fdd96d579b7efd7225a8aadf7fd01d4e8cb80"},
{file = "kivy_deps.angle-0.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4b78fd9d47c76e5ff52fcbaa040920a2f97bff038f4c534346dd833dc8f3145"},
{file = "kivy_deps.angle-0.3.0-cp36-cp36m-win32.whl", hash = "sha256:a2cea09e8a5e899629466403fbd540459f1cdef8d08c6c479b6607b95309be02"},
{file = "kivy_deps.angle-0.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:b167e19b3eea55a9a8c606a607bb909ec1bedda88deee40347c780b310155a79"},
{file = "kivy_deps.angle-0.3.0-cp37-cp37m-win32.whl", hash = "sha256:d0e7b7b9eb9669837a5d70808a7ea45f2b61961b56f9f69a233bad6bd36ce260"},
{file = "kivy_deps.angle-0.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b9d07976b0bf6bac724a42aa8ed5a8c7caa95609046db30c8f15bb731f8e4d36"},
{file = "kivy_deps.angle-0.3.0-cp38-cp38-win32.whl", hash = "sha256:99c40d53582a958748e251dfbd61aa67fb85963e27529ca08a21f2f5eeed04e1"},
{file = "kivy_deps.angle-0.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:50605fdd4c9fdbe9f717069734a598a9aba0afe5d3f0412afbe2ecff0326e92d"},
{file = "kivy_deps.angle-0.3.0-cp39-cp39-win32.whl", hash = "sha256:64ac7f33c000585dc30194e604aed925972c6b7c3848b5c3b073ae916fb0b55c"},
{file = "kivy_deps.angle-0.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:bb4d53f15a093214adbbe205c108ede5cc0f6af6eff104c1b8c468ddaaf6400a"},
]
"kivy-deps.glew" = [
{file = "kivy_deps.glew-0.1.12-cp27-cp27m-win32.whl", hash = "sha256:92e72fa2c425887987d1aa861c99537033dc20d68ae1c54864871f0401682586"},
{file = "kivy_deps.glew-0.1.12-cp27-cp27m-win_amd64.whl", hash = "sha256:c843104690c0c8f3a58105c53c57f31506f6f90562c18de00bd19317cc1045a7"},
{file = "kivy_deps.glew-0.1.12-cp35-cp35m-win32.whl", hash = "sha256:ee8ab67abb2c98d84feede657cae472e7723e529af07394244bdd33caafb1a38"},
{file = "kivy_deps.glew-0.1.12-cp35-cp35m-win_amd64.whl", hash = "sha256:ab81783a82bef88a8d2bcf8a93bc21df6b8b0db6ee551eb802727d18f9074b17"},
{file = "kivy_deps.glew-0.1.12-cp36-cp36m-win32.whl", hash = "sha256:45aa7f0e8d9bcf5fc1810c9c38bc20edf7dee61df81ecf62102e0f84153f924a"},
{file = "kivy_deps.glew-0.1.12-cp36-cp36m-win_amd64.whl", hash = "sha256:ef1116d99bd9cc737cb8c0e13e676955c17d6e4d6d1af5cfccef089a430071bb"},
{file = "kivy_deps.glew-0.1.12-cp37-cp37m-win32.whl", hash = "sha256:1e28e40017af9d081fc0fc95b4fadaf31d15e9f63478dcee1c4257d67079894e"},
{file = "kivy_deps.glew-0.1.12-cp37-cp37m-win_amd64.whl", hash = "sha256:6bb435620c3187d2c61054adb9ec277ed487256b457a0a7b1491bc0cb7247e18"},
{file = "kivy_deps.glew-0.1.12-cp38-cp38-win32.whl", hash = "sha256:09f72ee5ef33ff273332e2a229dc97d650d29818a0189339421949e4e0f63d93"},
{file = "kivy_deps.glew-0.1.12-cp38-cp38-win_amd64.whl", hash = "sha256:cf351aad171796f8051af8e49ec430a9aa128d8557d8643e73f2bb1e5f9c2dab"},
]
"kivy-deps.gstreamer" = [
{file = "kivy_deps.gstreamer-0.1.17-cp27-cp27m-win32.whl", hash = "sha256:309eca64dee5939f16f8465e5cbb08bdde7c90ded1af6a00690c7e928326af79"},
{file = "kivy_deps.gstreamer-0.1.17-cp27-cp27m-win_amd64.whl", hash = "sha256:0d9598d2d31c0e780adf4b767fa3a691123621fd0ffef94b83cf82c2da84341b"},
{file = "kivy_deps.gstreamer-0.1.17-cp35-cp35m-win32.whl", hash = "sha256:4f2ddd61d185310258d338ae80a646df7822efdd7d67e57f49dc7b87555c5d7e"},
{file = "kivy_deps.gstreamer-0.1.17-cp35-cp35m-win_amd64.whl", hash = "sha256:6fa9f76afe600baa221abee31ce7dc63e653d0affe0f6c558bfc4f35af96396f"},
{file = "kivy_deps.gstreamer-0.1.17-cp36-cp36m-win32.whl", hash = "sha256:c4709765e2b17c6c96b46a92207b0457def147544d825654077603eaf0d424de"},
{file = "kivy_deps.gstreamer-0.1.17-cp36-cp36m-win_amd64.whl", hash = "sha256:c29cfc63fe70a58dad889e631f1ba4711c9ea80103f2b2b8d670a97f093076c8"},
{file = "kivy_deps.gstreamer-0.1.17-cp37-cp37m-win32.whl", hash = "sha256:4d996377111e854b3dea90846f9b2f98766a44529fd8b72125e18c552381d928"},
{file = "kivy_deps.gstreamer-0.1.17-cp37-cp37m-win_amd64.whl", hash = "sha256:739cd331b9f33a822d700273674a79a3157054e9358a01a0d553f094a5f4a8c9"},
{file = "kivy_deps.gstreamer-0.1.17-cp38-cp38-win_amd64.whl", hash = "sha256:3d53d2c84c0a997c4cac6c239b1e0a6486e533836321003dc365ec42b97a664b"},
]
"kivy-deps.sdl2" = [
{file = "kivy_deps.sdl2-0.1.22-cp27-cp27m-win32.whl", hash = "sha256:1b987bdd4fbbcb31baf0d7fc9584ad99912179b8968311bb7e30fbeb14e98e0d"},
{file = "kivy_deps.sdl2-0.1.22-cp27-cp27m-win_amd64.whl", hash = "sha256:228128cdd8112dc7505ac43027a770476e9ef282e0b84ca68037133cd025960b"},
{file = "kivy_deps.sdl2-0.1.22-cp35-cp35m-win32.whl", hash = "sha256:053f26e8c05d5545bdbc7eeb8c450b8e4410ee355792e9345af536110fe247e2"},
{file = "kivy_deps.sdl2-0.1.22-cp35-cp35m-win_amd64.whl", hash = "sha256:5ce23f1a3286d6288751a12b0eaefd02f947ea101bb807e9781b964e496fc3f3"},
{file = "kivy_deps.sdl2-0.1.22-cp36-cp36m-win32.whl", hash = "sha256:96e1fa89fd8b5351f2d3c26bbffd50df8d554b03fba4025ecc941d773d241698"},
{file = "kivy_deps.sdl2-0.1.22-cp36-cp36m-win_amd64.whl", hash = "sha256:c3ace0ddde0e59cdcaf260eda1daa0c05ca9bf8cd0c4ea404539de25a5dcaec7"},
{file = "kivy_deps.sdl2-0.1.22-cp37-cp37m-win32.whl", hash = "sha256:7928746eaed51944c10d1bb36fcefebe3d1aff1b97ba32359c2c97ba74707e1b"},
{file = "kivy_deps.sdl2-0.1.22-cp37-cp37m-win_amd64.whl", hash = "sha256:2c2fd5a12a7a9afe3bb962b273561099a180edae91bb9c8f8386b72253fcae4a"},
{file = "kivy_deps.sdl2-0.1.22-cp38-cp38-win32.whl", hash = "sha256:9270fa8ed5130074b167a7a3a9c85efc3cfe3c04584ab084cb6ae9e4edfa8168"},
{file = "kivy_deps.sdl2-0.1.22-cp38-cp38-win_amd64.whl", hash = "sha256:92ed97d3247bc8ce98f336cbc940bb889310199326e9ccf251c49ae7e4b80de8"},
]
kivy-garden = [
{file = "Kivy Garden-0.1.4.tar.gz", hash = "sha256:9b7d9de5efacbcd0c4b3dd873b30622a86093c9965aa47b523c7a32f3eb34610"},
{file = "kivy-garden-0.1.4.tar.gz", hash = "sha256:c256f42788421273a08fbb0a228f0fb0e80dd86b629fb8c0920507f645be6c72"},
]
packaging = [
{file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"},
{file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
]
pdf2image = [
{file = "pdf2image-1.12.1.tar.gz", hash = "sha256:a0d9906f5507192210a8d5d7ead63145e9dec4bccc4564b1fb644e923913c31c"},
]
pefile = [
{file = "pefile-2021.9.3.tar.gz", hash = "sha256:344a49e40a94e10849f0fe34dddc80f773a12b40675bf2f7be4b8be578bdd94a"},
]
pillow = [
{file = "Pillow-7.2.0-cp35-cp35m-macosx_10_10_intel.whl", hash = "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae"},
{file = "Pillow-7.2.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f"},
{file = "Pillow-7.2.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38"},
{file = "Pillow-7.2.0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5"},
{file = "Pillow-7.2.0-cp35-cp35m-win32.whl", hash = "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad"},
{file = "Pillow-7.2.0-cp35-cp35m-win_amd64.whl", hash = "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f"},
{file = "Pillow-7.2.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d"},
{file = "Pillow-7.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233"},
{file = "Pillow-7.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f"},
{file = "Pillow-7.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8"},
{file = "Pillow-7.2.0-cp36-cp36m-win32.whl", hash = "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a"},
{file = "Pillow-7.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce"},
{file = "Pillow-7.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4"},
{file = "Pillow-7.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727"},
{file = "Pillow-7.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b"},
{file = "Pillow-7.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d"},
{file = "Pillow-7.2.0-cp37-cp37m-win32.whl", hash = "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63"},
{file = "Pillow-7.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1"},
{file = "Pillow-7.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6"},
{file = "Pillow-7.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9"},
{file = "Pillow-7.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41"},
{file = "Pillow-7.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8"},
{file = "Pillow-7.2.0-cp38-cp38-win32.whl", hash = "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f"},
{file = "Pillow-7.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6"},
{file = "Pillow-7.2.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6"},
{file = "Pillow-7.2.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117"},
{file = "Pillow-7.2.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d"},
{file = "Pillow-7.2.0.tar.gz", hash = "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626"},
]
pygments = [
{file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"},
{file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"},
]
pyinstaller = [
{file = "PyInstaller-3.6.tar.gz", hash = "sha256:3730fa80d088f8bb7084d32480eb87cbb4ddb64123363763cf8f2a1378c1c4b7"},
]
pyparsing = [
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
]
pywin32-ctypes = [
{file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"},
{file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"},
]
requests = [
{file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
{file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
]
urllib3 = [
{file = "urllib3-1.25.9-py2.py3-none-any.whl", hash = "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"},
{file = "urllib3-1.25.9.tar.gz", hash = "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"},
]
xlrd = [
{file = "xlrd-1.2.0-py2.py3-none-any.whl", hash = "sha256:e551fb498759fa3a5384a94ccd4c3c02eb7c00ea424426e212ac0c57be9dfbde"},
{file = "xlrd-1.2.0.tar.gz", hash = "sha256:546eb36cee8db40c3eaa46c351e67ffee6eeb5fa2650b71bc4c758a29a1b29b2"},
]
+39
View File
@@ -89,6 +89,45 @@
} }
] ]
}, },
{
"pypy_version": "7.3.4rc1",
"python_version": "2.7.18",
"stable": false,
"latest_pypy": false,
"date": "2021-03-19",
"files": [
{
"filename": "pypy2.7-v7.3.4rc1-aarch64.tar.bz2",
"arch": "aarch64",
"platform": "linux",
"download_url": "https://test.downloads.python.org/pypy/pypy2.7-v7.3.4rc1-aarch64.tar.bz2"
},
{
"filename": "pypy2.7-v7.3.4rc1-linux32.tar.bz2",
"arch": "i686",
"platform": "linux",
"download_url": "https://test.downloads.python.org/pypy/pypy2.7-v7.3.4rc1-linux32.tar.bz2"
},
{
"filename": "pypy2.7-v7.3.4rc1-linux64.tar.bz2",
"arch": "x64",
"platform": "linux",
"download_url": "https://test.downloads.python.org/pypy/pypy2.7-v7.3.4rc1-linux64.tar.bz2"
},
{
"filename": "pypy2.7-v7.3.4rc1-osx64.tar.bz2",
"arch": "x64",
"platform": "darwin",
"download_url": "https://test.downloads.python.org/pypy/pypy2.7-v7.3.4rc1-osx64.tar.bz2"
},
{
"filename": "pypy2.7-v7.3.4rc1-win64.zip",
"arch": "x64",
"platform": "win64",
"download_url": "https://test.downloads.python.org/pypy/pypy2.7-v7.3.4rc1-win64.zip"
}
]
},
{ {
"pypy_version": "7.3.3rc2", "pypy_version": "7.3.3rc2",
"python_version": "3.7.7", "python_version": "3.7.7",
+12
View File
@@ -0,0 +1,12 @@
certifi==2020.6.20
chardet==3.0.4
docutils==0.16
idna==2.10
Kivy==2.0.0rc3
Kivy-Garden==0.1.4
packaging==20.7
pdf2image==1.12.1
Pygments==2.6.1
requests==2.24.0
urllib3==1.25.10
xlrd==1.2.0
+47
View File
@@ -0,0 +1,47 @@
altgraph==0.17.2
certifi==2020.6.20
chardet==3.0.4
docutils==0.16
future==0.18.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'
idna==2.9
itsdangerous==1.1.0
kivy-deps-angle==0.3.0
kivy-deps.glew==0.1.12
kivy-deps.gstreamer==0.1.17
kivy-deps.sdl2==0.1.22
kivy-garden==0.1.4
kivy==1.11.1
packaging==21.0
pdf2image==1.12.1
pefile==2021.9.3; python_full_version >= '3.6.0'
pillow==7.2
pygments==2.6.1
pyinstaller==3.6
pyparsing==2.4.7; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'
pywin32-ctypes==0.2.0
requests==2.24.0
urllib3==1.25.9
xlrd==1.2.0
+4 -12
View File
@@ -35,7 +35,7 @@ describe('Finder tests', () => {
await io.mkdirP(pythonDir); await io.mkdirP(pythonDir);
fs.writeFileSync(`${pythonDir}.complete`, 'hello'); fs.writeFileSync(`${pythonDir}.complete`, 'hello');
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists) // This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
await finder.findPythonVersion('3.x', 'x64'); await finder.useCpythonVersion('3.x', 'x64');
}); });
it('Finds stable Python version if it is not installed, but exists in the manifest', async () => { it('Finds stable Python version if it is not installed, but exists in the manifest', async () => {
@@ -52,7 +52,7 @@ describe('Finder tests', () => {
fs.writeFileSync(`${pythonDir}.complete`, 'hello'); fs.writeFileSync(`${pythonDir}.complete`, 'hello');
}); });
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists) // This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
await finder.findPythonVersion('1.2.3', 'x64'); await finder.useCpythonVersion('1.2.3', 'x64');
}); });
it('Finds pre-release Python version in the manifest', async () => { it('Finds pre-release Python version in the manifest', async () => {
@@ -74,25 +74,17 @@ describe('Finder tests', () => {
fs.writeFileSync(`${pythonDir}.complete`, 'hello'); fs.writeFileSync(`${pythonDir}.complete`, 'hello');
}); });
// This will throw if it doesn't find it in the manifest (because no such version exists) // This will throw if it doesn't find it in the manifest (because no such version exists)
await finder.findPythonVersion('1.2.3-beta.2', 'x64'); await finder.useCpythonVersion('1.2.3-beta.2', 'x64');
}); });
it('Errors if Python is not installed', async () => { it('Errors if Python is not installed', async () => {
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists) // This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
let thrown = false; let thrown = false;
try { try {
await finder.findPythonVersion('3.300000', 'x64'); await finder.useCpythonVersion('3.300000', 'x64');
} catch { } catch {
thrown = true; thrown = true;
} }
expect(thrown).toBeTruthy(); expect(thrown).toBeTruthy();
}); });
it('Finds PyPy if it is installed', async () => {
const pythonDir: string = path.join(toolDir, 'PyPy', '2.0.0', 'x64');
await io.mkdirP(pythonDir);
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
// This will throw if it doesn't find it in the cache (because no such version exists)
await finder.findPythonVersion('pypy2', 'x64');
});
}); });
+43 -1
View File
@@ -1,8 +1,14 @@
import * as cache from '@actions/cache';
import * as core from '@actions/core';
import { import {
validateVersion, validateVersion,
validatePythonVersionFormatForPyPy validatePythonVersionFormatForPyPy,
isCacheFeatureAvailable
} from '../src/utils'; } from '../src/utils';
jest.mock('@actions/cache');
jest.mock('@actions/core');
describe('validatePythonVersionFormatForPyPy', () => { describe('validatePythonVersionFormatForPyPy', () => {
it.each([ it.each([
['3.6', true], ['3.6', true],
@@ -32,3 +38,39 @@ describe('validateVersion', () => {
expect(validateVersion(input)).toEqual(expected); expect(validateVersion(input)).toEqual(expected);
}); });
}); });
describe('isCacheFeatureAvailable', () => {
it('isCacheFeatureAvailable disabled on GHES', () => {
jest.spyOn(cache, 'isFeatureAvailable').mockImplementation(() => false);
try {
process.env['GITHUB_SERVER_URL'] = 'http://example.com';
isCacheFeatureAvailable();
} catch (error) {
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 {
delete process.env['GITHUB_SERVER_URL'];
}
});
it('isCacheFeatureAvailable disabled on dotcom', () => {
jest.spyOn(cache, 'isFeatureAvailable').mockImplementation(() => false);
const infoMock = jest.spyOn(core, 'warning');
const message =
'The runner was not able to contact the cache service. Caching will be skipped';
try {
process.env['GITHUB_SERVER_URL'] = 'http://github.com';
expect(isCacheFeatureAvailable()).toBe(false);
expect(infoMock).toHaveBeenCalledWith(message);
} finally {
delete process.env['GITHUB_SERVER_URL'];
}
});
it('isCacheFeatureAvailable is enabled', () => {
jest.spyOn(cache, 'isFeatureAvailable').mockImplementation(() => true);
expect(isCacheFeatureAvailable()).toBe(true);
});
});
+9 -2
View File
@@ -6,17 +6,24 @@ inputs:
python-version: python-version:
description: "Version range or exact version of a Python version to use, using SemVer's version range syntax." description: "Version range or exact version of a Python version to use, using SemVer's version range syntax."
default: '3.x' default: '3.x'
cache:
description: 'Used to specify a package manager for caching in the default directory. Supported values: pip, pipenv, poetry.'
required: false
architecture: architecture:
description: 'The target architecture (x86, x64) of the Python interpreter.' description: 'The target architecture (x86, x64) of the Python interpreter.'
token: token:
description: Used to pull python distributions from actions/python-versions. Since there's a default, this is typically not supplied by the user. description: Used to pull python distributions from actions/python-versions. Since there's a default, this is typically not supplied by the user.
default: ${{ github.token }} default: ${{ github.token }}
cache-dependency-path:
description: 'Used to specify the path to dependency files. Supports wildcards or a list of file names for caching multiple dependencies.'
outputs: outputs:
python-version: python-version:
description: "The installed python version. Useful when given a version range as input." description: "The installed python version. Useful when given a version range as input."
runs: runs:
using: 'node12' using: 'node16'
main: 'dist/index.js' main: 'dist/setup/index.js'
post: 'dist/cache-save/index.js'
post-if: success()
branding: branding:
icon: 'code' icon: 'code'
color: 'yellow' color: 'yellow'
+58407
View File
File diff suppressed because one or more lines are too long
-7603
View File
File diff suppressed because it is too large Load Diff
+65309
View File
File diff suppressed because one or more lines are too long
+93
View File
@@ -0,0 +1,93 @@
## 0. Caching dependencies
Date: 2021-10-01
Status: Accepted
## Context
`actions/setup-python` is one the most popular python's action in GitHub Actions. A lot of customers use it in conjunction with `actions/cache` to speed up dependencies installation.
See more examples on proper usage in [actions/cache documentation](https://github.com/actions/cache/blob/main/examples.md#python---pip).
## Goals & Anti-Goals
Integration of caching functionality into `actions/setup-python` action will bring the following benefits for action users:
- Decrease the entry threshold for using the cache for Python dependencies and simplify initial configuration
- Simplify YAML pipelines by reducing the need for additional steps to enable caching
- More users will use caching for Python so users will have faster builds!
We will add support for Pip and Pipenv dependency caching.
We won't pursue the goal to provide wide customization of caching in the scope of `actions/setup-python` action. The purpose of this integration is to cover ~90% of basic use-cases. If users need flexible customization, we will advise them to use `actions/cache` directly.
## Decision
- Add a `cache` input parameter to `actions/setup-python`. For now the input will accept the following values:
- pip - enable caching for pip dependencies
- pipenv - enable caching for pipenv dependencies
- '' - disable caching (default value)
- Cache feature will be disabled by default to make sure that we don't break existing customers.
- Action will try to search dependencies files (requirements.txt for pip and Pipfile.lock for pipenv) in the repository root (or relative to the repository root, if patterns are used) and throw error if no one is found.
- The hash of found file will be used as part of cache key (the same approach like actions/cache recommends)
- The following cache key will be used for pip: `setup-python-${{ runner.os }}-pip-${{ hashFiles('<package-file-path>') }}`
- The following cache key will be used for pipenv: `setup-python-${{ runner.os }}-python-${{ python-version }}-pipenv-${{ hashFiles('<package-file-path>') }}`. We add the python version to the cache key because the created virtualenv folder with the project name contains a copy of the python binary as a symlink to paths like `/opt/hostedtoolcache/Python/3.7.11`, so the cache can be fetched with a wrong python version. See details in the related [pull request](https://github.com/actions/cache/pull/607) in the actions/cache.
- Action will save the packages global cache:
- Pip (retrieved via pip cache dir). The command is available With pip 20.1 or later. We always update pip during installation, that is why this command should be available.
- Pipenv (default cache paths):
- ~/.local/share/virtualenvs (macOS)
- ~/.virtualenvs (Windows)
- ~/.local/share/virtualenvs (Ubuntu)
- Add a `cache-dependency-path` input parameter to `actions/setup-python`. The new input will accept an array or regex of dependency files. The field will accept a path (relative to the repository root) to dependency files. If the provided path contains wildcards, the action will search all matching files and calculate a common hash like the `${{ hashFiles('**/requirements-dev.txt') }}` YAML construction does
## Example of real use-cases
- Pip package manager
```
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
cache: pip
```
- Pipenv package manager
```
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
cache: pipenv
```
- With `cache-dependency-path`
```
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
cache: pip
cache-dependency-path: |
**/requirements-dev.txt
**/requirements-test.txt
**/requirements.txt
```
```
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
cache: pip
cache-dependency-path: **/requirements-dev.txt
```
## Release process
As soon as the functionality is implemented, we will release a minor update of the action. No need to bump the major version since there are no breaking changes for existing users. After that, we will update [starter-workflows](https://github.com/actions/starter-workflows/blob/main/ci/python-app.yml) and [GitHub Action documentation](https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#caching-dependencies).
+18429 -2986
View File
File diff suppressed because it is too large Load Diff
+15 -12
View File
@@ -1,14 +1,14 @@
{ {
"name": "setup-python", "name": "setup-python",
"version": "2.0.1", "version": "3.1.0",
"private": true, "private": true,
"description": "Setup python action", "description": "Setup python action",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
"build": "tsc", "build": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/cache-save src/cache-save.ts",
"format": "prettier --write **/*.ts", "format": "prettier --write \"{,!(node_modules)/**/}*.ts\"",
"format-check": "prettier --check **/*.ts", "format-check": "prettier --check \"{,!(node_modules)/**/}*.ts\"",
"release": "ncc build src/setup-python.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" "test": "jest"
}, },
"repository": { "repository": {
@@ -23,22 +23,25 @@
"author": "GitHub", "author": "GitHub",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/cache": "^2.0.0",
"@actions/core": "^1.2.3", "@actions/core": "^1.2.3",
"@actions/exec": "^1.1.0",
"@actions/glob": "^0.2.0",
"@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" "semver": "^7.1.3"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^25.1.4", "@types/jest": "^27.0.2",
"@types/node": "^12.12.31", "@types/node": "^16.11.25",
"@types/semver": "^7.1.0", "@types/semver": "^7.1.0",
"@zeit/ncc": "^0.22.0", "@zeit/ncc": "^0.22.0",
"husky": "^4.2.3", "husky": "^7.0.2",
"jest": "^25.1.0", "jest": "^27.2.5",
"jest-circus": "^25.1.0", "jest-circus": "^27.2.5",
"prettier": "^2.0.2", "prettier": "^2.0.2",
"ts-jest": "^25.2.1", "ts-jest": "^27.0.5",
"typescript": "^3.8.3" "typescript": "^4.2.3"
}, },
"husky": { "husky": {
"skipCI": true, "skipCI": true,
@@ -0,0 +1,53 @@
import * as cache from '@actions/cache';
import * as core from '@actions/core';
export enum State {
STATE_CACHE_PRIMARY_KEY = 'cache-primary-key',
CACHE_MATCHED_KEY = 'cache-matched-key',
CACHE_PATHS = 'cache-paths'
}
abstract class CacheDistributor {
protected CACHE_KEY_PREFIX = 'setup-python';
constructor(
protected packageManager: string,
protected cacheDependencyPath: string
) {}
protected abstract getCacheGlobalDirectories(): Promise<string[]>;
protected abstract computeKeys(): Promise<{
primaryKey: string;
restoreKey: string[] | undefined;
}>;
public async restoreCache() {
const {primaryKey, restoreKey} = await this.computeKeys();
if (primaryKey.endsWith('-')) {
throw new Error(
`No file in ${process.cwd()} matched to [${this.cacheDependencyPath
.split('\n')
.join(',')}], make sure you have checked out the target repository`
);
}
const cachePath = await this.getCacheGlobalDirectories();
core.saveState(State.CACHE_PATHS, cachePath);
core.saveState(State.STATE_CACHE_PRIMARY_KEY, primaryKey);
const matchedKey = await cache.restoreCache(
cachePath,
primaryKey,
restoreKey
);
if (matchedKey) {
core.saveState(State.CACHE_MATCHED_KEY, matchedKey);
core.info(`Cache restored from key: ${matchedKey}`);
} else {
core.info(`${this.packageManager} cache is not found`);
}
}
}
export default CacheDistributor;
+26
View File
@@ -0,0 +1,26 @@
import PipCache from './pip-cache';
import PipenvCache from './pipenv-cache';
import PoetryCache from './poetry-cache';
export enum PackageManagers {
Pip = 'pip',
Pipenv = 'pipenv',
Poetry = 'poetry'
}
export function getCacheDistributor(
packageManager: string,
pythonVersion: string,
cacheDependencyPath: string | undefined
) {
switch (packageManager) {
case PackageManagers.Pip:
return new PipCache(pythonVersion, cacheDependencyPath);
case PackageManagers.Pipenv:
return new PipenvCache(pythonVersion, cacheDependencyPath);
case PackageManagers.Poetry:
return new PoetryCache(pythonVersion, cacheDependencyPath);
default:
throw new Error(`Caching for '${packageManager}' is not supported`);
}
}
+70
View File
@@ -0,0 +1,70 @@
import * as glob from '@actions/glob';
import * as core from '@actions/core';
import * as exec from '@actions/exec';
import * as child_process from 'child_process';
import utils from 'util';
import * as path from 'path';
import os from 'os';
import CacheDistributor from './cache-distributor';
import {IS_WINDOWS} from '../utils';
class PipCache extends CacheDistributor {
constructor(
private pythonVersion: string,
cacheDependencyPath: string = '**/requirements.txt'
) {
super('pip', cacheDependencyPath);
}
protected async getCacheGlobalDirectories() {
let exitCode = 1;
let stdout = '';
let stderr = '';
// Add temporary fix for Windows
// On windows it is necessary to execute through an exec
// because the getExecOutput gives a non zero code or writes to stderr for pip 22.0.2,
// or spawn must be started with the shell option enabled for getExecOutput
// Related issue: https://github.com/actions/setup-python/issues/328
if (IS_WINDOWS) {
const execPromisify = utils.promisify(child_process.exec);
({stdout: stdout, stderr: stderr} = await execPromisify('pip cache dir'));
} else {
({
stdout: stdout,
stderr: stderr,
exitCode: exitCode
} = await exec.getExecOutput('pip cache dir'));
}
if (exitCode && stderr) {
throw new Error(
`Could not get cache folder path for pip package manager`
);
}
let resolvedPath = stdout.trim();
if (resolvedPath.includes('~')) {
resolvedPath = path.join(os.homedir(), resolvedPath.slice(1));
}
core.debug(`global cache directory path is ${resolvedPath}`);
return [resolvedPath];
}
protected async computeKeys() {
const hash = await glob.hashFiles(this.cacheDependencyPath);
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
const restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}`;
return {
primaryKey,
restoreKey: [restoreKey]
};
}
}
export default PipCache;
+44
View File
@@ -0,0 +1,44 @@
import * as glob from '@actions/glob';
import * as os from 'os';
import * as path from 'path';
import * as core from '@actions/core';
import CacheDistributor from './cache-distributor';
class PipenvCache extends CacheDistributor {
constructor(
private pythonVersion: string,
protected patterns: string = '**/Pipfile.lock'
) {
super('pipenv', patterns);
}
protected async getCacheGlobalDirectories() {
let virtualEnvRelativePath;
// Default virtualenv directories are hardcoded,
// because pipenv is not preinstalled on hosted images and virtualenv is not created:
// https://github.com/pypa/pipenv/blob/1daaa0de9a0b00d386c6baeb809d8d4ee6795cfd/pipenv/utils.py#L1990-L2002
if (process.platform === 'win32') {
virtualEnvRelativePath = '.virtualenvs';
} else {
virtualEnvRelativePath = '.local/share/virtualenvs';
}
const resolvedPath = path.join(os.homedir(), virtualEnvRelativePath);
core.debug(`global cache directory path is ${resolvedPath}`);
return [resolvedPath];
}
protected async computeKeys() {
const hash = await glob.hashFiles(this.patterns);
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
const restoreKey = undefined;
return {
primaryKey,
restoreKey
};
}
}
export default PipenvCache;
+76
View File
@@ -0,0 +1,76 @@
import * as glob from '@actions/glob';
import * as os from 'os';
import * as path from 'path';
import * as exec from '@actions/exec';
import CacheDistributor from './cache-distributor';
class PoetryCache extends CacheDistributor {
constructor(
private pythonVersion: string,
protected patterns: string = '**/poetry.lock'
) {
super('poetry', patterns);
}
protected async getCacheGlobalDirectories() {
const poetryConfig = await this.getPoetryConfiguration();
const cacheDir = poetryConfig['cache-dir'];
const virtualenvsPath = poetryConfig['virtualenvs.path'].replace(
'{cache-dir}',
cacheDir
);
const paths = [virtualenvsPath];
if (poetryConfig['virtualenvs.in-project'] === true) {
paths.push(path.join(process.cwd(), '.venv'));
}
return paths;
}
protected async computeKeys() {
const hash = await glob.hashFiles(this.patterns);
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
const restoreKey = undefined;
return {
primaryKey,
restoreKey
};
}
private async getPoetryConfiguration() {
const {stdout, stderr, exitCode} = await exec.getExecOutput('poetry', [
'config',
'--list'
]);
if (exitCode && stderr) {
throw new Error(
'Could not get cache folder path for poetry package manager'
);
}
const lines = stdout.trim().split('\n');
const config: any = {};
for (let line of lines) {
line = line.replace(/#.*$/, '');
const [key, value] = line.split('=').map(part => part.trim());
config[key] = JSON.parse(value);
}
return config as {
'cache-dir': string;
'virtualenvs.in-project': boolean;
'virtualenvs.path': string;
};
}
}
export default PoetryCache;
+67
View File
@@ -0,0 +1,67 @@
import * as core from '@actions/core';
import * as cache from '@actions/cache';
import fs from 'fs';
import {State} from './cache-distributions/cache-distributor';
export async function run() {
try {
const cache = core.getInput('cache');
if (cache) {
await saveCache(cache);
}
} catch (error) {
const err = error as Error;
core.setFailed(err.message);
}
}
async function saveCache(packageManager: string) {
const cachePaths = JSON.parse(core.getState(State.CACHE_PATHS)) as string[];
core.debug(`paths for caching are ${cachePaths.join(', ')}`);
if (!isCacheDirectoryExists(cachePaths)) {
throw new Error(
`Cache folder path is retrieved for ${packageManager} but doesn't exist on disk: ${cachePaths.join(
', '
)}`
);
}
const primaryKey = core.getState(State.STATE_CACHE_PRIMARY_KEY);
const matchedKey = core.getState(State.CACHE_MATCHED_KEY);
if (!primaryKey) {
core.warning('Error retrieving key from state.');
return;
} else if (matchedKey === primaryKey) {
// no change in target directories
core.info(
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
);
return;
}
try {
await cache.saveCache(cachePaths, primaryKey);
core.info(`Cache saved with the key: ${primaryKey}`);
} catch (error) {
const err = error as Error;
if (err.name === cache.ReserveCacheError.name) {
core.info(err.message);
} else {
throw error;
}
}
}
function isCacheDirectoryExists(cacheDirectory: string[]) {
const result = cacheDirectory.reduce((previousValue, currentValue) => {
return previousValue || fs.existsSync(currentValue);
}, false);
return result;
}
run();
+15 -6
View File
@@ -2,6 +2,7 @@ import * as path from 'path';
import * as pypyInstall from './install-pypy'; import * as pypyInstall from './install-pypy';
import { import {
IS_WINDOWS, IS_WINDOWS,
WINDOWS_ARCHS,
validateVersion, validateVersion,
getPyPyVersionFromPath, getPyPyVersionFromPath,
readExactPyPyVersionFile, readExactPyPyVersionFile,
@@ -27,11 +28,6 @@ export async function findPyPyVersion(
const pypyVersionSpec = parsePyPyVersion(versionSpec); const pypyVersionSpec = parsePyPyVersion(versionSpec);
// PyPy only precompiles binaries for x86, but the architecture parameter defaults to x64.
if (IS_WINDOWS && architecture === 'x64') {
architecture = 'x86';
}
({installDir, resolvedPythonVersion, resolvedPyPyVersion} = findPyPyToolCache( ({installDir, resolvedPythonVersion, resolvedPyPyVersion} = findPyPyToolCache(
pypyVersionSpec.pythonVersion, pypyVersionSpec.pythonVersion,
pypyVersionSpec.pypyVersion, pypyVersionSpec.pypyVersion,
@@ -67,7 +63,9 @@ export function findPyPyToolCache(
) { ) {
let resolvedPyPyVersion = ''; let resolvedPyPyVersion = '';
let resolvedPythonVersion = ''; let resolvedPythonVersion = '';
let installDir: string | null = tc.find('PyPy', pythonVersion, architecture); let installDir: string | null = IS_WINDOWS
? findPyPyInstallDirForWindows(pythonVersion)
: tc.find('PyPy', pythonVersion, architecture);
if (installDir) { if (installDir) {
// 'tc.find' finds tool based on Python version but we also need to check // 'tc.find' finds tool based on Python version but we also need to check
@@ -129,3 +127,14 @@ export function parsePyPyVersion(versionSpec: string): IPyPyVersionSpec {
pythonVersion: pythonVersion pythonVersion: pythonVersion
}; };
} }
export function findPyPyInstallDirForWindows(pythonVersion: string): string {
let installDir = '';
WINDOWS_ARCHS.forEach(
architecture =>
(installDir = installDir || tc.find('PyPy', pythonVersion, architecture))
);
return installDir;
}
+1 -61
View File
@@ -30,52 +30,7 @@ function binDir(installDir: string): string {
} }
} }
// Note on the tool cache layout for PyPy: export async function useCpythonVersion(
// PyPy has its own versioning scheme that doesn't follow the Python versioning scheme.
// A particular version of PyPy may contain one or more versions of the Python interpreter.
// For example, PyPy 7.0 contains Python 2.7, 3.5, and 3.6-alpha.
// We only care about the Python version, so we don't use the PyPy version for the tool cache.
function usePyPy(
majorVersion: '2' | '3.6',
architecture: string
): InstalledVersion {
const findPyPy = tc.find.bind(undefined, 'PyPy', majorVersion);
let installDir: string | null = findPyPy(architecture);
if (!installDir && IS_WINDOWS) {
// PyPy only precompiles binaries for x86, but the architecture parameter defaults to x64.
// On our Windows virtual environments, we only install an x86 version.
// Fall back to x86.
installDir = findPyPy('x86');
}
if (!installDir) {
// PyPy not installed in $(Agent.ToolsDirectory)
throw new Error(`PyPy ${majorVersion} not found`);
}
// For PyPy, Windows uses 'bin', not 'Scripts'.
const _binDir = path.join(installDir, 'bin');
// On Linux and macOS, the Python interpreter is in 'bin'.
// On Windows, it is in the installation root.
const pythonLocation = IS_WINDOWS ? installDir : _binDir;
core.exportVariable('pythonLocation', pythonLocation);
core.addPath(installDir);
core.addPath(_binDir);
// Starting from PyPy 7.3.1, the folder that is used for pip and anything that pip installs should be "Scripts" on Windows.
if (IS_WINDOWS) {
core.addPath(path.join(installDir, 'Scripts'));
}
const impl = 'pypy' + majorVersion.toString();
core.setOutput('python-version', impl);
return {impl: impl, version: versionFromPath(installDir)};
}
async function useCpythonVersion(
version: string, version: string,
architecture: string architecture: string
): Promise<InstalledVersion> { ): Promise<InstalledVersion> {
@@ -186,18 +141,3 @@ export function pythonVersionToSemantic(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'); return versionSpec.replace(prereleaseVersion, '$1-$2');
} }
export async function findPythonVersion(
version: string,
architecture: string
): Promise<InstalledVersion> {
switch (version.toUpperCase()) {
case 'PYPY2':
return usePyPy('2', architecture);
case 'PYPY3':
// keep pypy3 pointing to 3.6 for backward compatibility
return usePyPy('3.6', architecture);
default:
return await useCpythonVersion(version, architecture);
}
}
+44 -6
View File
@@ -8,6 +8,8 @@ import fs from 'fs';
import { import {
IS_WINDOWS, IS_WINDOWS,
WINDOWS_ARCHS,
WINDOWS_PLATFORMS,
IPyPyManifestRelease, IPyPyManifestRelease,
createSymlinkInFolder, createSymlinkInFolder,
isNightlyKeyword, isNightlyKeyword,
@@ -143,9 +145,9 @@ export function findRelease(
semver.satisfies(pypyVersionToSemantic(item.pypy_version), pypyVersion); semver.satisfies(pypyVersionToSemantic(item.pypy_version), pypyVersion);
const isArchPresent = const isArchPresent =
item.files && item.files &&
item.files.some( (IS_WINDOWS
file => file.arch === architecture && file.platform === process.platform ? isArchPresentForWindows(item)
); : isArchPresentForMacOrLinux(item, architecture, process.platform));
return isPythonVersionSatisfied && isPyPyVersionSatisfied && isArchPresent; return isPythonVersionSatisfied && isPyPyVersionSatisfied && isArchPresent;
}); });
@@ -167,9 +169,9 @@ export function findRelease(
}); });
const foundRelease = sortedReleases[0]; const foundRelease = sortedReleases[0];
const foundAsset = foundRelease.files.find( const foundAsset = IS_WINDOWS
item => item.arch === architecture && item.platform === process.platform ? findAssetForWindows(foundRelease)
); : findAssetForMacOrLinux(foundRelease, architecture, process.platform);
return { return {
foundAsset, foundAsset,
@@ -191,3 +193,39 @@ 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');
} }
export function isArchPresentForWindows(item: any) {
return item.files.some(
(file: any) =>
WINDOWS_ARCHS.includes(file.arch) &&
WINDOWS_PLATFORMS.includes(file.platform)
);
}
export function isArchPresentForMacOrLinux(
item: any,
architecture: string,
platform: string
) {
return item.files.some(
(file: any) => file.arch === architecture && file.platform === platform
);
}
export function findAssetForWindows(releases: any) {
return releases.files.find(
(item: any) =>
WINDOWS_ARCHS.includes(item.arch) &&
WINDOWS_PLATFORMS.includes(item.platform)
);
}
export function findAssetForMacOrLinux(
releases: any,
architecture: string,
platform: string
) {
return releases.files.find(
(item: any) => item.arch === architecture && item.platform === platform
);
}
+1 -8
View File
@@ -3,7 +3,7 @@ import * as core from '@actions/core';
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import {ExecOptions} from '@actions/exec/lib/interfaces'; import {ExecOptions} from '@actions/exec/lib/interfaces';
import {IS_WINDOWS, IS_LINUX} from './utils'; import {IS_WINDOWS, IS_LINUX, isGhes} from './utils';
const TOKEN = core.getInput('token'); const TOKEN = core.getInput('token');
const AUTH = !TOKEN || isGhes() ? undefined : `token ${TOKEN}`; const AUTH = !TOKEN || isGhes() ? undefined : `token ${TOKEN}`;
@@ -71,10 +71,3 @@ export async function installCpythonFromRelease(release: tc.IToolRelease) {
core.info('Execute installation script'); core.info('Execute installation script');
await installPython(pythonExtractedFolder); await installPython(pythonExtractedFolder);
} }
function isGhes(): boolean {
const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
);
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}
+26 -7
View File
@@ -3,32 +3,51 @@ import * as finder from './find-python';
import * as finderPyPy from './find-pypy'; import * as finderPyPy from './find-pypy';
import * as path from 'path'; import * as path from 'path';
import * as os from 'os'; import * as os from 'os';
import {getCacheDistributor} from './cache-distributions/cache-factory';
import {isCacheFeatureAvailable} from './utils';
function isPyPyVersion(versionSpec: string) { function isPyPyVersion(versionSpec: string) {
return versionSpec.startsWith('pypy-'); return versionSpec.startsWith('pypy-');
} }
async function cacheDependencies(cache: string, pythonVersion: string) {
const cacheDependencyPath =
core.getInput('cache-dependency-path') || undefined;
const cacheDistributor = getCacheDistributor(
cache,
pythonVersion,
cacheDependencyPath
);
await cacheDistributor.restoreCache();
}
async function run() { async function run() {
try { try {
let version = core.getInput('python-version'); const version = core.getInput('python-version');
if (version) { if (version) {
let pythonVersion: string;
const arch: string = core.getInput('architecture') || os.arch(); const arch: string = core.getInput('architecture') || os.arch();
if (isPyPyVersion(version)) { if (isPyPyVersion(version)) {
const installed = await finderPyPy.findPyPyVersion(version, arch); const installed = await finderPyPy.findPyPyVersion(version, arch);
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
core.info( core.info(
`Successfully setup PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})` `Successfully setup PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`
); );
} else { } else {
const installed = await finder.findPythonVersion(version, arch); const installed = await finder.useCpythonVersion(version, arch);
core.info( pythonVersion = installed.version;
`Successfully setup ${installed.impl} (${installed.version})` core.info(`Successfully setup ${installed.impl} (${pythonVersion})`);
); }
const cache = core.getInput('cache');
if (cache && isCacheFeatureAvailable()) {
await cacheDependencies(cache, pythonVersion);
} }
} }
const matchersPath = path.join(__dirname, '..', '.github'); const matchersPath = path.join(__dirname, '../..', '.github');
core.info(`##[add-matcher]${path.join(matchersPath, 'python.json')}`); core.info(`##[add-matcher]${path.join(matchersPath, 'python.json')}`);
} catch (err) { } catch (err) {
core.setFailed(err.message); core.setFailed((err as Error).message);
} }
} }
+30 -1
View File
@@ -1,9 +1,13 @@
import * as cache from '@actions/cache';
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';
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';
export const WINDOWS_ARCHS = ['x86', 'x64'];
export const WINDOWS_PLATFORMS = ['win32', 'win64'];
const PYPY_VERSION_FILE = 'PYPY_VERSION'; const PYPY_VERSION_FILE = 'PYPY_VERSION';
export interface IPyPyManifestAsset { export interface IPyPyManifestAsset {
@@ -21,7 +25,7 @@ export interface IPyPyManifestRelease {
files: IPyPyManifestAsset[]; files: IPyPyManifestAsset[];
} }
/** create Symlinks for downloaded PyPy /** create Symlinks for downflfoaded PyPy
* It should be executed only for downloaded versions in runtime, because * It should be executed only for downloaded versions in runtime, because
* toolcache versions have this setup. * toolcache versions have this setup.
*/ */
@@ -90,3 +94,28 @@ export function validatePythonVersionFormatForPyPy(version: string) {
const re = /^\d+\.\d+$/; const re = /^\d+\.\d+$/;
return re.test(version); return re.test(version);
} }
export function isGhes(): boolean {
const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
);
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}
export function isCacheFeatureAvailable(): boolean {
if (!cache.isFeatureAvailable()) {
if (isGhes()) {
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'
);
}
return false;
}
return true;
}