From df4b80eebe3ea88a8f072318ee01ba1ab87916a9 Mon Sep 17 00:00:00 2001 From: peterz Date: Tue, 20 Feb 2024 17:14:10 +0300 Subject: [PATCH] Added Liberica distribution --- .github/workflows/test.yml | 44 +++++++++++- __tests__/graalvm.test.ts | 3 +- __tests__/liberica.test.ts | 138 +++++++++++++++++++++++++++++++++++++ dist/cleanup/index.js | Bin 3679950 -> 3683881 bytes dist/main/index.js | Bin 3752937 -> 3763092 bytes src/constants.ts | 1 + src/graalvm.ts | 3 + src/liberica.ts | 105 ++++++++++++++++++++++++++++ src/main.ts | 4 ++ src/utils.ts | 9 ++- 10 files changed, 302 insertions(+), 5 deletions(-) create mode 100644 __tests__/liberica.test.ts create mode 100644 src/liberica.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 258965f..58490bb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -83,7 +83,7 @@ jobs: java --version native-image --version test-ce: # make sure the action works on a clean machine without building - needs: test + needs: test name: CE ${{ matrix.version }} + JDK${{ matrix.java-version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: @@ -243,6 +243,48 @@ jobs: java --version native-image --version if: runner.os == 'Windows' + test-liberica: + needs: test + name: ${{ matrix.version }} + JDK${{ matrix.java-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + java-version: ['17', '21.0.2'] + version: ['core', 'full', 'std', ''] + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + - name: Run setup-graalvm action + uses: ./ + with: + distribution: liberica + java-version: ${{ matrix.java-version }} + version: ${{ matrix.version }} + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Check environment + run: | + echo "GRAALVM_HOME: $GRAALVM_HOME" + [[ "$GRAALVM_HOME" == *"$RUNNER_TOOL_CACHE"* ]] || exit 12 + echo "JAVA_HOME: $JAVA_HOME" + java --version + java --version | fgrep -qw ${{ matrix.java-version }} || exit 23 + native-image --version + native-image --version | fgrep -qw ${{ matrix.java-version }} || exit 24 + if: runner.os != 'Windows' + - name: Check Windows environment + shell: pwsh + run: | + echo "GRAALVM_HOME: $env:GRAALVM_HOME" + echo "JAVA_HOME: $env:JAVA_HOME" + java --version + if (!(java --version | findstr \<${{ matrix.java-version }}\>)) { + exit 23 + } + native-image --version + if (!(native-image --version | findstr \<${{ matrix.java-version }}\>)) { + exit 24 + } + if: runner.os == 'Windows' test-native-image-windows: name: native-image on windows-latest runs-on: windows-latest diff --git a/__tests__/graalvm.test.ts b/__tests__/graalvm.test.ts index 13e6c02..29edaa7 100644 --- a/__tests__/graalvm.test.ts +++ b/__tests__/graalvm.test.ts @@ -7,7 +7,7 @@ import { findHighestJavaVersion, findLatestEABuildDownloadUrl } from '../src/graalvm' -import {GRAALVM_RELEASES_REPO} from '../src/constants' +import {GRAALVM_GH_USER, GRAALVM_RELEASES_REPO} from '../src/constants' process.env['RUNNER_TOOL_CACHE'] = path.join(__dirname, 'TOOL_CACHE') process.env['RUNNER_TEMP'] = path.join(__dirname, 'TEMP') @@ -53,6 +53,7 @@ test('find version/javaVersion', async () => { expect(error.message).toContain('Unable to find the latest Java version for') const latestRelease = await getTaggedRelease( + GRAALVM_GH_USER, GRAALVM_RELEASES_REPO, 'vm-22.3.1' ) diff --git a/__tests__/liberica.test.ts b/__tests__/liberica.test.ts new file mode 100644 index 0000000..ad3a742 --- /dev/null +++ b/__tests__/liberica.test.ts @@ -0,0 +1,138 @@ +import * as liberica from '../src/liberica' +import * as path from 'path' +import * as semver from 'semver' +import {expect, test} from '@jest/globals' + +process.env['RUNNER_TOOL_CACHE'] = path.join(__dirname, 'TOOL_CACHE') +process.env['RUNNER_TEMP'] = path.join(__dirname, 'TEMP') + +test('find latest JDK version', async () => { + // Make sure the action can find the latest Java version for known major versions + await expectLatestToBe('11', atLeast('11.0.22+12')) + await expectLatestToBe('11.0.22', upToBuild('11.0.22+12')) + await expectLatestToBe('11.0.22+12', exactly('11.0.22+12')) + + await expectLatestToBe('17', atLeast('17.0.10+13')) + await expectLatestToBe('17.0.10', upToBuild('17.0.10+13')) + await expectLatestToBe('17.0.10+13', exactly('17.0.10+13')) + + await expectLatestToBe('21', atLeast('21.0.2+14')) + await expectLatestToBe('21.0.2', upToBuild('21.0.2+14')) + await expectLatestToBe('21.0.2+14', exactly('21.0.2+14')) + + // Outdated major version + await expectLatestToFail('20') + + // Outdated CPU versions + await expectLatestToFail('11.0.2') // should not resolve to 11.0.22 + await expectLatestToFail('17.0.1') // should not resolve to 17.0.10 + await expectLatestToFail('17.0.7+11') + await expectLatestToFail('21.0.0+8') + await expectLatestToFail('21.0.1') + + // Incorrect build number + await expectLatestToFail('17.0.10+10') +}, 30000) + +test('find asset URL', async () => { + await expectURL( + '17.0.10+13', + 'core', + 'bellsoft-liberica-vm-core-openjdk17.0.10' + ) + await expectURL('17.0.10+13', 'std', 'bellsoft-liberica-vm-openjdk17.0.10') + await expectURL( + '21.0.2+14', + 'full', + 'bellsoft-liberica-vm-full-openjdk21.0.2' + ) + await expectURL('21.0.2+14', '', 'bellsoft-liberica-vm-openjdk21.0.2') +}, 10000) + +type verifier = ( + version: string, + major: number, + minor: number, + patch: number +) => void + +function atLeast(expectedMinVersion: string): verifier { + const expectedMajor = semver.major(expectedMinVersion) + return function ( + version: string, + major: number, + minor: number, + patch: number + ) { + expect(major).toBe(expectedMajor) + if (semver.compareBuild(version, expectedMinVersion) < 0) { + throw new Error(`Version ${version} is older than ${expectedMinVersion}`) + } + } +} + +function upToBuild(expectedMinVersion: string): verifier { + const expectedMinor = semver.minor(expectedMinVersion) + const expectedPatch = semver.patch(expectedMinVersion) + const atLeastVerifier = atLeast(expectedMinVersion) + return function ( + version: string, + major: number, + minor: number, + patch: number + ) { + atLeastVerifier(version, major, minor, patch) + expect(minor).toBe(expectedMinor) + expect(patch).toBe(expectedPatch) + } +} + +function exactly(expectedVersion: string): verifier { + return function ( + version: string, + major: number, + minor: number, + patch: number + ) { + if (semver.compareBuild(version, expectedVersion) != 0) { + throw new Error(`Expected version ${expectedVersion} but got ${version}`) + } + } +} + +async function expectLatestToBe(pattern: string, verify: verifier) { + const result = await liberica.findLatestLibericaJavaVersion(pattern) + expect(semver.valid(result)).toBeDefined() + const major = semver.major(result) + const minor = semver.minor(result) + const patch = semver.patch(result) + verify(result, major, minor, patch) +} + +async function expectLatestToFail(pattern: string) { + try { + const result = await liberica.findLatestLibericaJavaVersion(pattern) + throw new Error( + `findLatest(${pattern}) should have failed but returned ${result}` + ) + } catch (err) { + if (!(err instanceof Error)) { + throw new Error(`Unexpected non-Error: ${err}`) + } + expect(err.message).toContain( + `Unable to find the latest version for JDK${pattern}` + ) + } +} + +async function expectURL( + javaVersion: string, + version: string, + expectedPrefix: string +) { + const url = await liberica.findLibericaURL(javaVersion, version) + expect(url).toBeDefined() + const parts = url.split('/') + const file = parts[parts.length - 1] + expect(file.startsWith(expectedPrefix)).toBe(true) +} diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index b586444e96b87dabe35d2ff6a0cf24aa6fe14503..c3736ab0e0d6b468f1032797121b5501afe4f020 100644 GIT binary patch delta 1326 zcma)*T}V@57{@uAn>w}4&-|*r=5*&(r{~O%X{53ID8oQSq|^~}JL+U?vduCrsCdvD zvoP=}lqkB25Q>7+MHk%^U8p5Q1yQJ7L=a^Wb=$kt(dp{E@H@OOy!`*q|2faQvb@IK z`Lf2H2M#o#g*ecG9^xSZ62SmTkPIoX3yffbR7it#$bd}9g58h}Igkr^kPmxcFPMQq z0qlc9us{(MgY_Z*QI~vRYmp<(&L-T@@g4E?TMI8fHP7oPH4uWU)tXFfcxHZ6*s!&T z)9aPIq|Mhag?t`2@%qA%kgtC<;tK|3yPjXvD}?0_$vm&!QI-u$<~R zM5lBnUe=lDvQ{a)nofUfc7(C9ojk$(4l-<2QKuoAY`v$jNZ!n%>)8rjd|5%g#_diR zJi$OXLMHHgojwmYhAcVwxrrp=*Mr-w`o|Bk1YD5c&kOaMMF!tMcy)RR+g|ER>05K+^^(^XHC@A?lV+l!hQ3lH!S^-P9}9A?j~p-W z6>N1bW@AtzYd|44F%wTZD6i0!Cmr-K7ZaX$ks>FH=q>4W+s2)?9-C`>!^~_h7F>;+ zyi`j~$`QqP4fJ}!L35Pew@)Gf}Q6%`yC{AYmFV-HC27F^z3pQKklEhDV fH&v{SUM!rlr-=+Zqmog@sAkkK9I`#Fb|K>r5kaKY delta 384 zcmW;9IY`4$90y?1G)Y@)y{gu$r_nsm7jIm&I$7P^Toei}g1b<#$Pa>piyi(>o;V2V zAj(4or;Z+@gP<)sItX5ih>s56@EyGsl)Kx4l7#{nRJfr*hXD_~@SzNTlp}x&RH6#i zs6i05s6#zMXh0*H(2N#@5kV9&#F0QNOthi>+yT*#vn`6-Vpnszr1PC+z9iF^I>-7CjJo9=xU6; z((yQ3QhdZ4y|r|iWEPz|30-_9*|Xv$> zUU8P<*E6|Y!$Zy?FBhA{*Hl{F<{|O8&0VVS?(?k`aoS{8xN_$EL~bFgi}|qaQz+76 r_tSdBUKGC(d%1K|9gU{x>44WcnV2p0N&V7*G$;*8!y*$KISKv)lQW3L diff --git a/dist/main/index.js b/dist/main/index.js index 0fb3aa2d95c1d1fc0d104c47e711c07702881528..30c225f0c637889b6bd392c2a286718612279b58 100644 GIT binary patch delta 3497 zcmbtW4Qx}_6_)MzC;x%`8Im;DknrsI+3|G)B;}`diAxep2o9tjA#xo1g_t_F=l7hj z8WU*m#)LEk3e5?FuIYX{RTUCbC%tKERhU?-O@pNriA|#2R<)X{OI=e|!*f1r*BJQqtYR! zZ`)m#;z&!D`zfh-j`cZUeRY}a2vfo~j#DB`z za*dkA)x+o+cGTtQ8KWV}R>3GRRj`+f%kqN?R-5;DX$-Y{XkOut(V*iwkK>SIEcZgA zIL2tUs|s`e#r6jA=auZe(xPDSVk7%dMQ;hAS~xd>D!Ujd2O5lqbgVX&(`U>q6$ce3 zl}_=D+12r*qHy9HZ@CtA>lAWv#}e_Rz&pKu|2}`9uYXYd;4Rnd8k4X8Nw2?umj@DW zyOOYGufATaz2@rHtg1%?*Ii9I*mlEJS*Q4+RAHjI^DG{^;j-MR4LNuFJs#i4-oWm@ zz`&i+_2SuA@vmBSJzKirm4NPJakGLu53ZlY^(I9DzLU5q|L^((+zSJ@a5tPdh3ydi9J>xFFcl~g+zBokb?~#yw%iF+ zZTw!J*E8%L4*0!8`y@*x&|v?b|FMP(FJLsR?xood`Mpo|Kcht8%W9o*2emJADmu+zUePbP*xQH=)aamsm*Dm7q zhHRYqFW#+R_%2>!oXNHjf~So2^S8cX*djSa4Bjedf%vI-DjG)9a&kGX9lIGs?>`ia z3yLM2H)?8;HRmk{0Bzra5`{3~>;E>I8B2ukjU1uVS+NdH1(P1ZT4ZXo$&euNAvRh< z@##b`$vv5hM8nE1HeXa5f;E|S=2ErF@LpX@9b7(9U$u}jR@JC_L8hW zkyV)DP&6pe_MjXZG#O7K+Gg6?JSj79?kpZ%KzgHIb_U;BYiO?W$r}|4!o5q_tdf4= zGH!Te#US{yGowkuz^mA?OJ&$s(owZ6<|4Ag)z|2;S-gR(<|u)}9PQ+EB*qQV{AfTsYeSDhV*$D<=z%E8nSZaplKj>;;IgD$D<(+cNrEWWN={$yt zyH5vDHfL!e_nKjm0$tK7@0oGHv-*19iq_L)S7-_b>sM75mfkfqxMcD&ybxy4@#6}h ztdc>GBZfhbN&QpcIlyIf+vim1Hk6h}Qj!x=^n_DOl}P_br$@4N$4z zxqk&Wj1{V_3YyMNx%kmYEOpE=o#LY`QUGRkZRwCxBD%-Ft1ly4jIT&TUWZbnyLph( zEt9{mlra-hg5^AioVkJHvZ0;Qz-G{y=N8k$fag?8qxkeY*j;K;?nL<0eDiwU zmhLX`Qpwz^oPmO{jd!ufPY-swsXR)hi^^syTc~WM(k%{l_FSs_FM_z0_5c6? delta 409 zcmW;By-!n77{~FPmY3er-ty93pyizwZn>v;?*Ty=WMg1L1BCDg5TdA)34?>fFS!E? ziNZ4w3=0EcK?8XP1_lQR0fm6YL{>CSSP=*|@r2>Ce7_&hFGaz>6z?GrKoBJ;MH$Kw zf`Kq95J40sVyHwFs!@YD60lHYMDi?#i5|_SU&yrz5d$n?z-_%JV2*q@QrivY1+?bSK47zBS*M2S|wVc+i zJ>p!(&8~e|Ee}!nn`@Er(~a@Mf%_uJ>nCn9NRc0Ih;PnZ^NhW9ugv5T_VUgA`^r-W z5C8T48Qb@&dX$sTR4l-6$5mF)>Rig8m$Rub?T#snK8>mjZ9Z2fU5}}0o*7rK#X}T> cG{j!LUzku@kJhU_*7~%5?Fknq1~%gV0i8ykb^rhX diff --git a/src/constants.ts b/src/constants.ts index fc63ffa..4b76c4c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -20,6 +20,7 @@ export const EXECUTABLE_SUFFIX = IS_WINDOWS ? '.exe' : '' export const DISTRIBUTION_GRAALVM = 'graalvm' export const DISTRIBUTION_GRAALVM_COMMUNITY = 'graalvm-community' export const DISTRIBUTION_MANDREL = 'mandrel' +export const DISTRIBUTION_LIBERICA = 'liberica' export const VERSION_DEV = 'dev' export const VERSION_LATEST = 'latest' diff --git a/src/graalvm.ts b/src/graalvm.ts index 5abc3b3..4a4431c 100644 --- a/src/graalvm.ts +++ b/src/graalvm.ts @@ -138,6 +138,8 @@ export async function findLatestGraalVMJDKCEJavaVersion( majorJavaVersion: string ): Promise { const matchingRefs = await getMatchingTags( + c.GRAALVM_GH_USER, + c.GRAALVM_RELEASES_REPO, `${GRAALVM_JDK_TAG_PREFIX}${majorJavaVersion}` ) const lowestNonExistingVersion = '0.0.1' @@ -244,6 +246,7 @@ export async function setUpGraalVMLatest_22_X( return setUpGraalVMRelease(gdsToken, lockedVersion, javaVersion) } const latestRelease = await getTaggedRelease( + c.GRAALVM_GH_USER, c.GRAALVM_RELEASES_REPO, GRAALVM_TAG_PREFIX + lockedVersion ) diff --git a/src/liberica.ts b/src/liberica.ts new file mode 100644 index 0000000..eb6bc41 --- /dev/null +++ b/src/liberica.ts @@ -0,0 +1,105 @@ +import * as c from './constants' +import * as semver from 'semver' +import { + downloadExtractAndCacheJDK, + getTaggedRelease, + getMatchingTags +} from './utils' +import {downloadTool} from '@actions/tool-cache' + +const LIBERICA_GH_USER = 'bell-sw' +const LIBERICA_RELEASES_REPO = 'LibericaNIK' +const LIBERICA_JDK_TAG_PREFIX = 'jdk-' +const LIBERICA_VM_PREFIX = 'bellsoft-liberica-vm-' + +export async function setUpLiberica( + javaVersion: string, + version: string +): Promise { + const resolvedJavaVersion = await findLatestLibericaJavaVersion(javaVersion) + const downloadUrl = await findLibericaURL(resolvedJavaVersion, version) + const toolName = determineToolName(javaVersion, version) + return downloadExtractAndCacheJDK( + async () => downloadTool(downloadUrl), + toolName, + javaVersion + ) +} + +export async function findLatestLibericaJavaVersion( + javaVersion: string +): Promise { + const matchingRefs = await getMatchingTags( + LIBERICA_GH_USER, + LIBERICA_RELEASES_REPO, + `${LIBERICA_JDK_TAG_PREFIX}${javaVersion}` + ) + const noMatch = '0.0.1' + let bestMatch = noMatch + const prefixLength = `refs/tags/${LIBERICA_JDK_TAG_PREFIX}`.length + const patternLength = javaVersion.length + for (const matchingRef of matchingRefs) { + const version = matchingRef.ref.substring(prefixLength) + if ( + semver.valid(version) && + // pattern '17.0.1' should match '17.0.1+12' but not '17.0.10' + (version.length <= patternLength || + !isDigit(version.charAt(patternLength))) && + semver.compareBuild(version, bestMatch) == 1 + ) { + bestMatch = version + } + } + if (bestMatch === noMatch) { + throw new Error( + `Unable to find the latest version for JDK${javaVersion}. Please make sure the java-version is set correctly. ${c.ERROR_HINT}` + ) + } + return bestMatch +} + +export async function findLibericaURL( + javaVersion: string, + version: string +): Promise { + const release = await getTaggedRelease( + LIBERICA_GH_USER, + LIBERICA_RELEASES_REPO, + LIBERICA_JDK_TAG_PREFIX + javaVersion + ) + const platform = determinePlatformPart() + const assetPrefix = `${LIBERICA_VM_PREFIX}${determineToolVersionPart( + version + )}openjdk${javaVersion}` + const assetSuffix = `-${platform}${c.GRAALVM_FILE_EXTENSION}` + for (const asset of release.assets) { + if ( + asset.name.startsWith(assetPrefix) && + asset.name.endsWith(assetSuffix) + ) { + return asset.browser_download_url + } + } + throw new Error( + `Unable to find asset for java-version: ${javaVersion}, version: ${version}, platform: ${platform}` + ) +} + +function determineToolVersionPart(version: string) { + return version === 'std' || version === '' ? '' : `${version}-` +} + +function determineToolName(javaVersion: string, version: string) { + return `${LIBERICA_VM_PREFIX}${determineToolVersionPart( + version + )}openjdk${javaVersion}-${determinePlatformPart()}` +} + +function determinePlatformPart() { + // for linux-musl, return `linux-${c.JDK_ARCH}-musl` + return `${c.JDK_PLATFORM}-${c.GRAALVM_ARCH}` +} + +function isDigit(c: string) { + return c.charAt(0) >= '0' && c.charAt(0) <= '9' +} diff --git a/src/main.ts b/src/main.ts index 75f3248..586712f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,6 +8,7 @@ import {restore} from './features/cache' import {setUpDependencies} from './dependencies' import {setUpGUComponents} from './gu' import {setUpMandrel} from './mandrel' +import {setUpLiberica} from './liberica' import {checkForUpdates} from './features/check-for-updates' import {setUpNativeImageMusl} from './features/musl' import {setUpWindowsEnvironment} from './msvc' @@ -65,6 +66,9 @@ async function run(): Promise { case c.DISTRIBUTION_MANDREL: graalVMHome = await setUpMandrel(graalVMVersion, javaVersion) break + case c.DISTRIBUTION_LIBERICA: + graalVMHome = await setUpLiberica(javaVersion, graalVMVersion) + break case '': if (javaVersion === c.VERSION_DEV) { core.info( diff --git a/src/utils.ts b/src/utils.ts index f4b5f5b..1bd5335 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -65,6 +65,7 @@ export async function getContents( } export async function getTaggedRelease( + owner: string, repo: string, tag: string ): Promise { @@ -73,7 +74,7 @@ export async function getTaggedRelease( const octokit = new GitHubDotCom(options) return ( await octokit.request('GET /repos/{owner}/{repo}/releases/tags/{tag}', { - owner: c.GRAALVM_GH_USER, + owner, repo, tag }) @@ -81,6 +82,8 @@ export async function getTaggedRelease( } export async function getMatchingTags( + owner: string, + repo: string, tagPrefix: string ): Promise { const githubToken = getGitHubToken() @@ -90,8 +93,8 @@ export async function getMatchingTags( await octokit.request( 'GET /repos/{owner}/{repo}/git/matching-refs/tags/{tagPrefix}', { - owner: c.GRAALVM_GH_USER, - repo: c.GRAALVM_RELEASES_REPO, + owner, + repo, tagPrefix } )