Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 19e4675

Browse filesBrowse files
Add support for .tool-versions file in setup-python (actions#1043)
* add support for .tool-versions file * update regex * optimize code * update test-python.yml for .tool-versions * fix format-check errors * fix formatting in test-python.yml * Fix test-python.yml error * workflow update with latest versions * update test cases * fix lint issue
1 parent 6fd11e1 commit 19e4675
Copy full SHA for 19e4675

File tree

Expand file treeCollapse file tree

5 files changed

+193
-6
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+193
-6
lines changed

‎.github/workflows/test-python.yml

Copy file name to clipboardExpand all lines: .github/workflows/test-python.yml
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,39 @@ jobs:
245245
- name: Run simple code
246246
run: python -c 'import math; print(math.factorial(5))'
247247

248+
setup-versions-from-tool-versions-file:
249+
name: Setup ${{ matrix.python }} ${{ matrix.os }} .tool-versions file
250+
runs-on: ${{ matrix.os }}
251+
strategy:
252+
fail-fast: false
253+
matrix:
254+
os:
255+
[
256+
macos-latest,
257+
windows-latest,
258+
ubuntu-20.04,
259+
ubuntu-22.04,
260+
macos-13,
261+
ubuntu-latest
262+
]
263+
python: [3.13.0, 3.14-dev, pypy3.11-7.3.18, graalpy-24.1.2]
264+
exclude:
265+
- os: windows-latest
266+
python: graalpy-24.1.2
267+
steps:
268+
- name: Checkout
269+
uses: actions/checkout@v4
270+
271+
- name: build-tool-versions-file ${{ matrix.python }}
272+
run: |
273+
echo "python ${{ matrix.python }}" > .tool-versions
274+
275+
- name: setup-python using .tool-versions ${{ matrix.python }}
276+
id: setup-python-tool-versions
277+
uses: ./
278+
with:
279+
python-version-file: .tool-versions
280+
248281
setup-pre-release-version-from-manifest:
249282
name: Setup 3.14.0-alpha.1 ${{ matrix.os }}
250283
runs-on: ${{ matrix.os }}

‎__tests__/utils.test.ts

Copy file name to clipboardExpand all lines: __tests__/utils.test.ts
+78-1Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {
1515
getNextPageUrl,
1616
isGhes,
1717
IS_WINDOWS,
18-
getDownloadFileName
18+
getDownloadFileName,
19+
getVersionInputFromToolVersions
1920
} from '../src/utils';
2021

2122
jest.mock('@actions/cache');
@@ -139,6 +140,82 @@ describe('Version from file test', () => {
139140
expect(_fn(pythonVersionFilePath)).toEqual([]);
140141
}
141142
);
143+
it.each([getVersionInputFromToolVersions])(
144+
'Version from .tool-versions',
145+
async _fn => {
146+
const toolVersionFileName = '.tool-versions';
147+
const toolVersionFilePath = path.join(tempDir, toolVersionFileName);
148+
const toolVersionContent = 'python 3.9.10\nnodejs 16';
149+
fs.writeFileSync(toolVersionFilePath, toolVersionContent);
150+
expect(_fn(toolVersionFilePath)).toEqual(['3.9.10']);
151+
}
152+
);
153+
154+
it.each([getVersionInputFromToolVersions])(
155+
'Version from .tool-versions with comment',
156+
async _fn => {
157+
const toolVersionFileName = '.tool-versions';
158+
const toolVersionFilePath = path.join(tempDir, toolVersionFileName);
159+
const toolVersionContent = '# python 3.8\npython 3.9';
160+
fs.writeFileSync(toolVersionFilePath, toolVersionContent);
161+
expect(_fn(toolVersionFilePath)).toEqual(['3.9']);
162+
}
163+
);
164+
165+
it.each([getVersionInputFromToolVersions])(
166+
'Version from .tool-versions with whitespace',
167+
async _fn => {
168+
const toolVersionFileName = '.tool-versions';
169+
const toolVersionFilePath = path.join(tempDir, toolVersionFileName);
170+
const toolVersionContent = ' python 3.10 ';
171+
fs.writeFileSync(toolVersionFilePath, toolVersionContent);
172+
expect(_fn(toolVersionFilePath)).toEqual(['3.10']);
173+
}
174+
);
175+
176+
it.each([getVersionInputFromToolVersions])(
177+
'Version from .tool-versions with v prefix',
178+
async _fn => {
179+
const toolVersionFileName = '.tool-versions';
180+
const toolVersionFilePath = path.join(tempDir, toolVersionFileName);
181+
const toolVersionContent = 'python v3.9.10';
182+
fs.writeFileSync(toolVersionFilePath, toolVersionContent);
183+
expect(_fn(toolVersionFilePath)).toEqual(['3.9.10']);
184+
}
185+
);
186+
187+
it.each([getVersionInputFromToolVersions])(
188+
'Version from .tool-versions with pypy version',
189+
async _fn => {
190+
const toolVersionFileName = '.tool-versions';
191+
const toolVersionFilePath = path.join(tempDir, toolVersionFileName);
192+
const toolVersionContent = 'python pypy3.10-7.3.14';
193+
fs.writeFileSync(toolVersionFilePath, toolVersionContent);
194+
expect(_fn(toolVersionFilePath)).toEqual(['pypy3.10-7.3.14']);
195+
}
196+
);
197+
198+
it.each([getVersionInputFromToolVersions])(
199+
'Version from .tool-versions with alpha Releases',
200+
async _fn => {
201+
const toolVersionFileName = '.tool-versions';
202+
const toolVersionFilePath = path.join(tempDir, toolVersionFileName);
203+
const toolVersionContent = 'python 3.14.0a5t';
204+
fs.writeFileSync(toolVersionFilePath, toolVersionContent);
205+
expect(_fn(toolVersionFilePath)).toEqual(['3.14.0a5t']);
206+
}
207+
);
208+
209+
it.each([getVersionInputFromToolVersions])(
210+
'Version from .tool-versions with dev suffix',
211+
async _fn => {
212+
const toolVersionFileName = '.tool-versions';
213+
const toolVersionFilePath = path.join(tempDir, toolVersionFileName);
214+
const toolVersionContent = 'python 3.14t-dev';
215+
fs.writeFileSync(toolVersionFilePath, toolVersionContent);
216+
expect(_fn(toolVersionFilePath)).toEqual(['3.14t-dev']);
217+
}
218+
);
142219
});
143220

