mirror of
				https://gitea.com/actions/setup-python.git
				synced 2025-10-31 07:47:08 +00:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | dc73133d4d | ||
|   | a1121449a2 | ||
|   | 66319ca9fa | ||
|   | 3105fb18c0 | ||
|   | 8c5ea631b2 | ||
|   | 2831efe49a | ||
|   | 3b3f2de1b1 | ||
|   | 723e46dad7 | ||
|   | 195f5c388b | ||
|   | 41b7212b16 | ||
|   | 878156f1de | 
							
								
								
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| * @actions/actions-service | ||||
							
								
								
									
										1
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							| @@ -2,6 +2,7 @@ name: "Code scanning - action" | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|   pull_request: | ||||
|   schedule: | ||||
|     - cron: '25 3 * * 5' | ||||
|  | ||||
|   | ||||
							
								
								
									
										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-latest] | ||||
|         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-2.7-v7.3.4rc1' | ||||
|         - '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:  | ||||
|   push: | ||||
|     branches: | ||||
| @@ -9,7 +9,7 @@ on: | ||||
|     paths-ignore: | ||||
|       - '**.md' | ||||
|   schedule: | ||||
|     - cron: 0 0 * * * | ||||
|     - cron: 30 3 * * * | ||||
| 
 | ||||
| jobs: | ||||
|   default-version: | ||||
| @@ -18,7 +18,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       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: | ||||
|     - name: Checkout | ||||
|       uses: actions/checkout@v2 | ||||
| @@ -38,7 +38,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       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] | ||||
|     steps: | ||||
|     - name: Checkout | ||||
| @@ -68,7 +68,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       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: | ||||
|     - name: Checkout | ||||
|       uses: actions/checkout@v2 | ||||
| @@ -90,3 +90,24 @@ jobs: | ||||
| 
 | ||||
|     - name: Run simple code | ||||
|       run: python -c 'import math; print(math.factorial(5))' | ||||
| 
 | ||||
