From 7c4d5773f1c38424c2fc84b3790eb22f1816fa43 Mon Sep 17 00:00:00 2001 From: lmvysakh Date: Wed, 14 May 2025 16:20:14 +0530 Subject: [PATCH] Enhance readability of .python-version --- __tests__/utils.test.ts | 34 +++++++++++++++++++++++++++++++--- dist/setup/index.js | 37 ++++++++++++++++++++++++++----------- src/setup-python.ts | 4 ++-- src/utils.ts | 31 +++++++++++++++++++++++-------- 4 files changed, 82 insertions(+), 24 deletions(-) diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index 6c0f0e13..ae6c69a0 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -10,7 +10,7 @@ import { validatePythonVersionFormatForPyPy, isCacheFeatureAvailable, getVersionInputFromFile, - getVersionInputFromPlainFile, + getVersionsInputFromPlainFile, getVersionInputFromTomlFile, getNextPageUrl, isGhes, @@ -95,17 +95,45 @@ const tempDir = path.join( ); describe('Version from file test', () => { - it.each([getVersionInputFromPlainFile, getVersionInputFromFile])( + it.each([getVersionsInputFromPlainFile, getVersionInputFromFile])( 'Version from plain file test', async _fn => { await io.mkdirP(tempDir); const pythonVersionFileName = 'python-version.file'; const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName); - const pythonVersionFileContent = '3.7'; + const pythonVersionFileContent = '3.9'; fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent); expect(_fn(pythonVersionFilePath)).toEqual([pythonVersionFileContent]); } ); + it.each([getVersionsInputFromPlainFile, getVersionInputFromFile])( + 'Versions from multiline plain file test', + async _fn => { + await io.mkdirP(tempDir); + const pythonVersionFileName = 'python-version.file'; + const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName); + const pythonVersionFileContent = '3.10\r\n3.9'; + fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent); + expect(_fn(pythonVersionFilePath)).toEqual(['3.10', '3.9']); + } + ); + it.each([getVersionsInputFromPlainFile, getVersionInputFromFile])( + 'Version from complex plain file test', + async _fn => { + await io.mkdirP(tempDir); + const pythonVersionFileName = 'python-version.file'; + const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName); + const pythonVersionFileContent = + '3.13/envs/virtualenv\r# 3.12\n3.11\r\n3.10\r\n 3.9 \r\n'; + fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent); + expect(_fn(pythonVersionFilePath)).toEqual([ + '3.13', + '3.11', + '3.10', + '3.9' + ]); + } + ); it.each([getVersionInputFromTomlFile, getVersionInputFromFile])( 'Version from standard pyproject.toml test', async _fn => { diff --git a/dist/setup/index.js b/dist/setup/index.js index 87c6a816..4e3f2673 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -96929,7 +96929,7 @@ function cacheDependencies(cache, pythonVersion) { } function resolveVersionInputFromDefaultFile() { const couples = [ - ['.python-version', utils_1.getVersionInputFromPlainFile] + ['.python-version', utils_1.getVersionsInputFromPlainFile] ]; for (const [versionFile, _fn] of couples) { (0, utils_1.logWarning)(`Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find '${versionFile}' file.`); @@ -97066,7 +97066,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getDownloadFileName = exports.getNextPageUrl = exports.getBinaryDirectory = exports.getVersionInputFromFile = exports.getVersionInputFromToolVersions = exports.getVersionInputFromPlainFile = exports.getVersionInputFromTomlFile = exports.getOSInfo = exports.getLinuxInfo = exports.logWarning = exports.isCacheFeatureAvailable = exports.isGhes = exports.validatePythonVersionFormatForPyPy = exports.writeExactPyPyVersionFile = exports.readExactPyPyVersionFile = exports.getPyPyVersionFromPath = exports.isNightlyKeyword = exports.validateVersion = exports.createSymlinkInFolder = exports.WINDOWS_PLATFORMS = exports.WINDOWS_ARCHS = exports.IS_MAC = exports.IS_LINUX = exports.IS_WINDOWS = void 0; +exports.getDownloadFileName = exports.getNextPageUrl = exports.getBinaryDirectory = exports.getVersionInputFromFile = exports.getVersionInputFromToolVersions = exports.getVersionsInputFromPlainFile = exports.getVersionInputFromTomlFile = exports.getOSInfo = exports.getLinuxInfo = exports.logWarning = exports.isCacheFeatureAvailable = exports.isGhes = exports.validatePythonVersionFormatForPyPy = exports.writeExactPyPyVersionFile = exports.readExactPyPyVersionFile = exports.getPyPyVersionFromPath = exports.isNightlyKeyword = exports.validateVersion = exports.createSymlinkInFolder = exports.WINDOWS_PLATFORMS = exports.WINDOWS_ARCHS = exports.IS_MAC = exports.IS_LINUX = exports.IS_WINDOWS = void 0; /* eslint no-unsafe-finally: "off" */ const cache = __importStar(__nccwpck_require__(5116)); const core = __importStar(__nccwpck_require__(7484)); @@ -97247,7 +97247,7 @@ function extractValue(obj, keys) { * If none is present, returns an empty list. */ function getVersionInputFromTomlFile(versionFile) { - core.debug(`Trying to resolve version form ${versionFile}`); + core.debug(`Trying to resolve version from ${versionFile}`); let pyprojectFile = fs_1.default.readFileSync(versionFile, 'utf8'); // Normalize the line endings in the pyprojectFile pyprojectFile = pyprojectFile.replace(/\r\n/g, '\n'); @@ -97280,15 +97280,30 @@ function getVersionInputFromTomlFile(versionFile) { } exports.getVersionInputFromTomlFile = getVersionInputFromTomlFile; /** - * Python version extracted from a plain text file. + * Python versions extracted from a plain text file. + * - Resolves multiple versions from multiple lines. + * - Handles pyenv-virtualenv pointers (e.g. `3.10/envs/virtualenv`). + * - Ignores empty lines and lines starting with `#` + * - Trims whitespace. */ -function getVersionInputFromPlainFile(versionFile) { - core.debug(`Trying to resolve version form ${versionFile}`); - const version = fs_1.default.readFileSync(versionFile, 'utf8').trim(); - core.info(`Resolved ${versionFile} as ${version}`); - return [version]; +function getVersionsInputFromPlainFile(versionFile) { + core.debug(`Trying to resolve versions from ${versionFile}`); + const content = fs_1.default.readFileSync(versionFile, 'utf8').trim(); + const lines = content.split(/\r\n|\r|\n/); + const versions = lines + .map(line => { + if (line.startsWith('#') || line.trim() === '') { + return undefined; + } + let version = line.trim(); + version = version.split('/')[0]; + return version; + }) + .filter(version => version !== undefined); + core.info(`Resolved ${versionFile} as ${versions.join(', ')}`); + return versions; } -exports.getVersionInputFromPlainFile = getVersionInputFromPlainFile; +exports.getVersionsInputFromPlainFile = getVersionsInputFromPlainFile; /** * Python version extracted from a .tool-versions file. */ @@ -97331,7 +97346,7 @@ function getVersionInputFromFile(versionFile) { return getVersionInputFromToolVersions(versionFile); } else { - return getVersionInputFromPlainFile(versionFile); + return getVersionsInputFromPlainFile(versionFile); } } exports.getVersionInputFromFile = getVersionInputFromFile; diff --git a/src/setup-python.ts b/src/setup-python.ts index ab5931b8..5d585d73 100644 --- a/src/setup-python.ts +++ b/src/setup-python.ts @@ -11,7 +11,7 @@ import { logWarning, IS_MAC, getVersionInputFromFile, - getVersionInputFromPlainFile + getVersionsInputFromPlainFile } from './utils'; function isPyPyVersion(versionSpec: string) { @@ -35,7 +35,7 @@ async function cacheDependencies(cache: string, pythonVersion: string) { function resolveVersionInputFromDefaultFile(): string[] { const couples: [string, (versionFile: string) => string[]][] = [ - ['.python-version', getVersionInputFromPlainFile] + ['.python-version', getVersionsInputFromPlainFile] ]; for (const [versionFile, _fn] of couples) { logWarning( diff --git a/src/utils.ts b/src/utils.ts index 6274895e..f39006d9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -228,7 +228,7 @@ function extractValue(obj: any, keys: string[]): string | undefined { * If none is present, returns an empty list. */ export function getVersionInputFromTomlFile(versionFile: string): string[] { - core.debug(`Trying to resolve version form ${versionFile}`); + core.debug(`Trying to resolve version from ${versionFile}`); let pyprojectFile = fs.readFileSync(versionFile, 'utf8'); // Normalize the line endings in the pyprojectFile @@ -269,13 +269,28 @@ export function getVersionInputFromTomlFile(versionFile: string): string[] { } /** - * Python version extracted from a plain text file. + * Python versions extracted from a plain text file. + * - Resolves multiple versions from multiple lines. + * - Handles pyenv-virtualenv pointers (e.g. `3.10/envs/virtualenv`). + * - Ignores empty lines and lines starting with `#` + * - Trims whitespace. */ -export function getVersionInputFromPlainFile(versionFile: string): string[] { - core.debug(`Trying to resolve version form ${versionFile}`); - const version = fs.readFileSync(versionFile, 'utf8').trim(); - core.info(`Resolved ${versionFile} as ${version}`); - return [version]; +export function getVersionsInputFromPlainFile(versionFile: string): string[] { + core.debug(`Trying to resolve versions from ${versionFile}`); + const content = fs.readFileSync(versionFile, 'utf8').trim(); + const lines = content.split(/\r\n|\r|\n/); + const versions = lines + .map(line => { + if (line.startsWith('#') || line.trim() === '') { + return undefined; + } + let version: string = line.trim(); + version = version.split('/')[0]; + return version; + }) + .filter(version => version !== undefined) as string[]; + core.info(`Resolved ${versionFile} as ${versions.join(', ')}`); + return versions; } /** @@ -319,7 +334,7 @@ export function getVersionInputFromFile(versionFile: string): string[] { } else if (versionFile.match('.tool-versions')) { return getVersionInputFromToolVersions(versionFile); } else { - return getVersionInputFromPlainFile(versionFile); + return getVersionsInputFromPlainFile(versionFile); } }