From 9bfc313c8c6d5f2849ba753897e947e59d167234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cgowridurgad=E2=80=9D?= <“hgowridurgad@github.com> Date: Wed, 10 Sep 2025 11:45:11 +0530 Subject: [PATCH 1/3] Add pip-install input --- .github/workflows/e2e-cache-freethreaded.yml | 53 ++++++++++++++++++++ .github/workflows/e2e-cache.yml | 53 ++++++++++++++++++++ README.md | 1 + action.yml | 2 + dist/setup/index.js | 19 +++++++ docs/advanced-usage.md | 18 +++++++ src/setup-python.ts | 21 ++++++++ 7 files changed, 167 insertions(+) diff --git a/.github/workflows/e2e-cache-freethreaded.yml b/.github/workflows/e2e-cache-freethreaded.yml index 97cc44bcf..60c6f42a2 100644 --- a/.github/workflows/e2e-cache-freethreaded.yml +++ b/.github/workflows/e2e-cache-freethreaded.yml @@ -219,3 +219,56 @@ jobs: pip-version: '25.0.1' - name: Install dependencies run: pip install numpy pandas requests + + python-pip-dependencies-caching-with-pip-install: + name: Test pip (Python ${{ matrix.python-version}}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + [ + ubuntu-latest, + ubuntu-22.04, + ubuntu-24.04-arm, + ubuntu-22.04-arm, + windows-latest, + macos-latest, + macos-13 + ] + python-version: [3.13.0t, 3.13.1t, 3.13.2t] + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: ./ + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + pip-install: numpy pandas requests + + python-pip-dependencies-caching-path-with-pip-install: + name: Test pip (Python ${{ matrix.python-version}}, ${{ matrix.os }}, caching path) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + [ + ubuntu-latest, + ubuntu-22.04, + ubuntu-24.04-arm, + ubuntu-22.04-arm, + windows-latest, + macos-latest, + macos-13 + ] + python-version: [3.13.0t, 3.13.1t, 3.13.2t] + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: ./ + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + cache-dependency-path: __tests__/data/requirements.txt + pip-install: numpy pandas requests diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml index 1e5dec8c1..592350b47 100644 --- a/.github/workflows/e2e-cache.yml +++ b/.github/workflows/e2e-cache.yml @@ -306,3 +306,56 @@ jobs: pip-version: '25.0.1' - name: Install dependencies run: pip install numpy pandas requests + + python-pip-dependencies-caching-with-pip-install: + name: Test pip (Python ${{ matrix.python-version}}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + [ + ubuntu-latest, + ubuntu-24.04-arm, + ubuntu-22.04, + ubuntu-22.04-arm, + windows-latest, + macos-latest, + macos-13 + ] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: ./ + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + pip-install: numpy pandas requests + + python-pip-dependencies-caching-path-with-pip-install: + name: Test pip (Python ${{ matrix.python-version}}, ${{ matrix.os }}, caching path) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + [ + ubuntu-latest, + ubuntu-24.04-arm, + ubuntu-22.04, + ubuntu-22.04-arm, + windows-latest, + macos-latest, + macos-13 + ] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: ./ + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + cache-dependency-path: __tests__/data/requirements.txt + pip-install: numpy pandas requests diff --git a/README.md b/README.md index 173c09768..9b7e2bf5a 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ See examples of using `cache` and `cache-dependency-path` for `pipenv` and `poet - [Using `setup-python` on GHES](docs/advanced-usage.md#using-setup-python-on-ghes) - [Allow pre-releases](docs/advanced-usage.md#allow-pre-releases) - [Using the pip-version input](docs/advanced-usage.md#using-the-pip-version-input) +- [Using the pip-install input](docs/advanced-usage.md#using-the-pip-install-input) ## Recommended permissions diff --git a/action.yml b/action.yml index df6c8235b..3b98d13a3 100644 --- a/action.yml +++ b/action.yml @@ -31,6 +31,8 @@ inputs: default: false pip-version: description: "Used to specify the version of pip to install with the Python. Supported format: major[.minor][.patch]." + pip-install: + description: "Used to specify the packages to install with pip after setting up Python. Can be a requirements file or package names." outputs: python-version: description: "The installed Python or PyPy version. Useful when given a version range as input." diff --git a/dist/setup/index.js b/dist/setup/index.js index ba888f22f..cfbfdd6ba 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -97944,12 +97944,30 @@ const os = __importStar(__nccwpck_require__(857)); const fs_1 = __importDefault(__nccwpck_require__(9896)); const cache_factory_1 = __nccwpck_require__(665); const utils_1 = __nccwpck_require__(1798); +const exec_1 = __nccwpck_require__(5236); function isPyPyVersion(versionSpec) { return versionSpec.startsWith('pypy'); } function isGraalPyVersion(versionSpec) { return versionSpec.startsWith('graalpy'); } +function installPipPackages() { + return __awaiter(this, void 0, void 0, function* () { + const pipInstall = core.getInput('pip-install'); + if (!pipInstall) { + return; + } + core.info(`Installing pip packages: ${pipInstall}`); + try { + const installArgs = pipInstall.trim().split(/\s+/); + yield (0, exec_1.exec)('python', ['-m', 'pip', 'install', ...installArgs]); + core.info('Successfully installed pip packages'); + } + catch (error) { + core.setFailed(`Failed to install pip packages from "${pipInstall}". Please verify that the package names and versions in the requirements file are correct, that the specified packages and versions can be resolved from PyPI or the configured package index, and that your network connection is stable and allows access to the package index.`); + } + }); +} function cacheDependencies(cache, pythonVersion) { return __awaiter(this, void 0, void 0, function* () { const cacheDependencyPath = core.getInput('cache-dependency-path') || undefined; @@ -98038,6 +98056,7 @@ function run() { if (cache && (0, utils_1.isCacheFeatureAvailable)()) { yield cacheDependencies(cache, pythonVersion); } + yield installPipPackages(); } else { core.warning('The `python-version` input is not set. The version of Python currently in `PATH` will be used.'); diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 55b41d54f..a412622f7 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -23,6 +23,7 @@ - [Using `setup-python` on GHES](advanced-usage.md#using-setup-python-on-ghes) - [Allow pre-releases](advanced-usage.md#allow-pre-releases) - [Using the pip-version input](advanced-usage.md#using-the-pip-version-input) +- [Using the pip-install input](advanced-usage.md#using-the-pip-install-input) ## Using the `python-version` input @@ -672,3 +673,20 @@ The version of Pip should be specified in the format `major`, `major.minor`, or > The `pip-version` input is supported only with standard Python versions. It is not available when using PyPy or GraalPy. > Using a specific or outdated version of pip may result in compatibility or security issues and can cause job failures. For best practices and guidance, refer to the official [pip documentation](https://pip.pypa.io/en/stable/). + +## Using the pip-install input + +The `pip-install` input allows you to install dependencies as part of the Python setup step. + + +```yaml + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.13' + pip-install: -r requirements.txt +``` +> Note: This feature is intended for standard pip-based dependency installations. +For complex workflows, or alternative package managers (e.g., poetry, pipenv), we recommend using separate steps to maintain clarity and flexibility. diff --git a/src/setup-python.ts b/src/setup-python.ts index 5d585d734..c1732d7e0 100644 --- a/src/setup-python.ts +++ b/src/setup-python.ts @@ -13,6 +13,7 @@ import { getVersionInputFromFile, getVersionsInputFromPlainFile } from './utils'; +import {exec} from '@actions/exec'; function isPyPyVersion(versionSpec: string) { return versionSpec.startsWith('pypy'); @@ -22,6 +23,25 @@ function isGraalPyVersion(versionSpec: string) { return versionSpec.startsWith('graalpy'); } +async function installPipPackages() { + const pipInstall = core.getInput('pip-install'); + + if (!pipInstall) { + return; + } + core.info(`Installing pip packages: ${pipInstall}`); + + try { + const installArgs = pipInstall.trim().split(/\s+/); + await exec('python', ['-m', 'pip', 'install', ...installArgs]); + core.info('Successfully installed pip packages'); + } catch (error) { + core.setFailed( + `Failed to install pip packages from "${pipInstall}". Please verify that the package names and versions in the requirements file are correct, that the specified packages and versions can be resolved from PyPI or the configured package index, and that your network connection is stable and allows access to the package index.` + ); + } +} + async function cacheDependencies(cache: string, pythonVersion: string) { const cacheDependencyPath = core.getInput('cache-dependency-path') || undefined; @@ -145,6 +165,7 @@ async function run() { if (cache && isCacheFeatureAvailable()) { await cacheDependencies(cache, pythonVersion); } + await installPipPackages(); } else { core.warning( 'The `python-version` input is not set. The version of Python currently in `PATH` will be used.' From cb37554d2829b739ffb1f7935d20e52787af5c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cgowridurgad=E2=80=9D?= <“hgowridurgad@github.com> Date: Wed, 17 Sep 2025 11:59:15 +0530 Subject: [PATCH 2/3] Improve error message --- .github/workflows/e2e-cache-freethreaded.yml | 4 ++-- .github/workflows/e2e-cache.yml | 4 ++-- action.yml | 4 ++-- dist/setup/index.js | 2 +- docs/advanced-usage.md | 4 ++-- src/setup-python.ts | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/e2e-cache-freethreaded.yml b/.github/workflows/e2e-cache-freethreaded.yml index 60c6f42a2..2c28be524 100644 --- a/.github/workflows/e2e-cache-freethreaded.yml +++ b/.github/workflows/e2e-cache-freethreaded.yml @@ -238,7 +238,7 @@ jobs: ] python-version: [3.13.0t, 3.13.1t, 3.13.2t] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup Python uses: ./ with: @@ -264,7 +264,7 @@ jobs: ] python-version: [3.13.0t, 3.13.1t, 3.13.2t] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup Python uses: ./ with: diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml index 592350b47..ed03de63a 100644 --- a/.github/workflows/e2e-cache.yml +++ b/.github/workflows/e2e-cache.yml @@ -325,7 +325,7 @@ jobs: ] python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup Python uses: ./ with: @@ -351,7 +351,7 @@ jobs: ] python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup Python uses: ./ with: diff --git a/action.yml b/action.yml index 3b98d13a3..7a9a7b634 100644 --- a/action.yml +++ b/action.yml @@ -31,8 +31,8 @@ inputs: default: false pip-version: description: "Used to specify the version of pip to install with the Python. Supported format: major[.minor][.patch]." - pip-install: - description: "Used to specify the packages to install with pip after setting up Python. Can be a requirements file or package names." + pip-install: + description: "Used to specify the packages to install with pip after setting up Python. Can be a requirements file or package names." outputs: python-version: description: "The installed Python or PyPy version. Useful when given a version range as input." diff --git a/dist/setup/index.js b/dist/setup/index.js index cfbfdd6ba..92f2ee09e 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -97964,7 +97964,7 @@ function installPipPackages() { core.info('Successfully installed pip packages'); } catch (error) { - core.setFailed(`Failed to install pip packages from "${pipInstall}". Please verify that the package names and versions in the requirements file are correct, that the specified packages and versions can be resolved from PyPI or the configured package index, and that your network connection is stable and allows access to the package index.`); + core.setFailed(`Failed to install pip packages from "${pipInstall}". Please verify that the package names, versions, or requirements files provided are correct, that the specified packages and versions can be resolved from PyPI or the configured package index, and that your network connection is stable and allows access to the package index.`); } }); } diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index a412622f7..e20eb0582 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -681,9 +681,9 @@ The `pip-install` input allows you to install dependencies as part of the Python ```yaml steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.13' pip-install: -r requirements.txt diff --git a/src/setup-python.ts b/src/setup-python.ts index c1732d7e0..482d9fc1b 100644 --- a/src/setup-python.ts +++ b/src/setup-python.ts @@ -37,7 +37,7 @@ async function installPipPackages() { core.info('Successfully installed pip packages'); } catch (error) { core.setFailed( - `Failed to install pip packages from "${pipInstall}". Please verify that the package names and versions in the requirements file are correct, that the specified packages and versions can be resolved from PyPI or the configured package index, and that your network connection is stable and allows access to the package index.` + `Failed to install pip packages from "${pipInstall}". Please verify that the package names, versions, or requirements files provided are correct, that the specified packages and versions can be resolved from PyPI or the configured package index, and that your network connection is stable and allows access to the package index.` ); } } From 388e4a9c162039cb5bc0fbbb85a44c383b52c68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cgowridurgad=E2=80=9D?= <“hgowridurgad@github.com> Date: Wed, 17 Sep 2025 17:10:45 +0530 Subject: [PATCH 3/3] Logic update --- dist/setup/index.js | 13 ++++++------- src/setup-python.ts | 15 ++++++--------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/dist/setup/index.js b/dist/setup/index.js index 92f2ee09e..6cd801411 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -97951,12 +97951,8 @@ function isPyPyVersion(versionSpec) { function isGraalPyVersion(versionSpec) { return versionSpec.startsWith('graalpy'); } -function installPipPackages() { +function installPipPackages(pipInstall) { return __awaiter(this, void 0, void 0, function* () { - const pipInstall = core.getInput('pip-install'); - if (!pipInstall) { - return; - } core.info(`Installing pip packages: ${pipInstall}`); try { const installArgs = pipInstall.trim().split(/\s+/); @@ -97964,7 +97960,7 @@ function installPipPackages() { core.info('Successfully installed pip packages'); } catch (error) { - core.setFailed(`Failed to install pip packages from "${pipInstall}". Please verify that the package names, versions, or requirements files provided are correct, that the specified packages and versions can be resolved from PyPI or the configured package index, and that your network connection is stable and allows access to the package index.`); + core.setFailed(`Failed to install pip packages from "${pipInstall}". Please verify that the package names, versions, or requirements files provided are correct and installable, that the specified packages and versions can be resolved from PyPI or the configured package index, and that your network connection is stable and allows access to the package index.`); } }); } @@ -98056,7 +98052,10 @@ function run() { if (cache && (0, utils_1.isCacheFeatureAvailable)()) { yield cacheDependencies(cache, pythonVersion); } - yield installPipPackages(); + const pipInstall = core.getInput('pip-install'); + if (pipInstall) { + yield installPipPackages(pipInstall); + } } else { core.warning('The `python-version` input is not set. The version of Python currently in `PATH` will be used.'); diff --git a/src/setup-python.ts b/src/setup-python.ts index 482d9fc1b..91a0c1761 100644 --- a/src/setup-python.ts +++ b/src/setup-python.ts @@ -23,21 +23,15 @@ function isGraalPyVersion(versionSpec: string) { return versionSpec.startsWith('graalpy'); } -async function installPipPackages() { - const pipInstall = core.getInput('pip-install'); - - if (!pipInstall) { - return; - } +async function installPipPackages(pipInstall: string) { core.info(`Installing pip packages: ${pipInstall}`); - try { const installArgs = pipInstall.trim().split(/\s+/); await exec('python', ['-m', 'pip', 'install', ...installArgs]); core.info('Successfully installed pip packages'); } catch (error) { core.setFailed( - `Failed to install pip packages from "${pipInstall}". Please verify that the package names, versions, or requirements files provided are correct, that the specified packages and versions can be resolved from PyPI or the configured package index, and that your network connection is stable and allows access to the package index.` + `Failed to install pip packages from "${pipInstall}". Please verify that the package names, versions, or requirements files provided are correct and installable, that the specified packages and versions can be resolved from PyPI or the configured package index, and that your network connection is stable and allows access to the package index.` ); } } @@ -165,7 +159,10 @@ async function run() { if (cache && isCacheFeatureAvailable()) { await cacheDependencies(cache, pythonVersion); } - await installPipPackages(); + const pipInstall = core.getInput('pip-install'); + if (pipInstall) { + await installPipPackages(pipInstall); + } } else { core.warning( 'The `python-version` input is not set. The version of Python currently in `PATH` will be used.'