2019-08-20 10:27:52 -04:00
import * as core from '@actions/core' ;
import * as finder from './find-python' ;
2020-12-17 18:03:54 +03:00
import * as finderPyPy from './find-pypy' ;
2023-10-10 14:59:54 +02:00
import * as finderGraalPy from './find-graalpy' ;
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' ;
2022-06-02 07:37:57 -07:00
import fs from 'fs' ;
2021-11-17 13:31:22 +03:00
import { getCacheDistributor } from './cache-distributions/cache-factory' ;
2023-06-28 22:02:44 +02:00
import {
isCacheFeatureAvailable ,
logWarning ,
IS_MAC ,
getVersionInputFromFile ,
2025-05-21 23:19:28 +02:00
getVersionsInputFromPlainFile
2023-06-28 22:02:44 +02:00
} from './utils' ;
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' ) ;
}
2025-06-25 10:10:44 +05:30
export async function cacheDependencies ( cache : string , pythonVersion : string ) {
2021-11-17 13:31:22 +03:00
const cacheDependencyPath =
core . getInput ( 'cache-dependency-path' ) || undefined ;
2025-06-25 10:10:44 +05:30
let resolvedDependencyPath : string | undefined = undefined ;
2025-07-14 17:59:48 +05:30
const overwrite = core . getBooleanInput ( 'overwrite' , { required : false } ) ;
2025-06-25 10:10:44 +05:30
if ( cacheDependencyPath ) {
const actionPath = process . env . GITHUB_ACTION_PATH || '' ;
const workspace = process . env . GITHUB_WORKSPACE || process . cwd ( ) ;
const sourcePath = path . resolve ( actionPath , cacheDependencyPath ) ;
const relativePath = path . relative ( actionPath , sourcePath ) ;
const targetPath = path . resolve ( workspace , relativePath ) ;
try {
const sourceExists = await fs . promises
. access ( sourcePath , fs . constants . F_OK )
. then ( ( ) = > true )
. catch ( ( ) = > false ) ;
if ( ! sourceExists ) {
core . warning (
` The resolved cache-dependency-path does not exist: ${ sourcePath } `
) ;
} else {
if ( sourcePath !== targetPath ) {
const targetDir = path . dirname ( targetPath ) ;
await fs . promises . mkdir ( targetDir , { recursive : true } ) ;
2025-07-11 13:33:16 +05:30
const targetExists = await fs . promises
. access ( targetPath , fs . constants . F_OK )
. then ( ( ) = > true )
. catch ( ( ) = > false ) ;
if ( targetExists && ! overwrite ) {
const filename = path . basename ( cacheDependencyPath ) ;
core . warning (
` A file named ' ${ filename } ' exists in both the composite action and the workspace. The file in the workspace will be used. To avoid ambiguity, consider renaming one of the files or setting 'overwrite: true'. `
) ;
core . info (
` Skipped copying ${ sourcePath } — target already exists at ${ targetPath } `
) ;
} else {
await fs . promises . copyFile ( sourcePath , targetPath ) ;
core . info (
` ${ targetExists ? 'Overwrote' : 'Copied' } ${ sourcePath } to ${ targetPath } `
) ;
}
2025-06-25 10:10:44 +05:30
} else {
core . info (
` Dependency file is already inside the workspace: ${ sourcePath } `
) ;
}
resolvedDependencyPath = path
. relative ( workspace , targetPath )
. replace ( /\\/g , '/' ) ;
core . info ( ` Resolved cache-dependency-path: ${ resolvedDependencyPath } ` ) ;
}
} catch ( error ) {
core . warning (
` Failed to copy file from ${ sourcePath } to ${ targetPath } : ${ error } `
) ;
}
}
// Pass resolvedDependencyPath if available, else fallback to original input
const dependencyPathForCache = resolvedDependencyPath ? ? cacheDependencyPath ;
2021-11-17 13:31:22 +03:00
const cacheDistributor = getCacheDistributor (
cache ,
pythonVersion ,
2025-06-25 10:10:44 +05:30
dependencyPathForCache
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 [ ] ;
}
2022-06-02 07:37:57 -07:00
2023-06-28 22:02:44 +02:00
function resolveVersionInput() {
let versions = core . getMultilineInput ( 'python-version' ) ;
const versionFile = core . getInput ( 'python-version-file' ) ;
2022-06-02 07:37:57 -07:00
2023-06-28 22:02:44 +02:00
if ( versions . length ) {
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-07-15 16:52:20 +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 ) ) {
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 ) ) {
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
}
2021-11-17 13:31:22 +03:00
const matchersPath = path . join ( __dirname , '../..' , '.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 ( ) ;