|   setup-pypy-legacy: | ||||
|     name: Setup PyPy ${{ matrix.os }} | ||||
|     runs-on: ${{ matrix.os }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         os: [macos-latest, windows-latest, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04] | ||||
|     steps: | ||||
|     - name: Checkout | ||||
|       uses: actions/checkout@v2 | ||||
| 
 | ||||
|     - name: setup-python pypy3 | ||||
|       uses: ./ | ||||
|       with: | ||||
|         python-version: 'pypy3' | ||||
| 
 | ||||
|     - name: setup-python pypy2 | ||||
|       uses: ./ | ||||
|       with: | ||||
|         python-version: 'pypy2' | ||||
							
								
								
									
										99
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										99
									
								
								README.md
									
									
									
									
									
								
							| @@ -6,17 +6,18 @@ | ||||
|  | ||||
| This action sets up a Python environment for use in actions by: | ||||
|  | ||||
| - optionally installing and adding to PATH a version of Python that is already installed in the tools cache | ||||
| - downloading, installing and adding to PATH an available version of Python from GitHub Releases ([actions/python-versions](https://github.com/actions/python-versions/releases)) if a specific version is not available in the tools cache | ||||
| - failing if a specific version of Python is not preinstalled or available for download | ||||
| - registering problem matchers for error output | ||||
| - optionally installing and adding to PATH a version of Python that is already installed in the tools cache. | ||||
| - downloading, installing and adding to PATH an available version of Python from GitHub Releases ([actions/python-versions](https://github.com/actions/python-versions/releases)) if a specific version is not available in the tools cache. | ||||
| - failing if a specific version of Python is not preinstalled or available for download. | ||||
| - registering problem matchers for error output. | ||||
|  | ||||
| # What's new | ||||
|  | ||||
| - Ability to download, install and set up Python packages from `actions/python-versions` that do not come preinstalled on runners | ||||
|   - 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 | ||||
| - Support for pre-release versions of Python | ||||
| - Ability to download, install and set up Python packages from `actions/python-versions` that do not come preinstalled on runners. | ||||
|   - 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. | ||||
| - Support for pre-release versions of Python. | ||||
| - Support for installing any version of PyPy on-flight | ||||
|  | ||||
| # Usage | ||||
|  | ||||
| @@ -40,7 +41,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       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 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
| @@ -60,12 +61,12 @@ jobs: | ||||
|     strategy: | ||||
|       matrix: | ||||
|         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: | ||||
|           - os: macos-latest | ||||
|             python-version: 3.8 | ||||
|             python-version: '3.8' | ||||
|           - os: windows-latest | ||||
|             python-version: 3.6 | ||||
|             python-version: '3.6' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
|       - name: Set up Python | ||||
| @@ -84,14 +85,13 @@ jobs: | ||||
|     strategy: | ||||
|       matrix: | ||||
|         # in this example, there is a newer version already installed, 3.7.7, so the older version will be downloaded | ||||
|         python-version: [3.5, 3.6, 3.7.4, 3.8] | ||||
|         python-version: ['3.5', '3.6', '3.7.4', '3.8'] | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/setup-python@v2 | ||||
|       with: | ||||
|         python-version: ${{ matrix.python-version }} | ||||
|     - run: python my_script.py | ||||
|  | ||||
| ``` | ||||
|  | ||||
| Download and set up an accurate pre-release version of Python: | ||||
| @@ -114,6 +114,27 @@ steps: | ||||
| - 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 | ||||
|  | ||||
| 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). | ||||
| @@ -122,15 +143,29 @@ Check out our detailed guide on using [Python with GitHub Actions](https://help. | ||||
|  | ||||
| `setup-python` is able to configure Python from two sources: | ||||
|  | ||||
| - Preinstalled versions of Python in the tools cache on GitHub-hosted runners | ||||
| - Preinstalled versions of Python in the tools cache on GitHub-hosted runners. | ||||
|     - For detailed information regarding the available versions of Python that are installed see [Supported software](https://docs.github.com/en/actions/reference/specifications-for-github-hosted-runners#supported-software). | ||||
|     - For every minor version of Python, expect only the latest patch to be preinstalled. | ||||
|     - If `3.8.1` is installed for example, and `3.8.2` is released, expect `3.8.1` to be removed and replaced by `3.8.2` in the tools cache. | ||||
|     - If 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. | ||||
|     - 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 | ||||
|  | ||||
| GitHub hosted runners have a tools cache that comes with a few versions of Python + PyPy already installed. This tools cache helps speed up runs and tool setup by not requiring any new downloads. There is an environment variable called `RUNNER_TOOL_CACHE` on each runner that describes the location of this tools cache and there is where you will find Python and PyPy installed. `setup-python` works by taking a specific version of Python or PyPy in this tools cache and adding it to PATH. | ||||
| @@ -156,6 +191,20 @@ You should specify only a major and minor version if you are okay with the most | ||||
|   - 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. | ||||
|  | ||||
| # 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 | ||||
|  | ||||
| Python distributions are only available for the same [environments](https://github.com/actions/virtual-environments#available-environments) that GitHub Actions hosted environments are available for. If you are using an unsupported version of Ubuntu such as `19.04` or another Linux distribution such as Fedora, `setup-python` will not work. If you have a supported self-hosted runner and you would like to use `setup-python`, there are a few extra things you need to make sure are set up so that new versions of Python can be downloaded and configured on your runner. | ||||
| @@ -174,22 +223,22 @@ If you are experiencing problems while configuring Python on your self-hosted ru | ||||
|  | ||||
| - The Python packages that are downloaded from `actions/python-versions` are originally compiled from source in `/opt/hostedtoolcache/` with the [--enable-shared](https://github.com/actions/python-versions/blob/94f04ae6806c6633c82db94c6406a16e17decd5c/builders/ubuntu-python-builder.psm1#L35) flag, which makes them non-relocatable. | ||||
| - Create an environment variable called `AGENT_TOOLSDIRECTORY` and set it to `/opt/hostedtoolcache`. This controls where the runner downloads and installs tools. | ||||
|   - In the same shell that your runner is using, type `export AGENT_TOOLSDIRECTORY=/opt/hostedtoolcache` | ||||
|   - In the same shell that your runner is using, type `export AGENT_TOOLSDIRECTORY=/opt/hostedtoolcache`. | ||||
|   - A more permanent way of setting the environment variable is to create a `.env` file in the same directory as your runner and to add `AGENT_TOOLSDIRECTORY=/opt/hostedtoolcache`. This ensures the variable is always set if your runner is configured as a service. | ||||
| - Create a directory called `hostedtoolcache` inside `/opt`. | ||||
| - The user starting the runner must have write permission to the `/opt/hostedtoolcache` directory. It is not possible to start the Linux runner with `sudo` and the `/opt` directory usually requires root privileges to write to. Check the current user and group that the runner belongs to by typing `ls -l` inside the runners root directory. | ||||
| - The runner can be granted write access to the `/opt/hostedtoolcache` directory using a few techniques: | ||||
|   - The user starting the runner is the owner, and the owner has write permission | ||||
|   - The user starting the runner is in the owning group, and the owning group has write permission | ||||
|   - All users have write permission | ||||
| - One quick way to grant access is to change the user and group of `/opt/hostedtoolcache` to be the same as the runners using `chown` | ||||
|     - `sudo chown runner-user:runner-group opt/hostedtoolcache/` | ||||
|   - The user starting the runner is the owner, and the owner has write permission. | ||||
|   - The user starting the runner is in the owning group, and the owning group has write permission. | ||||
|   - All users have write permission. | ||||
| - One quick way to grant access is to change the user and group of `/opt/hostedtoolcache` to be the same as the runners using `chown`. | ||||
|     - `sudo chown runner-user:runner-group opt/hostedtoolcache/`. | ||||
| - If your runner is configured as a service and you run into problems, make sure the user that the service is running as is correct. For more information, you can [check the status of your self-hosted runner](https://help.github.com/en/actions/hosting-your-own-runners/configuring-the-self-hosted-runner-application-as-a-service#checking-the-status-of-the-service). | ||||
|  | ||||
| ### Mac | ||||
|  | ||||
| - The same setup that applies to `Linux` also applies to `Mac`, just with a different tools cache directory. | ||||
| - Create a directory called `/Users/runner/hostedtoolcache` | ||||
| - Create a directory called `/Users/runner/hostedtoolcache`. | ||||
| - Set the `AGENT_TOOLSDIRECTORY` environment variable to `/Users/runner/hostedtoolcache`. | ||||
| - Change the permissions of `/Users/runner/hostedtoolcache` so that the runner has write access. | ||||
|  | ||||
| @@ -200,8 +249,8 @@ If you are experiencing problems while configuring Python on your self-hosted ru | ||||
|  | ||||
| # License | ||||
|  | ||||
| The scripts and documentation in this project are released under the [MIT License](LICENSE) | ||||
| The scripts and documentation in this project are released under the [MIT License](LICENSE). | ||||
|  | ||||
| # Contributions | ||||
|  | ||||
| Contributions are welcome! See our [Contributor's Guide](docs/contributors.md) | ||||
| Contributions are welcome! See our [Contributor's Guide](docs/contributors.md). | ||||
|   | ||||
							
								
								
									
										533
									
								
								__tests__/data/pypy.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										533
									
								
								__tests__/data/pypy.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,533 @@ | ||||
| [ | ||||
|   { | ||||
|     "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.4rc1", | ||||
|     "python_version": "2.7.18", | ||||
|     "stable": false, | ||||
|     "latest_pypy": false, | ||||
|     "date": "2021-03-19", | ||||
|     "files": [ | ||||
|       { | ||||
|         "filename": "pypy2.7-v7.3.4rc1-aarch64.tar.bz2", | ||||
|         "arch": "aarch64", | ||||
|         "platform": "linux", | ||||
|         "download_url": "https://test.downloads.python.org/pypy/pypy2.7-v7.3.4rc1-aarch64.tar.bz2" | ||||
|       }, | ||||
|       { | ||||
|         "filename": "pypy2.7-v7.3.4rc1-linux32.tar.bz2", | ||||
|         "arch": "i686", | ||||
|         "platform": "linux", | ||||
|         "download_url": "https://test.downloads.python.org/pypy/pypy2.7-v7.3.4rc1-linux32.tar.bz2" | ||||
|       }, | ||||
|       { | ||||
|         "filename": "pypy2.7-v7.3.4rc1-linux64.tar.bz2", | ||||
|         "arch": "x64", | ||||
|         "platform": "linux", | ||||
|         "download_url": "https://test.downloads.python.org/pypy/pypy2.7-v7.3.4rc1-linux64.tar.bz2" | ||||
|       }, | ||||
|       { | ||||
|         "filename": "pypy2.7-v7.3.4rc1-osx64.tar.bz2", | ||||
|         "arch": "x64", | ||||
|         "platform": "darwin", | ||||
|         "download_url": "https://test.downloads.python.org/pypy/pypy2.7-v7.3.4rc1-osx64.tar.bz2" | ||||
|       }, | ||||
|       { | ||||
|         "filename": "pypy2.7-v7.3.4rc1-win64.zip", | ||||
|         "arch": "x64", | ||||
|         "platform": "win64", | ||||
|         "download_url": "https://test.downloads.python.org/pypy/pypy2.7-v7.3.4rc1-win64.zip" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "pypy_version": "7.3.3rc2", | ||||
|     "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); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										414
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										414
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							| @@ -1072,6 +1072,117 @@ function _readLinuxVersionFile() { | ||||
| exports._readLinuxVersionFile = _readLinuxVersionFile; | ||||
| //# 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); | ||||
|         ({ 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 = utils_1.IS_WINDOWS | ||||
|         ? findPyPyInstallDirForWindows(pythonVersion) | ||||
|         : 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; | ||||
| function findPyPyInstallDirForWindows(pythonVersion) { | ||||
|     let installDir = ''; | ||||
|     utils_1.WINDOWS_ARCHS.forEach(architecture => (installDir = installDir || tc.find('PyPy', pythonVersion, architecture))); | ||||
|     return installDir; | ||||
| } | ||||
| exports.findPyPyInstallDirForWindows = findPyPyInstallDirForWindows; | ||||
|  | ||||
|  | ||||
| /***/ }), | ||||
|  | ||||
| /***/ 65: | ||||
| @@ -2197,6 +2308,94 @@ if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) { | ||||
| 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'; | ||||
| exports.WINDOWS_ARCHS = ['x86', 'x64']; | ||||
| exports.WINDOWS_PLATFORMS = ['win32', 'win64']; | ||||
| 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: | ||||
| @@ -2443,17 +2642,27 @@ var __importStar = (this && this.__importStar) || function (mod) { | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| const core = __importStar(__webpack_require__(470)); | ||||
| const finder = __importStar(__webpack_require__(927)); | ||||
| const finderPyPy = __importStar(__webpack_require__(50)); | ||||
| const path = __importStar(__webpack_require__(622)); | ||||
| const os = __importStar(__webpack_require__(87)); | ||||
| function isPyPyVersion(versionSpec) { | ||||
|     return versionSpec.startsWith('pypy-'); | ||||
| } | ||||
| function run() { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         try { | ||||
|             let version = core.getInput('python-version'); | ||||
|             if (version) { | ||||
|                 const arch = core.getInput('architecture') || os.arch(); | ||||
|                 if (isPyPyVersion(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'); | ||||
|             core.info(`##[add-matcher]${path.join(matchersPath, 'python.json')}`); | ||||
|         } | ||||
| @@ -2580,6 +2789,173 @@ module.exports = ltr | ||||
|  | ||||
| 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 && | ||||
|             (utils_1.IS_WINDOWS | ||||
|                 ? isArchPresentForWindows(item) | ||||
|                 : isArchPresentForMacOrLinux(item, architecture, 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 = utils_1.IS_WINDOWS | ||||
|         ? findAssetForWindows(foundRelease) | ||||
|         : findAssetForMacOrLinux(foundRelease, architecture, 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; | ||||
| function isArchPresentForWindows(item) { | ||||
|     return item.files.some((file) => utils_1.WINDOWS_ARCHS.includes(file.arch) && | ||||
|         utils_1.WINDOWS_PLATFORMS.includes(file.platform)); | ||||
| } | ||||
| exports.isArchPresentForWindows = isArchPresentForWindows; | ||||
| function isArchPresentForMacOrLinux(item, architecture, platform) { | ||||
|     return item.files.some((file) => file.arch === architecture && file.platform === platform); | ||||
| } | ||||
| exports.isArchPresentForMacOrLinux = isArchPresentForMacOrLinux; | ||||
| function findAssetForWindows(releases) { | ||||
|     return releases.files.find((item) => utils_1.WINDOWS_ARCHS.includes(item.arch) && | ||||
|         utils_1.WINDOWS_PLATFORMS.includes(item.platform)); | ||||
| } | ||||
| exports.findAssetForWindows = findAssetForWindows; | ||||
| function findAssetForMacOrLinux(releases, architecture, platform) { | ||||
|     return releases.files.find((item) => item.arch === architecture && item.platform === platform); | ||||
| } | ||||
| exports.findAssetForMacOrLinux = findAssetForMacOrLinux; | ||||
|  | ||||
|  | ||||
| /***/ }), | ||||
|  | ||||
| /***/ 413: | ||||
| @@ -6422,16 +6798,17 @@ var __importStar = (this && this.__importStar) || function (mod) { | ||||
|     return result; | ||||
| }; | ||||
| 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 exec = __importStar(__webpack_require__(986)); | ||||
| const utils_1 = __webpack_require__(163); | ||||
| const TOKEN = core.getInput('token'); | ||||
| const AUTH = !TOKEN || isGhes() ? undefined : `token ${TOKEN}`; | ||||
| const MANIFEST_REPO_OWNER = 'actions'; | ||||
| const MANIFEST_REPO_NAME = 'python-versions'; | ||||
| const MANIFEST_REPO_BRANCH = 'main'; | ||||
| 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'; | ||||
| function findReleaseFromManifest(semanticVersionSpec, architecture) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         const manifest = yield tc.getManifestFromRepo(MANIFEST_REPO_OWNER, MANIFEST_REPO_NAME, AUTH, MANIFEST_REPO_BRANCH); | ||||
| @@ -6443,6 +6820,7 @@ function installPython(workingDirectory) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         const options = { | ||||
|             cwd: workingDirectory, | ||||
|             env: Object.assign(Object.assign({}, process.env), (utils_1.IS_LINUX && { LD_LIBRARY_PATH: path.join(workingDirectory, 'lib') })), | ||||
|             silent: true, | ||||
|             listeners: { | ||||
|                 stdout: (data) => { | ||||
| @@ -6453,7 +6831,7 @@ function installPython(workingDirectory) { | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|         if (IS_WINDOWS) { | ||||
|         if (utils_1.IS_WINDOWS) { | ||||
|             yield exec.exec('powershell', ['./setup.ps1'], options); | ||||
|         } | ||||
|         else { | ||||
| @@ -6468,7 +6846,7 @@ function installCpythonFromRelease(release) { | ||||
|         const pythonPath = yield tc.downloadTool(downloadUrl, undefined, AUTH); | ||||
|         core.info('Extract downloaded archive'); | ||||
|         let pythonExtractedFolder; | ||||
|         if (IS_WINDOWS) { | ||||
|         if (utils_1.IS_WINDOWS) { | ||||
|             pythonExtractedFolder = yield tc.extractZip(pythonPath); | ||||
|         } | ||||
|         else { | ||||
| @@ -6683,11 +7061,11 @@ var __importStar = (this && this.__importStar) || function (mod) { | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| const os = __importStar(__webpack_require__(87)); | ||||
| const path = __importStar(__webpack_require__(622)); | ||||
| const utils_1 = __webpack_require__(163); | ||||
| const semver = __importStar(__webpack_require__(876)); | ||||
| const installer = __importStar(__webpack_require__(824)); | ||||
| const core = __importStar(__webpack_require__(470)); | ||||
| const tc = __importStar(__webpack_require__(533)); | ||||
| const IS_WINDOWS = process.platform === 'win32'; | ||||
| // 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. | ||||
| // There is a seperate directory for `pip install --user`. | ||||
| @@ -6701,7 +7079,7 @@ const IS_WINDOWS = process.platform === 'win32'; | ||||
| //      (--user) %APPDATA%\Python\PythonXY\Scripts | ||||
| // See https://docs.python.org/3/library/sysconfig.html | ||||
| function binDir(installDir) { | ||||
|     if (IS_WINDOWS) { | ||||
|     if (utils_1.IS_WINDOWS) { | ||||
|         return path.join(installDir, 'Scripts'); | ||||
|     } | ||||
|     else { | ||||
| @@ -6714,9 +7092,9 @@ function binDir(installDir) { | ||||
| // For example, PyPy 7.0 contains Python 2.7, 3.5, and 3.6-alpha. | ||||
| // We only care about the Python version, so we don't use the PyPy version for the tool cache. | ||||
| function usePyPy(majorVersion, architecture) { | ||||
|     const findPyPy = tc.find.bind(undefined, 'PyPy', majorVersion.toString()); | ||||
|     const findPyPy = tc.find.bind(undefined, 'PyPy', majorVersion); | ||||
|     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. | ||||
|         // On our Windows virtual environments, we only install an x86 version. | ||||
|         // Fall back to x86. | ||||
| @@ -6730,10 +7108,14 @@ function usePyPy(majorVersion, architecture) { | ||||
|     const _binDir = path.join(installDir, 'bin'); | ||||
|     // On Linux and macOS, the Python interpreter is in 'bin'. | ||||
|     // On Windows, it is in the installation root. | ||||
|     const pythonLocation = IS_WINDOWS ? installDir : _binDir; | ||||
|     const pythonLocation = utils_1.IS_WINDOWS ? installDir : _binDir; | ||||
|     core.exportVariable('pythonLocation', pythonLocation); | ||||
|     core.addPath(installDir); | ||||
|     core.addPath(_binDir); | ||||
|     // Starting from PyPy 7.3.1, the folder that is used for pip and anything that pip installs should be "Scripts" on Windows. | ||||
|     if (utils_1.IS_WINDOWS) { | ||||
|         core.addPath(path.join(installDir, 'Scripts')); | ||||
|     } | ||||
|     const impl = 'pypy' + majorVersion.toString(); | ||||
|     core.setOutput('python-version', impl); | ||||
|     return { impl: impl, version: versionFromPath(installDir) }; | ||||
| @@ -6760,9 +7142,18 @@ function useCpythonVersion(version, architecture) { | ||||
|             ].join(os.EOL)); | ||||
|         } | ||||
|         core.exportVariable('pythonLocation', installDir); | ||||
|         if (utils_1.IS_LINUX) { | ||||
|             const libPath = process.env.LD_LIBRARY_PATH | ||||
|                 ? `:${process.env.LD_LIBRARY_PATH}` | ||||
|                 : ''; | ||||
|             const pyLibPath = path.join(installDir, 'lib'); | ||||
|             if (!libPath.split(':').includes(pyLibPath)) { | ||||
|                 core.exportVariable('LD_LIBRARY_PATH', pyLibPath + libPath); | ||||
|             } | ||||
|         } | ||||
|         core.addPath(installDir); | ||||
|         core.addPath(binDir(installDir)); | ||||
|         if (IS_WINDOWS) { | ||||
|         if (utils_1.IS_WINDOWS) { | ||||
|             // Add --user directory | ||||
|             // `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` | ||||
| @@ -6808,9 +7199,10 @@ function findPythonVersion(version, architecture) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         switch (version.toUpperCase()) { | ||||
|             case 'PYPY2': | ||||
|                 return usePyPy(2, architecture); | ||||
|                 return usePyPy('2', architecture); | ||||
|             case 'PYPY3': | ||||
|                 return usePyPy(3, architecture); | ||||
|                 // keep pypy3 pointing to 3.6 for backward compatibility | ||||
|                 return usePyPy('3.6', architecture); | ||||
|             default: | ||||
|                 return yield useCpythonVersion(version, architecture); | ||||
|         } | ||||
|   | ||||
| @@ -13,12 +13,14 @@ In order to avoid uploading `node_modules/` to the repository, we use [vercel/nc | ||||
| ### Developing | ||||
|  | ||||
| If you're developing locally, you can run | ||||
| ``` | ||||
|  | ||||
| ```sh | ||||
| npm install | ||||
| tsc | ||||
| ncc build src/setup-python.ts | ||||
| ``` | ||||
| Any files generated using `tsc` will be added to `lib/`, however those files also are not uploaded to the repository and are exluded using `.gitignore`. | ||||
|  | ||||
| Any files generated using `tsc` will be added to `lib/`, however those files also are not uploaded to the repository and are excluded using `.gitignore`. | ||||
|  | ||||
| During the commit step, Husky will take care of formatting all files with [Prettier](https://github.com/prettier/prettier) (to run manually, use `npm run format`). | ||||
|  | ||||
|   | ||||
							
								
								
									
										140
									
								
								src/find-pypy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/find-pypy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| import * as path from 'path'; | ||||
| import * as pypyInstall from './install-pypy'; | ||||
| import { | ||||
|   IS_WINDOWS, | ||||
|   WINDOWS_ARCHS, | ||||
|   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); | ||||
|  | ||||
|   ({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 = IS_WINDOWS | ||||
|     ? findPyPyInstallDirForWindows(pythonVersion) | ||||
|     : 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 | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export function findPyPyInstallDirForWindows(pythonVersion: string): string { | ||||
|   let installDir = ''; | ||||
|  | ||||
|   WINDOWS_ARCHS.forEach( | ||||
|     architecture => | ||||
|       (installDir = installDir || tc.find('PyPy', pythonVersion, architecture)) | ||||
|   ); | ||||
|  | ||||
|   return installDir; | ||||
| } | ||||
| @@ -1,5 +1,6 @@ | ||||
| import * as os from 'os'; | ||||
| import * as path from 'path'; | ||||
| import {IS_WINDOWS, IS_LINUX} from './utils'; | ||||
|  | ||||
| import * as semver from 'semver'; | ||||
|  | ||||
| @@ -8,8 +9,6 @@ import * as installer from './install-python'; | ||||
| import * as core from '@actions/core'; | ||||
| import * as tc from '@actions/tool-cache'; | ||||
|  | ||||
| const IS_WINDOWS = process.platform === 'win32'; | ||||
|  | ||||
| // 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. | ||||
| // There is a seperate directory for `pip install --user`. | ||||
| @@ -36,8 +35,11 @@ function binDir(installDir: string): string { | ||||
| // A particular version of PyPy may contain one or more versions of the Python interpreter. | ||||
| // For example, PyPy 7.0 contains Python 2.7, 3.5, and 3.6-alpha. | ||||
| // We only care about the Python version, so we don't use the PyPy version for the tool cache. | ||||
| function usePyPy(majorVersion: 2 | 3, architecture: string): InstalledVersion { | ||||
|   const findPyPy = tc.find.bind(undefined, 'PyPy', majorVersion.toString()); | ||||
| function usePyPy( | ||||
|   majorVersion: '2' | '3.6', | ||||
|   architecture: string | ||||
| ): InstalledVersion { | ||||
|   const findPyPy = tc.find.bind(undefined, 'PyPy', majorVersion); | ||||
|   let installDir: string | null = findPyPy(architecture); | ||||
|  | ||||
|   if (!installDir && IS_WINDOWS) { | ||||
| @@ -62,6 +64,10 @@ function usePyPy(majorVersion: 2 | 3, architecture: string): InstalledVersion { | ||||
|  | ||||
|   core.addPath(installDir); | ||||
|   core.addPath(_binDir); | ||||
|   // Starting from PyPy 7.3.1, the folder that is used for pip and anything that pip installs should be "Scripts" on Windows. | ||||
|   if (IS_WINDOWS) { | ||||
|     core.addPath(path.join(installDir, 'Scripts')); | ||||
|   } | ||||
|  | ||||
|   const impl = 'pypy' + majorVersion.toString(); | ||||
|   core.setOutput('python-version', impl); | ||||
| @@ -109,6 +115,18 @@ async function useCpythonVersion( | ||||
|   } | ||||
|  | ||||
|   core.exportVariable('pythonLocation', installDir); | ||||
|  | ||||
|   if (IS_LINUX) { | ||||
|     const libPath = process.env.LD_LIBRARY_PATH | ||||
|       ? `:${process.env.LD_LIBRARY_PATH}` | ||||
|       : ''; | ||||
|     const pyLibPath = path.join(installDir, 'lib'); | ||||
|  | ||||
|     if (!libPath.split(':').includes(pyLibPath)) { | ||||
|       core.exportVariable('LD_LIBRARY_PATH', pyLibPath + libPath); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   core.addPath(installDir); | ||||
|   core.addPath(binDir(installDir)); | ||||
|  | ||||
| @@ -175,9 +193,10 @@ export async function findPythonVersion( | ||||
| ): Promise<InstalledVersion> { | ||||
|   switch (version.toUpperCase()) { | ||||
|     case 'PYPY2': | ||||
|       return usePyPy(2, architecture); | ||||
|       return usePyPy('2', architecture); | ||||
|     case 'PYPY3': | ||||
|       return usePyPy(3, architecture); | ||||
|       // keep pypy3 pointing to 3.6 for backward compatibility | ||||
|       return usePyPy('3.6', architecture); | ||||
|     default: | ||||
|       return await useCpythonVersion(version, architecture); | ||||
|   } | ||||
|   | ||||
							
								
								
									
										231
									
								
								src/install-pypy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								src/install-pypy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,231 @@ | ||||
| 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, | ||||
|   WINDOWS_ARCHS, | ||||
|   WINDOWS_PLATFORMS, | ||||
|   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 && | ||||
|       (IS_WINDOWS | ||||
|         ? isArchPresentForWindows(item) | ||||
|         : isArchPresentForMacOrLinux(item, architecture, 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 = IS_WINDOWS | ||||
|     ? findAssetForWindows(foundRelease) | ||||
|     : findAssetForMacOrLinux(foundRelease, architecture, 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'); | ||||
| } | ||||
|  | ||||
| export function isArchPresentForWindows(item: any) { | ||||
|   return item.files.some( | ||||
|     (file: any) => | ||||
|       WINDOWS_ARCHS.includes(file.arch) && | ||||
|       WINDOWS_PLATFORMS.includes(file.platform) | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function isArchPresentForMacOrLinux( | ||||
|   item: any, | ||||
|   architecture: string, | ||||
|   platform: string | ||||
| ) { | ||||
|   return item.files.some( | ||||
|     (file: any) => file.arch === architecture && file.platform === platform | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function findAssetForWindows(releases: any) { | ||||
|   return releases.files.find( | ||||
|     (item: any) => | ||||
|       WINDOWS_ARCHS.includes(item.arch) && | ||||
|       WINDOWS_PLATFORMS.includes(item.platform) | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function findAssetForMacOrLinux( | ||||
|   releases: any, | ||||
|   architecture: string, | ||||
|   platform: string | ||||
| ) { | ||||
|   return releases.files.find( | ||||
|     (item: any) => item.arch === architecture && item.platform === platform | ||||
|   ); | ||||
| } | ||||
| @@ -3,7 +3,7 @@ import * as core from '@actions/core'; | ||||
| import * as tc from '@actions/tool-cache'; | ||||
| import * as exec from '@actions/exec'; | ||||
| import {ExecOptions} from '@actions/exec/lib/interfaces'; | ||||
| import {stderr} from 'process'; | ||||
| import {IS_WINDOWS, IS_LINUX} from './utils'; | ||||
|  | ||||
| const TOKEN = core.getInput('token'); | ||||
| const AUTH = !TOKEN || isGhes() ? undefined : `token ${TOKEN}`; | ||||
| @@ -12,8 +12,6 @@ const MANIFEST_REPO_NAME = 'python-versions'; | ||||
| 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`; | ||||
|  | ||||
| const IS_WINDOWS = process.platform === 'win32'; | ||||
|  | ||||
| export async function findReleaseFromManifest( | ||||
|   semanticVersionSpec: string, | ||||
|   architecture: string | ||||
| @@ -35,6 +33,10 @@ export async function findReleaseFromManifest( | ||||
| async function installPython(workingDirectory: string) { | ||||
|   const options: ExecOptions = { | ||||
|     cwd: workingDirectory, | ||||
|     env: { | ||||
|       ...process.env, | ||||
|       ...(IS_LINUX && {LD_LIBRARY_PATH: path.join(workingDirectory, 'lib')}) | ||||
|     }, | ||||
|     silent: true, | ||||
|     listeners: { | ||||
|       stdout: (data: Buffer) => { | ||||
|   | ||||
| @@ -1,15 +1,29 @@ | ||||
| import * as core from '@actions/core'; | ||||
| import * as finder from './find-python'; | ||||
| import * as finderPyPy from './find-pypy'; | ||||
| import * as path from 'path'; | ||||
| import * as os from 'os'; | ||||
|  | ||||
| function isPyPyVersion(versionSpec: string) { | ||||
|   return versionSpec.startsWith('pypy-'); | ||||
| } | ||||
|  | ||||
| async function run() { | ||||
|   try { | ||||
|     let version = core.getInput('python-version'); | ||||
|     if (version) { | ||||
|       const arch: string = core.getInput('architecture') || os.arch(); | ||||
|       if (isPyPyVersion(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})`); | ||||
|         core.info( | ||||
|           `Successfully setup ${installed.impl} (${installed.version})` | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
|     const matchersPath = path.join(__dirname, '..', '.github'); | ||||
|     core.info(`##[add-matcher]${path.join(matchersPath, 'python.json')}`); | ||||
|   | ||||
							
								
								
									
										94
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/utils.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| 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'; | ||||
| export const WINDOWS_ARCHS = ['x86', 'x64']; | ||||
| export const WINDOWS_PLATFORMS = ['win32', 'win64']; | ||||
| 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); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user