2022-01-03 08:52:11 +01:00
import * as c from './constants'
import * as core from '@actions/core'
2022-11-03 17:13:30 +01:00
import * as github from '@actions/github'
2022-01-03 08:52:11 +01:00
import * as httpClient from '@actions/http-client'
2024-02-13 15:44:08 +01:00
import * as semver from 'semver'
2022-01-03 08:52:11 +01:00
import * as tc from '@actions/tool-cache'
2025-01-21 09:00:42 +01:00
import * as fs from 'fs'
2022-03-11 10:35:03 +01:00
import { ExecOptions , exec as e } from '@actions/exec'
2022-03-03 17:24:24 +01:00
import { readFileSync , readdirSync } from 'fs'
2022-01-03 08:52:11 +01:00
import { Octokit } from '@octokit/core'
2022-03-03 17:24:24 +01:00
import { createHash } from 'crypto'
2022-01-03 08:52:11 +01:00
import { join } from 'path'
2025-01-21 09:00:42 +01:00
import { tmpdir } from 'os'
2022-01-03 08:52:11 +01:00
2022-12-13 09:12:43 +01:00
// Set up Octokit for github.com only and in the same way as @actions/github (see https://git.io/Jy9YP)
const baseUrl = 'https://api.github.com'
const GitHubDotCom = Octokit . defaults ( {
2022-01-03 08:52:11 +01:00
baseUrl ,
request : {
agent : new httpClient . HttpClient ( ) . getAgent ( baseUrl )
}
} )
2022-03-11 10:35:03 +01:00
export async function exec (
commandLine : string ,
args? : string [ ] ,
options? : ExecOptions | undefined
) : Promise < void > {
const exitCode = await e ( commandLine , args , options )
if ( exitCode !== 0 ) {
throw new Error (
` ' ${ [ commandLine ]
. concat ( args || [ ] )
. join ( ' ' ) } ' exited with a non - zero code : $ { exitCode } `
)
}
}
2024-02-21 10:06:08 +01:00
export async function getLatestRelease (
2024-02-13 13:37:00 +01:00
repo : string
2024-02-21 10:06:08 +01:00
) : Promise < c.LatestReleaseResponse [ 'data' ] > {
2024-02-13 13:37:00 +01:00
const githubToken = getGitHubToken ( )
const options = githubToken . length > 0 ? { auth : githubToken } : { }
const octokit = new GitHubDotCom ( options )
2024-02-21 10:06:08 +01:00
return (
await octokit . request ( 'GET /repos/{owner}/{repo}/releases/latest' , {
2024-02-13 13:37:00 +01:00
owner : c.GRAALVM_GH_USER ,
repo
} )
) . data
}
2024-02-21 10:06:08 +01:00
export async function getContents (
repo : string ,
path : string
) : Promise < c.ContentsResponse [ 'data' ] > {
2022-11-03 17:13:30 +01:00
const githubToken = getGitHubToken ( )
2022-01-03 08:52:11 +01:00
const options = githubToken . length > 0 ? { auth : githubToken } : { }
2022-12-13 09:12:43 +01:00
const octokit = new GitHubDotCom ( options )
2022-01-03 08:52:11 +01:00
return (
2024-02-21 10:06:08 +01:00
await octokit . request ( 'GET /repos/{owner}/{repo}/contents/{path}' , {
2022-01-03 08:52:11 +01:00
owner : c.GRAALVM_GH_USER ,
2024-02-21 10:06:08 +01:00
repo ,
path
2022-01-03 08:52:11 +01:00
} )
) . data
}
2023-06-13 19:17:04 +02:00
export async function getTaggedRelease (
2024-02-20 17:14:10 +03:00
owner : string ,
2023-06-13 19:17:04 +02:00
repo : string ,
tag : string
) : Promise < c.LatestReleaseResponse [ 'data' ] > {
const githubToken = getGitHubToken ( )
const options = githubToken . length > 0 ? { auth : githubToken } : { }
const octokit = new GitHubDotCom ( options )
return (
await octokit . request ( 'GET /repos/{owner}/{repo}/releases/tags/{tag}' , {
2024-02-20 17:14:10 +03:00
owner ,
2023-06-13 19:17:04 +02:00
repo ,
tag
} )
) . data
}
2023-06-16 09:19:49 +02:00
export async function getMatchingTags (
2024-02-20 17:14:10 +03:00
owner : string ,
repo : string ,
2023-06-16 09:19:49 +02:00
tagPrefix : string
) : Promise < c.MatchingRefsResponse [ 'data' ] > {
const githubToken = getGitHubToken ( )
const options = githubToken . length > 0 ? { auth : githubToken } : { }
const octokit = new GitHubDotCom ( options )
return (
await octokit . request (
'GET /repos/{owner}/{repo}/git/matching-refs/tags/{tagPrefix}' ,
{
2024-02-20 17:14:10 +03:00
owner ,
repo ,
2023-06-16 09:19:49 +02:00
tagPrefix
}
)
) . data
}
2022-01-03 08:52:11 +01:00
export async function downloadAndExtractJDK (
downloadUrl : string
) : Promise < string > {
2022-03-03 17:24:24 +01:00
return findJavaHomeInSubfolder (
await extract ( await tc . downloadTool ( downloadUrl ) )
)
2022-03-01 10:50:00 +01:00
}
export async function downloadExtractAndCacheJDK (
2022-03-03 17:24:24 +01:00
downloader : ( ) = > Promise < string > ,
2022-03-01 10:50:00 +01:00
toolName : string ,
version : string
) : Promise < string > {
2022-03-08 11:12:22 +01:00
const semVersion = toSemVer ( version )
let toolPath = tc . find ( toolName , semVersion )
2022-03-01 10:50:00 +01:00
if ( toolPath ) {
core . info ( ` Found ${ toolName } ${ version } in tool-cache @ ${ toolPath } ` )
} else {
2022-03-03 17:24:24 +01:00
const extractDir = await extract ( await downloader ( ) )
2022-03-01 10:50:00 +01:00
core . info ( ` Adding ${ toolName } ${ version } to tool-cache ... ` )
2022-03-08 11:12:22 +01:00
toolPath = await tc . cacheDir ( extractDir , toolName , semVersion )
2022-03-01 10:50:00 +01:00
}
return findJavaHomeInSubfolder ( toolPath )
}
2022-03-03 17:24:24 +01:00
export function calculateSHA256 ( filePath : string ) : string {
const hashSum = createHash ( 'sha256' )
hashSum . update ( readFileSync ( filePath ) )
return hashSum . digest ( 'hex' )
}
async function extract ( downloadPath : string ) : Promise < string > {
if ( c . GRAALVM_FILE_EXTENSION === '.tar.gz' ) {
2022-03-01 10:50:00 +01:00
return await tc . extractTar ( downloadPath )
2022-03-03 17:24:24 +01:00
} else if ( c . GRAALVM_FILE_EXTENSION === '.zip' ) {
2022-03-01 10:50:00 +01:00
return await tc . extractZip ( downloadPath )
2022-01-03 08:52:11 +01:00
} else {
2022-03-03 17:24:24 +01:00
throw new Error (
` Unexpected filetype downloaded: ${ c . GRAALVM_FILE_EXTENSION } `
)
2022-01-03 08:52:11 +01:00
}
}
2022-03-01 10:50:00 +01:00
function findJavaHomeInSubfolder ( searchPath : string ) : string {
2022-01-03 08:52:11 +01:00
const baseContents = readdirSync ( searchPath )
if ( baseContents . length === 1 ) {
return join ( searchPath , baseContents [ 0 ] , c . JDK_HOME_SUFFIX )
} else {
throw new Error (
` Unexpected amount of directory items found: ${ baseContents . length } `
)
}
}
2022-03-08 11:12:22 +01:00
2022-11-02 14:00:51 +01:00
export function toSemVer ( version : string ) : string {
2022-03-08 11:12:22 +01:00
const parts = version . split ( '.' )
2024-02-13 15:44:08 +01:00
if ( parts . length === 4 ) {
/ * *
* Turn legacy GraalVM version numbers ( e . g . , ` 22.0.0.2 ` ) into valid
* semver . org versions ( e . g . , ` 22.0.0-2 ` ) .
* /
return ` ${ parts [ 0 ] } . ${ parts [ 1 ] } . ${ parts . slice ( 2 ) . join ( '-' ) } `
}
const versionParts = version . split ( '-' , 2 )
const suffix = versionParts . length === 2 ? '-' + versionParts [ 1 ] : ''
const validVersion = semver . valid ( semver . coerce ( versionParts [ 0 ] ) + suffix )
if ( ! validVersion ) {
throw new Error (
` Unable to convert ' ${ version } ' to semantic version. ${ c . ERROR_HINT } `
)
}
return validVersion
2022-03-08 11:12:22 +01:00
}
2022-11-03 17:13:30 +01:00
export function isPREvent ( ) : boolean {
return process . env [ c . ENV_GITHUB_EVENT_NAME ] === c . EVENT_NAME_PULL_REQUEST
}
function getGitHubToken ( ) : string {
return core . getInput ( c . INPUT_GITHUB_TOKEN )
}
2024-08-07 21:07:39 +03:00
export async function findExistingPRCommentId (
bodyStartsWith : string
) : Promise < number | undefined > {
if ( ! isPREvent ( ) ) {
throw new Error ( 'Not a PR event.' )
}
const context = github . context
const octokit = github . getOctokit ( getGitHubToken ( ) )
try {
const comments = await octokit . paginate ( octokit . rest . issues . listComments , {
. . . context . repo ,
issue_number : context.payload.pull_request?.number as number
} )
const matchingComment = comments . reverse ( ) . find ( comment = > {
return comment . body && comment . body . startsWith ( bodyStartsWith )
} )
return matchingComment ? matchingComment.id : undefined
} catch ( err ) {
core . error (
` Failed to list pull request comments. Please make sure this job has 'write' permissions for the 'pull-requests' scope (see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions)? Internal error: ${ err } `
)
}
}
export async function updatePRComment (
content : string ,
commentId : number
) : Promise < void > {
if ( ! isPREvent ( ) ) {
throw new Error ( 'Not a PR event.' )
}
try {
await github . getOctokit ( getGitHubToken ( ) ) . rest . issues . updateComment ( {
. . . github . context . repo ,
comment_id : commentId ,
body : content
} )
} catch ( err ) {
core . error (
` Failed to update pull request comment. Please make sure this job has 'write' permissions for the 'pull-requests' scope (see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions)? Internal error: ${ err } `
)
}
}
2022-11-03 17:13:30 +01:00
export async function createPRComment ( content : string ) : Promise < void > {
if ( ! isPREvent ( ) ) {
throw new Error ( 'Not a PR event.' )
}
const context = github . context
2022-11-08 18:15:51 +01:00
try {
await github . getOctokit ( getGitHubToken ( ) ) . rest . issues . createComment ( {
. . . context . repo ,
issue_number : context.payload.pull_request?.number as number ,
body : content
} )
} catch ( err ) {
core . error (
` Failed to create pull request comment. Please make sure this job has 'write' permissions for the 'pull-requests' scope (see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions)? Internal error: ${ err } `
)
}
2022-11-03 17:13:30 +01:00
}
2025-01-21 09:00:42 +01:00
export function tmpfile ( fileName : string ) {
return join ( tmpdir ( ) , fileName )
}
export function setNativeImageOption (
javaVersionOrDev : string ,
optionValue : string
) : void {
const coercedJavaVersionOrDev = semver . coerce ( javaVersionOrDev )
if (
( coercedJavaVersionOrDev &&
semver . gte ( coercedJavaVersionOrDev , '22.0.0' ) ) ||
javaVersionOrDev === c . VERSION_DEV ||
javaVersionOrDev . endsWith ( '-ea' )
) {
/* NATIVE_IMAGE_OPTIONS was introduced in GraalVM for JDK 22 (so were EA builds). */
let newOptionValue = optionValue
const existingOptions = process . env [ c . NATIVE_IMAGE_OPTIONS_ENV ]
if ( existingOptions ) {
newOptionValue = ` ${ existingOptions } ${ newOptionValue } `
}
core . exportVariable ( c . NATIVE_IMAGE_OPTIONS_ENV , newOptionValue )
} else {
const optionsFile = getNativeImageOptionsFile ( )
if ( fs . existsSync ( optionsFile ) ) {
fs . appendFileSync ( optionsFile , ` ${ optionValue } ` )
} else {
fs . writeFileSync ( optionsFile , ` NativeImageArgs = ${ optionValue } ` )
}
}
}
const NATIVE_IMAGE_CONFIG_FILE = tmpfile ( 'native-image-options.properties' )
const NATIVE_IMAGE_CONFIG_FILE_ENV = 'NATIVE_IMAGE_CONFIG_FILE'
function getNativeImageOptionsFile ( ) : string {
let optionsFile = process . env [ NATIVE_IMAGE_CONFIG_FILE_ENV ]
if ( optionsFile === undefined ) {
optionsFile = NATIVE_IMAGE_CONFIG_FILE
core . exportVariable ( NATIVE_IMAGE_CONFIG_FILE_ENV , optionsFile )
}
return optionsFile
}