2020-12-17 16:03:54 +01:00
import * as path from 'path' ;
import * as pypyInstall from './install-pypy' ;
import {
IS_WINDOWS ,
2021-04-12 19:59:38 +02:00
WINDOWS_ARCHS ,
2020-12-17 16:03:54 +01:00
validateVersion ,
getPyPyVersionFromPath ,
readExactPyPyVersionFile ,
2022-07-25 16:54:04 +02:00
validatePythonVersionFormatForPyPy ,
IPyPyManifestRelease
2020-12-17 16:03:54 +01:00
} from './utils' ;
import * as semver from 'semver' ;
import * as core from '@actions/core' ;
import * as tc from '@actions/tool-cache' ;
interface IPyPyVersionSpec {
pypyVersion : string ;
pythonVersion : string ;
}
export async function findPyPyVersion (
versionSpec : string ,
2022-06-29 17:00:51 +02:00
architecture : string ,
2022-07-25 16:54:04 +02:00
updateEnvironment : boolean ,
2023-01-27 22:19:31 +01:00
checkLatest : boolean ,
allowPreReleases : boolean
2020-12-17 16:03:54 +01:00
) : Promise < { resolvedPyPyVersion : string ; resolvedPythonVersion : string } > {
let resolvedPyPyVersion = '' ;
let resolvedPythonVersion = '' ;
let installDir : string | null ;
2022-07-25 16:54:04 +02:00
let releases : IPyPyManifestRelease [ ] | undefined ;
2020-12-17 16:03:54 +01:00
const pypyVersionSpec = parsePyPyVersion ( versionSpec ) ;
2022-07-25 16:54:04 +02:00
if ( checkLatest ) {
releases = await pypyInstall . getAvailablePyPyVersions ( ) ;
if ( releases && releases . length > 0 ) {
const releaseData = pypyInstall . findRelease (
releases ,
pypyVersionSpec . pythonVersion ,
pypyVersionSpec . pypyVersion ,
2023-01-27 22:19:31 +01:00
architecture ,
false
2022-07-25 16:54:04 +02:00
) ;
if ( releaseData ) {
core . info (
` Resolved as PyPy ${ releaseData . resolvedPyPyVersion } with Python ( ${ releaseData . resolvedPythonVersion } ) `
) ;
pypyVersionSpec . pythonVersion = releaseData . resolvedPythonVersion ;
pypyVersionSpec . pypyVersion = releaseData . resolvedPyPyVersion ;
} else {
core . info (
` Failed to resolve PyPy ${ pypyVersionSpec . pypyVersion } with Python ( ${ pypyVersionSpec . pythonVersion } ) from manifest `
) ;
}
}
}
2020-12-17 16:03:54 +01:00
( { installDir , resolvedPythonVersion , resolvedPyPyVersion } = findPyPyToolCache (
pypyVersionSpec . pythonVersion ,
pypyVersionSpec . pypyVersion ,
architecture
) ) ;
if ( ! installDir ) {
( {
installDir ,
resolvedPythonVersion ,
resolvedPyPyVersion
} = await pypyInstall . installPyPy (
pypyVersionSpec . pypyVersion ,
pypyVersionSpec . pythonVersion ,
2022-07-25 16:54:04 +02:00
architecture ,
2023-01-27 22:19:31 +01:00
allowPreReleases ,
2022-07-25 16:54:04 +02:00
releases
2020-12-17 16:03:54 +01:00
) ) ;
}
const pipDir = IS_WINDOWS ? 'Scripts' : 'bin' ;
const _binDir = path . join ( installDir , pipDir ) ;
2022-05-23 20:59:37 +02:00
const binaryExtension = IS_WINDOWS ? '.exe' : '' ;
const pythonPath = path . join (
IS_WINDOWS ? installDir : _binDir ,
` python ${ binaryExtension } `
) ;
2020-12-17 16:03:54 +01:00
const pythonLocation = pypyInstall . getPyPyBinaryPath ( installDir ) ;
2022-06-29 17:00:51 +02:00
if ( updateEnvironment ) {
core . exportVariable ( 'pythonLocation' , installDir ) ;
2022-06-28 08:37:00 +02:00
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
core . exportVariable ( 'Python_ROOT_DIR' , installDir ) ;
// https://cmake.org/cmake/help/latest/module/FindPython2.html#module:FindPython2
core . exportVariable ( 'Python2_ROOT_DIR' , installDir ) ;
// https://cmake.org/cmake/help/latest/module/FindPython3.html#module:FindPython3
core . exportVariable ( 'Python3_ROOT_DIR' , installDir ) ;
2022-06-29 17:00:51 +02:00
core . exportVariable ( 'PKG_CONFIG_PATH' , pythonLocation + '/lib/pkgconfig' ) ;
core . addPath ( pythonLocation ) ;
core . addPath ( _binDir ) ;
}
2022-04-04 19:12:56 +02:00
core . setOutput ( 'python-version' , 'pypy' + resolvedPyPyVersion . trim ( ) ) ;
2022-05-23 20:59:37 +02:00
core . setOutput ( 'python-path' , pythonPath ) ;
2020-12-17 16:03:54 +01:00
return { resolvedPyPyVersion , resolvedPythonVersion } ;
}
export function findPyPyToolCache (
pythonVersion : string ,
pypyVersion : string ,
architecture : string
) {
let resolvedPyPyVersion = '' ;
let resolvedPythonVersion = '' ;
2021-04-12 19:59:38 +02:00
let installDir : string | null = IS_WINDOWS
? findPyPyInstallDirForWindows ( pythonVersion )
: tc . find ( 'PyPy' , pythonVersion , architecture ) ;
2020-12-17 16:03:54 +01:00
if ( installDir ) {
// 'tc.find' finds tool based on Python version but we also need to check
// whether PyPy version satisfies requested version.
resolvedPythonVersion = getPyPyVersionFromPath ( installDir ) ;
resolvedPyPyVersion = readExactPyPyVersionFile ( installDir ) ;
const isPyPyVersionSatisfies = semver . satisfies (
resolvedPyPyVersion ,
pypyVersion
) ;
if ( ! isPyPyVersionSatisfies ) {
installDir = null ;
resolvedPyPyVersion = '' ;
resolvedPythonVersion = '' ;
}
}
if ( ! installDir ) {
core . info (
` PyPy version ${ pythonVersion } ( ${ pypyVersion } ) was not found in the local cache `
) ;
}
return { installDir , resolvedPythonVersion , resolvedPyPyVersion } ;
}
export function parsePyPyVersion ( versionSpec : string ) : IPyPyVersionSpec {
const versions = versionSpec . split ( '-' ) . filter ( item = > ! ! item ) ;
2022-05-18 15:20:53 +02:00
if ( /^(pypy)(.+)/ . test ( versions [ 0 ] ) ) {
let pythonVersion = versions [ 0 ] . replace ( 'pypy' , '' ) ;
versions . splice ( 0 , 1 , 'pypy' , pythonVersion ) ;
}
2020-12-17 16:03:54 +01:00
if ( versions . length < 2 || versions [ 0 ] != 'pypy' ) {
throw new Error (
2022-05-18 15:20:53 +02:00
"Invalid 'version' property for PyPy. PyPy version should be specified as 'pypy<python-version>' or 'pypy-<python-version>'. See README for examples and documentation."
2020-12-17 16:03:54 +01:00
) ;
}
const pythonVersion = versions [ 1 ] ;
let pypyVersion : string ;
if ( versions . length > 2 ) {
pypyVersion = pypyInstall . pypyVersionToSemantic ( versions [ 2 ] ) ;
} else {
pypyVersion = 'x' ;
}
if ( ! validateVersion ( pythonVersion ) || ! validateVersion ( pypyVersion ) ) {
throw new Error (
"Invalid 'version' property for PyPy. Both Python version and PyPy versions should satisfy SemVer notation. See README for examples and documentation."
) ;
}
if ( ! validatePythonVersionFormatForPyPy ( pythonVersion ) ) {
throw new Error (
"Invalid format of Python version for PyPy. Python version should be specified in format 'x.y'. See README for examples and documentation."
) ;
}
return {
pypyVersion : pypyVersion ,
pythonVersion : pythonVersion
} ;
}
2021-04-12 19:59:38 +02:00
export function findPyPyInstallDirForWindows ( pythonVersion : string ) : string {
let installDir = '' ;
WINDOWS_ARCHS . forEach (
architecture = >
( installDir = installDir || tc . find ( 'PyPy' , pythonVersion , architecture ) )
) ;
return installDir ;
}