144221
describe('getNextPageUrl', () => {

‎dist/setup/index.js

Copy file name to clipboardExpand all lines: dist/setup/index.js
+36-2Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100535,7 +100535,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
100535100535
return (mod && mod.__esModule) ? mod : { "default": mod };
100536100536
};
100537100537
Object.defineProperty(exports, "__esModule", ({ value: true }));
100538-
exports.getDownloadFileName = exports.getNextPageUrl = exports.getBinaryDirectory = exports.getVersionInputFromFile = 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;
100538+
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;
100539100539
/* eslint no-unsafe-finally: "off" */
100540100540
const cache = __importStar(__nccwpck_require__(5116));
100541100541
const core = __importStar(__nccwpck_require__(7484));
@@ -100759,12 +100759,46 @@ function getVersionInputFromPlainFile(versionFile) {
100759100759
}
100760100760
exports.getVersionInputFromPlainFile = getVersionInputFromPlainFile;
100761100761
/**
100762-
* Python version extracted from a plain or TOML file.
100762+
* Python version extracted from a .tool-versions file.
100763+
*/
100764+
function getVersionInputFromToolVersions(versionFile) {
100765+
var _a;
100766+
if (!fs_1.default.existsSync(versionFile)) {
100767+
core.warning(`File ${versionFile} does not exist.`);
100768+
return [];
100769+
}
100770+
try {
100771+
const fileContents = fs_1.default.readFileSync(versionFile, 'utf8');
100772+
const lines = fileContents.split('\n');
100773+
for (const line of lines) {
100774+
// Skip commented lines
100775+
if (line.trim().startsWith('#')) {
100776+
continue;
100777+
}
100778+
const match = line.match(/^\s*python\s*v?\s*(?<version>[^\s]+)\s*$/);
100779+
if (match) {
100780+
return [((_a = match.groups) === null || _a === void 0 ? void 0 : _a.version.trim()) || ''];
100781+
}
100782+
}
100783+
core.warning(`No Python version found in ${versionFile}`);
100784+
return [];
100785+
}
100786+
catch (error) {
100787+
core.error(`Error reading ${versionFile}: ${error.message}`);
100788+
return [];
100789+
}
100790+
}
100791+
exports.getVersionInputFromToolVersions = getVersionInputFromToolVersions;
100792+
/**
100793+
* Python version extracted from a plain, .tool-versions or TOML file.
100763100794
*/
100764100795
function getVersionInputFromFile(versionFile) {
100765100796
if (versionFile.endsWith('.toml')) {
100766100797
return getVersionInputFromTomlFile(versionFile);
100767100798
}
100799+
else if (versionFile.match('.tool-versions')) {
100800+
return getVersionInputFromToolVersions(versionFile);
100801+
}
100768100802
else {
100769100803
return getVersionInputFromPlainFile(versionFile);
100770100804
}

‎docs/advanced-usage.md

Copy file name to clipboardExpand all lines: docs/advanced-usage.md
+11-2Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,9 @@ jobs:
278278
279279
## Using the `python-version-file` input
280280

281-
`setup-python` action can read the Python or PyPy version from a version file. `python-version-file` input is used to specify the path to the version file. If the file that was supplied to `python-version-file` input doesn't exist, the action will fail with an error.
281+
`setup-python` action can read Python or PyPy version from a version file. `python-version-file` input is used for specifying the path to the version file. If the file that was supplied to `python-version-file` input doesn't exist, the action will fail with error.
282282

283-
>In case both `python-version` and `python-version-file` inputs are supplied, the `python-version-file` input will be ignored due to its lower priority.
283+
>In case both `python-version` and `python-version-file` inputs are supplied, the `python-version-file` input will be ignored due to its lower priority. The .tool-versions file supports version specifications in accordance with asdf standards, adhering to Semantic Versioning ([semver](https://semver.org)).
284284

285285
```yaml
286286
steps:
@@ -300,6 +300,15 @@ steps:
300300
- run: python my_script.py
301301
```
302302

303+
```yaml
304+
steps:
305+
- uses: actions/checkout@v4
306+
- uses: actions/setup-python@v5
307+
with:
308+
python-version-file: '.tool-versions' # Read python version from a file .tool-versions
309+
- run: python my_script.py
310+
```
311+
303312
## Check latest version
304313

305314
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.

‎src/utils.ts

Copy file name to clipboardExpand all lines: src/utils.ts
+35-1Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,45 @@ export function getVersionInputFromPlainFile(versionFile: string): string[] {
279279
}
280280

281281
/**
282-
* Python version extracted from a plain or TOML file.
282+
* Python version extracted from a .tool-versions file.
283+
*/
284+
export function getVersionInputFromToolVersions(versionFile: string): string[] {
285+
if (!fs.existsSync(versionFile)) {
286+
core.warning(`File ${versionFile} does not exist.`);
287+
return [];
288+
}
289+
290+
try {
291+
const fileContents = fs.readFileSync(versionFile, 'utf8');
292+
const lines = fileContents.split('\n');
293+
294+
for (const line of lines) {
295+
// Skip commented lines
296+
if (line.trim().startsWith('#')) {
297+
continue;
298+
}
299+
const match = line.match(/^\s*python\s*v?\s*(?<version>[^\s]+)\s*$/);
300+
if (match) {
301+
return [match.groups?.version.trim() || ''];
302+
}
303+
}
304+
305+
core.warning(`No Python version found in ${versionFile}`);
306+
307+
return [];
308+
} catch (error) {
309+
core.error(`Error reading ${versionFile}: ${(error as Error).message}`);
310+
return [];
311+
}
312+
}
313+
/**
314+
* Python version extracted from a plain, .tool-versions or TOML file.
283315
*/
284316
export function getVersionInputFromFile(versionFile: string): string[] {
285317
if (versionFile.endsWith('.toml')) {
286318
return getVersionInputFromTomlFile(versionFile);
319+
} else if (versionFile.match('.tool-versions')) {
320+
return getVersionInputFromToolVersions(versionFile);
287321
} else {
288322
return getVersionInputFromPlainFile(versionFile);
289323
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.