4
0
Fork 0
mirror of https://github.com/actions/setup-python.git synced 2025-11-06 21:16:57 +00:00

Compare commits

...

3 commits

Author SHA1 Message Date
Ed Morley
6d41c00029
Merge 262ac43476 into cfd55ca824 2025-10-28 02:51:09 -05:00
Tim Felgentreff
cfd55ca824
graalpy: add graalpy early-access and windows builds (#880) 2025-10-22 11:16:57 -05:00
Ed Morley
262ac43476
Skip the post run step entirely if the cache is disabled
Previously if the `cache: false` (the default for this Action), the
Action's "post run" step would still be executed.

Whilst this step was fast (since it returned early if the cache was
disabled), it still causes unnecessary noise in the job's steps list.

For example as seen in:
https://github.com/pypa/get-pip/actions/runs/8679713478/job/23798960684

Now, the post run step is skipped if the cache is disabled, thanks
to the `post-if` syntax supporting the `github.events.inputs.*`
context.

See:
https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runspost-if
0c45773b62/action.yml (L40)
2025-07-08 10:12:47 +01:00
9 changed files with 879 additions and 926 deletions

View file

@ -106,7 +106,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, macos-13]
os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
steps:
- uses: actions/checkout@v5
- name: Setup GraalPy and check latest

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@ import * as path from 'path';
import * as semver from 'semver';
import * as finder from '../src/find-graalpy';
import {IGraalPyManifestRelease, IS_WINDOWS} from '../src/utils';
import {IGraalPyManifestRelease} from '../src/utils';
import manifestData from './data/graalpy.json';
@ -19,9 +19,6 @@ const architecture = 'x64';
const toolDir = path.join(__dirname, 'runner', 'tools');
const tempDir = path.join(__dirname, 'runner', 'temp');
/* GraalPy doesn't have a windows release yet */
const describeSkipOnWindows = IS_WINDOWS ? describe.skip : describe;
describe('parseGraalPyVersion', () => {
it.each([
['graalpy-23', '23'],
@ -108,7 +105,7 @@ describe('findGraalPyToolCache', () => {
});
});
describeSkipOnWindows('findGraalPyVersion', () => {
describe('findGraalPyVersion', () => {
let getBooleanInputSpy: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
let debugSpy: jest.SpyInstance;
@ -358,13 +355,13 @@ describeSkipOnWindows('findGraalPyVersion', () => {
it('found and install successfully, pre-release fallback', async () => {
spyCacheDir = jest.spyOn(tc, 'cacheDir');
spyCacheDir.mockImplementation(() =>
path.join(toolDir, 'GraalPy', '23.1', architecture)
path.join(toolDir, 'GraalPy', '24.1', architecture)
);
spyChmodSync = jest.spyOn(fs, 'chmodSync');
spyChmodSync.mockImplementation(() => undefined);
await expect(
finder.findGraalPyVersion(
'graalpy23.1',
'graalpy24.1',
architecture,
false,
false,
@ -372,7 +369,7 @@ describeSkipOnWindows('findGraalPyVersion', () => {
)
).rejects.toThrow();
await expect(
finder.findGraalPyVersion('graalpy23.1', architecture, false, false, true)
).resolves.toEqual('23.1.0-a.1');
finder.findGraalPyVersion('graalpy24.1', architecture, false, false, true)
).resolves.toEqual('24.1.0-ea.9');
});
});

View file

@ -21,24 +21,21 @@ const architecture = 'x64';
const toolDir = path.join(__dirname, 'runner', 'tools');
const tempDir = path.join(__dirname, 'runner', 'temp');
/* GraalPy doesn't have a windows release yet */
const describeSkipOnWindows = IS_WINDOWS ? describe.skip : describe;
describe('graalpyVersionToSemantic', () => {
it.each([
['23.0.0a1', '23.0.0a1'],
['23.0.0', '23.0.0'],
['23.0.x', '23.0.x'],
['23.x', '23.x']
['graalpy-24.1.0-ea.09', '24.1.0-ea.9'],
['graal-23.0.0', '23.0.0'],
['vm-23.0.x', '23.0.x'],
['graal-23.x', '23.x']
])('%s -> %s', (input, expected) => {
expect(installer.graalPyTagToVersion(input)).toEqual(expected);
});
});
describeSkipOnWindows('findRelease', () => {
describe('findRelease', () => {
const result = JSON.stringify(manifestData);
const releases = JSON.parse(result) as IGraalPyManifestRelease[];
const extension = 'tar.gz';
const extension = IS_WINDOWS ? 'zip' : 'tar.gz';
const arch = installer.toGraalPyArchitecture(architecture);
const platform = installer.toGraalPyPlatform(process.platform);
const extensionName = `${platform}-${arch}.${extension}`;
@ -47,8 +44,8 @@ describeSkipOnWindows('findRelease', () => {
browser_download_url: `https://github.com/oracle/graalpython/releases/download/graal-23.0.0/graalpython-23.0.0-${extensionName}`
};
const filesRC1: IGraalPyManifestAsset = {
name: `graalpython-23.1.0a1-${extensionName}`,
browser_download_url: `https://github.com/oracle/graalpython/releases/download/graal-23.1.0a1/graalpython-23.1.0a1-${extensionName}`
name: `graalpy-24.1.0-ea.09-${extensionName}`,
browser_download_url: `https://github.com/graalvm/graal-languages-ea-builds/releases/download/graalpy-24.1.0-ea.09/graalpy-24.1.0-ea.09-${extensionName}`
};
let warningSpy: jest.SpyInstance;
@ -84,15 +81,15 @@ describeSkipOnWindows('findRelease', () => {
});
it('Preview version of GraalPy is found', () => {
const graalpyVersion = installer.graalPyTagToVersion('vm-23.1.0a1');
const graalpyVersion = installer.graalPyTagToVersion('vm-24.1.0-ea.09');
expect(
installer.findRelease(releases, graalpyVersion, architecture, false)
).toMatchObject({
foundAsset: {
name: `graalpython-23.1.0a1-${extensionName}`,
browser_download_url: `https://github.com/oracle/graalpython/releases/download/graal-23.1.0a1/graalpython-23.1.0a1-${extensionName}`
name: `graalpy-24.1.0-ea.09-${extensionName}`,
browser_download_url: `https://github.com/graalvm/graal-languages-ea-builds/releases/download/graalpy-24.1.0-ea.09/graalpy-24.1.0-ea.09-${extensionName}`
},
resolvedGraalPyVersion: '23.1.0-a.1'
resolvedGraalPyVersion: '24.1.0-ea.9'
});
});
@ -107,7 +104,7 @@ describeSkipOnWindows('findRelease', () => {
});
it('GraalPy version matches semver (pre-release)', () => {
const graalpyVersion = '23.1.x';
const graalpyVersion = '24.1.x';
expect(
installer.findRelease(releases, graalpyVersion, architecture, false)
).toBeNull();
@ -115,12 +112,12 @@ describeSkipOnWindows('findRelease', () => {
installer.findRelease(releases, graalpyVersion, architecture, true)
).toMatchObject({
foundAsset: filesRC1,
resolvedGraalPyVersion: '23.1.0-a.1'
resolvedGraalPyVersion: '24.1.0-ea.9'
});
});
});
describeSkipOnWindows('installGraalPy', () => {
describe('installGraalPy', () => {
let tcFind: jest.SpyInstance;
let warningSpy: jest.SpyInstance;
let debugSpy: jest.SpyInstance;
@ -232,20 +229,20 @@ describeSkipOnWindows('installGraalPy', () => {
it('found and install GraalPy, pre-release fallback', async () => {
spyCacheDir = jest.spyOn(tc, 'cacheDir');
spyCacheDir.mockImplementation(() =>
path.join(toolDir, 'GraalPy', '23.1.0', architecture)
path.join(toolDir, 'GraalPy', '24.1.0', architecture)
);
spyChmodSync = jest.spyOn(fs, 'chmodSync');
spyChmodSync.mockImplementation(() => undefined);
await expect(
installer.installGraalPy('23.1.x', architecture, false, undefined)
installer.installGraalPy('24.1.x', architecture, false, undefined)
).rejects.toThrow();
await expect(
installer.installGraalPy('23.1.x', architecture, true, undefined)
installer.installGraalPy('24.1.x', architecture, true, undefined)
).resolves.toEqual({
installDir: path.join(toolDir, 'GraalPy', '23.1.0', architecture),
resolvedGraalPyVersion: '23.1.0-a.1'
installDir: path.join(toolDir, 'GraalPy', '24.1.0', architecture),
resolvedGraalPyVersion: '24.1.0-ea.9'
});
expect(spyHttpClient).toHaveBeenCalled();

View file

@ -44,7 +44,7 @@ runs:
using: 'node24'
main: 'dist/setup/index.js'
post: 'dist/cache-save/index.js'
post-if: success()
post-if: 'success() && github.event.inputs.cache'
branding:
icon: 'code'
color: 'yellow'

34
dist/setup/index.js vendored
View file

@ -96769,8 +96769,8 @@ async function findGraalPyVersion(versionSpec, architecture, updateEnvironment,
const pipDir = utils_1.IS_WINDOWS ? 'Scripts' : 'bin';
const _binDir = path.join(installDir, pipDir);
const binaryExtension = utils_1.IS_WINDOWS ? '.exe' : '';
const pythonPath = path.join(utils_1.IS_WINDOWS ? installDir : _binDir, `python${binaryExtension}`);
const pythonLocation = (0, utils_1.getBinaryDirectory)(installDir);
const pythonPath = path.join(_binDir, `python${binaryExtension}`);
const pythonLocation = path.join(installDir, 'bin');
if (updateEnvironment) {
core.exportVariable('pythonLocation', installDir);
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
@ -97315,7 +97315,12 @@ async function installGraalPy(graalpyVersion, architecture, allowPreReleases, re
try {
const graalpyPath = await tc.downloadTool(downloadUrl, undefined, AUTH);
core.info('Extracting downloaded archive...');
if (utils_1.IS_WINDOWS) {
downloadDir = await tc.extractZip(graalpyPath);
}
else {
downloadDir = await tc.extractTar(graalpyPath);
}
// root folder in archive can have unpredictable name so just take the first folder
// downloadDir is unique folder under TEMP and can't contain any other folders
const archiveName = fs_1.default.readdirSync(downloadDir)[0];
@ -97324,7 +97329,7 @@ async function installGraalPy(graalpyVersion, architecture, allowPreReleases, re
if (!(0, utils_1.isNightlyKeyword)(resolvedGraalPyVersion)) {
installDir = await tc.cacheDir(toolDir, 'GraalPy', resolvedGraalPyVersion, architecture);
}
const binaryPath = (0, utils_1.getBinaryDirectory)(installDir);
const binaryPath = path.join(installDir, 'bin');
await createGraalPySymlink(binaryPath, resolvedGraalPyVersion);
await installPip(binaryPath);
return { installDir, resolvedGraalPyVersion };
@ -97352,6 +97357,9 @@ async function getAvailableGraalPyVersions() {
if (AUTH) {
headers.authorization = AUTH;
}
/*
Get releases first.
*/
let url = 'https://api.github.com/repos/oracle/graalpython/releases';
const result = [];
do {
@ -97362,6 +97370,19 @@ async function getAvailableGraalPyVersions() {
result.push(...response.result);
url = (0, utils_1.getNextPageUrl)(response);
} while (url);
/*
Add pre-release builds.
*/
url =
'https://api.github.com/repos/graalvm/graal-languages-ea-builds/releases';
do {
const response = await http.getJson(url, headers);
if (!response.result) {
throw new Error(`Unable to retrieve the list of available GraalPy versions from '${url}'`);
}
result.push(...response.result);
url = (0, utils_1.getNextPageUrl)(response);
} while (url);
return result;
}
async function createGraalPySymlink(graalpyBinaryPath, graalpyVersion) {
@ -97381,7 +97402,7 @@ async function installPip(pythonLocation) {
await exec.exec(`${pythonBinary} -m ensurepip --default-pip`);
}
function graalPyTagToVersion(tag) {
const versionPattern = /.*-(\d+\.\d+\.\d+(?:\.\d+)?)((?:a|b|rc))?(\d*)?/;
const versionPattern = /.*-(\d+\.\d+\.\d+(?:\.\d+)?)(?:-((?:ea|a|b|rc))\.0*(\d+))?/;
const match = tag.match(versionPattern);
if (match && match[2]) {
return `${match[1]}-${match[2]}.${match[3]}`;
@ -97431,8 +97452,9 @@ function toGraalPyArchitecture(architecture) {
function findAsset(item, architecture, platform) {
const graalpyArch = toGraalPyArchitecture(architecture);
const graalpyPlatform = toGraalPyPlatform(platform);
const graalpyExt = platform == 'win32' ? 'zip' : 'tar.gz';
const found = item.assets.filter(file => file.name.startsWith('graalpy') &&
file.name.endsWith(`-${graalpyPlatform}-${graalpyArch}.tar.gz`));
file.name.endsWith(`-${graalpyPlatform}-${graalpyArch}.${graalpyExt}`));
/*
In the future there could be more variants of GraalPy for a single release. Pick the shortest name, that one is the most likely to be the primary variant.
*/
@ -98363,7 +98385,7 @@ function getVersionInputFromFile(versionFile) {
}
}
/**
* Get the directory containing interpreter binary from installation directory of PyPy or GraalPy
* Get the directory containing interpreter binary from installation directory of PyPy
* - On Linux and macOS, the Python interpreter is in 'bin'.
* - On Windows, it is in the installation root.
*/

View file

@ -1,11 +1,6 @@
import * as path from 'path';
import * as graalpyInstall from './install-graalpy';
import {
IS_WINDOWS,
validateVersion,
IGraalPyManifestRelease,
getBinaryDirectory
} from './utils';
import {IS_WINDOWS, validateVersion, IGraalPyManifestRelease} from './utils';
import * as semver from 'semver';
import * as core from '@actions/core';
@ -62,11 +57,8 @@ export async function findGraalPyVersion(
const pipDir = IS_WINDOWS ? 'Scripts' : 'bin';
const _binDir = path.join(installDir, pipDir);
const binaryExtension = IS_WINDOWS ? '.exe' : '';
const pythonPath = path.join(
IS_WINDOWS ? installDir : _binDir,
`python${binaryExtension}`
);
const pythonLocation = getBinaryDirectory(installDir);
const pythonPath = path.join(_binDir, `python${binaryExtension}`);
const pythonLocation = path.join(installDir, 'bin');
if (updateEnvironment) {
core.exportVariable('pythonLocation', installDir);
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython

View file

@ -15,7 +15,6 @@ import {
IGraalPyManifestRelease,
createSymlinkInFolder,
isNightlyKeyword,
getBinaryDirectory,
getNextPageUrl
} from './utils';
@ -64,7 +63,11 @@ export async function installGraalPy(
const graalpyPath = await tc.downloadTool(downloadUrl, undefined, AUTH);
core.info('Extracting downloaded archive...');
if (IS_WINDOWS) {
downloadDir = await tc.extractZip(graalpyPath);
} else {
downloadDir = await tc.extractTar(graalpyPath);
}
// root folder in archive can have unpredictable name so just take the first folder
// downloadDir is unique folder under TEMP and can't contain any other folders
@ -81,7 +84,7 @@ export async function installGraalPy(
);
}
const binaryPath = getBinaryDirectory(installDir);
const binaryPath = path.join(installDir, 'bin');
await createGraalPySymlink(binaryPath, resolvedGraalPyVersion);
await installPip(binaryPath);
@ -115,6 +118,9 @@ export async function getAvailableGraalPyVersions() {
headers.authorization = AUTH;
}
/*
Get releases first.
*/
let url: string | null =
'https://api.github.com/repos/oracle/graalpython/releases';
const result: IGraalPyManifestRelease[] = [];
@ -130,6 +136,23 @@ export async function getAvailableGraalPyVersions() {
url = getNextPageUrl(response);
} while (url);
/*
Add pre-release builds.
*/
url =
'https://api.github.com/repos/graalvm/graal-languages-ea-builds/releases';
do {
const response: ifm.TypedResponse<IGraalPyManifestRelease[]> =
await http.getJson(url, headers);
if (!response.result) {
throw new Error(
`Unable to retrieve the list of available GraalPy versions from '${url}'`
);
}
result.push(...response.result);
url = getNextPageUrl(response);
} while (url);
return result;
}
@ -175,7 +198,8 @@ async function installPip(pythonLocation: string) {
}
export function graalPyTagToVersion(tag: string) {
const versionPattern = /.*-(\d+\.\d+\.\d+(?:\.\d+)?)((?:a|b|rc))?(\d*)?/;
const versionPattern =
/.*-(\d+\.\d+\.\d+(?:\.\d+)?)(?:-((?:ea|a|b|rc))\.0*(\d+))?/;
const match = tag.match(versionPattern);
if (match && match[2]) {
return `${match[1]}-${match[2]}.${match[3]}`;
@ -251,10 +275,11 @@ export function findAsset(
) {
const graalpyArch = toGraalPyArchitecture(architecture);
const graalpyPlatform = toGraalPyPlatform(platform);
const graalpyExt = platform == 'win32' ? 'zip' : 'tar.gz';
const found = item.assets.filter(
file =>
file.name.startsWith('graalpy') &&
file.name.endsWith(`-${graalpyPlatform}-${graalpyArch}.tar.gz`)
file.name.endsWith(`-${graalpyPlatform}-${graalpyArch}.${graalpyExt}`)
);
/*
In the future there could be more variants of GraalPy for a single release. Pick the shortest name, that one is the most likely to be the primary variant.

View file

@ -379,7 +379,7 @@ export function getVersionInputFromFile(versionFile: string): string[] {
}
/**
* Get the directory containing interpreter binary from installation directory of PyPy or GraalPy
* Get the directory containing interpreter binary from installation directory of PyPy
* - On Linux and macOS, the Python interpreter is in 'bin'.
* - On Windows, it is in the installation root.
*/