diff --git a/.github/workflows/basic-validation.yml b/.github/workflows/basic-validation.yml new file mode 100644 index 000000000..f69f9a1d5 --- /dev/null +++ b/.github/workflows/basic-validation.yml @@ -0,0 +1,15 @@ +name: Basic validation + +on: + push: + branches: + - main + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' +jobs: + call-basic-validation: + name: Basic validation + uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@main \ No newline at end of file diff --git a/.github/workflows/check-dist.yml b/.github/workflows/check-dist.yml index 6274fd288..1251c11d9 100644 --- a/.github/workflows/check-dist.yml +++ b/.github/workflows/check-dist.yml @@ -1,8 +1,3 @@ -# `dist/index.js` is a special file in Actions. -# When you reference an action with `uses:` in a workflow, -# `index.js` is the code that will run. -# For our project, we generate this file through a build process from other source files. -# We need to make sure the checked-in `index.js` actually matches what we expect it to be. name: Check dist/ on: @@ -17,36 +12,6 @@ on: workflow_dispatch: jobs: - check-dist: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set Node.js 16.x - uses: actions/setup-node@v3 - with: - node-version: 16.x - cache: npm - - - name: Install dependencies - run: npm ci - - - name: Rebuild the dist/ directory - run: npm run build - - - name: Compare the expected and actual dist/ directories - run: | - if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then - echo "Detected uncommitted changes after build. See status below:" - git diff - exit 1 - fi - id: diff - - # If index.js was different than expected, upload the expected version as an artifact - - uses: actions/upload-artifact@v3 - if: ${{ failure() && steps.diff.conclusion == 'failure' }} - with: - name: dist - path: dist/ + call-check-dist: + name: Check dist/ + uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3ea240d83..fd273502c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,47 +1,13 @@ -name: "Code scanning - action" +name: CodeQL analysis on: push: branches: [ 'main' ] pull_request: schedule: - - cron: '25 3 * * 5' + - cron: '0 3 * * 0' jobs: - CodeQL-Build: - - strategy: - fail-fast: false - - # CodeQL runs on ubuntu-latest and windows-latest - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - # Override language selection by uncommenting this and choosing your languages - # with: - # languages: go, javascript, csharp, python, cpp, java - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + call-codeQL-analysis: + name: CodeQL analysis + uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main \ No newline at end of file diff --git a/.github/workflows/workflow.yml b/.github/workflows/e2e-tests.yml similarity index 86% rename from .github/workflows/workflow.yml rename to .github/workflows/e2e-tests.yml index 3575845e5..21dead7ee 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/e2e-tests.yml @@ -1,4 +1,5 @@ -name: Main workflow +name: e2e tests + on: push: branches: @@ -8,9 +9,10 @@ on: pull_request: paths-ignore: - '**.md' + jobs: - run: - name: Run + test-setup-python: + name: Test setup-python runs-on: ${{ matrix.operating-system }} strategy: matrix: @@ -19,21 +21,6 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Set Node.js 16.x - uses: actions/setup-node@v3 - with: - node-version: 16.x - cache: npm - - - name: npm ci - run: npm ci - - - name: Lint - run: npm run format-check - - - name: npm test - run: npm test - - name: Run with setup-python 2.7 uses: ./ with: @@ -98,4 +85,4 @@ jobs: - name: Verify 3.10 run: python __tests__/verify-python.py 3.10 - name: Run python-path sample 3.10 - run: pipx run --python '${{ steps.cp310.outputs.python-path }}' nox --version + run: pipx run --python '${{ steps.cp310.outputs.python-path }}' nox --version \ No newline at end of file diff --git a/.github/workflows/licensed.yml b/.github/workflows/licensed.yml index 6f4cd9223..88d3eae61 100644 --- a/.github/workflows/licensed.yml +++ b/.github/workflows/licensed.yml @@ -9,21 +9,6 @@ on: - main jobs: - test: - runs-on: ubuntu-latest - name: Check licenses - steps: - - uses: actions/checkout@v3 - - name: Set Node.js 16.x - uses: actions/setup-node@v3 - with: - node-version: 16.x - cache: npm - - run: npm ci - - name: Install licensed - run: | - cd $RUNNER_TEMP - curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.4.4/licensed-3.4.4-linux-x64.tar.gz - sudo tar -xzf licensed.tar.gz - sudo mv licensed /usr/local/bin/licensed - - run: licensed status + call-licensed: + name: Licensed + uses: actions/reusable-workflows/.github/workflows/licensed.yml@main \ No newline at end of file diff --git a/.github/workflows/release-new-action-version.yml b/.github/workflows/release-new-action-version.yml index b8076d438..a28a50ce9 100644 --- a/.github/workflows/release-new-action-version.yml +++ b/.github/workflows/release-new-action-version.yml @@ -1,4 +1,5 @@ name: Release new action version + on: release: types: [released] @@ -24,4 +25,4 @@ jobs: uses: actions/publish-action@v0.2.1 with: source-tag: ${{ env.TAG_NAME }} - slack-webhook: ${{ secrets.SLACK_WEBHOOK }} + slack-webhook: ${{ secrets.SLACK_WEBHOOK }} \ No newline at end of file diff --git a/.github/workflows/test-pypy.yml b/.github/workflows/test-pypy.yml index de9ba6b79..5340c12f3 100644 --- a/.github/workflows/test-pypy.yml +++ b/.github/workflows/test-pypy.yml @@ -1,4 +1,5 @@ name: Validate PyPy e2e + on: push: branches: @@ -123,4 +124,46 @@ jobs: EXECUTABLE=${EXECUTABLE/-/} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe ${EXECUTABLE} --version + shell: bash + + setup-pypy-multiple-versions: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + steps: + - uses: actions/checkout@v3 + - name: Setup PyPy and check latest + uses: ./ + with: + python-version: | + pypy-3.7-v7.3.x + pypy3.8 + check-latest: true + - name: PyPy and Python version + run: python --version + + - name: Run simple code + run: python -c 'import math; print(math.factorial(5))' + + - name: Assert PyPy is running + run: | + import platform + assert platform.python_implementation().lower() == "pypy" + shell: python + + - name: Assert expected binaries (or symlinks) are present + run: | + EXECUTABLE="pypy-3.7-v7.3.x" + EXECUTABLE=${EXECUTABLE/-/} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name + EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe + ${EXECUTABLE} --version + shell: bash + - name: Assert expected binaries (or symlinks) are present + run: | + EXECUTABLE='pypy3.8' + EXECUTABLE=${EXECUTABLE/pypy-/pypy} # remove the first '-' in "pypy-X.Y" -> "pypyX.Y" to match executable name + EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe + ${EXECUTABLE} --version shell: bash \ No newline at end of file diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index 921449fb1..4f1ffd7f2 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -1,4 +1,5 @@ name: Validate Python e2e + on: push: branches: @@ -190,8 +191,35 @@ jobs: - name: Validate version run: | $pythonVersion = (python --version) - if ("$pythonVersion" -NotMatch "${{ matrix.python }}"){ - Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}" + if ("$pythonVersion" -NotMatch "${{ matrix.python-version }}"){ + Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python-version }}" + exit 1 + } + $pythonVersion + shell: pwsh + + setup-python-multiple-python-versions: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + steps: + - uses: actions/checkout@v3 + - name: Setup Python and check latest + uses: ./ + with: + python-version: | + 3.7 + 3.8 + 3.9 + 3.10 + check-latest: true + - name: Validate version + run: | + $pythonVersion = (python --version) + if ("$pythonVersion" -NotMatch "3.10"){ + Write-Host "The current version is $pythonVersion; expected version is 3.10" exit 1 } $pythonVersion diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index 04ec7c51f..30fc61cb4 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -42,14 +42,13 @@ describe('validateVersion', () => { describe('isCacheFeatureAvailable', () => { it('isCacheFeatureAvailable disabled on GHES', () => { jest.spyOn(cache, 'isFeatureAvailable').mockImplementation(() => false); + const infoMock = jest.spyOn(core, 'warning'); + const message = + 'Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.'; try { process.env['GITHUB_SERVER_URL'] = 'http://example.com'; - isCacheFeatureAvailable(); - } catch (error) { - expect(error).toHaveProperty( - 'message', - 'Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.' - ); + expect(isCacheFeatureAvailable()).toBeFalsy(); + expect(infoMock).toHaveBeenCalledWith(message); } finally { delete process.env['GITHUB_SERVER_URL']; } diff --git a/dist/setup/index.js b/dist/setup/index.js index 6cf4871db..7d1b84946 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -66867,31 +66867,31 @@ function cacheDependencies(cache, pythonVersion) { }); } function resolveVersionInput() { - let version = core.getInput('python-version'); + let versions = core.getMultilineInput('python-version'); let versionFile = core.getInput('python-version-file'); - if (version && versionFile) { + if (versions.length && versionFile) { core.warning('Both python-version and python-version-file inputs are specified, only python-version will be used.'); } - if (version) { - return version; + if (versions.length) { + return versions; } if (versionFile) { if (!fs_1.default.existsSync(versionFile)) { throw new Error(`The specified python version file at: ${versionFile} doesn't exist.`); } - version = fs_1.default.readFileSync(versionFile, 'utf8'); + const version = fs_1.default.readFileSync(versionFile, 'utf8'); core.info(`Resolved ${versionFile} as ${version}`); - return version; + return [version]; } utils_1.logWarning("Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find '.python-version' file."); versionFile = '.python-version'; if (fs_1.default.existsSync(versionFile)) { - version = fs_1.default.readFileSync(versionFile, 'utf8'); + const version = fs_1.default.readFileSync(versionFile, 'utf8'); core.info(`Resolved ${versionFile} as ${version}`); - return version; + return [version]; } utils_1.logWarning(`${versionFile} doesn't exist.`); - return version; + return versions; } function run() { var _a; @@ -66904,22 +66904,26 @@ function run() { } core.debug(`Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}`); try { - const version = resolveVersionInput(); + const versions = resolveVersionInput(); const checkLatest = core.getBooleanInput('check-latest'); - if (version) { - let pythonVersion; + if (versions.length) { + let pythonVersion = ''; const arch = core.getInput('architecture') || os.arch(); const updateEnvironment = core.getBooleanInput('update-environment'); - if (isPyPyVersion(version)) { - const installed = yield finderPyPy.findPyPyVersion(version, arch, updateEnvironment, checkLatest); - pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`; - core.info(`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`); - } - else { - const installed = yield finder.useCpythonVersion(version, arch, updateEnvironment, checkLatest); - pythonVersion = installed.version; - core.info(`Successfully set up ${installed.impl} (${pythonVersion})`); + core.startGroup('Installed versions'); + for (const version of versions) { + if (isPyPyVersion(version)) { + const installed = yield finderPyPy.findPyPyVersion(version, arch, updateEnvironment, checkLatest); + pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`; + core.info(`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`); + } + else { + const installed = yield finder.useCpythonVersion(version, arch, updateEnvironment, checkLatest); + pythonVersion = installed.version; + core.info(`Successfully set up ${installed.impl} (${pythonVersion})`); + } } + core.endGroup(); const cache = core.getInput('cache'); if (cache && utils_1.isCacheFeatureAvailable()) { yield cacheDependencies(cache, pythonVersion); @@ -67057,16 +67061,15 @@ function isGhes() { } exports.isGhes = isGhes; function isCacheFeatureAvailable() { - if (!cache.isFeatureAvailable()) { - if (isGhes()) { - throw new Error('Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.'); - } - else { - core.warning('The runner was not able to contact the cache service. Caching will be skipped'); - } + if (cache.isFeatureAvailable()) { + return true; + } + if (isGhes()) { + core.warning('Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.'); return false; } - return true; + core.warning('The runner was not able to contact the cache service. Caching will be skipped'); + return false; } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; function logWarning(message) { diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 643e73dd6..fa022d930 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -2,6 +2,7 @@ - [Using the python-version input](advanced-usage.md#using-the-python-version-input) - [Specifying a Python version](advanced-usage.md#specifying-a-python-version) - [Specifying a PyPy version](advanced-usage.md#specifying-a-pypy-version) + - [Specifying multiple Python and PyPy versions](advanced-usage.md#specifying-multiple-python/pypy-version) - [Matrix Testing](advanced-usage.md#matrix-testing) - [Using the python-version-file input](advanced-usage.md#using-the-python-version-file-input) - [Check latest version](advanced-usage.md#check-latest-version) @@ -132,6 +133,62 @@ jobs: ``` More details on PyPy syntax can be found in the [Available versions of PyPy](#pypy) section. +### Specifying multiple Python/PyPy version +The python-version input can get multiple python/pypy versions. The last specified version will be used as a default one. + +Download and set up multiple Python versions: + +```yaml +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.9 + 3.10 + - run: python my_script.py +``` + +Download and set up multiple PyPy versions: + +```yaml +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: | + pypy-3.7-v7.3.x + pypy3.9-nightly + pypy3.8 + - run: python my_script.py +``` + +Download and set up multiple Python/PyPy versions: + +```yaml +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.9 + pypy3.9-nightly + pypy3.8 + 3.10 + - run: python my_script.py +``` + ### Matrix Testing Using `setup-python` it's possible to use [matrix syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix) to install several versions of Python or PyPy: diff --git a/package.json b/package.json index 9f6c3d5d5..b8bfb1ec3 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/cache-save src/cache-save.ts", "format": "prettier --write \"{,!(node_modules)/**/}*.ts\"", "format-check": "prettier --check \"{,!(node_modules)/**/}*.ts\"", + "lint": "echo \"Fake command that does nothing. It is used in reusable workflows\"", "release": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/cache-save src/cache-save.ts && git add -f dist/", "test": "jest --coverage" }, diff --git a/src/setup-python.ts b/src/setup-python.ts index d6e6bdaf0..0089b4016 100644 --- a/src/setup-python.ts +++ b/src/setup-python.ts @@ -22,18 +22,18 @@ async function cacheDependencies(cache: string, pythonVersion: string) { await cacheDistributor.restoreCache(); } -function resolveVersionInput(): string { - let version = core.getInput('python-version'); +function resolveVersionInput() { + let versions = core.getMultilineInput('python-version'); let versionFile = core.getInput('python-version-file'); - if (version && versionFile) { + if (versions.length && versionFile) { core.warning( 'Both python-version and python-version-file inputs are specified, only python-version will be used.' ); } - if (version) { - return version; + if (versions.length) { + return versions; } if (versionFile) { @@ -42,9 +42,9 @@ function resolveVersionInput(): string { `The specified python version file at: ${versionFile} doesn't exist.` ); } - version = fs.readFileSync(versionFile, 'utf8'); + const version = fs.readFileSync(versionFile, 'utf8'); core.info(`Resolved ${versionFile} as ${version}`); - return version; + return [version]; } logWarning( @@ -52,14 +52,14 @@ function resolveVersionInput(): string { ); versionFile = '.python-version'; if (fs.existsSync(versionFile)) { - version = fs.readFileSync(versionFile, 'utf8'); + const version = fs.readFileSync(versionFile, 'utf8'); core.info(`Resolved ${versionFile} as ${version}`); - return version; + return [version]; } logWarning(`${versionFile} doesn't exist.`); - return version; + return versions; } async function run() { @@ -75,35 +75,38 @@ async function run() { `Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}` ); try { - const version = resolveVersionInput(); + const versions = resolveVersionInput(); const checkLatest = core.getBooleanInput('check-latest'); - if (version) { - let pythonVersion: string; + if (versions.length) { + let pythonVersion = ''; const arch: string = core.getInput('architecture') || os.arch(); const updateEnvironment = core.getBooleanInput('update-environment'); - if (isPyPyVersion(version)) { - const installed = await finderPyPy.findPyPyVersion( - version, - arch, - updateEnvironment, - checkLatest - ); - pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`; - core.info( - `Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})` - ); - } else { - const installed = await finder.useCpythonVersion( - version, - arch, - updateEnvironment, - checkLatest - ); - pythonVersion = installed.version; - core.info(`Successfully set up ${installed.impl} (${pythonVersion})`); + core.startGroup('Installed versions'); + for (const version of versions) { + if (isPyPyVersion(version)) { + const installed = await finderPyPy.findPyPyVersion( + version, + arch, + updateEnvironment, + checkLatest + ); + pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`; + core.info( + `Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})` + ); + } else { + const installed = await finder.useCpythonVersion( + version, + arch, + updateEnvironment, + checkLatest + ); + pythonVersion = installed.version; + core.info(`Successfully set up ${installed.impl} (${pythonVersion})`); + } } - + core.endGroup(); const cache = core.getInput('cache'); if (cache && isCacheFeatureAvailable()) { await cacheDependencies(cache, pythonVersion); diff --git a/src/utils.ts b/src/utils.ts index 37059cb66..fe32eda94 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -105,21 +105,21 @@ export function isGhes(): boolean { } export function isCacheFeatureAvailable(): boolean { - if (!cache.isFeatureAvailable()) { - if (isGhes()) { - throw new Error( - 'Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.' - ); - } else { - core.warning( - 'The runner was not able to contact the cache service. Caching will be skipped' - ); - } + if (cache.isFeatureAvailable()) { + return true; + } + if (isGhes()) { + core.warning( + 'Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.' + ); return false; } - return true; + core.warning( + 'The runner was not able to contact the cache service. Caching will be skipped' + ); + return false; } export function logWarning(message: string): void { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy