mirror of
				https://git.mirrors.martin98.com/https://github.com/actions/setup-python
				synced 2025-10-31 13:41:06 +08:00 
			
		
		
		
	Adding support for more PyPy versions and installing them on-flight (#168)
* add support to install pypy * resolved comments, update readme, add e2e tests. * resolve throw error * Add pypy unit tests to cover code * add tests * Update test-pypy.yml * Update test-python.yml * Update test-python.yml * Update README.md * fixing tests * change order Co-authored-by: Maxim Lobanov <v-malob@microsoft.com> * add pypy tests and fix issue with pypy-3-nightly Co-authored-by: Maxim Lobanov <v-malob@microsoft.com>
This commit is contained in:
		
							parent
							
								
									2831efe49a
								
							
						
					
					
						commit
						8c5ea631b2
					
				
							
								
								
									
										47
									
								
								.github/workflows/test-pypy.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								.github/workflows/test-pypy.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | name: Validate PyPy e2e | ||||||
|  | on:  | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - main | ||||||
|  |     paths-ignore: | ||||||
|  |       - '**.md' | ||||||
|  |   pull_request: | ||||||
|  |     paths-ignore: | ||||||
|  |       - '**.md' | ||||||
|  |   schedule: | ||||||
|  |     - cron: 30 3 * * * | ||||||
|  | 
 | ||||||
|  | jobs: | ||||||
|  |   setup-pypy: | ||||||
|  |     name: Setup PyPy ${{ matrix.pypy }} ${{ matrix.os }} | ||||||
|  |     runs-on: ${{ matrix.os }} | ||||||
|  |     strategy: | ||||||
|  |       fail-fast: false | ||||||
|  |       matrix: | ||||||
|  |         os: [macos-latest, windows-latest, ubuntu-18.04, ubuntu-20.04] | ||||||
|  |         pypy: | ||||||
|  |         - 'pypy-2.7' | ||||||
|  |         - 'pypy-3.6' | ||||||
|  |         - 'pypy-3.7' | ||||||
|  |         - 'pypy-2.7-v7.3.2' | ||||||
|  |         - 'pypy-3.6-v7.3.2' | ||||||
|  |         - 'pypy-3.7-v7.3.2' | ||||||
|  |         - 'pypy-3.6-v7.3.x' | ||||||
|  |         - 'pypy-3.7-v7.x' | ||||||
|  |         - 'pypy-3.6-v7.3.3rc1' | ||||||
|  |         - 'pypy-3.7-nightly' | ||||||
|  | 
 | ||||||
|  |     steps: | ||||||
|  |       - name: Checkout | ||||||
|  |         uses: actions/checkout@v2 | ||||||
|  |    | ||||||
|  |       - name: setup-python ${{ matrix.pypy }} | ||||||
|  |         uses: ./ | ||||||
|  |         with: | ||||||
|  |           python-version: ${{ matrix.pypy }} | ||||||
|  |    | ||||||
|  |       - name: PyPy and Python version | ||||||
|  |         run: python --version | ||||||
|  |    | ||||||
|  |       - name: Run simple code | ||||||
|  |         run: python -c 'import math; print(math.factorial(5))' | ||||||
| @ -1,4 +1,4 @@ | |||||||
| name: Validate 'setup-python' | name: Validate Python e2e | ||||||
| on:  | on:  | ||||||
|   push: |   push: | ||||||
|     branches: |     branches: | ||||||
| @ -9,7 +9,7 @@ on: | |||||||
|     paths-ignore: |     paths-ignore: | ||||||
|       - '**.md' |       - '**.md' | ||||||
|   schedule: |   schedule: | ||||||
|     - cron: 0 0 * * * |     - cron: 30 3 * * * | ||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   default-version: |   default-version: | ||||||
| @ -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] |         os: [macos-latest, windows-latest, ubuntu-16.04, 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] |         os: [macos-latest, windows-latest, ubuntu-16.04, 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] |         os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04] | ||||||
|     steps: |     steps: | ||||||
|     - name: Checkout |     - name: Checkout | ||||||
|       uses: actions/checkout@v2 |       uses: actions/checkout@v2 | ||||||
| @ -91,13 +91,13 @@ 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: |   setup-pypy-legacy: | ||||||
|     name: Setup PyPy ${{ matrix.os }} |     name: Setup PyPy ${{ matrix.os }} | ||||||
|     runs-on: ${{ matrix.os }} |     runs-on: ${{ matrix.os }} | ||||||
|     strategy: |     strategy: | ||||||
|       fail-fast: false |       fail-fast: false | ||||||
|       matrix: |       matrix: | ||||||
|         os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04] |         os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04] | ||||||
|     steps: |     steps: | ||||||
|     - name: Checkout |     - name: Checkout | ||||||
|       uses: actions/checkout@v2 |       uses: actions/checkout@v2 | ||||||
							
								
								
									
										57
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								README.md
									
									
									
									
									
								
							| @ -17,6 +17,7 @@ This action sets up a Python environment for use in actions by: | |||||||
|   - Allows for pinning to a specific patch version of Python without the worry of it ever being removed or changed. |   - Allows for pinning to a specific patch version of Python without the worry of it ever being removed or changed. | ||||||
| - 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 | ||||||
| 
 | 
 | ||||||
| # Usage | # Usage | ||||||
| 
 | 
 | ||||||
| @ -40,7 +41,7 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     strategy: |     strategy: | ||||||
|       matrix: |       matrix: | ||||||
|         python-version: [ '2.x', '3.x', 'pypy2', 'pypy3' ] |         python-version: [ '2.x', '3.x', 'pypy-2.7', 'pypy-3.6', 'pypy-3.7' ] | ||||||
|     name: Python ${{ matrix.python-version }} sample |     name: Python ${{ matrix.python-version }} sample | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v2 |       - uses: actions/checkout@v2 | ||||||
| @ -60,7 +61,7 @@ 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, pypy2, pypy3] |         python-version: [2.7, 3.6, 3.7, 3.8, pypy-2.7, pypy-3.6] | ||||||
|         exclude: |         exclude: | ||||||
|           - os: macos-latest |           - os: macos-latest | ||||||
|             python-version: 3.8 |             python-version: 3.8 | ||||||
| @ -91,7 +92,6 @@ jobs: | |||||||
|       with: |       with: | ||||||
|         python-version: ${{ matrix.python-version }} |         python-version: ${{ matrix.python-version }} | ||||||
|     - run: python my_script.py |     - run: python my_script.py | ||||||
| 
 |  | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Download and set up an accurate pre-release version of Python: | Download and set up an accurate pre-release version of Python: | ||||||
| @ -114,6 +114,27 @@ steps: | |||||||
| - run: python my_script.py | - run: python my_script.py | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | Download and set up PyPy:   | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | jobs: | ||||||
|  |   build: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     strategy: | ||||||
|  |       matrix: | ||||||
|  |         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-v7.3.3 # Python 3.7 and PyPy 7.3.3 | ||||||
|  |     steps: | ||||||
|  |     - uses: actions/checkout@v2 | ||||||
|  |     - uses: actions/setup-python@v2 | ||||||
|  |       with: | ||||||
|  |         python-version: ${{ matrix.python-version }} | ||||||
|  |     - run: python my_script.py | ||||||
|  | ``` | ||||||
|  | More details on PyPy syntax and examples of using preview / nightly versions of PyPy can be found in the [Available versions of PyPy](#available-versions-of-pypy) section. | ||||||
|  | 
 | ||||||
| # Getting started with Python + Actions | # Getting started with Python + Actions | ||||||
| 
 | 
 | ||||||
| Check out our detailed guide on using [Python with GitHub Actions](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-python-with-github-actions). | Check out our detailed guide on using [Python with GitHub Actions](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-python-with-github-actions). | ||||||
| @ -129,7 +150,21 @@ Check out our detailed guide on using [Python with GitHub Actions](https://help. | |||||||
|     - 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. | ||||||
| - Downloadable Python versions from GitHub Releases ([actions/python-versions](https://github.com/actions/python-versions/releases)). | - Downloadable Python versions from GitHub Releases ([actions/python-versions](https://github.com/actions/python-versions/releases)). | ||||||
|     - All available versions are listed in the [version-manifest.json](https://github.com/actions/python-versions/blob/main/versions-manifest.json) file. |     - All available versions are listed in the [version-manifest.json](https://github.com/actions/python-versions/blob/main/versions-manifest.json) file. | ||||||
|     - If there is a specific version of Python that is not available, you can open an issue here. |     - If there is a specific version of Python that is not available, you can open an issue here | ||||||
|  |      | ||||||
|  |  # Available versions of PyPy | ||||||
|  |   | ||||||
|  |  `setup-python` is able to configure PyPy from two sources: | ||||||
|  |   | ||||||
|  | - 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 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`. | ||||||
|  | 
 | ||||||
|  | - 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. | ||||||
|  |   - PyPy < 7.3.3 are not available to install on-flight. | ||||||
|  |   - If some versions are not available, you can open an issue in https://foss.heptapod.net/pypy/pypy/ | ||||||
| 
 | 
 | ||||||
| # Hosted Tool Cache | # Hosted Tool Cache | ||||||
| 
 | 
 | ||||||
| @ -155,6 +190,20 @@ You should specify only a major and minor version if you are okay with the most | |||||||
|   - There will be a single patch version already installed on each runner for every minor version of Python that is supported. |   - There will be a single patch version already installed on each runner for every minor version of Python that is supported. | ||||||
|   - The patch version that will be preinstalled, will generally be the latest and every time there is a new patch released, the older version that is preinstalled will be replaced. |   - The patch version that will be preinstalled, will generally be the latest and every time there is a new patch released, the older version that is preinstalled will be replaced. | ||||||
|   - Using the most recent patch version will result in a very quick setup since no downloads will be required since a locally installed version Python on the runner will be used. |   - Using the most recent patch version will result in a very quick setup since no downloads will be required since a locally installed version Python on the runner will be used. | ||||||
|  |    | ||||||
|  | # Specifying a PyPy version | ||||||
|  | The version of PyPy should be specified in the format `pypy-<python_version>[-v<pypy_version>]`.   | ||||||
|  | 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-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.x # Python 3.7 and the latest available PyPy 7.x | ||||||
|  | pypy-3.7-v7.3.3rc1 # Python 3.7 and preview version of PyPy | ||||||
|  | pypy-3.7-nightly # Python 3.7 and nightly PyPy | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| # Using `setup-python` with a self hosted runner | # Using `setup-python` with a self hosted runner | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										494
									
								
								__tests__/data/pypy.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										494
									
								
								__tests__/data/pypy.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,494 @@ | |||||||
