2019-08-20 10:27:52 -04:00
import * as core from '@actions/core' ;
2026-07-15 22:10:44 +05:30
import * as finder from './find-python.js' ;
import * as finderPyPy from './find-pypy.js' ;
import * as finderGraalPy from './find-graalpy.js' ;
2026-09-09 00:31:48 +02:00
import { isMirrorCustomized } from './install-python.js' ;
2019-08-20 10:27:52 -04:00
import * as path from 'path' ;
2020-04-29 20:57:02 +03:00
import * as os from 'os' ;
2026-07-15 22:10:44 +05:30
import { fileURLToPath } from 'url' ;
2022-06-02 07:37:57 -07:00
import fs from 'fs' ;
2026-07-15 22:10:44 +05:30
import { getCacheDistributor } from './cache-distributions/cache-factory.js' ;
2023-06-28 22:02:44 +02:00
import {
isCacheFeatureAvailable ,
logWarning ,
IS_MAC ,
getVersionInputFromFile ,
2025-05-21 23:19:28 +02:00
getVersionsInputFromPlainFile
2026-07-15 22:10:44 +05:30
} from './utils.js' ;
2019-08-20 10:27:52 -04:00
2020-12-17 18:03:54 +03:00
function isPyPyVersion ( versionSpec : string ) {
2022-05-18 15:20:53 +02:00
return versionSpec . startsWith ( 'pypy' );
2020-12-17 18:03:54 +03:00
}
2023-10-10 14:59:54 +02:00
function isGraalPyVersion ( versionSpec : string ) {
return versionSpec . startsWith ( 'graalpy' );
}
2026-09-09 00:31:48 +02:00
// `mirror` only redirects CPython distributions. PyPy and GraalPy resolve from
// downloads.python.org and the GitHub releases API respectively, so warn rather
// than let the input look like it applied. Only warns when the user actually
// set a custom mirror: action.yml gives `mirror` a default, so a plain
// getInput() check would fire on every pypy-*/graalpy-* run.
function warnIfMirrorUnsupported ( versionSpec : string ) {
if ( ! isMirrorCustomized ()) {
return ;
}
const implementation = isPyPyVersion ( versionSpec ) ? 'PyPy' : 'GraalPy' ;
core . warning (
`The 'mirror' input only applies to CPython distributions and is ignored for ${ implementation } (' ${ versionSpec } '), which is downloaded from its own upstream source.`
);
}
2025-08-29 00:00:09 +05:30
async function cacheDependencies ( cache : string , pythonVersion : string ) {
2021-11-17 13:31:22 +03:00
const cacheDependencyPath =
core . getInput ( 'cache-dependency-path' ) || undefined ;
const cacheDistributor = getCacheDistributor (
cache ,
pythonVersion ,
2025-08-29 00:00:09 +05:30
cacheDependencyPath
2021-11-17 13:31:22 +03:00
);
await cacheDistributor . restoreCache ();
}
2023-06-28 22:02:44 +02:00
function resolveVersionInputFromDefaultFile () : string [] {
const couples : [ string , ( versionFile : string ) => string []][] = [
2025-05-21 23:19:28 +02:00
[ '.python-version' , getVersionsInputFromPlainFile ]
2023-06-28 22:02:44 +02:00
];
for ( const [ versionFile , _fn ] of couples ) {
logWarning (
`Neither 'python-version' nor 'python-version-file' inputs were supplied. Attempting to find ' ${ versionFile } ' file.`
2022-06-02 07:37:57 -07:00
);
2023-06-28 22:02:44 +02:00
if ( fs . existsSync ( versionFile )) {
return _fn ( versionFile );
} else {
logWarning ( ` ${ versionFile } doesn't exist.` );
}
2022-06-02 07:37:57 -07:00
}
2023-06-28 22:02:44 +02:00
return [];
}
function resolveVersionInput() {
let versions = core . getMultilineInput ( 'python-version' );
const versionFile = core . getInput ( 'python-version-file' );
2022-06-02 07:37:57 -07:00
2022-12-22 13:02:09 +01:00
if ( versions . length ) {
2023-06-28 22:02:44 +02:00
if ( versionFile ) {
core . warning (
'Both python-version and python-version-file inputs are specified, only python-version will be used.'
2022-06-30 17:32:12 +02:00
);
2022-06-30 13:38:43 +02:00
}
2023-06-28 22:02:44 +02:00
} else {
if ( versionFile ) {
if ( ! fs . existsSync ( versionFile )) {
throw new Error (
`The specified python version file at: ${ versionFile } doesn't exist.`
);
}
versions = getVersionInputFromFile ( versionFile );
} else {
versions = resolveVersionInputFromDefaultFile ();
}
2022-06-30 13:38:43 +02:00
}
2022-12-22 13:02:09 +01:00
return versions ;
2022-06-02 07:37:57 -07:00
}
2019-08-20 10:27:52 -04:00
async function run() {
2022-07-22 16:03:42 +08:00
if ( IS_MAC ) {
process . env [ 'AGENT_TOOLSDIRECTORY' ] = '/Users/runner/hostedtoolcache' ;
}
2022-07-15 12:57:51 +08:00
if ( process . env . AGENT_TOOLSDIRECTORY ? . trim ()) {
2022-02-17 19:21:13 +00:00
process . env [ 'RUNNER_TOOL_CACHE' ] = process . env [ 'AGENT_TOOLSDIRECTORY' ];
2022-02-17 18:35:19 +00:00
}
2022-07-22 16:03:42 +08:00
2022-04-27 11:28:44 +05:00
core . debug (
2022-07-26 20:40:49 +08:00
`Python is expected to be installed into ${ process . env [ 'RUNNER_TOOL_CACHE' ] } `
2022-04-27 11:28:44 +05:00
);
2019-08-20 10:27:52 -04:00
try {
2022-12-22 13:02:09 +01:00
const versions = resolveVersionInput ();
2022-07-25 16:54:04 +02:00
const checkLatest = core . getBooleanInput ( 'check-latest' );
2023-01-27 22:19:31 +01:00
const allowPreReleases = core . getBooleanInput ( 'allow-prereleases' );
2025-03-04 17:49:43 -05:00
const freethreaded = core . getBooleanInput ( 'freethreaded' );
2022-07-25 16:54:04 +02:00
2022-12-22 13:02:09 +01:00
if ( versions . length ) {
let pythonVersion = '' ;
2020-04-29 20:57:02 +03:00
const arch : string = core . getInput ( 'architecture' ) || os . arch ();
2022-06-29 17:00:51 +02:00
const updateEnvironment = core . getBooleanInput ( 'update-environment' );
2022-12-22 13:02:09 +01:00
core . startGroup ( 'Installed versions' );
for ( const version of versions ) {
if ( isPyPyVersion ( version )) {
2026-09-09 00:31:48 +02:00
warnIfMirrorUnsupported ( version );
2022-12-22 13:02:09 +01:00
const installed = await finderPyPy . findPyPyVersion (
version ,
arch ,
updateEnvironment ,
2023-01-27 22:19:31 +01:00
checkLatest ,
allowPreReleases
2022-12-22 13:02:09 +01:00
);
pythonVersion = ` ${ installed . resolvedPyPyVersion } - ${ installed . resolvedPythonVersion } ` ;
core . info (
`Successfully set up PyPy ${ installed . resolvedPyPyVersion } with Python ( ${ installed . resolvedPythonVersion } )`
);
2023-10-10 14:59:54 +02:00
} else if ( isGraalPyVersion ( version )) {
2026-09-09 00:31:48 +02:00
warnIfMirrorUnsupported ( version );
2023-10-10 14:59:54 +02:00
const installed = await finderGraalPy . findGraalPyVersion (
version ,
arch ,
updateEnvironment ,
checkLatest ,
allowPreReleases
);
pythonVersion = ` ${ installed } ` ;
core . info ( `Successfully set up GraalPy ${ installed } ` );
2022-12-22 13:02:09 +01:00
} else {
2023-05-24 14:37:35 +02:00
if ( version . startsWith ( '2' )) {
core . warning (
2023-10-16 02:19:29 -07:00
'The support for python 2.7 was removed on June 19, 2023. Related issue: https://github.com/actions/setup-python/issues/672'
2023-05-24 14:37:35 +02:00
);
}
2022-12-22 13:02:09 +01:00
const installed = await finder . useCpythonVersion (
version ,
arch ,
updateEnvironment ,
2023-01-27 22:19:31 +01:00
checkLatest ,
2025-03-04 17:49:43 -05:00
allowPreReleases ,
freethreaded
2022-12-22 13:02:09 +01:00
);
pythonVersion = installed . version ;
core . info ( `Successfully set up ${ installed . impl } ( ${ pythonVersion } )` );
}
2021-11-17 13:31:22 +03:00
}
2022-12-22 13:02:09 +01:00
core . endGroup ();
2021-11-17 13:31:22 +03:00
const cache = core . getInput ( 'cache' );
2022-04-01 00:41:27 +05:30
if ( cache && isCacheFeatureAvailable ()) {
2021-11-17 13:31:22 +03:00
await cacheDependencies ( cache , pythonVersion );
2020-12-17 18:03:54 +03:00
}
2022-04-29 09:14:59 +05:00
} else {
core . warning (
2022-05-04 12:55:36 +05:00
'The `python-version` input is not set. The version of Python currently in `PATH` will be used.'
2022-04-29 09:14:59 +05:00
);
2019-08-20 10:27:52 -04:00
}
2026-07-15 22:10:44 +05:30
const matchersPath = path . join (
path . dirname ( fileURLToPath ( import . meta . url )),
'../..' ,
'.github'
);
2020-03-09 10:16:37 +01:00
core . info ( `##[add-matcher] ${ path . join ( matchersPath , 'python.json' ) } ` );
2019-08-20 10:27:52 -04:00
} catch ( err ) {
2021-11-17 13:31:22 +03:00
core . setFailed (( err as Error ). message );
2019-08-20 10:27:52 -04:00
}
}
run ();