Skip to content

Add version parsing from Pipfile #1067

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions .github/workflows/test-python-freethreaded.yml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,86 @@ jobs:
with:
python-version-file: .tool-versions

setup-versions-from-pipfile-with-python_version:
name: Setup ${{ matrix.python }} ${{ matrix.os }} Pipfile
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
[
macos-latest,
windows-latest,
ubuntu-22.04,
ubuntu-22.04-arm,
macos-13,
ubuntu-latest,
ubuntu-24.04-arm
]
python: [3.13t, 3.14t-dev]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: build-version-file ${{ matrix.python }}
run: |
echo '[requires]
python_version = "${{ matrix.python }}"
' > Pipfile

- name: setup-python ${{ matrix.python }}
id: setup-python
uses: ./
with:
python-version-file: Pipfile

- name: Check python-path
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
shell: bash

- name: Run simple code
run: python -c 'import math; print(math.factorial(5))'

setup-versions-from-pipfile-with-python_full_version:
name: Setup ${{ matrix.python }} ${{ matrix.os }} .tool-versions file
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
[
macos-latest,
windows-latest,
ubuntu-22.04,
ubuntu-22.04-arm,
macos-13,
ubuntu-latest,
ubuntu-24.04-arm
]
python: [3.13.0t, 3.13.1t, 3.13.2t, 3.14t-dev]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: build-version-file ${{ matrix.python }}
run: |
echo '[requires]
python_full_version = "${{ matrix.python }}"
' > Pipfile

- name: setup-python ${{ matrix.python }}
id: setup-python
uses: ./
with:
python-version-file: Pipfile

- name: Check python-path
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
shell: bash

- name: Run simple code
run: python -c 'import math; print(math.factorial(5))'

setup-pre-release-version-from-manifest:
name: Setup 3.14.0-alpha.6 ${{ matrix.os }}
runs-on: ${{ matrix.os }}
Expand Down
100 changes: 100 additions & 0 deletions .github/workflows/test-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,106 @@ jobs:
with:
python-version-file: .tool-versions

setup-versions-from-pipfile-with-python_version:
name: Setup ${{ matrix.python }} ${{ matrix.os }} Pipfile with python_version
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
[
macos-latest,
windows-latest,
ubuntu-22.04,
ubuntu-22.04-arm,
macos-13,
ubuntu-latest,
ubuntu-24.04-arm
]
python: [3.9.13, 3.10.11, 3.11.9, 3.13.2]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: build-version-file ${{ matrix.python }}
run: |
echo '[requires]
python_version = "${{ matrix.python }}"
' > Pipfile

- name: setup-python ${{ matrix.python }}
id: setup-python
uses: ./
with:
python-version-file: Pipfile

- name: Check python-path
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
shell: bash

- name: Validate version
run: |
$pythonVersion = (python --version)
if ("Python ${{ matrix.python }}".replace("==", "") -ne "$pythonVersion"){
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
exit 1
}
$pythonVersion
shell: pwsh

- name: Run simple code
run: python -c 'import math; print(math.factorial(5))'

setup-versions-from-pipfile-with-python_full_version:
name: Setup ${{ matrix.python }} ${{ matrix.os }} Pipfile with python_full_version
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
[
macos-latest,
windows-latest,
ubuntu-22.04,
ubuntu-22.04-arm,
macos-13,
ubuntu-latest,
ubuntu-24.04-arm
]
python: [3.9.13, 3.10.11, 3.11.9, 3.13.2]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: build-version-file ${{ matrix.python }}
run: |
echo '[requires]
python_full_version = "${{ matrix.python }}"
' > Pipfile

- name: setup-python ${{ matrix.python }}
id: setup-python
uses: ./
with:
python-version-file: Pipfile

- name: Check python-path
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
shell: bash

- name: Validate version
run: |
$pythonVersion = (python --version)
if ("Python ${{ matrix.python }}".replace("==", "") -ne "$pythonVersion"){
Write-Host "The current version is $pythonVersion; expected version is ${{ matrix.python }}"
exit 1
}
$pythonVersion
shell: pwsh

- name: Run simple code
run: python -c 'import math; print(math.factorial(5))'

setup-pre-release-version-from-manifest:
name: Setup 3.14.0-alpha.6 ${{ matrix.os }}
runs-on: ${{ matrix.os }}
Expand Down
39 changes: 39 additions & 0 deletions __tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
getVersionInputFromFile,
getVersionsInputFromPlainFile,
getVersionInputFromTomlFile,
getVersionInputFromPipfileFile,
getNextPageUrl,
isGhes,
IS_WINDOWS,
Expand Down Expand Up @@ -244,6 +245,44 @@ describe('Version from file test', () => {
expect(_fn(toolVersionFilePath)).toEqual(['3.14t-dev']);
}
);

it.each([getVersionInputFromPipfileFile, getVersionInputFromFile])(
'Version from python_version in Pipfile',
async _fn => {
await io.mkdirP(tempDir);
const pythonVersionFileName = 'Pipfile';
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
const pythonVersion = '3.13';
const pythonVersionFileContent = `[requires]\npython_version = "${pythonVersion}"`;
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
}
);