|  | [ | ||||||
|  |   { | ||||||
|  |     "pypy_version": "7.3.3", | ||||||
|  |     "python_version": "3.6.12", | ||||||
|  |     "stable": true, | ||||||
|  |     "latest_pypy": true, | ||||||
|  |     "date": "2020-11-21", | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3-aarch64.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3-aarch64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3-linux32.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3-linux32.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3-linux64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3-linux64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3-darwin64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3-darwin64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3-win32.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3-win32.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3-s390x.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3-s390x.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "pypy_version": "7.3.3rc1", | ||||||
|  |     "python_version": "3.6.12", | ||||||
|  |     "stable": false, | ||||||
|  |     "latest_pypy": false, | ||||||
|  |     "date": "2020-11-11", | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3rc1-aarch64.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3rc1-aarch64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3-linux32rc1.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3rc1-linux32.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3rc1-linux64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3rc1-linux64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3rc1-osx64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3rc1-osx64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3-win32rc1.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3rc1-win32.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.3rc1-s390x.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.3rc1-s390x.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "pypy_version": "7.3.3rc2", | ||||||
|  |     "python_version": "3.7.7", | ||||||
|  |     "stable": false, | ||||||
|  |     "latest_pypy": false, | ||||||
|  |     "date": "2020-11-11", | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "test.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "test.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "test.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "test.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "test.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "test.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "test.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "test.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "test.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "test.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "test.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "test.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "pypy_version": "7.3.3", | ||||||
|  |     "python_version": "3.7.9", | ||||||
|  |     "stable": true, | ||||||
|  |     "latest_pypy": true, | ||||||
|  |     "date": "2020-11-21", | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.3-aarch64.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.3-aarch64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.3-linux32.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.3-linux32.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.3-linux64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.3-linux64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.3-osx64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.3-osx64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.3-win32.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.3-win32.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.3-s390x.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.3-s390x.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "pypy_version": "7.3.3", | ||||||
|  |     "python_version": "2.7.18", | ||||||
|  |     "stable": true, | ||||||
|  |     "latest_pypy": true, | ||||||
|  |     "date": "2020-11-21", | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.3-aarch64.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.3-aarch64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.3-linux32.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.3-linux32.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.3-linux64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.3-linux64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.3-osx64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.3-osx64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.3-win32.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.3-win32.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.3-s390x.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.3-s390x.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "pypy_version": "7.3.2", | ||||||
|  |     "python_version": "3.6.9", | ||||||
|  |     "stable": true, | ||||||
|  |     "latest_pypy": true, | ||||||
|  |     "date": "2020-09-25", | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.2-aarch64.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.2-aarch64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.2-linux32.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.2-linux32.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.2-linux64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.2-linux64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.2-osx64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.2-osx64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.2-win32.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.2-win32.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.6-v7.3.2-s390x.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.6-v7.3.2-s390x.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "pypy_version": "7.3.2", | ||||||
|  |     "python_version": "3.7.9", | ||||||
|  |     "stable": true, | ||||||
|  |     "latest_pypy": false, | ||||||
|  |     "date": "2020-09-25", | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.2-aarch64.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.2-aarch64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.2-linux32.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.2-linux32.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.2-linux64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.2-linux64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.2-osx64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.2-osx64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.2-win32.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.2-win32.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy3.7-v7.3.2-s390x.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy3.7-v7.3.2-s390x.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "pypy_version": "7.3.2", | ||||||
|  |     "python_version": "2.7.13", | ||||||
|  |     "stable": true, | ||||||
|  |     "latest_pypy": true, | ||||||
|  |     "date": "2020-09-25", | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.2-aarch64.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.2-aarch64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.2-linux32.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.2-linux32.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.2-linux64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.2-linux64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.2-osx64.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.2-osx64.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.2-win32.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.2-win32.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "pypy2.7-v7.3.2-s390x.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "https://test.download.python.org/pypy/pypy2.7-v7.3.2-s390x.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "pypy_version": "nightly", | ||||||
|  |     "python_version": "2.7", | ||||||
|  |     "stable": false, | ||||||
|  |     "latest_pypy": false, | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "pypy_version": "nightly", | ||||||
|  |     "python_version": "3.7", | ||||||
|  |     "stable": false, | ||||||
|  |     "latest_pypy": false, | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "pypy_version": "nightly", | ||||||
|  |     "python_version": "3.6", | ||||||
|  |     "stable": false, | ||||||
|  |     "latest_pypy": false, | ||||||
|  |     "files": [ | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "aarch64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "i686", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "x64", | ||||||
|  |         "platform": "darwin", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.zip", | ||||||
|  |         "arch": "x86", | ||||||
|  |         "platform": "win32", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.zip" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "filename": "filename.tar.bz2", | ||||||
|  |         "arch": "s390x", | ||||||
|  |         "platform": "linux", | ||||||
|  |         "download_url": "http://nightlyBuilds.org/filename.tar.bz2" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   } | ||||||
|  | ] | ||||||
							
								
								
									
										237
									
								
								__tests__/find-pypy.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								__tests__/find-pypy.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,237 @@ | |||||||
|  | import fs from 'fs'; | ||||||
|  | 
 | ||||||
|  | import * as utils from '../src/utils'; | ||||||
|  | import {HttpClient} from '@actions/http-client'; | ||||||
|  | import * as ifm from '@actions/http-client/interfaces'; | ||||||
|  | import * as tc from '@actions/tool-cache'; | ||||||
|  | import * as exec from '@actions/exec'; | ||||||
|  | 
 | ||||||
|  | import * as path from 'path'; | ||||||
|  | import * as semver from 'semver'; | ||||||
|  | 
 | ||||||
|  | import * as finder from '../src/find-pypy'; | ||||||
|  | import { | ||||||
|  |   IPyPyManifestRelease, | ||||||
|  |   IS_WINDOWS, | ||||||
|  |   validateVersion, | ||||||
|  |   getPyPyVersionFromPath | ||||||
|  | } from '../src/utils'; | ||||||
|  | 
 | ||||||
|  | const manifestData = require('./data/pypy.json'); | ||||||
|  | 
 | ||||||
|  | let architecture: string; | ||||||
|  | 
 | ||||||
|  | if (IS_WINDOWS) { | ||||||
|  |   architecture = 'x86'; | ||||||
|  | } else { | ||||||
|  |   architecture = 'x64'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const toolDir = path.join(__dirname, 'runner', 'tools'); | ||||||
|  | const tempDir = path.join(__dirname, 'runner', 'temp'); | ||||||
|  | 
 | ||||||
|  | describe('parsePyPyVersion', () => { | ||||||
|  |   it.each([ | ||||||
|  |     ['pypy-3.6-v7.3.3', {pythonVersion: '3.6', pypyVersion: 'v7.3.3'}], | ||||||
|  |     ['pypy-3.6-v7.3.x', {pythonVersion: '3.6', pypyVersion: 'v7.3.x'}], | ||||||
|  |     ['pypy-3.6-v7.x', {pythonVersion: '3.6', pypyVersion: 'v7.x'}], | ||||||
|  |     ['pypy-3.6', {pythonVersion: '3.6', pypyVersion: 'x'}], | ||||||
|  |     ['pypy-3.6-nightly', {pythonVersion: '3.6', pypyVersion: 'nightly'}], | ||||||
|  |     ['pypy-3.6-v7.3.3rc1', {pythonVersion: '3.6', pypyVersion: 'v7.3.3-rc.1'}] | ||||||
|  |   ])('%s -> %s', (input, expected) => { | ||||||
|  |     expect(finder.parsePyPyVersion(input)).toEqual(expected); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('throw on invalid input', () => { | ||||||
|  |     expect(() => finder.parsePyPyVersion('pypy-')).toThrowError( | ||||||
|  |       "Invalid 'version' property for PyPy. PyPy version should be specified as 'pypy-<python-version>'. See README for examples and documentation." | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | describe('getPyPyVersionFromPath', () => { | ||||||
|  |   it('/fake/toolcache/PyPy/3.6.5/x64 -> 3.6.5', () => { | ||||||
|  |     expect(getPyPyVersionFromPath('/fake/toolcache/PyPy/3.6.5/x64')).toEqual( | ||||||
|  |       '3.6.5' | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | describe('findPyPyToolCache', () => { | ||||||
|  |   const actualPythonVersion = '3.6.17'; | ||||||
|  |   const actualPyPyVersion = '7.5.4'; | ||||||
|  |   const pypyPath = path.join('PyPy', actualPythonVersion, architecture); | ||||||
|  |   let tcFind: jest.SpyInstance; | ||||||
|  |   let spyReadExactPyPyVersion: jest.SpyInstance; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     tcFind = jest.spyOn(tc, 'find'); | ||||||
|  |     tcFind.mockImplementation((toolname: string, pythonVersion: string) => { | ||||||
|  |       const semverVersion = new semver.Range(pythonVersion); | ||||||
|  |       return semver.satisfies(actualPythonVersion, semverVersion) | ||||||
|  |         ? pypyPath | ||||||
|  |         : ''; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     spyReadExactPyPyVersion = jest.spyOn(utils, 'readExactPyPyVersionFile'); | ||||||
|  |     spyReadExactPyPyVersion.mockImplementation(() => actualPyPyVersion); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   afterEach(() => { | ||||||
|  |     jest.resetAllMocks(); | ||||||
|  |     jest.clearAllMocks(); | ||||||
|  |     jest.restoreAllMocks(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('PyPy exists on the path and versions are satisfied', () => { | ||||||
|  |     expect(finder.findPyPyToolCache('3.6.17', 'v7.5.4', architecture)).toEqual({ | ||||||
|  |       installDir: pypyPath, | ||||||
|  |       resolvedPythonVersion: actualPythonVersion, | ||||||
|  |       resolvedPyPyVersion: actualPyPyVersion | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('PyPy exists on the path and versions are satisfied with semver', () => { | ||||||
|  |     expect(finder.findPyPyToolCache('3.6', 'v7.5.x', architecture)).toEqual({ | ||||||
|  |       installDir: pypyPath, | ||||||
|  |       resolvedPythonVersion: actualPythonVersion, | ||||||
|  |       resolvedPyPyVersion: actualPyPyVersion | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it("PyPy exists on the path, but Python version doesn't match", () => { | ||||||
|  |     expect(finder.findPyPyToolCache('3.7', 'v7.5.4', architecture)).toEqual({ | ||||||
|  |       installDir: '', | ||||||
|  |       resolvedPythonVersion: '', | ||||||
|  |       resolvedPyPyVersion: '' | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it("PyPy exists on the path, but PyPy version doesn't match", () => { | ||||||
|  |     expect(finder.findPyPyToolCache('3.6', 'v7.5.1', architecture)).toEqual({ | ||||||
|  |       installDir: null, | ||||||
|  |       resolvedPythonVersion: '', | ||||||
|  |       resolvedPyPyVersion: '' | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | describe('findPyPyVersion', () => { | ||||||
|  |   let tcFind: jest.SpyInstance; | ||||||
|  |   let spyExtractZip: jest.SpyInstance; | ||||||
|  |   let spyExtractTar: jest.SpyInstance; | ||||||
|  |   let spyHttpClient: jest.SpyInstance; | ||||||
|  |   let spyExistsSync: jest.SpyInstance; | ||||||
|  |   let spyExec: jest.SpyInstance; | ||||||
|  |   let spySymlinkSync: jest.SpyInstance; | ||||||
|  |   let spyDownloadTool: jest.SpyInstance; | ||||||
|  |   let spyReadExactPyPyVersion: jest.SpyInstance; | ||||||
|  |   let spyFsReadDir: jest.SpyInstance; | ||||||
|  |   let spyWriteExactPyPyVersionFile: jest.SpyInstance; | ||||||
|  |   let spyCacheDir: jest.SpyInstance; | ||||||
|  |   let spyChmodSync: jest.SpyInstance; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     tcFind = jest.spyOn(tc, 'find'); | ||||||
|  |     tcFind.mockImplementation((tool: string, version: string) => { | ||||||
|  |       const semverRange = new semver.Range(version); | ||||||
|  |       let pypyPath = ''; | ||||||
|  |       if (semver.satisfies('3.6.12', semverRange)) { | ||||||
|  |         pypyPath = path.join(toolDir, 'PyPy', '3.6.12', architecture); | ||||||
|  |       } | ||||||
|  |       return pypyPath; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     spyWriteExactPyPyVersionFile = jest.spyOn( | ||||||
|  |       utils, | ||||||
|  |       'writeExactPyPyVersionFile' | ||||||
|  |     ); | ||||||
|  |     spyWriteExactPyPyVersionFile.mockImplementation(() => null); | ||||||
|  | 
 | ||||||
|  |     spyReadExactPyPyVersion = jest.spyOn(utils, 'readExactPyPyVersionFile'); | ||||||
|  |     spyReadExactPyPyVersion.mockImplementation(() => '7.3.3'); | ||||||
|  | 
 | ||||||
|  |     spyDownloadTool = jest.spyOn(tc, 'downloadTool'); | ||||||
|  |     spyDownloadTool.mockImplementation(() => path.join(tempDir, 'PyPy')); | ||||||
|  | 
 | ||||||
|  |     spyExtractZip = jest.spyOn(tc, 'extractZip'); | ||||||
|  |     spyExtractZip.mockImplementation(() => tempDir); | ||||||
|  | 
 | ||||||
|  |     spyExtractTar = jest.spyOn(tc, 'extractTar'); | ||||||
|  |     spyExtractTar.mockImplementation(() => tempDir); | ||||||
|  | 
 | ||||||
|  |     spyFsReadDir = jest.spyOn(fs, 'readdirSync'); | ||||||
|  |     spyFsReadDir.mockImplementation((directory: string) => ['PyPyTest']); | ||||||
|  | 
 | ||||||
|  |     spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson'); | ||||||
|  |     spyHttpClient.mockImplementation( | ||||||
|  |       async (): Promise<ifm.ITypedResponse<IPyPyManifestRelease[]>> => { | ||||||
|  |         const result = JSON.stringify(manifestData); | ||||||
|  |         return { | ||||||
|  |           statusCode: 200, | ||||||
|  |           headers: {}, | ||||||
|  |           result: JSON.parse(result) as IPyPyManifestRelease[] | ||||||
|  |         }; | ||||||
|  |       } | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     spyExec = jest.spyOn(exec, 'exec'); | ||||||
|  |     spyExec.mockImplementation(() => undefined); | ||||||
|  | 
 | ||||||
|  |     spySymlinkSync = jest.spyOn(fs, 'symlinkSync'); | ||||||
|  |     spySymlinkSync.mockImplementation(() => undefined); | ||||||
|  | 
 | ||||||
|  |     spyExistsSync = jest.spyOn(fs, 'existsSync'); | ||||||
|  |     spyExistsSync.mockReturnValue(true); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   afterEach(() => { | ||||||
|  |     jest.resetAllMocks(); | ||||||
|  |     jest.clearAllMocks(); | ||||||
|  |     jest.restoreAllMocks(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('found PyPy in toolcache', async () => { | ||||||
|  |     await expect( | ||||||
|  |       finder.findPyPyVersion('pypy-3.6-v7.3.x', architecture) | ||||||
|  |     ).resolves.toEqual({ | ||||||
|  |       resolvedPythonVersion: '3.6.12', | ||||||
|  |       resolvedPyPyVersion: '7.3.3' | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('throw on invalid input format', async () => { | ||||||
|  |     await expect( | ||||||
|  |       finder.findPyPyVersion('pypy3.7-v7.3.x', architecture) | ||||||
|  |     ).rejects.toThrow(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('throw on invalid input format pypy3.7-7.3.x', async () => { | ||||||
|  |     await expect( | ||||||
|  |       finder.findPyPyVersion('pypy3.7-v7.3.x', architecture) | ||||||
|  |     ).rejects.toThrow(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('found and install successfully', async () => { | ||||||
|  |     spyCacheDir = jest.spyOn(tc, 'cacheDir'); | ||||||
|  |     spyCacheDir.mockImplementation(() => | ||||||
|  |       path.join(toolDir, 'PyPy', '3.7.7', architecture) | ||||||
|  |     ); | ||||||
|  |     spyChmodSync = jest.spyOn(fs, 'chmodSync'); | ||||||
|  |     spyChmodSync.mockImplementation(() => undefined); | ||||||
|  |     await expect( | ||||||
|  |       finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture) | ||||||
|  |     ).resolves.toEqual({ | ||||||
|  |       resolvedPythonVersion: '3.7.9', | ||||||
|  |       resolvedPyPyVersion: '7.3.3' | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('throw if release is not found', async () => { | ||||||
|  |     await expect( | ||||||
|  |       finder.findPyPyVersion('pypy-3.7-v7.5.x', architecture) | ||||||
|  |     ).rejects.toThrowError( | ||||||
|  |       `PyPy version 3.7 (v7.5.x) with arch ${architecture} not found` | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										230
									
								
								__tests__/install-pypy.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								__tests__/install-pypy.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,230 @@ | |||||||
|  | import fs from 'fs'; | ||||||
|  | 
 | ||||||
|  | import {HttpClient} from '@actions/http-client'; | ||||||
|  | import * as ifm from '@actions/http-client/interfaces'; | ||||||
|  | import * as tc from '@actions/tool-cache'; | ||||||
|  | import * as exec from '@actions/exec'; | ||||||
|  | import * as path from 'path'; | ||||||
|  | 
 | ||||||
|  | import * as installer from '../src/install-pypy'; | ||||||
|  | import { | ||||||
|  |   IPyPyManifestRelease, | ||||||
|  |   IPyPyManifestAsset, | ||||||
|  |   IS_WINDOWS | ||||||
|  | } from '../src/utils'; | ||||||
|  | 
 | ||||||
|  | const manifestData = require('./data/pypy.json'); | ||||||
|  | 
 | ||||||
|  | let architecture: string; | ||||||
|  | if (IS_WINDOWS) { | ||||||
|  |   architecture = 'x86'; | ||||||
|  | } else { | ||||||
|  |   architecture = 'x64'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const toolDir = path.join(__dirname, 'runner', 'tools'); | ||||||
|  | const tempDir = path.join(__dirname, 'runner', 'temp'); | ||||||
|  | 
 | ||||||
|  | describe('pypyVersionToSemantic', () => { | ||||||
|  |   it.each([ | ||||||
|  |     ['7.3.3rc1', '7.3.3-rc.1'], | ||||||
|  |     ['7.3.3', '7.3.3'], | ||||||
|  |     ['7.3.x', '7.3.x'], | ||||||
|  |     ['7.x', '7.x'], | ||||||
|  |     ['nightly', 'nightly'] | ||||||
|  |   ])('%s -> %s', (input, expected) => { | ||||||
|  |     expect(installer.pypyVersionToSemantic(input)).toEqual(expected); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | describe('findRelease', () => { | ||||||
|  |   const result = JSON.stringify(manifestData); | ||||||
|  |   const releases = JSON.parse(result) as IPyPyManifestRelease[]; | ||||||
|  |   const extension = IS_WINDOWS ? '.zip' : '.tar.bz2'; | ||||||
|  |   const extensionName = IS_WINDOWS | ||||||
|  |     ? `${process.platform}${extension}` | ||||||
|  |     : `${process.platform}64${extension}`; | ||||||
|  |   const files: IPyPyManifestAsset = { | ||||||
|  |     filename: `pypy3.6-v7.3.3-${extensionName}`, | ||||||
|  |     arch: architecture, | ||||||
|  |     platform: process.platform, | ||||||
|  |     download_url: `https://test.download.python.org/pypy/pypy3.6-v7.3.3-${extensionName}` | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   it("Python version is found, but PyPy version doesn't match", () => { | ||||||
|  |     const pythonVersion = '3.6'; | ||||||
|  |     const pypyVersion = '7.3.7'; | ||||||
|  |     expect( | ||||||
|  |       installer.findRelease(releases, pythonVersion, pypyVersion, architecture) | ||||||
|  |     ).toEqual(null); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('Python version is found and PyPy version matches', () => { | ||||||
|  |     const pythonVersion = '3.6'; | ||||||
|  |     const pypyVersion = '7.3.3'; | ||||||
|  |     expect( | ||||||
|  |       installer.findRelease(releases, pythonVersion, pypyVersion, architecture) | ||||||
|  |     ).toEqual({ | ||||||
|  |       foundAsset: files, | ||||||
|  |       resolvedPythonVersion: '3.6.12', | ||||||
|  |       resolvedPyPyVersion: pypyVersion | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('Python version is found in toolcache and PyPy version matches semver', () => { | ||||||
|  |     const pythonVersion = '3.6'; | ||||||
|  |     const pypyVersion = '7.x'; | ||||||
|  |     expect( | ||||||
|  |       installer.findRelease(releases, pythonVersion, pypyVersion, architecture) | ||||||
|  |     ).toEqual({ | ||||||
|  |       foundAsset: files, | ||||||
|  |       resolvedPythonVersion: '3.6.12', | ||||||
|  |       resolvedPyPyVersion: '7.3.3' | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('Python and preview version of PyPy are found', () => { | ||||||
|  |     const pythonVersion = '3.7'; | ||||||
|  |     const pypyVersion = installer.pypyVersionToSemantic('7.3.3rc2'); | ||||||
|  |     expect( | ||||||
|  |       installer.findRelease(releases, pythonVersion, pypyVersion, architecture) | ||||||
|  |     ).toEqual({ | ||||||
|  |       foundAsset: { | ||||||
|  |         filename: `test${extension}`, | ||||||
|  |         arch: architecture, | ||||||
|  |         platform: process.platform, | ||||||
|  |         download_url: `test${extension}` | ||||||
|  |       }, | ||||||
|  |       resolvedPythonVersion: '3.7.7', | ||||||
|  |       resolvedPyPyVersion: '7.3.3rc2' | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('Python version with latest PyPy is found', () => { | ||||||
|  |     const pythonVersion = '3.6'; | ||||||
|  |     const pypyVersion = 'x'; | ||||||
|  |     expect( | ||||||
|  |       installer.findRelease(releases, pythonVersion, pypyVersion, architecture) | ||||||
|  |     ).toEqual({ | ||||||
|  |       foundAsset: files, | ||||||
|  |       resolvedPythonVersion: '3.6.12', | ||||||
|  |       resolvedPyPyVersion: '7.3.3' | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('Nightly release is found', () => { | ||||||
|  |     const pythonVersion = '3.6'; | ||||||
|  |     const pypyVersion = 'nightly'; | ||||||
|  |     const filename = IS_WINDOWS ? 'filename.zip' : 'filename.tar.bz2'; | ||||||
|  |     expect( | ||||||
|  |       installer.findRelease(releases, pythonVersion, pypyVersion, architecture) | ||||||
|  |     ).toEqual({ | ||||||
|  |       foundAsset: { | ||||||
|  |         filename: filename, | ||||||
|  |         arch: architecture, | ||||||
|  |         platform: process.platform, | ||||||
|  |         download_url: `http://nightlyBuilds.org/${filename}` | ||||||
|  |       }, | ||||||
|  |       resolvedPythonVersion: '3.6', | ||||||
|  |       resolvedPyPyVersion: pypyVersion | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | describe('installPyPy', () => { | ||||||
|  |   let tcFind: jest.SpyInstance; | ||||||
|  |   let spyExtractZip: jest.SpyInstance; | ||||||
|  |   let spyExtractTar: jest.SpyInstance; | ||||||
|  |   let spyFsReadDir: jest.SpyInstance; | ||||||
|  |   let spyFsWriteFile: jest.SpyInstance; | ||||||
|  |   let spyHttpClient: jest.SpyInstance; | ||||||
|  |   let spyExistsSync: jest.SpyInstance; | ||||||
|  |   let spyExec: jest.SpyInstance; | ||||||
|  |   let spySymlinkSync: jest.SpyInstance; | ||||||
|  |   let spyDownloadTool: jest.SpyInstance; | ||||||
|  |   let spyCacheDir: jest.SpyInstance; | ||||||
|  |   let spyChmodSync: jest.SpyInstance; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     tcFind = jest.spyOn(tc, 'find'); | ||||||
|  |     tcFind.mockImplementation(() => path.join('PyPy', '3.6.12', architecture)); | ||||||
|  | 
 | ||||||
|  |     spyDownloadTool = jest.spyOn(tc, 'downloadTool'); | ||||||
|  |     spyDownloadTool.mockImplementation(() => path.join(tempDir, 'PyPy')); | ||||||
|  | 
 | ||||||
|  |     spyExtractZip = jest.spyOn(tc, 'extractZip'); | ||||||
|  |     spyExtractZip.mockImplementation(() => tempDir); | ||||||
|  | 
 | ||||||
|  |     spyExtractTar = jest.spyOn(tc, 'extractTar'); | ||||||
|  |     spyExtractTar.mockImplementation(() => tempDir); | ||||||
|  | 
 | ||||||
|  |     spyFsReadDir = jest.spyOn(fs, 'readdirSync'); | ||||||
|  |     spyFsReadDir.mockImplementation(() => ['PyPyTest']); | ||||||
|  | 
 | ||||||
|  |     spyFsWriteFile = jest.spyOn(fs, 'writeFileSync'); | ||||||
|  |     spyFsWriteFile.mockImplementation(() => undefined); | ||||||
|  | 
 | ||||||
|  |     spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson'); | ||||||
|  |     spyHttpClient.mockImplementation( | ||||||
|  |       async (): Promise<ifm.ITypedResponse<IPyPyManifestRelease[]>> => { | ||||||
|  |         const result = JSON.stringify(manifestData); | ||||||
|  |         return { | ||||||
|  |           statusCode: 200, | ||||||
|  |           headers: {}, | ||||||
|  |           result: JSON.parse(result) as IPyPyManifestRelease[] | ||||||
|  |         }; | ||||||
|  |       } | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     spyExec = jest.spyOn(exec, 'exec'); | ||||||
|  |     spyExec.mockImplementation(() => undefined); | ||||||
|  | 
 | ||||||
|  |     spySymlinkSync = jest.spyOn(fs, 'symlinkSync'); | ||||||
|  |     spySymlinkSync.mockImplementation(() => undefined); | ||||||
|  | 
 | ||||||
|  |     spyExistsSync = jest.spyOn(fs, 'existsSync'); | ||||||
|  |     spyExistsSync.mockImplementation(() => false); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   afterEach(() => { | ||||||
|  |     jest.resetAllMocks(); | ||||||
|  |     jest.clearAllMocks(); | ||||||
|  |     jest.restoreAllMocks(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('throw if release is not found', async () => { | ||||||
|  |     await expect( | ||||||
|  |       installer.installPyPy('7.3.3', '3.6.17', architecture) | ||||||
|  |     ).rejects.toThrowError( | ||||||
|  |       `PyPy version 3.6.17 (7.3.3) with arch ${architecture} not found` | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     expect(spyHttpClient).toHaveBeenCalled(); | ||||||
|  |     expect(spyDownloadTool).not.toHaveBeenCalled(); | ||||||
|  |     expect(spyExec).not.toHaveBeenCalled(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('found and install PyPy', async () => { | ||||||
|  |     spyCacheDir = jest.spyOn(tc, 'cacheDir'); | ||||||
|  |     spyCacheDir.mockImplementation(() => | ||||||
|  |       path.join(toolDir, 'PyPy', '3.6.12', architecture) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     spyChmodSync = jest.spyOn(fs, 'chmodSync'); | ||||||
|  |     spyChmodSync.mockImplementation(() => undefined); | ||||||
|  | 
 | ||||||
|  |     await expect( | ||||||
|  |       installer.installPyPy('7.3.x', '3.6.12', architecture) | ||||||
|  |     ).resolves.toEqual({ | ||||||
|  |       installDir: path.join(toolDir, 'PyPy', '3.6.12', architecture), | ||||||
|  |       resolvedPythonVersion: '3.6.12', | ||||||
|  |       resolvedPyPyVersion: '7.3.3' | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     expect(spyHttpClient).toHaveBeenCalled(); | ||||||
|  |     expect(spyDownloadTool).toHaveBeenCalled(); | ||||||
|  |     expect(spyExistsSync).toHaveBeenCalled(); | ||||||
|  |     expect(spyCacheDir).toHaveBeenCalled(); | ||||||
|  |     expect(spyExec).toHaveBeenCalled(); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										34
									
								
								__tests__/utils.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								__tests__/utils.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | import { | ||||||
|  |   validateVersion, | ||||||
|  |   validatePythonVersionFormatForPyPy | ||||||
|  | } from '../src/utils'; | ||||||
|  | 
 | ||||||
|  | describe('validatePythonVersionFormatForPyPy', () => { | ||||||
|  |   it.each([ | ||||||
|  |     ['3.6', true], | ||||||
|  |     ['3.7', true], | ||||||
|  |     ['3.6.x', false], | ||||||
|  |     ['3.7.x', false], | ||||||
|  |     ['3.x', false], | ||||||
|  |     ['3', false] | ||||||
|  |   ])('%s -> %s', (input, expected) => { | ||||||
|  |     expect(validatePythonVersionFormatForPyPy(input)).toEqual(expected); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | describe('validateVersion', () => { | ||||||
|  |   it.each([ | ||||||
|  |     ['v7.3.3', true], | ||||||
|  |     ['v7.3.x', true], | ||||||
|  |     ['v7.x', true], | ||||||
|  |     ['x', true], | ||||||
|  |     ['v7.3.3-rc.1', true], | ||||||
|  |     ['nightly', true], | ||||||
|  |     ['v7.3.b', false], | ||||||
|  |     ['3.6', true], | ||||||
|  |     ['3.b', false], | ||||||
|  |     ['3', true] | ||||||
|  |   ])('%s -> %s', (input, expected) => { | ||||||
|  |     expect(validateVersion(input)).toEqual(expected); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										374
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										374
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							| @ -1072,6 +1072,113 @@ function _readLinuxVersionFile() { | |||||||
| exports._readLinuxVersionFile = _readLinuxVersionFile; | exports._readLinuxVersionFile = _readLinuxVersionFile; | ||||||
| //# sourceMappingURL=manifest.js.map
 | //# sourceMappingURL=manifest.js.map
 | ||||||
| 
 | 
 | ||||||
|  | /***/ }), | ||||||
|  | 
 | ||||||
|  | /***/ 50: | ||||||
|  | /***/ (function(__unusedmodule, exports, __webpack_require__) { | ||||||
|  | 
 | ||||||
|  | "use strict"; | ||||||
|  | 
 | ||||||
|  | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||||||
|  |     function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||||||
|  |     return new (P || (P = Promise))(function (resolve, reject) { | ||||||
|  |         function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||||||
|  |         function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||||||
|  |         function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||||||
|  |         step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  | var __importStar = (this && this.__importStar) || function (mod) { | ||||||
|  |     if (mod && mod.__esModule) return mod; | ||||||
|  |     var result = {}; | ||||||
|  |     if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||||||
|  |     result["default"] = mod; | ||||||
|  |     return result; | ||||||
|  | }; | ||||||
|  | Object.defineProperty(exports, "__esModule", { value: true }); | ||||||
|  | const path = __importStar(__webpack_require__(622)); | ||||||
|  | const pypyInstall = __importStar(__webpack_require__(369)); | ||||||
|  | const utils_1 = __webpack_require__(163); | ||||||
|  | const semver = __importStar(__webpack_require__(876)); | ||||||
|  | const core = __importStar(__webpack_require__(470)); | ||||||
|  | const tc = __importStar(__webpack_require__(533)); | ||||||
|  | function findPyPyVersion(versionSpec, architecture) { | ||||||
|  |     return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |         let resolvedPyPyVersion = ''; | ||||||
|  |         let resolvedPythonVersion = ''; | ||||||
|  |         let installDir; | ||||||
|  |         const pypyVersionSpec = parsePyPyVersion(versionSpec); | ||||||
|  |         // PyPy only precompiles binaries for x86, but the architecture parameter defaults to x64.
 | ||||||
|  |         if (utils_1.IS_WINDOWS && architecture === 'x64') { | ||||||
|  |             architecture = 'x86'; | ||||||
|  |         } | ||||||
|  |         ({ installDir, resolvedPythonVersion, resolvedPyPyVersion } = findPyPyToolCache(pypyVersionSpec.pythonVersion, pypyVersionSpec.pypyVersion, architecture)); | ||||||
|  |         if (!installDir) { | ||||||
|  |             ({ | ||||||
|  |                 installDir, | ||||||
|  |                 resolvedPythonVersion, | ||||||
|  |                 resolvedPyPyVersion | ||||||
|  |             } = yield pypyInstall.installPyPy(pypyVersionSpec.pypyVersion, pypyVersionSpec.pythonVersion, architecture)); | ||||||
|  |         } | ||||||
|  |         const pipDir = utils_1.IS_WINDOWS ? 'Scripts' : 'bin'; | ||||||
|  |         const _binDir = path.join(installDir, pipDir); | ||||||
|  |         const pythonLocation = pypyInstall.getPyPyBinaryPath(installDir); | ||||||
|  |         core.exportVariable('pythonLocation', pythonLocation); | ||||||
|  |         core.addPath(pythonLocation); | ||||||
|  |         core.addPath(_binDir); | ||||||
|  |         return { resolvedPyPyVersion, resolvedPythonVersion }; | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | exports.findPyPyVersion = findPyPyVersion; | ||||||
|  | function findPyPyToolCache(pythonVersion, pypyVersion, architecture) { | ||||||
|  |     let resolvedPyPyVersion = ''; | ||||||
|  |     let resolvedPythonVersion = ''; | ||||||
|  |     let installDir = tc.find('PyPy', pythonVersion, architecture); | ||||||
|  |     if (installDir) { | ||||||
|  |         // 'tc.find' finds tool based on Python version but we also need to check
 | ||||||
|  |         // whether PyPy version satisfies requested version.
 | ||||||
|  |         resolvedPythonVersion = utils_1.getPyPyVersionFromPath(installDir); | ||||||
|  |         resolvedPyPyVersion = utils_1.readExactPyPyVersionFile(installDir); | ||||||
|  |         const isPyPyVersionSatisfies = semver.satisfies(resolvedPyPyVersion, pypyVersion); | ||||||
|  |         if (!isPyPyVersionSatisfies) { | ||||||
|  |             installDir = null; | ||||||
|  |             resolvedPyPyVersion = ''; | ||||||
|  |             resolvedPythonVersion = ''; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (!installDir) { | ||||||
|  |         core.info(`PyPy version ${pythonVersion} (${pypyVersion}) was not found in the local cache`); | ||||||
|  |     } | ||||||
|  |     return { installDir, resolvedPythonVersion, resolvedPyPyVersion }; | ||||||
|  | } | ||||||
|  | exports.findPyPyToolCache = findPyPyToolCache; | ||||||
|  | function parsePyPyVersion(versionSpec) { | ||||||
|  |     const versions = versionSpec.split('-').filter(item => !!item); | ||||||
|  |     if (versions.length < 2 || versions[0] != 'pypy') { | ||||||
|  |         throw new Error("Invalid 'version' property for PyPy. PyPy version should be specified as 'pypy-<python-version>'. See README for examples and documentation."); | ||||||
|  |     } | ||||||
|  |     const pythonVersion = versions[1]; | ||||||
|  |     let pypyVersion; | ||||||
|  |     if (versions.length > 2) { | ||||||
|  |         pypyVersion = pypyInstall.pypyVersionToSemantic(versions[2]); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         pypyVersion = 'x'; | ||||||
|  |     } | ||||||
|  |     if (!utils_1.validateVersion(pythonVersion) || !utils_1.validateVersion(pypyVersion)) { | ||||||
|  |         throw new Error("Invalid 'version' property for PyPy. Both Python version and PyPy versions should satisfy SemVer notation. See README for examples and documentation."); | ||||||
|  |     } | ||||||
|  |     if (!utils_1.validatePythonVersionFormatForPyPy(pythonVersion)) { | ||||||
|  |         throw new Error("Invalid format of Python version for PyPy. Python version should be specified in format 'x.y'. See README for examples and documentation."); | ||||||
|  |     } | ||||||
|  |     return { | ||||||
|  |         pypyVersion: pypyVersion, | ||||||
|  |         pythonVersion: pythonVersion | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | exports.parsePyPyVersion = parsePyPyVersion; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /***/ }), | /***/ }), | ||||||
| 
 | 
 | ||||||
| /***/ 65: | /***/ 65: | ||||||
| @ -2197,6 +2304,92 @@ if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) { | |||||||
| exports.debug = debug; // for test
 | exports.debug = debug; // for test
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /***/ }), | ||||||
|  | 
 | ||||||
|  | /***/ 163: | ||||||
|  | /***/ (function(__unusedmodule, exports, __webpack_require__) { | ||||||
|  | 
 | ||||||
|  | "use strict"; | ||||||
|  | 
 | ||||||
|  | var __importDefault = (this && this.__importDefault) || function (mod) { | ||||||
|  |     return (mod && mod.__esModule) ? mod : { "default": mod }; | ||||||
|  | }; | ||||||
|  | var __importStar = (this && this.__importStar) || function (mod) { | ||||||
|  |     if (mod && mod.__esModule) return mod; | ||||||
|  |     var result = {}; | ||||||
|  |     if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||||||
|  |     result["default"] = mod; | ||||||
|  |     return result; | ||||||
|  | }; | ||||||
|  | Object.defineProperty(exports, "__esModule", { value: true }); | ||||||
|  | const fs_1 = __importDefault(__webpack_require__(747)); | ||||||
|  | const path = __importStar(__webpack_require__(622)); | ||||||
|  | const semver = __importStar(__webpack_require__(876)); | ||||||
|  | exports.IS_WINDOWS = process.platform === 'win32'; | ||||||
|  | exports.IS_LINUX = process.platform === 'linux'; | ||||||
|  | const PYPY_VERSION_FILE = 'PYPY_VERSION'; | ||||||
|  | /** create Symlinks for downloaded PyPy | ||||||
|  |  *  It should be executed only for downloaded versions in runtime, because | ||||||
|  |  *  toolcache versions have this setup. | ||||||
|  |  */ | ||||||
|  | function createSymlinkInFolder(folderPath, sourceName, targetName, setExecutable = false) { | ||||||
|  |     const sourcePath = path.join(folderPath, sourceName); | ||||||
|  |     const targetPath = path.join(folderPath, targetName); | ||||||
|  |     if (fs_1.default.existsSync(targetPath)) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     fs_1.default.symlinkSync(sourcePath, targetPath); | ||||||
|  |     if (!exports.IS_WINDOWS && setExecutable) { | ||||||
|  |         fs_1.default.chmodSync(targetPath, '755'); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | exports.createSymlinkInFolder = createSymlinkInFolder; | ||||||
|  | function validateVersion(version) { | ||||||
|  |     return isNightlyKeyword(version) || Boolean(semver.validRange(version)); | ||||||
|  | } | ||||||
|  | exports.validateVersion = validateVersion; | ||||||
|  | function isNightlyKeyword(pypyVersion) { | ||||||
|  |     return pypyVersion === 'nightly'; | ||||||
|  | } | ||||||
|  | exports.isNightlyKeyword = isNightlyKeyword; | ||||||
|  | function getPyPyVersionFromPath(installDir) { | ||||||
|  |     return path.basename(path.dirname(installDir)); | ||||||
|  | } | ||||||
|  | exports.getPyPyVersionFromPath = getPyPyVersionFromPath; | ||||||
|  | /** | ||||||
|  |  * In tool-cache, we put PyPy to '<toolcache_root>/PyPy/<python_version>/x64' | ||||||
|  |  * There is no easy way to determine what PyPy version is located in specific folder | ||||||
|  |  * 'pypy --version' is not reliable enough since it is not set properly for preview versions | ||||||
|  |  * "7.3.3rc1" is marked as '7.3.3' in 'pypy --version' | ||||||
|  |  * so we put PYPY_VERSION file to PyPy directory when install it to VM and read it when we need to know version | ||||||
|  |  * PYPY_VERSION contains exact version from 'versions.json' | ||||||
|  |  */ | ||||||
|  | function readExactPyPyVersionFile(installDir) { | ||||||
|  |     let pypyVersion = ''; | ||||||
|  |     let fileVersion = path.join(installDir, PYPY_VERSION_FILE); | ||||||
|  |     if (fs_1.default.existsSync(fileVersion)) { | ||||||
|  |         pypyVersion = fs_1.default.readFileSync(fileVersion).toString(); | ||||||
|  |     } | ||||||
|  |     return pypyVersion; | ||||||
|  | } | ||||||
|  | exports.readExactPyPyVersionFile = readExactPyPyVersionFile; | ||||||
|  | function writeExactPyPyVersionFile(installDir, resolvedPyPyVersion) { | ||||||
|  |     const pypyFilePath = path.join(installDir, PYPY_VERSION_FILE); | ||||||
|  |     fs_1.default.writeFileSync(pypyFilePath, resolvedPyPyVersion); | ||||||
|  | } | ||||||
|  | exports.writeExactPyPyVersionFile = writeExactPyPyVersionFile; | ||||||
|  | /** | ||||||
|  |  * Python version should be specified explicitly like "x.y" (2.7, 3.6, 3.7) | ||||||
|  |  * "3.x" or "3" are not supported | ||||||
|  |  * because it could cause ambiguity when both PyPy version and Python version are not precise | ||||||
|  |  */ | ||||||
|  | function validatePythonVersionFormatForPyPy(version) { | ||||||
|  |     const re = /^\d+\.\d+$/; | ||||||
|  |     return re.test(version); | ||||||
|  | } | ||||||
|  | exports.validatePythonVersionFormatForPyPy = validatePythonVersionFormatForPyPy; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /***/ }), | /***/ }), | ||||||
| 
 | 
 | ||||||
| /***/ 164: | /***/ 164: | ||||||
| @ -2443,16 +2636,26 @@ var __importStar = (this && this.__importStar) || function (mod) { | |||||||
| Object.defineProperty(exports, "__esModule", { value: true }); | Object.defineProperty(exports, "__esModule", { value: true }); | ||||||
| const core = __importStar(__webpack_require__(470)); | const core = __importStar(__webpack_require__(470)); | ||||||
| const finder = __importStar(__webpack_require__(927)); | const finder = __importStar(__webpack_require__(927)); | ||||||
|  | const finderPyPy = __importStar(__webpack_require__(50)); | ||||||
| const path = __importStar(__webpack_require__(622)); | const path = __importStar(__webpack_require__(622)); | ||||||
| const os = __importStar(__webpack_require__(87)); | const os = __importStar(__webpack_require__(87)); | ||||||
|  | function isPyPyVersion(versionSpec) { | ||||||
|  |     return versionSpec.startsWith('pypy-'); | ||||||
|  | } | ||||||
| function run() { | function run() { | ||||||
|     return __awaiter(this, void 0, void 0, function* () { |     return __awaiter(this, void 0, void 0, function* () { | ||||||
|         try { |         try { | ||||||
|             let version = core.getInput('python-version'); |             let version = core.getInput('python-version'); | ||||||
|             if (version) { |             if (version) { | ||||||
|                 const arch = core.getInput('architecture') || os.arch(); |                 const arch = core.getInput('architecture') || os.arch(); | ||||||
|                 const installed = yield finder.findPythonVersion(version, arch); |                 if (isPyPyVersion(version)) { | ||||||
|                 core.info(`Successfully setup ${installed.impl} (${installed.version})`); |                     const installed = yield finderPyPy.findPyPyVersion(version, arch); | ||||||
|  |                     core.info(`Successfully setup PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`); | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     const installed = yield finder.findPythonVersion(version, arch); | ||||||
|  |                     core.info(`Successfully setup ${installed.impl} (${installed.version})`); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             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')}`); | ||||||
| @ -2580,6 +2783,151 @@ module.exports = ltr | |||||||
| 
 | 
 | ||||||
| module.exports = require("assert"); | module.exports = require("assert"); | ||||||
| 
 | 
 | ||||||
|  | /***/ }), | ||||||
|  | 
 | ||||||
|  | /***/ 369: | ||||||
|  | /***/ (function(__unusedmodule, exports, __webpack_require__) { | ||||||
|  | 
 | ||||||
|  | "use strict"; | ||||||
|  | 
 | ||||||
|  | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||||||
|  |     function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||||||
|  |     return new (P || (P = Promise))(function (resolve, reject) { | ||||||
|  |         function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||||||
|  |         function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||||||
|  |         function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||||||
|  |         step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  | var __importStar = (this && this.__importStar) || function (mod) { | ||||||
|  |     if (mod && mod.__esModule) return mod; | ||||||
|  |     var result = {}; | ||||||
|  |     if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||||||
|  |     result["default"] = mod; | ||||||
|  |     return result; | ||||||
|  | }; | ||||||
|  | var __importDefault = (this && this.__importDefault) || function (mod) { | ||||||
|  |     return (mod && mod.__esModule) ? mod : { "default": mod }; | ||||||
|  | }; | ||||||
|  | Object.defineProperty(exports, "__esModule", { value: true }); | ||||||
|  | const path = __importStar(__webpack_require__(622)); | ||||||
|  | const core = __importStar(__webpack_require__(470)); | ||||||
|  | const tc = __importStar(__webpack_require__(533)); | ||||||
|  | const semver = __importStar(__webpack_require__(876)); | ||||||
|  | const httpm = __importStar(__webpack_require__(539)); | ||||||
|  | const exec = __importStar(__webpack_require__(986)); | ||||||
|  | const fs_1 = __importDefault(__webpack_require__(747)); | ||||||
|  | const utils_1 = __webpack_require__(163); | ||||||
|  | function installPyPy(pypyVersion, pythonVersion, architecture) { | ||||||
|  |     return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |         let downloadDir; | ||||||
|  |         const releases = yield getAvailablePyPyVersions(); | ||||||
|  |         if (!releases || releases.length === 0) { | ||||||
|  |             throw new Error('No release was found in PyPy version.json'); | ||||||
|  |         } | ||||||
|  |         const releaseData = findRelease(releases, pythonVersion, pypyVersion, architecture); | ||||||
|  |         if (!releaseData || !releaseData.foundAsset) { | ||||||
|  |             throw new Error(`PyPy version ${pythonVersion} (${pypyVersion}) with arch ${architecture} not found`); | ||||||
|  |         } | ||||||
|  |         const { foundAsset, resolvedPythonVersion, resolvedPyPyVersion } = releaseData; | ||||||
|  |         let downloadUrl = `${foundAsset.download_url}`; | ||||||
|  |         core.info(`Downloading PyPy from "${downloadUrl}" ...`); | ||||||
|  |         const pypyPath = yield tc.downloadTool(downloadUrl); | ||||||
|  |         core.info('Extracting downloaded archive...'); | ||||||
|  |         if (utils_1.IS_WINDOWS) { | ||||||
|  |             downloadDir = yield tc.extractZip(pypyPath); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             downloadDir = yield tc.extractTar(pypyPath, undefined, 'x'); | ||||||
|  |         } | ||||||
|  |         // root folder in archive can have unpredictable name so just take the first folder
 | ||||||
|  |         // downloadDir is unique folder under TEMP and can't contain any other folders
 | ||||||
|  |         const archiveName = fs_1.default.readdirSync(downloadDir)[0]; | ||||||
|  |         const toolDir = path.join(downloadDir, archiveName); | ||||||
|  |         let installDir = toolDir; | ||||||
|  |         if (!utils_1.isNightlyKeyword(resolvedPyPyVersion)) { | ||||||
|  |             installDir = yield tc.cacheDir(toolDir, 'PyPy', resolvedPythonVersion, architecture); | ||||||
|  |         } | ||||||
|  |         utils_1.writeExactPyPyVersionFile(installDir, resolvedPyPyVersion); | ||||||
|  |         const binaryPath = getPyPyBinaryPath(installDir); | ||||||
|  |         yield createPyPySymlink(binaryPath, resolvedPythonVersion); | ||||||
|  |         yield installPip(binaryPath); | ||||||
|  |         return { installDir, resolvedPythonVersion, resolvedPyPyVersion }; | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | exports.installPyPy = installPyPy; | ||||||
|  | function getAvailablePyPyVersions() { | ||||||
|  |     return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |         const url = 'https://downloads.python.org/pypy/versions.json'; | ||||||
|  |         const http = new httpm.HttpClient('tool-cache'); | ||||||
|  |         const response = yield http.getJson(url); | ||||||
|  |         if (!response.result) { | ||||||
|  |             throw new Error(`Unable to retrieve the list of available PyPy versions from '${url}'`); | ||||||
|  |         } | ||||||
|  |         return response.result; | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | function createPyPySymlink(pypyBinaryPath, pythonVersion) { | ||||||
|  |     return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |         const version = semver.coerce(pythonVersion); | ||||||
|  |         const pythonBinaryPostfix = semver.major(version); | ||||||
|  |         const pypyBinaryPostfix = pythonBinaryPostfix === 2 ? '' : '3'; | ||||||
|  |         let binaryExtension = utils_1.IS_WINDOWS ? '.exe' : ''; | ||||||
|  |         core.info('Creating symlinks...'); | ||||||
|  |         utils_1.createSymlinkInFolder(pypyBinaryPath, `pypy${pypyBinaryPostfix}${binaryExtension}`, `python${pythonBinaryPostfix}${binaryExtension}`, true); | ||||||
|  |         utils_1.createSymlinkInFolder(pypyBinaryPath, `pypy${pypyBinaryPostfix}${binaryExtension}`, `python${binaryExtension}`, true); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | function installPip(pythonLocation) { | ||||||
|  |     return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |         core.info('Installing and updating pip'); | ||||||
|  |         const pythonBinary = path.join(pythonLocation, 'python'); | ||||||
|  |         yield exec.exec(`${pythonBinary} -m ensurepip`); | ||||||
|  |         yield exec.exec(`${pythonLocation}/python -m pip install --ignore-installed pip`); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | function findRelease(releases, pythonVersion, pypyVersion, architecture) { | ||||||
|  |     const filterReleases = releases.filter(item => { | ||||||
|  |         const isPythonVersionSatisfied = semver.satisfies(semver.coerce(item.python_version), pythonVersion); | ||||||
|  |         const isPyPyNightly = utils_1.isNightlyKeyword(pypyVersion) && utils_1.isNightlyKeyword(item.pypy_version); | ||||||
|  |         const isPyPyVersionSatisfied = isPyPyNightly || | ||||||
|  |             semver.satisfies(pypyVersionToSemantic(item.pypy_version), pypyVersion); | ||||||
|  |         const isArchPresent = item.files && | ||||||
|  |             item.files.some(file => file.arch === architecture && file.platform === process.platform); | ||||||
|  |         return isPythonVersionSatisfied && isPyPyVersionSatisfied && isArchPresent; | ||||||
|  |     }); | ||||||
|  |     if (filterReleases.length === 0) { | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |     const sortedReleases = filterReleases.sort((previous, current) => { | ||||||
|  |         return (semver.compare(semver.coerce(pypyVersionToSemantic(current.pypy_version)), semver.coerce(pypyVersionToSemantic(previous.pypy_version))) || | ||||||
|  |             semver.compare(semver.coerce(current.python_version), semver.coerce(previous.python_version))); | ||||||
|  |     }); | ||||||
|  |     const foundRelease = sortedReleases[0]; | ||||||
|  |     const foundAsset = foundRelease.files.find(item => item.arch === architecture && item.platform === process.platform); | ||||||
|  |     return { | ||||||
|  |         foundAsset, | ||||||
|  |         resolvedPythonVersion: foundRelease.python_version, | ||||||
|  |         resolvedPyPyVersion: foundRelease.pypy_version | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | exports.findRelease = findRelease; | ||||||
|  | /** Get PyPy binary location from the tool of installation directory | ||||||
|  |  *  - On Linux and macOS, the Python interpreter is in 'bin'. | ||||||
|  |  *  - On Windows, it is in the installation root. | ||||||
|  |  */ | ||||||
|  | function getPyPyBinaryPath(installDir) { | ||||||
|  |     const _binDir = path.join(installDir, 'bin'); | ||||||
|  |     return utils_1.IS_WINDOWS ? installDir : _binDir; | ||||||
|  | } | ||||||
|  | exports.getPyPyBinaryPath = getPyPyBinaryPath; | ||||||
|  | function pypyVersionToSemantic(versionSpec) { | ||||||
|  |     const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g; | ||||||
|  |     return versionSpec.replace(prereleaseVersion, '$1-$2.$3'); | ||||||
|  | } | ||||||
|  | exports.pypyVersionToSemantic = pypyVersionToSemantic; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /***/ }), | /***/ }), | ||||||
| 
 | 
 | ||||||
| /***/ 413: | /***/ 413: | ||||||
| @ -6426,14 +6774,13 @@ const path = __importStar(__webpack_require__(622)); | |||||||
| const core = __importStar(__webpack_require__(470)); | const core = __importStar(__webpack_require__(470)); | ||||||
| const tc = __importStar(__webpack_require__(533)); | const tc = __importStar(__webpack_require__(533)); | ||||||
| const exec = __importStar(__webpack_require__(986)); | const exec = __importStar(__webpack_require__(986)); | ||||||
|  | const utils_1 = __webpack_require__(163); | ||||||
| const TOKEN = core.getInput('token'); | const TOKEN = core.getInput('token'); | ||||||
| const AUTH = !TOKEN || isGhes() ? undefined : `token ${TOKEN}`; | const AUTH = !TOKEN || isGhes() ? undefined : `token ${TOKEN}`; | ||||||
| const MANIFEST_REPO_OWNER = 'actions'; | const MANIFEST_REPO_OWNER = 'actions'; | ||||||
| const MANIFEST_REPO_NAME = 'python-versions'; | const MANIFEST_REPO_NAME = 'python-versions'; | ||||||
| const MANIFEST_REPO_BRANCH = 'main'; | const MANIFEST_REPO_BRANCH = 'main'; | ||||||
| exports.MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`; | exports.MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`; | ||||||
| const IS_WINDOWS = process.platform === 'win32'; |  | ||||||
| const IS_LINUX = process.platform === 'linux'; |  | ||||||
| function findReleaseFromManifest(semanticVersionSpec, architecture) { | function findReleaseFromManifest(semanticVersionSpec, architecture) { | ||||||
|     return __awaiter(this, void 0, void 0, function* () { |     return __awaiter(this, void 0, void 0, function* () { | ||||||
|         const manifest = yield tc.getManifestFromRepo(MANIFEST_REPO_OWNER, MANIFEST_REPO_NAME, AUTH, MANIFEST_REPO_BRANCH); |         const manifest = yield tc.getManifestFromRepo(MANIFEST_REPO_OWNER, MANIFEST_REPO_NAME, AUTH, MANIFEST_REPO_BRANCH); | ||||||
| @ -6445,7 +6792,7 @@ function installPython(workingDirectory) { | |||||||
|     return __awaiter(this, void 0, void 0, function* () { |     return __awaiter(this, void 0, void 0, function* () { | ||||||
|         const options = { |         const options = { | ||||||
|             cwd: workingDirectory, |             cwd: workingDirectory, | ||||||
|             env: Object.assign(Object.assign({}, process.env), (IS_LINUX && { LD_LIBRARY_PATH: path.join(workingDirectory, 'lib') })), |             env: Object.assign(Object.assign({}, process.env), (utils_1.IS_LINUX && { LD_LIBRARY_PATH: path.join(workingDirectory, 'lib') })), | ||||||
|             silent: true, |             silent: true, | ||||||
|             listeners: { |             listeners: { | ||||||
|                 stdout: (data) => { |                 stdout: (data) => { | ||||||
| @ -6456,7 +6803,7 @@ function installPython(workingDirectory) { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         if (IS_WINDOWS) { |         if (utils_1.IS_WINDOWS) { | ||||||
|             yield exec.exec('powershell', ['./setup.ps1'], options); |             yield exec.exec('powershell', ['./setup.ps1'], options); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
| @ -6471,7 +6818,7 @@ function installCpythonFromRelease(release) { | |||||||
|         const pythonPath = yield tc.downloadTool(downloadUrl, undefined, AUTH); |         const pythonPath = yield tc.downloadTool(downloadUrl, undefined, AUTH); | ||||||
|         core.info('Extract downloaded archive'); |         core.info('Extract downloaded archive'); | ||||||
|         let pythonExtractedFolder; |         let pythonExtractedFolder; | ||||||
|         if (IS_WINDOWS) { |         if (utils_1.IS_WINDOWS) { | ||||||
|             pythonExtractedFolder = yield tc.extractZip(pythonPath); |             pythonExtractedFolder = yield tc.extractZip(pythonPath); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
| @ -6686,12 +7033,11 @@ var __importStar = (this && this.__importStar) || function (mod) { | |||||||
| Object.defineProperty(exports, "__esModule", { value: true }); | Object.defineProperty(exports, "__esModule", { value: true }); | ||||||
| const os = __importStar(__webpack_require__(87)); | const os = __importStar(__webpack_require__(87)); | ||||||
| const path = __importStar(__webpack_require__(622)); | const path = __importStar(__webpack_require__(622)); | ||||||
|  | const utils_1 = __webpack_require__(163); | ||||||
| const semver = __importStar(__webpack_require__(876)); | const semver = __importStar(__webpack_require__(876)); | ||||||
| const installer = __importStar(__webpack_require__(824)); | const installer = __importStar(__webpack_require__(824)); | ||||||
| const core = __importStar(__webpack_require__(470)); | const core = __importStar(__webpack_require__(470)); | ||||||
| const tc = __importStar(__webpack_require__(533)); | const tc = __importStar(__webpack_require__(533)); | ||||||
| const IS_WINDOWS = process.platform === 'win32'; |  | ||||||
| const IS_LINUX = process.platform === 'linux'; |  | ||||||
| // Python has "scripts" or "bin" directories where command-line tools that come with packages are installed.
 | // Python has "scripts" or "bin" directories where command-line tools that come with packages are installed.
 | ||||||
| // This is where pip is, along with anything that pip installs.
 | // This is where pip is, along with anything that pip installs.
 | ||||||
| // There is a seperate directory for `pip install --user`.
 | // There is a seperate directory for `pip install --user`.
 | ||||||
| @ -6705,7 +7051,7 @@ const IS_LINUX = process.platform === 'linux'; | |||||||
| //      (--user) %APPDATA%\Python\PythonXY\Scripts
 | //      (--user) %APPDATA%\Python\PythonXY\Scripts
 | ||||||
| // See https://docs.python.org/3/library/sysconfig.html
 | // See https://docs.python.org/3/library/sysconfig.html
 | ||||||
| function binDir(installDir) { | function binDir(installDir) { | ||||||
|     if (IS_WINDOWS) { |     if (utils_1.IS_WINDOWS) { | ||||||
|         return path.join(installDir, 'Scripts'); |         return path.join(installDir, 'Scripts'); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
| @ -6720,7 +7066,7 @@ function binDir(installDir) { | |||||||
| function usePyPy(majorVersion, architecture) { | function usePyPy(majorVersion, architecture) { | ||||||
|     const findPyPy = tc.find.bind(undefined, 'PyPy', majorVersion); |     const findPyPy = tc.find.bind(undefined, 'PyPy', majorVersion); | ||||||
|     let installDir = findPyPy(architecture); |     let installDir = findPyPy(architecture); | ||||||
|     if (!installDir && IS_WINDOWS) { |     if (!installDir && utils_1.IS_WINDOWS) { | ||||||
|         // PyPy only precompiles binaries for x86, but the architecture parameter defaults to x64.
 |         // PyPy only precompiles binaries for x86, but the architecture parameter defaults to x64.
 | ||||||
|         // On our Windows virtual environments, we only install an x86 version.
 |         // On our Windows virtual environments, we only install an x86 version.
 | ||||||
|         // Fall back to x86.
 |         // Fall back to x86.
 | ||||||
| @ -6734,7 +7080,7 @@ function usePyPy(majorVersion, architecture) { | |||||||
|     const _binDir = path.join(installDir, 'bin'); |     const _binDir = path.join(installDir, 'bin'); | ||||||
|     // On Linux and macOS, the Python interpreter is in 'bin'.
 |     // On Linux and macOS, the Python interpreter is in 'bin'.
 | ||||||
|     // On Windows, it is in the installation root.
 |     // On Windows, it is in the installation root.
 | ||||||
|     const pythonLocation = IS_WINDOWS ? installDir : _binDir; |     const pythonLocation = utils_1.IS_WINDOWS ? installDir : _binDir; | ||||||
|     core.exportVariable('pythonLocation', pythonLocation); |     core.exportVariable('pythonLocation', pythonLocation); | ||||||
|     core.addPath(installDir); |     core.addPath(installDir); | ||||||
|     core.addPath(_binDir); |     core.addPath(_binDir); | ||||||
| @ -6768,7 +7114,7 @@ function useCpythonVersion(version, architecture) { | |||||||
|             ].join(os.EOL)); |             ].join(os.EOL)); | ||||||
|         } |         } | ||||||
|         core.exportVariable('pythonLocation', installDir); |         core.exportVariable('pythonLocation', installDir); | ||||||
|         if (IS_LINUX) { |         if (utils_1.IS_LINUX) { | ||||||
|             const libPath = process.env.LD_LIBRARY_PATH |             const libPath = process.env.LD_LIBRARY_PATH | ||||||
|                 ? `:${process.env.LD_LIBRARY_PATH}` |                 ? `:${process.env.LD_LIBRARY_PATH}` | ||||||
|                 : ''; |                 : ''; | ||||||
| @ -6779,7 +7125,7 @@ function useCpythonVersion(version, architecture) { | |||||||
|         } |         } | ||||||
|         core.addPath(installDir); |         core.addPath(installDir); | ||||||
|         core.addPath(binDir(installDir)); |         core.addPath(binDir(installDir)); | ||||||
|         if (IS_WINDOWS) { |         if (utils_1.IS_WINDOWS) { | ||||||
|             // Add --user directory
 |             // Add --user directory
 | ||||||
|             // `installDir` from tool cache should look like $RUNNER_TOOL_CACHE/Python/<semantic version>/x64/
 |             // `installDir` from tool cache should look like $RUNNER_TOOL_CACHE/Python/<semantic version>/x64/
 | ||||||
|             // So if `findLocalTool` succeeded above, we must have a conformant `installDir`
 |             // So if `findLocalTool` succeeded above, we must have a conformant `installDir`
 | ||||||
|  | |||||||
							
								
								
									
										131
									
								
								src/find-pypy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/find-pypy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | |||||||
|  | import * as path from 'path'; | ||||||
|  | import * as pypyInstall from './install-pypy'; | ||||||
|  | import { | ||||||
|  |   IS_WINDOWS, | ||||||
|  |   validateVersion, | ||||||
|  |   getPyPyVersionFromPath, | ||||||
|  |   readExactPyPyVersionFile, | ||||||
|  |   validatePythonVersionFormatForPyPy | ||||||
|  | } from './utils'; | ||||||
|  | 
 | ||||||
|  | import * as semver from 'semver'; | ||||||
|  | import * as core from '@actions/core'; | ||||||
|  | import * as tc from '@actions/tool-cache'; | ||||||
|  | 
 | ||||||
|  | interface IPyPyVersionSpec { | ||||||
|  |   pypyVersion: string; | ||||||
|  |   pythonVersion: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export async function findPyPyVersion( | ||||||
|  |   versionSpec: string, | ||||||
|  |   architecture: string | ||||||
|  | ): Promise<{resolvedPyPyVersion: string; resolvedPythonVersion: string}> { | ||||||
|  |   let resolvedPyPyVersion = ''; | ||||||
|  |   let resolvedPythonVersion = ''; | ||||||
|  |   let installDir: string | null; | ||||||
|  | 
 | ||||||
|  |   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( | ||||||
|  |     pypyVersionSpec.pythonVersion, | ||||||
|  |     pypyVersionSpec.pypyVersion, | ||||||
|  |     architecture | ||||||
|  |   )); | ||||||
|  | 
 | ||||||
|  |   if (!installDir) { | ||||||
|  |     ({ | ||||||
|  |       installDir, | ||||||
|  |       resolvedPythonVersion, | ||||||
|  |       resolvedPyPyVersion | ||||||
|  |     } = await pypyInstall.installPyPy( | ||||||
|  |       pypyVersionSpec.pypyVersion, | ||||||
|  |       pypyVersionSpec.pythonVersion, | ||||||
|  |       architecture | ||||||
|  |     )); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const pipDir = IS_WINDOWS ? 'Scripts' : 'bin'; | ||||||
|  |   const _binDir = path.join(installDir, pipDir); | ||||||
|  |   const pythonLocation = pypyInstall.getPyPyBinaryPath(installDir); | ||||||
|  |   core.exportVariable('pythonLocation', pythonLocation); | ||||||
|  |   core.addPath(pythonLocation); | ||||||
|  |   core.addPath(_binDir); | ||||||
|  | 
 | ||||||
|  |   return {resolvedPyPyVersion, resolvedPythonVersion}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function findPyPyToolCache( | ||||||
|  |   pythonVersion: string, | ||||||
|  |   pypyVersion: string, | ||||||
|  |   architecture: string | ||||||
|  | ) { | ||||||
|  |   let resolvedPyPyVersion = ''; | ||||||
|  |   let resolvedPythonVersion = ''; | ||||||
|  |   let installDir: string | null = tc.find('PyPy', pythonVersion, architecture); | ||||||
|  | 
 | ||||||
|  |   if (installDir) { | ||||||
|  |     // 'tc.find' finds tool based on Python version but we also need to check
 | ||||||
|  |     // whether PyPy version satisfies requested version.
 | ||||||
|  |     resolvedPythonVersion = getPyPyVersionFromPath(installDir); | ||||||
|  |     resolvedPyPyVersion = readExactPyPyVersionFile(installDir); | ||||||
|  | 
 | ||||||
|  |     const isPyPyVersionSatisfies = semver.satisfies( | ||||||
|  |       resolvedPyPyVersion, | ||||||
|  |       pypyVersion | ||||||
|  |     ); | ||||||
|  |     if (!isPyPyVersionSatisfies) { | ||||||
|  |       installDir = null; | ||||||
|  |       resolvedPyPyVersion = ''; | ||||||
|  |       resolvedPythonVersion = ''; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (!installDir) { | ||||||
|  |     core.info( | ||||||
|  |       `PyPy version ${pythonVersion} (${pypyVersion}) was not found in the local cache` | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return {installDir, resolvedPythonVersion, resolvedPyPyVersion}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function parsePyPyVersion(versionSpec: string): IPyPyVersionSpec { | ||||||
|  |   const versions = versionSpec.split('-').filter(item => !!item); | ||||||
|  | 
 | ||||||
|  |   if (versions.length < 2 || versions[0] != 'pypy') { | ||||||
|  |     throw new Error( | ||||||
|  |       "Invalid 'version' property for PyPy. PyPy version should be specified as 'pypy-<python-version>'. See README for examples and documentation." | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const pythonVersion = versions[1]; | ||||||
|  |   let pypyVersion: string; | ||||||
|  |   if (versions.length > 2) { | ||||||
|  |     pypyVersion = pypyInstall.pypyVersionToSemantic(versions[2]); | ||||||
|  |   } else { | ||||||
|  |     pypyVersion = 'x'; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (!validateVersion(pythonVersion) || !validateVersion(pypyVersion)) { | ||||||
|  |     throw new Error( | ||||||
|  |       "Invalid 'version' property for PyPy. Both Python version and PyPy versions should satisfy SemVer notation. See README for examples and documentation." | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (!validatePythonVersionFormatForPyPy(pythonVersion)) { | ||||||
|  |     throw new Error( | ||||||
|  |       "Invalid format of Python version for PyPy. Python version should be specified in format 'x.y'. See README for examples and documentation." | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     pypyVersion: pypyVersion, | ||||||
|  |     pythonVersion: pythonVersion | ||||||
|  |   }; | ||||||
|  | } | ||||||
| @ -1,5 +1,6 @@ | |||||||
| import * as os from 'os'; | import * as os from 'os'; | ||||||
| import * as path from 'path'; | import * as path from 'path'; | ||||||
|  | import {IS_WINDOWS, IS_LINUX} from './utils'; | ||||||
| 
 | 
 | ||||||
| import * as semver from 'semver'; | import * as semver from 'semver'; | ||||||
| 
 | 
 | ||||||
| @ -8,9 +9,6 @@ import * as installer from './install-python'; | |||||||
| import * as core from '@actions/core'; | import * as core from '@actions/core'; | ||||||
| import * as tc from '@actions/tool-cache'; | import * as tc from '@actions/tool-cache'; | ||||||
| 
 | 
 | ||||||
| const IS_WINDOWS = process.platform === 'win32'; |  | ||||||
| const IS_LINUX = process.platform === 'linux'; |  | ||||||
| 
 |  | ||||||
| // Python has "scripts" or "bin" directories where command-line tools that come with packages are installed.
 | // Python has "scripts" or "bin" directories where command-line tools that come with packages are installed.
 | ||||||
| // This is where pip is, along with anything that pip installs.
 | // This is where pip is, along with anything that pip installs.
 | ||||||
| // There is a seperate directory for `pip install --user`.
 | // There is a seperate directory for `pip install --user`.
 | ||||||
|  | |||||||
							
								
								
									
										193
									
								
								src/install-pypy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								src/install-pypy.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,193 @@ | |||||||
|  | import * as path from 'path'; | ||||||
|  | import * as core from '@actions/core'; | ||||||
|  | import * as tc from '@actions/tool-cache'; | ||||||
|  | import * as semver from 'semver'; | ||||||
|  | import * as httpm from '@actions/http-client'; | ||||||
|  | import * as exec from '@actions/exec'; | ||||||
|  | import fs from 'fs'; | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   IS_WINDOWS, | ||||||
|  |   IPyPyManifestRelease, | ||||||
|  |   createSymlinkInFolder, | ||||||
|  |   isNightlyKeyword, | ||||||
|  |   writeExactPyPyVersionFile | ||||||
|  | } from './utils'; | ||||||
|  | 
 | ||||||
|  | export async function installPyPy( | ||||||
|  |   pypyVersion: string, | ||||||
|  |   pythonVersion: string, | ||||||
|  |   architecture: string | ||||||
|  | ) { | ||||||
|  |   let downloadDir; | ||||||
|  | 
 | ||||||
|  |   const releases = await getAvailablePyPyVersions(); | ||||||
|  |   if (!releases || releases.length === 0) { | ||||||
|  |     throw new Error('No release was found in PyPy version.json'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const releaseData = findRelease( | ||||||
|  |     releases, | ||||||
|  |     pythonVersion, | ||||||
|  |     pypyVersion, | ||||||
|  |     architecture | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   if (!releaseData || !releaseData.foundAsset) { | ||||||
|  |     throw new Error( | ||||||
|  |       `PyPy version ${pythonVersion} (${pypyVersion}) with arch ${architecture} not found` | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const {foundAsset, resolvedPythonVersion, resolvedPyPyVersion} = releaseData; | ||||||
|  |   let downloadUrl = `${foundAsset.download_url}`; | ||||||
|  | 
 | ||||||
|  |   core.info(`Downloading PyPy from "${downloadUrl}" ...`); | ||||||
|  |   const pypyPath = await tc.downloadTool(downloadUrl); | ||||||
|  | 
 | ||||||
|  |   core.info('Extracting downloaded archive...'); | ||||||
|  |   if (IS_WINDOWS) { | ||||||
|  |     downloadDir = await tc.extractZip(pypyPath); | ||||||
|  |   } else { | ||||||
|  |     downloadDir = await tc.extractTar(pypyPath, undefined, 'x'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // root folder in archive can have unpredictable name so just take the first folder
 | ||||||
|  |   // downloadDir is unique folder under TEMP and can't contain any other folders
 | ||||||
|  |   const archiveName = fs.readdirSync(downloadDir)[0]; | ||||||
|  | 
 | ||||||
|  |   const toolDir = path.join(downloadDir, archiveName); | ||||||
|  |   let installDir = toolDir; | ||||||
|  |   if (!isNightlyKeyword(resolvedPyPyVersion)) { | ||||||
|  |     installDir = await tc.cacheDir( | ||||||
|  |       toolDir, | ||||||
|  |       'PyPy', | ||||||
|  |       resolvedPythonVersion, | ||||||
|  |       architecture | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   writeExactPyPyVersionFile(installDir, resolvedPyPyVersion); | ||||||
|  | 
 | ||||||
|  |   const binaryPath = getPyPyBinaryPath(installDir); | ||||||
|  |   await createPyPySymlink(binaryPath, resolvedPythonVersion); | ||||||
|  |   await installPip(binaryPath); | ||||||
|  | 
 | ||||||
|  |   return {installDir, resolvedPythonVersion, resolvedPyPyVersion}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function getAvailablePyPyVersions() { | ||||||
|  |   const url = 'https://downloads.python.org/pypy/versions.json'; | ||||||
|  |   const http: httpm.HttpClient = new httpm.HttpClient('tool-cache'); | ||||||
|  | 
 | ||||||
|  |   const response = await http.getJson<IPyPyManifestRelease[]>(url); | ||||||
|  |   if (!response.result) { | ||||||
|  |     throw new Error( | ||||||
|  |       `Unable to retrieve the list of available PyPy versions from '${url}'` | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return response.result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function createPyPySymlink( | ||||||
|  |   pypyBinaryPath: string, | ||||||
|  |   pythonVersion: string | ||||||
|  | ) { | ||||||
|  |   const version = semver.coerce(pythonVersion)!; | ||||||
|  |   const pythonBinaryPostfix = semver.major(version); | ||||||
|  |   const pypyBinaryPostfix = pythonBinaryPostfix === 2 ? '' : '3'; | ||||||
|  |   let binaryExtension = IS_WINDOWS ? '.exe' : ''; | ||||||
|  | 
 | ||||||
|  |   core.info('Creating symlinks...'); | ||||||
|  |   createSymlinkInFolder( | ||||||
|  |     pypyBinaryPath, | ||||||
|  |     `pypy${pypyBinaryPostfix}${binaryExtension}`, | ||||||
|  |     `python${pythonBinaryPostfix}${binaryExtension}`, | ||||||
|  |     true | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   createSymlinkInFolder( | ||||||
|  |     pypyBinaryPath, | ||||||
|  |     `pypy${pypyBinaryPostfix}${binaryExtension}`, | ||||||
|  |     `python${binaryExtension}`, | ||||||
|  |     true | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function installPip(pythonLocation: string) { | ||||||
|  |   core.info('Installing and updating pip'); | ||||||
|  |   const pythonBinary = path.join(pythonLocation, 'python'); | ||||||
|  |   await exec.exec(`${pythonBinary} -m ensurepip`); | ||||||
|  | 
 | ||||||
|  |   await exec.exec( | ||||||
|  |     `${pythonLocation}/python -m pip install --ignore-installed pip` | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function findRelease( | ||||||
|  |   releases: IPyPyManifestRelease[], | ||||||
|  |   pythonVersion: string, | ||||||
|  |   pypyVersion: string, | ||||||
|  |   architecture: string | ||||||
|  | ) { | ||||||
|  |   const filterReleases = releases.filter(item => { | ||||||
|  |     const isPythonVersionSatisfied = semver.satisfies( | ||||||
|  |       semver.coerce(item.python_version)!, | ||||||
|  |       pythonVersion | ||||||
|  |     ); | ||||||
|  |     const isPyPyNightly = | ||||||
|  |       isNightlyKeyword(pypyVersion) && isNightlyKeyword(item.pypy_version); | ||||||
|  |     const isPyPyVersionSatisfied = | ||||||
|  |       isPyPyNightly || | ||||||
|  |       semver.satisfies(pypyVersionToSemantic(item.pypy_version), pypyVersion); | ||||||
|  |     const isArchPresent = | ||||||
|  |       item.files && | ||||||
|  |       item.files.some( | ||||||
|  |         file => file.arch === architecture && file.platform === process.platform | ||||||
|  |       ); | ||||||
|  |     return isPythonVersionSatisfied && isPyPyVersionSatisfied && isArchPresent; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   if (filterReleases.length === 0) { | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const sortedReleases = filterReleases.sort((previous, current) => { | ||||||
|  |     return ( | ||||||
|  |       semver.compare( | ||||||
|  |         semver.coerce(pypyVersionToSemantic(current.pypy_version))!, | ||||||
|  |         semver.coerce(pypyVersionToSemantic(previous.pypy_version))! | ||||||
|  |       ) || | ||||||
|  |       semver.compare( | ||||||
|  |         semver.coerce(current.python_version)!, | ||||||
|  |         semver.coerce(previous.python_version)! | ||||||
|  |       ) | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   const foundRelease = sortedReleases[0]; | ||||||
|  |   const foundAsset = foundRelease.files.find( | ||||||
|  |     item => item.arch === architecture && item.platform === process.platform | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     foundAsset, | ||||||
|  |     resolvedPythonVersion: foundRelease.python_version, | ||||||
|  |     resolvedPyPyVersion: foundRelease.pypy_version | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** Get PyPy binary location from the tool of installation directory | ||||||
|  |  *  - On Linux and macOS, the Python interpreter is in 'bin'. | ||||||
|  |  *  - On Windows, it is in the installation root. | ||||||
|  |  */ | ||||||
|  | export function getPyPyBinaryPath(installDir: string) { | ||||||
|  |   const _binDir = path.join(installDir, 'bin'); | ||||||
|  |   return IS_WINDOWS ? installDir : _binDir; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function pypyVersionToSemantic(versionSpec: string) { | ||||||
|  |   const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g; | ||||||
|  |   return versionSpec.replace(prereleaseVersion, '$1-$2.$3'); | ||||||
|  | } | ||||||
| @ -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 {stderr} from 'process'; | import {IS_WINDOWS, IS_LINUX} 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}`; | ||||||
| @ -12,9 +12,6 @@ const MANIFEST_REPO_NAME = 'python-versions'; | |||||||
| const MANIFEST_REPO_BRANCH = 'main'; | const MANIFEST_REPO_BRANCH = 'main'; | ||||||
| export const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`; | export const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`; | ||||||
| 
 | 
 | ||||||
| const IS_WINDOWS = process.platform === 'win32'; |  | ||||||
| const IS_LINUX = process.platform === 'linux'; |  | ||||||
| 
 |  | ||||||
| export async function findReleaseFromManifest( | export async function findReleaseFromManifest( | ||||||
|   semanticVersionSpec: string, |   semanticVersionSpec: string, | ||||||
|   architecture: string |   architecture: string | ||||||
|  | |||||||
| @ -1,15 +1,29 @@ | |||||||
| import * as core from '@actions/core'; | import * as core from '@actions/core'; | ||||||
| import * as finder from './find-python'; | import * as finder from './find-python'; | ||||||
|  | import * as finderPyPy from './find-pypy'; | ||||||
| import * as path from 'path'; | import * as path from 'path'; | ||||||
| import * as os from 'os'; | import * as os from 'os'; | ||||||
| 
 | 
 | ||||||
|  | function isPyPyVersion(versionSpec: string) { | ||||||
|  |   return versionSpec.startsWith('pypy-'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| async function run() { | async function run() { | ||||||
|   try { |   try { | ||||||
|     let version = core.getInput('python-version'); |     let version = core.getInput('python-version'); | ||||||
|     if (version) { |     if (version) { | ||||||
|       const arch: string = core.getInput('architecture') || os.arch(); |       const arch: string = core.getInput('architecture') || os.arch(); | ||||||
|       const installed = await finder.findPythonVersion(version, arch); |       if (isPyPyVersion(version)) { | ||||||
|       core.info(`Successfully setup ${installed.impl} (${installed.version})`); |         const installed = await finderPyPy.findPyPyVersion(version, arch); | ||||||
|  |         core.info( | ||||||
|  |           `Successfully setup PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})` | ||||||
|  |         ); | ||||||
|  |       } else { | ||||||
|  |         const installed = await finder.findPythonVersion(version, arch); | ||||||
|  |         core.info( | ||||||
|  |           `Successfully setup ${installed.impl} (${installed.version})` | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|     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')}`); | ||||||
|  | |||||||
							
								
								
									
										92
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | |||||||
|  | import fs from 'fs'; | ||||||
|  | import * as path from 'path'; | ||||||
|  | import * as semver from 'semver'; | ||||||
|  | 
 | ||||||
|  | export const IS_WINDOWS = process.platform === 'win32'; | ||||||
|  | export const IS_LINUX = process.platform === 'linux'; | ||||||
|  | const PYPY_VERSION_FILE = 'PYPY_VERSION'; | ||||||
|  | 
 | ||||||
|  | export interface IPyPyManifestAsset { | ||||||
|  |   filename: string; | ||||||
|  |   arch: string; | ||||||
|  |   platform: string; | ||||||
|  |   download_url: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface IPyPyManifestRelease { | ||||||
|  |   pypy_version: string; | ||||||
|  |   python_version: string; | ||||||
|  |   stable: boolean; | ||||||
|  |   latest_pypy: boolean; | ||||||
|  |   files: IPyPyManifestAsset[]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** create Symlinks for downloaded PyPy | ||||||
|  |  *  It should be executed only for downloaded versions in runtime, because | ||||||
|  |  *  toolcache versions have this setup. | ||||||
|  |  */ | ||||||
|  | export function createSymlinkInFolder( | ||||||
|  |   folderPath: string, | ||||||
|  |   sourceName: string, | ||||||
|  |   targetName: string, | ||||||
|  |   setExecutable = false | ||||||
|  | ) { | ||||||
|  |   const sourcePath = path.join(folderPath, sourceName); | ||||||
|  |   const targetPath = path.join(folderPath, targetName); | ||||||
|  |   if (fs.existsSync(targetPath)) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   fs.symlinkSync(sourcePath, targetPath); | ||||||
|  |   if (!IS_WINDOWS && setExecutable) { | ||||||
|  |     fs.chmodSync(targetPath, '755'); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function validateVersion(version: string) { | ||||||
|  |   return isNightlyKeyword(version) || Boolean(semver.validRange(version)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function isNightlyKeyword(pypyVersion: string) { | ||||||
|  |   return pypyVersion === 'nightly'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function getPyPyVersionFromPath(installDir: string) { | ||||||
|  |   return path.basename(path.dirname(installDir)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * In tool-cache, we put PyPy to '<toolcache_root>/PyPy/<python_version>/x64' | ||||||
|  |  * There is no easy way to determine what PyPy version is located in specific folder | ||||||
|  |  * 'pypy --version' is not reliable enough since it is not set properly for preview versions | ||||||
|  |  * "7.3.3rc1" is marked as '7.3.3' in 'pypy --version' | ||||||
|  |  * so we put PYPY_VERSION file to PyPy directory when install it to VM and read it when we need to know version | ||||||
|  |  * PYPY_VERSION contains exact version from 'versions.json' | ||||||
|  |  */ | ||||||
|  | export function readExactPyPyVersionFile(installDir: string) { | ||||||
|  |   let pypyVersion = ''; | ||||||
|  |   let fileVersion = path.join(installDir, PYPY_VERSION_FILE); | ||||||
|  |   if (fs.existsSync(fileVersion)) { | ||||||
|  |     pypyVersion = fs.readFileSync(fileVersion).toString(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return pypyVersion; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function writeExactPyPyVersionFile( | ||||||
|  |   installDir: string, | ||||||
|  |   resolvedPyPyVersion: string | ||||||
|  | ) { | ||||||
|  |   const pypyFilePath = path.join(installDir, PYPY_VERSION_FILE); | ||||||
|  |   fs.writeFileSync(pypyFilePath, resolvedPyPyVersion); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Python version should be specified explicitly like "x.y" (2.7, 3.6, 3.7) | ||||||
|  |  * "3.x" or "3" are not supported | ||||||
|  |  * because it could cause ambiguity when both PyPy version and Python version are not precise | ||||||
|  |  */ | ||||||
|  | export function validatePythonVersionFormatForPyPy(version: string) { | ||||||
|  |   const re = /^\d+\.\d+$/; | ||||||
|  |   return re.test(version); | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dmitry Shibanov
						Dmitry Shibanov