diff --git a/action.yml b/action.yml index 7a9a7b63..85685201 100644 --- a/action.yml +++ b/action.yml @@ -33,6 +33,12 @@ inputs: 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." + preclean-pip: + description: "When 'true', removes all existing pip packages before installing new ones." + default: false + postclean-pip: + description: "When 'true', removes all pip packages installed by this action after the action completes." + default: false outputs: python-version: description: "The installed Python or PyPy version. Useful when given a version range as input." diff --git a/package.json b/package.json index 3383f219..c808525c 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,12 @@ "node": ">=24.0.0" }, "scripts": { - "build": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/cache-save src/cache-save.ts", + "build": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/post-python src/post-python.ts", "format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write \"**/*.{ts,yml,yaml}\"", "format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check \"**/*.{ts,yml,yaml}\"", "lint": "eslint --config ./.eslintrc.js \"**/*.ts\"", "lint:fix": "eslint --config ./.eslintrc.js \"**/*.ts\" --fix", - "release": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/cache-save src/cache-save.ts && git add -f dist/", + "release": "ncc build -o dist/setup src/setup-python.ts && ncc build -o dist/post-python src/post-python.ts && git add -f dist/", "test": "jest --runInBand --coverage" }, "repository": { diff --git a/src/clean-pip.ts b/src/clean-pip.ts new file mode 100644 index 00000000..233c8207 --- /dev/null +++ b/src/clean-pip.ts @@ -0,0 +1,18 @@ +import * as core from '@actions/core'; +import {exec} from '@actions/exec'; + +// Shared helper to uninstall all pip packages in the current environment. +export async function cleanPipPackages() { + core.info('Cleaning up pip packages'); + try { + // uninstall all currently installed packages (if any) + // Use a shell so we can pipe the output of pip freeze into xargs + await exec('bash', [ + '-c', + 'python -m pip freeze | xargs python -m pip uninstall' + ]); + core.info('Successfully cleaned up pip packages'); + } catch (error) { + core.setFailed(`Failed to clean up pip packages.`); + } +} diff --git a/src/cache-save.ts b/src/post-python.ts similarity index 70% rename from src/cache-save.ts rename to src/post-python.ts index abeef2f3..41abd179 100644 --- a/src/cache-save.ts +++ b/src/post-python.ts @@ -1,21 +1,34 @@ import * as core from '@actions/core'; import * as cache from '@actions/cache'; +import {cleanPipPackages} from './clean-pip'; import fs from 'fs'; import {State} from './cache-distributions/cache-distributor'; // Added early exit to resolve issue with slow post action step: -// - https://github.com/actions/setup-node/issues/878 -// https://github.com/actions/cache/pull/1217 +// See: https://github.com/actions/setup-node/issues/878 +// See: https://github.com/actions/cache/pull/1217 export async function run(earlyExit?: boolean) { try { const cache = core.getInput('cache'); if (cache) { await saveCache(cache); - + // Preserve early-exit behavior for the post action when requested. + // Some CI setups may want the post step to exit early to avoid long-running + // processes during cleanup. If enabled, exit with success immediately. if (earlyExit) { process.exit(0); } + // Optionally clean up pip packages after the post-action if requested. + // This mirrors the `preclean-pip` behavior used in the main action. + try { + const postcleanPip = core.getBooleanInput('postclean-pip'); + if (postcleanPip) { + await cleanPipPackages(); + } + } catch (err) { + // getBooleanInput throws if input missing in some contexts; ignore and continue + } } } catch (error) { const err = error as Error; @@ -84,4 +97,6 @@ function isCacheDirectoryExists(cacheDirectory: string[]) { return result; } -run(true); +// Invoke the post action runner. No early-exit — this must complete so the cache +// can be saved during the post step. +run(); diff --git a/src/setup-python.ts b/src/setup-python.ts index 91a0c176..f6157340 100644 --- a/src/setup-python.ts +++ b/src/setup-python.ts @@ -14,6 +14,7 @@ import { getVersionsInputFromPlainFile } from './utils'; import {exec} from '@actions/exec'; +import {cleanPipPackages} from './clean-pip'; function isPyPyVersion(versionSpec: string) { return versionSpec.startsWith('pypy'); @@ -159,6 +160,10 @@ async function run() { if (cache && isCacheFeatureAvailable()) { await cacheDependencies(cache, pythonVersion); } + const precleanPip = core.getBooleanInput('preclean-pip'); + if (precleanPip) { + await cleanPipPackages(); + } const pipInstall = core.getInput('pip-install'); if (pipInstall) { await installPipPackages(pipInstall);