it.each([getVersionInputFromPipfileFile, getVersionInputFromFile])(
'Version from python_full_version in Pipfile',
async _fn => {
await io.mkdirP(tempDir);
const pythonVersionFileName = 'Pipfile';
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
const pythonVersion = '3.13.0';
const pythonVersionFileContent = `[requires]\npython_full_version = "${pythonVersion}"`;
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
expect(_fn(pythonVersionFilePath)).toEqual([pythonVersion]);
}
);

it.each([getVersionInputFromPipfileFile, getVersionInputFromFile])(
'Pipfile undefined version',
async _fn => {
await io.mkdirP(tempDir);
const pythonVersionFileName = 'Pipfile';
const pythonVersionFilePath = path.join(tempDir, pythonVersionFileName);
const pythonVersionFileContent = ``;
fs.writeFileSync(pythonVersionFilePath, pythonVersionFileContent);
expect(_fn(pythonVersionFilePath)).toEqual([]);
}
);
});

describe('getNextPageUrl', () => {
Expand Down
41 changes: 39 additions & 2 deletions dist/setup/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -97067,7 +97067,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.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;
exports.getDownloadFileName = exports.getNextPageUrl = exports.getBinaryDirectory = exports.getVersionInputFromFile = exports.getVersionInputFromPipfileFile = 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));
Expand Down Expand Up @@ -97337,7 +97337,41 @@ function getVersionInputFromToolVersions(versionFile) {
}
exports.getVersionInputFromToolVersions = getVersionInputFromToolVersions;
/**
* Python version extracted from a plain, .tool-versions or TOML file.
* Python version extracted from the Pipfile file.
*/
function getVersionInputFromPipfileFile(versionFile) {
core.debug(`Trying to resolve version from ${versionFile}`);
if (!fs_1.default.existsSync(versionFile)) {
core.warning(`File ${versionFile} does not exist.`);
return [];
}
let pipfileFile = fs_1.default.readFileSync(versionFile, 'utf8');
// Normalize the line endings in the pipfileFile
pipfileFile = pipfileFile.replace(/\r\n/g, '\n');
const pipfileConfig = toml.parse(pipfileFile);
const keys = ['requires'];
if (!('requires' in pipfileConfig)) {
core.warning(`No Python version found in ${versionFile}`);
return [];
}
if ('python_full_version' in pipfileConfig['requires']) {
// specifies a full python version
keys.push('python_full_version');
}
else {
keys.push('python_version');
}
const versions = [];
const version = extractValue(pipfileConfig, keys);
if (version !== undefined) {
versions.push(version);
}
core.info(`Extracted ${versions} from ${versionFile}`);
return versions;
}
exports.getVersionInputFromPipfileFile = getVersionInputFromPipfileFile;
/**
* Python version extracted from a plain, .tool-versions, Pipfile or TOML file.
*/
function getVersionInputFromFile(versionFile) {
if (versionFile.endsWith('.toml')) {
Expand All @@ -97346,6 +97380,9 @@ function getVersionInputFromFile(versionFile) {
else if (versionFile.match('.tool-versions')) {
return getVersionInputFromToolVersions(versionFile);
}
else if (versionFile.match('Pipfile')) {
return getVersionInputFromPipfileFile(versionFile);
}
else {
return getVersionsInputFromPlainFile(versionFile);
}
Expand Down
9 changes: 9 additions & 0 deletions docs/advanced-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,15 @@ steps:
- run: python my_script.py
```

```yaml
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version-file: 'Pipfile' # Read python version from a file Pipfile
- run: python my_script.py
```

## Check latest version

The `check-latest` flag defaults to `false`. Use the default or set `check-latest` to `false` if you prefer stability and if you want to ensure a specific `Python or PyPy` version is always used.
Expand Down
42 changes: 41 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,14 +325,54 @@ export function getVersionInputFromToolVersions(versionFile: string): string[] {
return [];
}
}

/**
* Python version extracted from the Pipfile file.
*/
export function getVersionInputFromPipfileFile(versionFile: string): string[] {
core.debug(`Trying to resolve version from ${versionFile}`);

if (!fs.existsSync(versionFile)) {
core.warning(`File ${versionFile} does not exist.`);
return [];
}
let pipfileFile = fs.readFileSync(versionFile, 'utf8');
// Normalize the line endings in the pipfileFile
pipfileFile = pipfileFile.replace(/\r\n/g, '\n');

const pipfileConfig = toml.parse(pipfileFile);
const keys = ['requires'];

if (!('requires' in pipfileConfig)) {
core.warning(`No Python version found in ${versionFile}`);
return [];
}
if ('python_full_version' in (pipfileConfig['requires'] as toml.JsonMap)) {
// specifies a full python version
keys.push('python_full_version');
} else {
keys.push('python_version');
}
const versions = [];
const version = extractValue(pipfileConfig, keys);
if (version !== undefined) {
versions.push(version);
}

core.info(`Extracted ${versions} from ${versionFile}`);
return versions;
}

/**
* Python version extracted from a plain, .tool-versions or TOML file.
* Python version extracted from a plain, .tool-versions, Pipfile or TOML file.
*/
export function getVersionInputFromFile(versionFile: string): string[] {
if (versionFile.endsWith('.toml')) {
return getVersionInputFromTomlFile(versionFile);
} else if (versionFile.match('.tool-versions')) {
return getVersionInputFromToolVersions(versionFile);
} else if (versionFile.match('Pipfile')) {
return getVersionInputFromPipfileFile(versionFile);
} else {
return getVersionsInputFromPlainFile(versionFile);
}
Expand Down
Loading