Compare commits
209 Commits
x86_64-lin
...
v1.2.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
557ffcf459 | ||
|
|
f7c3ab9a9a | ||
|
|
dee12811d5 | ||
|
|
9dd2b41757 | ||
|
|
3aaf71e276 | ||
|
|
17d757cc41 | ||
|
|
24013ae277 | ||
|
|
caa712a441 | ||
|
|
6f327093bb | ||
|
|
4873ae0b28 | ||
|
|
c3163945bd | ||
|
|
c60701d448 | ||
|
|
22cc13fe88 | ||
|
|
6bb7c0d4f1 | ||
|
|
ee6894e12f | ||
|
|
cc51024a44 | ||
|
|
2911b2304b | ||
|
|
00a7ff5258 | ||
|
|
010c924385 | ||
|
|
bafd5b75bd | ||
|
|
e92be2ccca | ||
|
|
809512d83d | ||
|
|
2f25c0caae | ||
|
|
ee8b81f45c | ||
|
|
3e1010f34c | ||
|
|
fb9f5a0734 | ||
|
|
6c07077221 | ||
|
|
3d5584d4fc | ||
|
|
a19f51dc38 | ||
|
|
30261a858e | ||
|
|
6670574f7f | ||
|
|
d9e910f637 | ||
|
|
5302a697e3 | ||
|
|
049aa7c191 | ||
|
|
df4b80eebe | ||
|
|
3d7ab58c1d | ||
|
|
d3f9e14fc3 | ||
|
|
b03aef7455 | ||
|
|
5393c3d809 | ||
|
|
e878075a91 | ||
|
|
9a425fb910 | ||
|
|
7652cc43b3 | ||
|
|
a2b35c9a1c | ||
|
|
791f27b2b8 | ||
|
|
f6947ecb49 | ||
|
|
cb063c121c | ||
|
|
076347913e | ||
|
|
d72e3dbf5f | ||
|
|
22551b2bec | ||
|
|
bf3e45bce6 | ||
|
|
1588b03f82 | ||
|
|
6df9f698c6 | ||
|
|
2408275e34 | ||
|
|
a638430bc0 | ||
|
|
3bd233e767 | ||
|
|
c99915cc1a | ||
|
|
a0f131ece5 | ||
|
|
a1b47fdf04 | ||
|
|
129a551a10 | ||
|
|
b8dc5fccfb | ||
|
|
1c219f5b27 | ||
|
|
2a93b69fdf | ||
|
|
0b782b6b90 | ||
|
|
d5b07dd118 | ||
|
|
40dc6ae006 | ||
|
|
cde0daed27 | ||
|
|
22b65d7de0 | ||
|
|
c2fd2d6d8e | ||
|
|
2b3d0bde8f | ||
|
|
6c7d417a1e | ||
|
|
8dd4f51a5e | ||
|
|
2fb264a6b0 | ||
|
|
0a27862568 | ||
|
|
70003e7f9f | ||
|
|
814434c7a9 | ||
|
|
3282b5e43f | ||
|
|
26eec53160 | ||
|
|
0e29e36dce | ||
|
|
ac032b0e7e | ||
|
|
8cd3284efc | ||
|
|
8f1dbd2ce5 | ||
|
|
d3b90f817c | ||
|
|
570f6b20e6 | ||
|
|
265e01895c | ||
|
|
c569e64c0b | ||
|
|
d189178615 | ||
|
|
ddf952dc27 | ||
|
|
2358846013 | ||
|
|
473558a7d4 | ||
|
|
10c2dc22fc | ||
|
|
238098cb83 | ||
|
|
911e3cee00 | ||
|
|
c6bf0c3fb6 | ||
|
|
eaee860665 | ||
|
|
d67b180a8e | ||
|
|
8ae40e7db9 | ||
|
|
4a419f5908 | ||
|
|
9180004c5f | ||
|
|
6a5fa2da72 | ||
|
|
578fd31702 | ||
|
|
85ea19c002 | ||
|
|
4a5400ac9a | ||
|
|
91fd4d0716 | ||
|
|
04ca584c1b | ||
|
|
8f41000162 | ||
|
|
d476798211 | ||
|
|
db8d619f32 | ||
|
|
830cd48f7f | ||
|
|
c871f91ee0 | ||
|
|
2f50b91043 | ||
|
|
78f22073df | ||
|
|
b11d36630f | ||
|
|
babc303d7e | ||
|
|
5f2753d6bc | ||
|
|
6acd592e3e | ||
|
|
cf001e2d43 | ||
|
|
90a2c32c8b | ||
|
|
4297237826 | ||
|
|
c85a448ca4 | ||
|
|
c8fdb59e88 | ||
|
|
40947ba8ce | ||
|
|
4356481765 | ||
|
|
8b27096bbf | ||
|
|
93d8fee7fa | ||
|
|
76d605752b | ||
|
|
b53bc17dd7 | ||
|
|
d01f5f519f | ||
|
|
4aba115fa5 | ||
|
|
7c84ab1ba7 | ||
|
|
778af55c2a | ||
|
|
a20b6434b3 | ||
|
|
2ada328403 | ||
|
|
254814bb57 | ||
|
|
79e8ca0cfa | ||
|
|
d53592711c | ||
|
|
bb71d9a37f | ||
|
|
b4ccf78d3d | ||
|
|
5ec9a94dee | ||
|
|
6576e0ae31 | ||
|
|
ab2d9c9984 | ||
|
|
f34cb2f47c | ||
|
|
5704aa1938 | ||
|
|
f1c2f56b3e | ||
|
|
cb02f04137 | ||
|
|
c2c2a82199 | ||
|
|
d39e1ea8ea | ||
|
|
b0049dea1e | ||
|
|
70e1936e9c | ||
|
|
d3fbe4ee0f | ||
|
|
165f18581d | ||
|
|
01b9840538 | ||
|
|
3b96e2ea68 | ||
|
|
c641a461ac | ||
|
|
a39d51e58e | ||
|
|
f47d45565a | ||
|
|
c2b9039d01 | ||
|
|
e82ef137ec | ||
|
|
f409c6cadd | ||
|
|
73120ff7a9 | ||
|
|
1df377600f | ||
|
|
406690122e | ||
|
|
4f62eae3cc | ||
|
|
5336aa8b95 | ||
|
|
174eff573d | ||
|
|
e343c9a746 | ||
|
|
8cce0a82bf | ||
|
|
60c5807781 | ||
|
|
8085574f90 | ||
|
|
cf025f1a74 | ||
|
|
5a3116eab6 | ||
|
|
0c5c948baa | ||
|
|
bbe485154c | ||
|
|
b3777a3c57 | ||
|
|
77fd73038b | ||
|
|
2240cb3432 | ||
|
|
1a4a7ccb68 | ||
|
|
ce3d4ac06f | ||
|
|
1e505b051e | ||
|
|
a004c8ff68 | ||
|
|
0f01291c2e | ||
|
|
b76e2627a2 | ||
|
|
a02f4df8f7 | ||
|
|
66dc2bf069 | ||
|
|
61450f0977 | ||
|
|
f497c46877 | ||
|
|
778131f1d6 | ||
|
|
b1f65935b2 | ||
|
|
6d6c7fd57c | ||
|
|
0141e9fea8 | ||
|
|
f786ab1178 | ||
|
|
d0a541650c | ||
|
|
136636a1a5 | ||
|
|
0584de3fce | ||
|
|
308aa6ad2b | ||
|
|
edfd5f7c8d | ||
|
|
c0d9c01bc4 | ||
|
|
1eba352ba8 | ||
|
|
58367967e0 | ||
|
|
eff06eabf9 | ||
|
|
7a1abedd1b | ||
|
|
a44c598f75 | ||
|
|
cb74ef5a33 | ||
|
|
5bb7e0253d | ||
|
|
422fb42579 | ||
|
|
b7dfeb8214 | ||
|
|
c26d35376d | ||
|
|
a1ee297bf2 | ||
|
|
a686e47055 | ||
|
|
db50d45bd5 |
4
.eslintignore
Normal file
4
.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
||||
dist/
|
||||
lib/
|
||||
node_modules/
|
||||
jest.config.js
|
||||
55
.eslintrc.json
Normal file
55
.eslintrc.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"plugins": ["jest", "@typescript-eslint"],
|
||||
"extends": ["plugin:github/recommended"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 9,
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"i18n-text/no-en": "off",
|
||||
"eslint-comments/no-use": "off",
|
||||
"import/no-namespace": "off",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
|
||||
"@typescript-eslint/no-require-imports": "error",
|
||||
"@typescript-eslint/array-type": "error",
|
||||
"@typescript-eslint/await-thenable": "error",
|
||||
"@typescript-eslint/ban-ts-comment": "error",
|
||||
"camelcase": "off",
|
||||
"@typescript-eslint/consistent-type-assertions": "error",
|
||||
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
|
||||
"@typescript-eslint/func-call-spacing": ["error", "never"],
|
||||
"@typescript-eslint/no-array-constructor": "error",
|
||||
"@typescript-eslint/no-empty-interface": "error",
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"@typescript-eslint/no-extraneous-class": "error",
|
||||
"@typescript-eslint/no-for-in-array": "error",
|
||||
"@typescript-eslint/no-inferrable-types": "error",
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-namespace": "error",
|
||||
"@typescript-eslint/no-non-null-assertion": "warn",
|
||||
"@typescript-eslint/no-unnecessary-qualifier": "error",
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||
"@typescript-eslint/no-useless-constructor": "error",
|
||||
"@typescript-eslint/no-var-requires": "error",
|
||||
"@typescript-eslint/prefer-for-of": "warn",
|
||||
"@typescript-eslint/prefer-function-type": "warn",
|
||||
"@typescript-eslint/prefer-includes": "error",
|
||||
"@typescript-eslint/prefer-string-starts-ends-with": "error",
|
||||
"@typescript-eslint/promise-function-async": "error",
|
||||
"@typescript-eslint/require-array-sort-compare": "error",
|
||||
"@typescript-eslint/restrict-plus-operands": "error",
|
||||
"semi": "off",
|
||||
"@typescript-eslint/semi": ["error", "never"],
|
||||
"@typescript-eslint/type-annotation-spacing": "error",
|
||||
"@typescript-eslint/unbound-method": "error"
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true,
|
||||
"jest/globals": true
|
||||
}
|
||||
}
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
dist/** -diff linguist-generated=true
|
||||
55
.github/workflows/check-dist.yml
vendored
Normal file
55
.github/workflows/check-dist.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
# `dist/index.js` is a special file in Actions.
|
||||
# When you reference an action with `uses:` in a workflow,
|
||||
# `index.js` is the code that will run.
|
||||
# For our project, we generate this file through a build process from other source files.
|
||||
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
|
||||
name: Check dist/
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-dist:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set Node.js 20.x
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Rebuild the dist/ directory
|
||||
run: npm run build
|
||||
|
||||
- name: Compare the expected and actual dist/ directories
|
||||
run: |
|
||||
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
|
||||
echo "Detected uncommitted changes after build. See status below:"
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
id: diff
|
||||
|
||||
# If index.js was different than expected, upload the expected version as an artifact
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
419
.github/workflows/test.yml
vendored
Normal file
419
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,419 @@
|
||||
name: 'build-test'
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build: # make sure build/ci work properly
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
npm install
|
||||
- run: |
|
||||
npm run all
|
||||
test:
|
||||
name: GraalVM
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
java-version: ['23', '21', '17', '20', 'dev']
|
||||
distribution: ['graalvm', 'graalvm-community']
|
||||
os: [
|
||||
ubuntu-latest,
|
||||
macos-latest, # macOS on Apple silicon
|
||||
macos-13, # macOS on Intel
|
||||
windows-latest
|
||||
]
|
||||
set-gds-token: [false]
|
||||
components: ['']
|
||||
include:
|
||||
- java-version: 'latest-ea'
|
||||
distribution: 'graalvm'
|
||||
os: ubuntu-latest
|
||||
- java-version: '24-ea'
|
||||
distribution: 'graalvm'
|
||||
os: ubuntu-latest
|
||||
- java-version: '21'
|
||||
distribution: ''
|
||||
os: ubuntu-latest
|
||||
- java-version: 'dev'
|
||||
distribution: ''
|
||||
os: windows-latest
|
||||
- java-version: '21'
|
||||
distribution: 'graalvm-community'
|
||||
os: ubuntu-latest
|
||||
components: 'native-image' # should print a warning but not fail
|
||||
- java-version: '21.0.0' # test for GA version (see #63)
|
||||
distribution: 'graalvm'
|
||||
os: ubuntu-latest
|
||||
- java-version: '17'
|
||||
distribution: 'graalvm'
|
||||
os: ubuntu-latest
|
||||
set-gds-token: true
|
||||
- java-version: '17.0.13'
|
||||
distribution: 'graalvm'
|
||||
os: ubuntu-latest
|
||||
set-gds-token: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run setup-graalvm action
|
||||
uses: ./
|
||||
with:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
distribution: ${{ matrix.distribution }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
components: ${{ matrix.components }}
|
||||
gds-token: ${{ matrix.set-gds-token && secrets.GDS_TOKEN || '' }}
|
||||
- name: Check environment
|
||||
run: |
|
||||
echo "GRAALVM_HOME: $GRAALVM_HOME"
|
||||
if [[ "${{ matrix.java-version }}" == "dev" ]]; then
|
||||
[[ "$GRAALVM_HOME" == *"$RUNNER_TEMP"* ]] || exit 12
|
||||
else
|
||||
[[ "$GRAALVM_HOME" == *"$RUNNER_TOOL_CACHE"* ]] || exit 23
|
||||
fi
|
||||
echo "JAVA_HOME: $JAVA_HOME"
|
||||
java --version
|
||||
java --version | grep "GraalVM" || exit 34
|
||||
native-image --version
|
||||
if: runner.os != 'Windows'
|
||||
- name: Check Windows environment
|
||||
run: |
|
||||
echo "GRAALVM_HOME: $env:GRAALVM_HOME"
|
||||
echo "JAVA_HOME: $env:JAVA_HOME"
|
||||
java --version
|
||||
native-image --version
|
||||
test-ce: # make sure the action works on a clean machine without building
|
||||
needs: test
|
||||
name: CE ${{ matrix.version }} + JDK${{ matrix.java-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
version: ['latest', 'dev']
|
||||
java-version: ['17', '20']
|
||||
components: ['native-image']
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
exclude:
|
||||
- version: 'latest'
|
||||
java-version: '20'
|
||||
- version: 'dev'
|
||||
java-version: '19'
|
||||
include:
|
||||
- version: '22.2.0' # for update notifications
|
||||
java-version: '17'
|
||||
components: 'native-image'
|
||||
os: ubuntu-20.04
|
||||
- version: '21.2.0'
|
||||
java-version: '8' # for JDK 8 notification
|
||||
components: 'native-image'
|
||||
os: ubuntu-latest
|
||||
- version: '22.3.1'
|
||||
java-version: '11' # for JDK 11 notification
|
||||
components: 'native-image'
|
||||
os: macos-13
|
||||
- version: '22.3.1'
|
||||
java-version: '17'
|
||||
components: 'native-image'
|
||||
os: windows-2022
|
||||
- version: 'dev'
|
||||
java-version: 'dev'
|
||||
components: 'native-image'
|
||||
os: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run setup-graalvm action
|
||||
uses: ./
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
java-version: ${{ matrix.java-version }}
|
||||
components: ${{ matrix.components }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Check environment
|
||||
run: |
|
||||
echo "GRAALVM_HOME: $GRAALVM_HOME"
|
||||
if [[ "${{ matrix.version }}" == "dev" ]] && [[ "${{ matrix.java-version }}" == "dev" ]]; then
|
||||
[[ "$GRAALVM_HOME" == *"$RUNNER_TEMP"* ]] || exit 12
|
||||
else
|
||||
[[ "$GRAALVM_HOME" == *"$RUNNER_TOOL_CACHE"* ]] || exit 23
|
||||
fi
|
||||
echo "JAVA_HOME: $JAVA_HOME"
|
||||
java -version
|
||||
java -version 2>&1 | grep "GraalVM" || exit 34
|
||||
native-image --version
|
||||
if [[ "${{ matrix.java-version }}" != "dev" ]]; then
|
||||
gu list
|
||||
fi
|
||||
if: runner.os != 'Windows'
|
||||
- name: Check Windows environment
|
||||
run: |
|
||||
echo "GRAALVM_HOME: $env:GRAALVM_HOME"
|
||||
echo "JAVA_HOME: $env:JAVA_HOME"
|
||||
java -version
|
||||
native-image --version
|
||||
gu.cmd remove native-image
|
||||
if: runner.os == 'Windows'
|
||||
test-ee:
|
||||
needs: test
|
||||
name: EE ${{ matrix.version }} + JDK${{ matrix.java-version }} on ${{ matrix.os }}
|
||||
if: github.event_name != 'pull_request'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
version: ['latest']
|
||||
java-version: ['17']
|
||||
components: ['native-image']
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
include:
|
||||
- version: '22.3.3'
|
||||
java-version: '11'
|
||||
components: 'native-image'
|
||||
os: ubuntu-latest
|
||||
- version: '22.3.3'
|
||||
java-version: '17'
|
||||
components: 'native-image'
|
||||
os: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run setup-graalvm action
|
||||
uses: ./
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
gds-token: ${{ secrets.GDS_TOKEN }}
|
||||
java-version: ${{ matrix.java-version }}
|
||||
components: ${{ matrix.components }}
|
||||
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 | grep -e "GraalVM EE" -e "Oracle GraalVM" || exit 23
|
||||
native-image --version
|
||||
gu list
|
||||
if: runner.os != 'Windows'
|
||||
- name: Check Windows environment
|
||||
run: |
|
||||
echo "GRAALVM_HOME: $env:GRAALVM_HOME"
|
||||
echo "JAVA_HOME: $env:JAVA_HOME"
|
||||
java --version
|
||||
native-image --version
|
||||
gu.cmd remove native-image
|
||||
if: runner.os == 'Windows'
|
||||
test-mandrel:
|
||||
needs: test
|
||||
name: ${{ matrix.version }} + JDK${{ matrix.java-version }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
version: ['mandrel-22.2.0.0-Final', '23.0.1.2-Final', 'mandrel-latest']
|
||||
java-version: ['17']
|
||||
distribution: ['mandrel']
|
||||
os: [windows-latest, ubuntu-latest]
|
||||
include:
|
||||
- version: 'mandrel-latest'
|
||||
java-version: '17'
|
||||
distribution: '' # test empty distribution for backward compatibility
|
||||
os: ubuntu-latest
|
||||
- version: '' # test with no version
|
||||
java-version: '21'
|
||||
distribution: 'mandrel'
|
||||
os: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run setup-graalvm action
|
||||
uses: ./
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
distribution: ${{ matrix.distribution }}
|
||||
java-version: ${{ matrix.java-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 | grep "Temurin" || exit 23
|
||||
native-image --version
|
||||
if: runner.os != 'Windows'
|
||||
- name: Check Windows environment
|
||||
run: |
|
||||
echo "GRAALVM_HOME: $env:GRAALVM_HOME"
|
||||
echo "JAVA_HOME: $env:JAVA_HOME"
|
||||
java --version
|
||||
native-image --version
|
||||
if: runner.os == 'Windows'
|
||||
test-liberica:
|
||||
needs: test
|
||||
name: Liberica (${{ matrix.java-version }}, '${{ matrix.java-package }}', ${{ matrix.os }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
java-version: ['17', '21.0.2']
|
||||
java-package: ['', 'jdk', 'jdk+fx']
|
||||
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 }}
|
||||
java-package: ${{ matrix.java-package }}
|
||||
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
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write # for `native-image-pr-reports` option
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run setup-graalvm action
|
||||
uses: ./
|
||||
with:
|
||||
java-version: 'dev'
|
||||
distribution: 'graalvm-community'
|
||||
native-image-job-reports: 'true'
|
||||
native-image-pr-reports: 'true'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build HelloWorld executable with GraalVM Native Image on Windows
|
||||
run: |
|
||||
echo 'public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } }' > HelloWorld.java
|
||||
javac HelloWorld.java
|
||||
native-image HelloWorld
|
||||
./helloworld
|
||||
test-native-image-windows-msvc:
|
||||
name: native-image on windows-2022
|
||||
runs-on: windows-2022
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write # for `native-image-pr-reports` option
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run setup-graalvm action
|
||||
uses: ./
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'graalvm'
|
||||
native-image-job-reports: 'true'
|
||||
native-image-pr-reports: 'true'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build HelloWorld executable with GraalVM Native Image on Windows
|
||||
run: |
|
||||
echo 'public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } }' > HelloWorld.java
|
||||
javac HelloWorld.java
|
||||
native-image HelloWorld
|
||||
./helloworld
|
||||
test-native-image-musl:
|
||||
name: native-image-musl on ubuntu-latest
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write # for `native-image-pr-reports` option
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run setup-graalvm action
|
||||
uses: ./
|
||||
with:
|
||||
java-version: 'dev'
|
||||
distribution: 'graalvm-community'
|
||||
native-image-musl: 'true'
|
||||
native-image-job-reports: 'true'
|
||||
native-image-pr-reports: 'true'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build static HelloWorld executable with GraalVM Native Image and musl
|
||||
run: |
|
||||
echo 'public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } }' > HelloWorld.java
|
||||
javac HelloWorld.java
|
||||
native-image --static --libc=musl HelloWorld
|
||||
./helloworld
|
||||
test-extensive:
|
||||
name: extensive tests on ubuntu-latest
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write # for `native-image-pr-reports` option
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run setup-graalvm action
|
||||
uses: ./
|
||||
with:
|
||||
java-version: '17.0.8'
|
||||
distribution: 'graalvm'
|
||||
components: 'espresso,llvm-toolchain,native-image,nodejs,python,ruby,wasm'
|
||||
set-java-home: 'false'
|
||||
native-image-job-reports: 'true'
|
||||
native-image-pr-reports: 'true'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Check environment
|
||||
run: |
|
||||
echo "GRAALVM_HOME: $GRAALVM_HOME"
|
||||
echo "JAVA_HOME: $JAVA_HOME"
|
||||
[[ "$GRAALVM_HOME" != "$JAVA_HOME" ]] || exit 12
|
||||
[[ $(which java) == *"graalvm"* ]] || exit 23
|
||||
java --version
|
||||
java -truffle --version
|
||||
gu --version
|
||||
gu list
|
||||
[[ $(which lli) == *"graalvm"* ]] || exit 34
|
||||
lli --version
|
||||
native-image --version
|
||||
[[ $(which node) == *"graalvm"* ]] || exit 45
|
||||
node --version
|
||||
graalpy --version
|
||||
truffleruby --version
|
||||
wasm --version
|
||||
- name: Build HelloWorld.java with GraalVM Native Image
|
||||
run: |
|
||||
echo 'public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } }' > HelloWorld.java
|
||||
javac HelloWorld.java
|
||||
native-image -g HelloWorld
|
||||
./helloworld
|
||||
# - name: Build Ruby-FFI with TruffleRuby
|
||||
# run: |
|
||||
# [[ $(which bundle) == *"graalvm"* ]] || exit 57
|
||||
# git clone --depth 1 https://github.com/ffi/ffi.git
|
||||
# pushd ffi > /dev/null
|
||||
# # https://github.com/ffi/ffi/blob/447845cb3030194c79700c86fb388a12e6f81386/.github/workflows/ci.yml#L58-L62
|
||||
# bundle install
|
||||
# bundle exec rake libffi
|
||||
# bundle exec rake compile
|
||||
# bundle exec rake test
|
||||
# popd > /dev/null
|
||||
- name: Remove components
|
||||
run: gu remove espresso llvm-toolchain nodejs python ruby wasm
|
||||
99
.gitignore
vendored
Normal file
99
.gitignore
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
# Dependency directory
|
||||
node_modules
|
||||
|
||||
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# OS metadata
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Ignore built ts files
|
||||
__tests__/runner/*
|
||||
lib/**/*
|
||||
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@@ -0,0 +1,3 @@
|
||||
dist/
|
||||
lib/
|
||||
node_modules/
|
||||
10
.prettierrc.json
Normal file
10
.prettierrc.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": false,
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
290
README.md
290
README.md
@@ -1 +1,289 @@
|
||||
# GitHub Action for GraalVM
|
||||
# GitHub Action for GraalVM [](https://github.com/graalvm/setup-graalvm/actions/workflows/test.yml)
|
||||
This GitHub action sets up [Oracle GraalVM][graalvm-medium], GraalVM [Community Edition (CE)][repo], [Enterprise Edition (EE)][graalvm-ee], [Mandrel][mandrel], or [Liberica Native Image Kit][liberica] as well as [Native Image][native-image] and GraalVM components such as [Truffle languages][truffle-languages].
|
||||
|
||||
## Key Features
|
||||
|
||||
This action:
|
||||
|
||||
- supports Oracle GraalVM [releases][graalvm-dl], [EA builds][ea-builds], GraalVM Community Edition (CE) [releases], [dev builds][dev-builds], GraalVM Enterprise Edition (EE) [releases][graalvm-ee] (set [`gds-token`](#options)) 22.1.0 and later, [Mandrel][mandrel], and [Liberica Native Image Kit][liberica] (see [Options](#options))
|
||||
- exports a `$GRAALVM_HOME` environment variable
|
||||
- adds `$GRAALVM_HOME/bin` to the `$PATH` environment variable<br>(Native Image, Truffle languages, and tools can be invoked directly)
|
||||
- sets `$JAVA_HOME` to `$GRAALVM_HOME` by default<br>(can be disabled via `set-java-home: 'false'`, see [Options](#options))
|
||||
- supports `x64` and `aarch64` (selected automatically, `aarch64` requires a [self-hosted runner][gha-self-hosted-runners])
|
||||
- supports dependency caching for Apache Maven, Gradle, and sbt (see [`cache` option](#options))
|
||||
- sets up Windows environments with build tools using [vcvarsall.bat][vcvarsall]
|
||||
- has built-in support for GraalVM components and the [GraalVM Updater][gu]
|
||||
|
||||
|
||||
## Templates
|
||||
|
||||
### Quickstart Template
|
||||
|
||||
```yml
|
||||
name: GraalVM build
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: graalvm/setup-graalvm@v1
|
||||
with:
|
||||
java-version: '21' # See 'Options' for more details
|
||||
distribution: 'graalvm' # See 'Supported distributions' for available options
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Example step
|
||||
run: |
|
||||
echo "GRAALVM_HOME: $GRAALVM_HOME"
|
||||
echo "JAVA_HOME: $JAVA_HOME"
|
||||
java --version
|
||||
native-image --version
|
||||
- name: Example step using Maven plugin # https://graalvm.github.io/native-build-tools/latest/maven-plugin.html
|
||||
run: mvn -Pnative package
|
||||
- name: Example step using Gradle plugin # https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html
|
||||
run: gradlew nativeCompile
|
||||
```
|
||||
|
||||
### Building a HelloWorld with GraalVM Native Image on Different Platforms
|
||||
|
||||
```yml
|
||||
name: GraalVM Native Image builds
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
name: HelloWorld on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: graalvm/setup-graalvm@v1
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'graalvm'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
native-image-job-reports: 'true'
|
||||
|
||||
- name: Build and run HelloWorld.java
|
||||
run: |
|
||||
echo 'public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } }' > HelloWorld.java
|
||||
javac HelloWorld.java
|
||||
native-image HelloWorld
|
||||
./helloworld
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: helloworld-${{ matrix.os }}
|
||||
path: helloworld*
|
||||
```
|
||||
|
||||
### Template for Oracle GraalVM Early Access (EA) builds
|
||||
|
||||
```yml
|
||||
name: Oracle GraalVM Early Access build
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: graalvm/setup-graalvm@v1
|
||||
with:
|
||||
java-version: '24-ea' # or 'latest-ea' for the latest Java version available
|
||||
distribution: 'graalvm'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><h4>Template for Oracle GraalVM via GraalVM Download Service</h4></summary>
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
1. Obtain a token for the GraalVM Download Service. For this, replace `your@email.com` with your email address and run the following `curl` command:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST "https://gds.oracle.com/api/20220101/licenseAcceptance" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{ \"email\": \"your@email.com\", \"licenseId\": \"D53FA58D12817B3CE0530F15000A74CA\", \"type\": \"GENERATE_TOKEN_AND_ACCEPT_LICENSE\"}"
|
||||
```
|
||||
|
||||
The response should look like this:
|
||||
|
||||
```json
|
||||
{"token":"<your-token>","status":"UNVERIFIED"}
|
||||
```
|
||||
|
||||
2. Store the value of `<your-token>` as a [GitHub Action secret][gha-secrets]. For the following template, we use the name `GDS_TOKEN`.
|
||||
3. Check your emails and accept the license to activate the token.
|
||||
4. Use `java-version: '17'` (or a specific version such as `17.0.13`) and provide the `GDS_TOKEN` as shown in the following template:
|
||||
|
||||
```yml
|
||||
name: Build with Oracle GraalVM for JDK 17 via GDS
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: graalvm/setup-graalvm@v1
|
||||
with:
|
||||
distribution: 'graalvm'
|
||||
java-version: '17'
|
||||
gds-token: ${{ secrets.GDS_TOKEN }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Example step
|
||||
run: |
|
||||
java --version
|
||||
native-image --version
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><h4>Template for GraalVM Enterprise Edition</h4></summary>
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
1. Download the version of [GraalVM Enterprise Edition (EE)][graalvm-ee] you want to run on GitHub Actions.
|
||||
2. Use the [GraalVM Updater][gu] to install the GraalVM components you need on GitHub Actions and accept the corresponding licenses.
|
||||
3. Run `$GRAALVM_HOME/bin/gu --show-ee-token` to display your token for the GraalVM Download Service.
|
||||
4. Store this token as a [GitHub Action secret][gha-secrets]. In the following template, we use the name `GDS_TOKEN`:
|
||||
|
||||
```yml
|
||||
name: GraalVM Enterprise Edition build
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: graalvm/setup-graalvm@v1
|
||||
with:
|
||||
version: '22.3.0'
|
||||
gds-token: ${{ secrets.GDS_TOKEN }}
|
||||
java-version: '17'
|
||||
components: 'native-image'
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Example step
|
||||
run: |
|
||||
java --version
|
||||
native-image --version
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## Supported distributions
|
||||
|
||||
Currently, the following distributions are supported:
|
||||
|
||||
| Keyword | Distribution | Official site | License |
|
||||
|-|-|-|-|
|
||||
| `graalvm` | Oracle GraalVM | [Link](https://www.graalvm.org/) | [Link](https://www.oracle.com/downloads/licenses/graal-free-license.html) |
|
||||
| `graalvm-community` | GraalVM Community Edition | [Link](https://www.graalvm.org/) | [Link](https://github.com/oracle/graal/blob/master/LICENSE) |
|
||||
| `mandrel` | Mandrel | [Link](https://github.com/graalvm/mandrel) | [Link](https://github.com/graalvm/mandrel/blob/default/LICENSE) |
|
||||
| `liberica` | Liberica NIK | [Link](https://bell-sw.com/liberica-native-image-kit/) | [Link](https://bell-sw.com/liberica_nik_eula/) |
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
This actions can be configured with the following options:
|
||||
|
||||
| Name | Default | Description |
|
||||
|-----------------|:--------:|-------------|
|
||||
| `java-version`<br>*(required)* | n/a | Java version <ul><li>major versions: `'23'`, `'21'`, `'17'`, `'11'`, `'8'`</li><li>specific versions: `'21.0.3'`, `'17.0.11'`</li><li>early access (EA) builds: `'24-ea'` *(requires `distribution: 'graalvm'`)*</li><li>latest EA build: `'latest-ea'` *(requires `distribution: 'graalvm'`)*</li><li>dev builds: `'dev'`</li></ul> |
|
||||
| `distribution` | `'graalvm'` | GraalVM distribution (see [supported distributions](#supported-distributions)) |
|
||||
| `java-package` | `'jdk'` | The package type (`'jdk'` or `'jdk+fx'`). Currently applies to Liberica only. |
|
||||
| `github-token` | `'${{ github.token }}'` | Token for communication with the GitHub API. Please set this to `${{ secrets.GITHUB_TOKEN }}` (see [templates](#templates)) to allow the action to authenticate with the GitHub API, which helps reduce rate-limiting issues. |
|
||||
| `set-java-home` | `'true'` | If set to `'true'`, instructs the action to set `$JAVA_HOME` to the path of the GraalVM installation. Overrides any previous action or command that sets `$JAVA_HOME`. |
|
||||
| `cache` | `''` | Name of the build platform to cache dependencies. Turned off by default (`''`). It can also be `'maven'`, `'gradle'`, or `'sbt'` and works the same way as described in [actions/setup-java][setup-java-caching]. |
|
||||
| `check-for-updates` | `'true'` | [Annotate jobs][gha-annotations] with update notifications, for example when a new GraalVM release is available. |
|
||||
| `native-image-musl` | `'false'` | If set to `'true'`, sets up [musl] to build [static binaries][native-image-static] with GraalVM Native Image *(Linux only)*. [Example usage][native-image-musl-build] (be sure to replace `uses: ./` with `uses: graalvm/setup-graalvm@v1`). |
|
||||
| `native-image-job-reports` *) | `'false'` | If set to `'true'`, post a job summary containing a Native Image build report. |
|
||||
| `native-image-pr-reports` *) | `'false'` | If set to `'true'`, post a comment containing a Native Image build report on pull requests. Requires `write` permissions for the [`pull-requests` scope][gha-permissions]. |
|
||||
| `native-image-pr-reports-update-existing` *) | `'false'` | Instead of posting another comment, update an existing PR comment with the latest Native Image build report. Requires `native-image-pr-reports` to be `true`. |
|
||||
| `components` | `''` | Comma-separated list of GraalVM components (e.g., `native-image` or `ruby,nodejs`) that will be installed by the [GraalVM Updater][gu]. |
|
||||
| `version` | `''` | `X.Y.Z` (e.g., `22.3.0`) for a specific [GraalVM release][releases] up to `22.3.2`<br>`mandrel-X.Y.Z.W` or `X.Y.Z.W-Final` (e.g., `mandrel-21.3.0.0-Final` or `21.3.0.0-Final`) for a specific [Mandrel release][mandrel-releases],<br>`mandrel-latest` or `latest` for the latest Mandrel stable release. |
|
||||
| `gds-token` | `''` Download token for the GraalVM Download Service. If a non-empty token is provided, the action will set up Oracle GraalVM (see [Oracle GraalVM via GDS template](#template-for-oracle-graalvm-via-graalvm-download-service)) or GraalVM Enterprise Edition (see [GraalVM EE template](#template-for-graalvm-enterprise-edition)) via GDS. |
|
||||
|
||||
**) Make sure that Native Image is used only once per build job. Otherwise, the report is only generated for the last Native Image build.*
|
||||
|
||||
|
||||
## Notes on Oracle GraalVM for JDK 17
|
||||
|
||||
GraalVM for JDK 17.0.12 is the [last release of Oracle GraalVM for JDK 17 under the GFTC](https://blogs.oracle.com/java/post/jdk-17-approaches-endofpermissive-license).
|
||||
Updates after September 2024 will be licensed under the [GraalVM OTN License Including License for Early Adopter Versions](https://www.oracle.com/downloads/licenses/graalvm-otn-license.html) (GOTN) and production use beyond the limited free grants of the GraalVM OTN license will require a fee.
|
||||
|
||||
As a user of `setup-graalvm`, you have the following options:
|
||||
|
||||
- *Recommended*: Upgrade to Oracle GraalVM for JDK 21 or later to receive new updates.
|
||||
- *Not recommended*: Instead of `java-version: '17'`, use `java-version: '17.0.12'` in your workflow to keep using the last release under GFTC. This will also disable the warning. Note that switching to GraalVM Community Edition or other GraalVM distributions might provide you with even older releases of GraalVM.
|
||||
- Provide a `gds-token` to access Oracle GraalVM for JDK 17 under GOTN (see [Oracle GraalVM via GDS template](#template-for-oracle-graalvm-via-graalvm-download-service)).
|
||||
|
||||
|
||||
## Migrating from GraalVM 22.3 or Earlier to the New GraalVM for JDK 17 and Later
|
||||
|
||||
The [GraalVM for JDK 17 and JDK 20 release](https://medium.com/graalvm/a-new-graalvm-release-and-new-free-license-4aab483692f5) aligns the GraalVM version scheme with OpenJDK.
|
||||
As a result, this action no longer requires the `version` option to select a specific GraalVM version.
|
||||
At the same time, it introduces a new `distribution` option to select a specific GraalVM distribution (`graalvm`, `graalvm-community`, or `mandrel`).
|
||||
Therefore, to migrate your workflow to use the latest GraalVM release, replace the `version` with the `distribution` option in the workflow `yml` config, for example:
|
||||
|
||||
```yml
|
||||
# ...
|
||||
- uses: graalvm/setup-graalvm@v1
|
||||
with:
|
||||
java-version: '17'
|
||||
version: '22.3.2' # Old 'version' option for the GraalVM version
|
||||
# ...
|
||||
```
|
||||
|
||||
can be replaced with:
|
||||
|
||||
```yml
|
||||
# ...
|
||||
- uses: graalvm/setup-graalvm@v1
|
||||
with:
|
||||
java-version: '17.0.12' # for a specific JDK 17; or '17' for the latest JDK 17
|
||||
distribution: 'graalvm' # New 'distribution' option
|
||||
# ...
|
||||
```
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome code contributions. To get started, you will need to sign the [Oracle Contributor Agreement][oca] (OCA).
|
||||
|
||||
Only pull requests from committers that can be verified as having signed the OCA can be accepted.
|
||||
|
||||
|
||||
[dev-build]: https://github.com/graalvm/graalvm-ce-dev-builds/releases/latest
|
||||
[dev-builds]: https://github.com/graalvm/graalvm-ce-dev-builds
|
||||
[ea-builds]: https://github.com/graalvm/oracle-graalvm-ea-builds
|
||||
[gftc]: https://www.oracle.com/downloads/licenses/graal-free-license.html
|
||||
[gha-annotations]: https://github.com/actions/toolkit/tree/main/packages/core#annotations
|
||||
[gha-permissions]: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
||||
[gha-secrets]: https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository
|
||||
[gha-self-hosted-runners]: https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners
|
||||
[gu]: https://www.graalvm.org/reference-manual/graalvm-updater/
|
||||
[graalvm]: https://www.graalvm.org/
|
||||
[graalvm-dl]: https://www.oracle.com/java/technologies/downloads/
|
||||
[graalvm-medium]: https://medium.com/graalvm/a-new-graalvm-release-and-new-free-license-4aab483692f5
|
||||
[graalvm-ee]: https://www.oracle.com/downloads/graalvm-downloads.html
|
||||
[liberica]: https://bell-sw.com/liberica-native-image-kit/
|
||||
[mandrel]: https://github.com/graalvm/mandrel
|
||||
[mandrel-releases]: https://github.com/graalvm/mandrel/releases
|
||||
[mandrel-stable]: https://github.com/graalvm/mandrel/releases/latest
|
||||
[musl]: https://musl.libc.org/
|
||||
[native-image]: https://www.graalvm.org/native-image/
|
||||
[native-image-musl-build]: https://github.com/graalvm/setup-graalvm/blob/778131f1d6837ccd4b2e91382c31830896a2d56e/.github/workflows/test.yml#L74-L92
|
||||
[native-image-static]: https://github.com/oracle/graal/blob/fa6f4a974dedacf4688dcc430dd100849d9882f2/docs/reference-manual/native-image/StaticImages.md
|
||||
[oca]: https://oca.opensource.oracle.com
|
||||
[releases]: https://github.com/graalvm/graalvm-ce-builds/releases
|
||||
[repo]: https://github.com/oracle/graal
|
||||
[setup-java-caching]: https://github.com/actions/setup-java/tree/5b36705a13905facb447b6812d613a06a07e371d#caching-packages-dependencies
|
||||
[stable]: https://github.com/graalvm/graalvm-ce-builds/releases/latest
|
||||
[truffle-languages]: https://www.graalvm.org/reference-manual/languages/
|
||||
[vcvarsall]: https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line
|
||||
|
||||
385
__tests__/cache.test.ts
Normal file
385
__tests__/cache.test.ts
Normal file
@@ -0,0 +1,385 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 GitHub, Inc. and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Forked from https://github.com/actions/setup-java/blob/5b36705a13905facb447b6812d613a06a07e371d/__tests__/cache.test.ts
|
||||
*/
|
||||
|
||||
import {mkdtempSync} from 'fs'
|
||||
import {tmpdir} from 'os'
|
||||
import {join} from 'path'
|
||||
import {restore, save} from '../src/features/cache'
|
||||
import * as fs from 'fs'
|
||||
import * as os from 'os'
|
||||
import * as core from '@actions/core'
|
||||
import * as cache from '@actions/cache'
|
||||
|
||||
describe('dependency cache', () => {
|
||||
const ORIGINAL_RUNNER_OS = process.env['RUNNER_OS']
|
||||
const ORIGINAL_GITHUB_WORKSPACE = process.env['GITHUB_WORKSPACE']
|
||||
const ORIGINAL_CWD = process.cwd()
|
||||
let workspace: string
|
||||
let spyInfo: jest.SpyInstance<void, Parameters<typeof core.info>>
|
||||
let spyWarning: jest.SpyInstance<void, Parameters<typeof core.warning>>
|
||||
let spyDebug: jest.SpyInstance<void, Parameters<typeof core.debug>>
|
||||
let spySaveState: jest.SpyInstance<void, Parameters<typeof core.saveState>>
|
||||
|
||||
beforeEach(() => {
|
||||
workspace = mkdtempSync(join(tmpdir(), 'setup-graalvm-cache-'))
|
||||
switch (os.platform()) {
|
||||
case 'darwin':
|
||||
process.env['RUNNER_OS'] = 'macOS'
|
||||
break
|
||||
case 'win32':
|
||||
process.env['RUNNER_OS'] = 'Windows'
|
||||
break
|
||||
case 'linux':
|
||||
process.env['RUNNER_OS'] = 'Linux'
|
||||
break
|
||||
default:
|
||||
throw new Error(`unknown platform: ${os.platform()}`)
|
||||
}
|
||||
process.chdir(workspace)
|
||||
// This hack is necessary because @actions/glob ignores files not in the GITHUB_WORKSPACE
|
||||
// https://git.io/Jcxig
|
||||
process.env['GITHUB_WORKSPACE'] = projectRoot(workspace)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
spyInfo = jest.spyOn(core, 'info')
|
||||
spyInfo.mockImplementation(() => null)
|
||||
|
||||
spyWarning = jest.spyOn(core, 'warning')
|
||||
spyWarning.mockImplementation(() => null)
|
||||
|
||||
spyDebug = jest.spyOn(core, 'debug')
|
||||
spyDebug.mockImplementation(() => null)
|
||||
|
||||
spySaveState = jest.spyOn(core, 'saveState')
|
||||
spySaveState.mockImplementation(() => null)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
process.chdir(ORIGINAL_CWD)
|
||||
process.env['GITHUB_WORKSPACE'] = ORIGINAL_GITHUB_WORKSPACE
|
||||
process.env['RUNNER_OS'] = ORIGINAL_RUNNER_OS
|
||||
resetState()
|
||||
})
|
||||
|
||||
describe('restore', () => {
|
||||
let spyCacheRestore: jest.SpyInstance<
|
||||
ReturnType<typeof cache.restoreCache>,
|
||||
Parameters<typeof cache.restoreCache>
|
||||
>
|
||||
|
||||
beforeEach(() => {
|
||||
spyCacheRestore = jest
|
||||
.spyOn(cache, 'restoreCache')
|
||||
.mockImplementation((paths: string[], primaryKey: string) =>
|
||||
Promise.resolve(undefined)
|
||||
)
|
||||
spyWarning.mockImplementation(() => null)
|
||||
})
|
||||
|
||||
it('throws error if unsupported package manager specified', () => {
|
||||
return expect(restore('ant')).rejects.toThrow(
|
||||
'unknown package manager specified: ant'
|
||||
)
|
||||
})
|
||||
|
||||
describe('for maven', () => {
|
||||
it('throws error if no pom.xml found', async () => {
|
||||
await expect(restore('maven')).rejects.toThrow(
|
||||
`No file in ${projectRoot(
|
||||
workspace
|
||||
)} matched to [**/pom.xml], make sure you have checked out the target repository`
|
||||
)
|
||||
})
|
||||
it('downloads cache', async () => {
|
||||
createFile(join(workspace, 'pom.xml'))
|
||||
|
||||
await restore('maven')
|
||||
expect(spyCacheRestore).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith('maven cache is not found')
|
||||
})
|
||||
})
|
||||
describe('for gradle', () => {
|
||||
it('throws error if no build.gradle found', async () => {
|
||||
await expect(restore('gradle')).rejects.toThrow(
|
||||
`No file in ${projectRoot(
|
||||
workspace
|
||||
)} matched to [**/*.gradle*,**/gradle-wrapper.properties,buildSrc/**/Versions.kt,buildSrc/**/Dependencies.kt], make sure you have checked out the target repository`
|
||||
)
|
||||
})
|
||||
it('downloads cache based on build.gradle', async () => {
|
||||
createFile(join(workspace, 'build.gradle'))
|
||||
|
||||
await restore('gradle')
|
||||
expect(spyCacheRestore).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found')
|
||||
})
|
||||
it('downloads cache based on build.gradle.kts', async () => {
|
||||
createFile(join(workspace, 'build.gradle.kts'))
|
||||
|
||||
await restore('gradle')
|
||||
expect(spyCacheRestore).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found')
|
||||
})
|
||||
})
|
||||
it('downloads cache based on buildSrc/Versions.kt', async () => {
|
||||
createDirectory(join(workspace, 'buildSrc'))
|
||||
createFile(join(workspace, 'buildSrc', 'Versions.kt'))
|
||||
|
||||
await restore('gradle')
|
||||
expect(spyCacheRestore).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found')
|
||||
})
|
||||
describe('for sbt', () => {
|
||||
it('throws error if no build.sbt found', async () => {
|
||||
await expect(restore('sbt')).rejects.toThrow(
|
||||
`No file in ${projectRoot(
|
||||
workspace
|
||||
)} matched to [**/*.sbt,**/project/build.properties,**/project/**.{scala,sbt}], make sure you have checked out the target repository`
|
||||
)
|
||||
})
|
||||
it('downloads cache', async () => {
|
||||
createFile(join(workspace, 'build.sbt'))
|
||||
|
||||
await restore('sbt')
|
||||
expect(spyCacheRestore).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith('sbt cache is not found')
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('save', () => {
|
||||
let spyCacheSave: jest.SpyInstance<
|
||||
ReturnType<typeof cache.saveCache>,
|
||||
Parameters<typeof cache.saveCache>
|
||||
>
|
||||
|
||||
beforeEach(() => {
|
||||
spyCacheSave = jest
|
||||
.spyOn(cache, 'saveCache')
|
||||
.mockImplementation((paths: string[], key: string) =>
|
||||
Promise.resolve(0)
|
||||
)
|
||||
spyWarning.mockImplementation(() => null)
|
||||
})
|
||||
|
||||
it('throws error if unsupported package manager specified', () => {
|
||||
return expect(save('ant')).rejects.toThrow(
|
||||
'unknown package manager specified: ant'
|
||||
)
|
||||
})
|
||||
|
||||
it('save with -1 cacheId , should not fail workflow', async () => {
|
||||
spyCacheSave.mockImplementation(() => Promise.resolve(-1))
|
||||
createStateForMissingBuildFile()
|
||||
|
||||
await save('maven')
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/^Cache saved with the key:.*/)
|
||||
)
|
||||
})
|
||||
|
||||
it('saves with error from toolkit, should fail workflow', async () => {
|
||||
spyCacheSave.mockImplementation(() =>
|
||||
Promise.reject(new cache.ValidationError('Validation failed'))
|
||||
)
|
||||
createStateForMissingBuildFile()
|
||||
|
||||
expect.assertions(1)
|
||||
await expect(save('maven')).rejects.toEqual(
|
||||
new cache.ValidationError('Validation failed')
|
||||
)
|
||||
})
|
||||
|
||||
describe('for maven', () => {
|
||||
it('uploads cache even if no pom.xml found', async () => {
|
||||
createStateForMissingBuildFile()
|
||||
await save('maven')
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
})
|
||||
it('does not upload cache if no restore run before', async () => {
|
||||
createFile(join(workspace, 'pom.xml'))
|
||||
|
||||
await save('maven')
|
||||
expect(spyCacheSave).not.toHaveBeenCalled()
|
||||
expect(spyWarning).toHaveBeenCalledWith(
|
||||
'Error retrieving key from state.'
|
||||
)
|
||||
})
|
||||
it('uploads cache', async () => {
|
||||
createFile(join(workspace, 'pom.xml'))
|
||||
createStateForSuccessfulRestore()
|
||||
|
||||
await save('maven')
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/^Cache saved with the key:.*/)
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('for gradle', () => {
|
||||
it('uploads cache even if no build.gradle found', async () => {
|
||||
createStateForMissingBuildFile()
|
||||
|
||||
await save('gradle')
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
})
|
||||
it('does not upload cache if no restore run before', async () => {
|
||||
createFile(join(workspace, 'build.gradle'))
|
||||
|
||||
await save('gradle')
|
||||
expect(spyCacheSave).not.toHaveBeenCalled()
|
||||
expect(spyWarning).toHaveBeenCalledWith(
|
||||
'Error retrieving key from state.'
|
||||
)
|
||||
})
|
||||
it('uploads cache based on build.gradle', async () => {
|
||||
createFile(join(workspace, 'build.gradle'))
|
||||
createStateForSuccessfulRestore()
|
||||
|
||||
await save('gradle')
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/^Cache saved with the key:.*/)
|
||||
)
|
||||
})
|
||||
it('uploads cache based on build.gradle.kts', async () => {
|
||||
createFile(join(workspace, 'build.gradle.kts'))
|
||||
createStateForSuccessfulRestore()
|
||||
|
||||
await save('gradle')
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/^Cache saved with the key:.*/)
|
||||
)
|
||||
})
|
||||
it('uploads cache based on buildSrc/Versions.kt', async () => {
|
||||
createDirectory(join(workspace, 'buildSrc'))
|
||||
createFile(join(workspace, 'buildSrc', 'Versions.kt'))
|
||||
createStateForSuccessfulRestore()
|
||||
|
||||
await save('gradle')
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/^Cache saved with the key:.*/)
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('for sbt', () => {
|
||||
it('uploads cache even if no build.sbt found', async () => {
|
||||
createStateForMissingBuildFile()
|
||||
await save('sbt')
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
})
|
||||
it('does not upload cache if no restore run before', async () => {
|
||||
createFile(join(workspace, 'build.sbt'))
|
||||
|
||||
await save('sbt')
|
||||
expect(spyCacheSave).not.toHaveBeenCalled()
|
||||
expect(spyWarning).toHaveBeenCalledWith(
|
||||
'Error retrieving key from state.'
|
||||
)
|
||||
})
|
||||
it('uploads cache', async () => {
|
||||
createFile(join(workspace, 'build.sbt'))
|
||||
createStateForSuccessfulRestore()
|
||||
|
||||
await save('sbt')
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
expect(spyInfo).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/^Cache saved with the key:.*/)
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function resetState() {
|
||||
jest.spyOn(core, 'getState').mockReset()
|
||||
}
|
||||
|
||||
/**
|
||||
* Create states to emulate a restore process without build file.
|
||||
*/
|
||||
function createStateForMissingBuildFile() {
|
||||
jest.spyOn(core, 'getState').mockImplementation(name => {
|
||||
switch (name) {
|
||||
case 'cache-primary-key':
|
||||
return 'setup-graalvm-cache-'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Create states to emulate a successful restore process.
|
||||
*/
|
||||
function createStateForSuccessfulRestore() {
|
||||
jest.spyOn(core, 'getState').mockImplementation(name => {
|
||||
switch (name) {
|
||||
case 'cache-primary-key':
|
||||
return 'setup-graalvm-cache-primary-key'
|
||||
case 'cache-matched-key':
|
||||
return 'setup-graalvm-cache-matched-key'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function createFile(path: string) {
|
||||
core.info(`created a file at ${path}`)
|
||||
fs.writeFileSync(path, '')
|
||||
}
|
||||
|
||||
function createDirectory(path: string) {
|
||||
core.info(`created a directory at ${path}`)
|
||||
fs.mkdirSync(path)
|
||||
}
|
||||
|
||||
function projectRoot(workspace: string): string {
|
||||
if (os.platform() === 'darwin') {
|
||||
return `/private${workspace}`
|
||||
} else {
|
||||
return workspace
|
||||
}
|
||||
}
|
||||
12
__tests__/cache/gradle/.gitignore
vendored
Normal file
12
__tests__/cache/gradle/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
.gradle
|
||||
**/build/
|
||||
!src/**/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
17
__tests__/cache/gradle/build.gradle
vendored
Normal file
17
__tests__/cache/gradle/build.gradle
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
implementation 'org.codehaus.groovy:groovy:1.8.6'
|
||||
}
|
||||
tasks.register('downloadDependencies') {
|
||||
doLast {
|
||||
def total = configurations.compileClasspath.inject (0) { sum, file ->
|
||||
sum + file.length()
|
||||
}
|
||||
println total
|
||||
}
|
||||
}
|
||||
11
__tests__/cache/maven/.gitignore
vendored
Normal file
11
__tests__/cache/maven/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
16
__tests__/cache/maven/pom.xml
vendored
Normal file
16
__tests__/cache/maven/pom.xml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>io.github.actions</groupId>
|
||||
<artifactId>setup-java-maven-example</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.7.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
1
__tests__/cache/sbt/.gitignore
vendored
Normal file
1
__tests__/cache/sbt/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
target/
|
||||
3
__tests__/cache/sbt/build.sbt
vendored
Normal file
3
__tests__/cache/sbt/build.sbt
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
ThisBuild / scalaVersion := "2.12.15"
|
||||
|
||||
libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2"
|
||||
1
__tests__/cache/sbt/project/build.properties
vendored
Normal file
1
__tests__/cache/sbt/project/build.properties
vendored
Normal file
@@ -0,0 +1 @@
|
||||
sbt.version=1.6.2
|
||||
98
__tests__/cleanup.test.ts
Normal file
98
__tests__/cleanup.test.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 GitHub, Inc. and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Forked from https://github.com/actions/setup-java/blob/5b36705a13905facb447b6812d613a06a07e371d/__tests__/cleanup-java.test.ts
|
||||
*/
|
||||
|
||||
import {run as cleanup} from '../src/cleanup'
|
||||
import * as core from '@actions/core'
|
||||
import * as cache from '@actions/cache'
|
||||
|
||||
describe('cleanup', () => {
|
||||
let spyWarning: jest.SpyInstance<void, Parameters<typeof core.warning>>
|
||||
let spyInfo: jest.SpyInstance<void, Parameters<typeof core.info>>
|
||||
let spyCacheSave: jest.SpyInstance<
|
||||
ReturnType<typeof cache.saveCache>,
|
||||
Parameters<typeof cache.saveCache>
|
||||
>
|
||||
let spyJobStatusSuccess: jest.SpyInstance
|
||||
|
||||
beforeEach(() => {
|
||||
spyWarning = jest.spyOn(core, 'warning')
|
||||
spyWarning.mockImplementation(() => null)
|
||||
spyInfo = jest.spyOn(core, 'info')
|
||||
spyInfo.mockImplementation(() => null)
|
||||
spyCacheSave = jest.spyOn(cache, 'saveCache')
|
||||
createStateForSuccessfulRestore()
|
||||
})
|
||||
afterEach(() => {
|
||||
resetState()
|
||||
})
|
||||
|
||||
it('does not fail nor warn even when the save provess throws a ReserveCacheError', async () => {
|
||||
spyCacheSave.mockImplementation((paths: string[], key: string) =>
|
||||
Promise.reject(
|
||||
new cache.ReserveCacheError(
|
||||
'Unable to reserve cache with key, another job may be creating this cache.'
|
||||
)
|
||||
)
|
||||
)
|
||||
jest.spyOn(core, 'getInput').mockImplementation((name: string) => {
|
||||
return name === 'cache' ? 'gradle' : ''
|
||||
})
|
||||
await cleanup()
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
expect(spyWarning).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not fail even though the save process throws error', async () => {
|
||||
spyCacheSave.mockImplementation((paths: string[], key: string) =>
|
||||
Promise.reject(new Error('Unexpected error'))
|
||||
)
|
||||
jest.spyOn(core, 'getInput').mockImplementation((name: string) => {
|
||||
return name === 'cache' ? 'gradle' : ''
|
||||
})
|
||||
await cleanup()
|
||||
expect(spyCacheSave).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
function resetState() {
|
||||
jest.spyOn(core, 'getState').mockReset()
|
||||
}
|
||||
|
||||
/**
|
||||
* Create states to emulate a successful restore process.
|
||||
*/
|
||||
function createStateForSuccessfulRestore() {
|
||||
jest.spyOn(core, 'getState').mockImplementation(name => {
|
||||
switch (name) {
|
||||
case 'cache-primary-key':
|
||||
return 'setup-java-cache-primary-key'
|
||||
case 'cache-matched-key':
|
||||
return 'setup-java-cache-matched-key'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
}
|
||||
77
__tests__/gds.test.ts
Normal file
77
__tests__/gds.test.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import * as path from 'path'
|
||||
import {
|
||||
downloadGraalVM,
|
||||
downloadGraalVMEELegacy,
|
||||
fetchArtifact,
|
||||
fetchArtifactEE
|
||||
} from '../src/gds'
|
||||
import {expect, test} from '@jest/globals'
|
||||
|
||||
const TEST_USER_AGENT = 'GraalVMGitHubActionTest/1.0.4'
|
||||
|
||||
process.env['RUNNER_TEMP'] = path.join(__dirname, 'TEMP')
|
||||
|
||||
test('fetch artifacts', async () => {
|
||||
let artifact = await fetchArtifact(TEST_USER_AGENT, 'isBase:True', '17.0.12')
|
||||
expect(artifact.id).toBe('1C351E8F41BB8E9EE0631518000AE5F2')
|
||||
expect(artifact.checksum).toBe(
|
||||
'b6f3dace24cf1960ec790216f4c86f00d4f43df64e4e8b548f6382f04894713f'
|
||||
)
|
||||
artifact = await fetchArtifact(TEST_USER_AGENT, 'isBase:True', '17')
|
||||
expect(artifact.checksum).toHaveLength(
|
||||
'b6f3dace24cf1960ec790216f4c86f00d4f43df64e4e8b548f6382f04894713f'.length
|
||||
)
|
||||
})
|
||||
|
||||
test('errors when downloading artifacts', async () => {
|
||||
await expect(downloadGraalVM('invalid', '17')).rejects.toThrow(
|
||||
'The provided "gds-token" was rejected (reason: "Invalid download token", opc-request-id: '
|
||||
)
|
||||
await expect(downloadGraalVM('invalid', '1')).rejects.toThrow(
|
||||
'Unable to find GraalVM for JDK 1'
|
||||
)
|
||||
})
|
||||
|
||||
test('fetch legacy artifacts', async () => {
|
||||
let artifact = await fetchArtifactEE(
|
||||
TEST_USER_AGENT,
|
||||
'isBase:True',
|
||||
'22.1.0',
|
||||
'11'
|
||||
)
|
||||
expect(artifact.id).toBe('DCECD1C1B0B5B8DBE0536E16000A5C74')
|
||||
expect(artifact.checksum).toBe(
|
||||
'4280782f6c7fcabe0ba707e8389cbfaf7bbe6b0cf634d309e6efcd1b172e3ce6'
|
||||
)
|
||||
artifact = await fetchArtifactEE(
|
||||
TEST_USER_AGENT,
|
||||
'isBase:True',
|
||||
'22.1.0',
|
||||
'17'
|
||||
)
|
||||
expect(artifact.id).toBe('DCECD2068882A0E9E0536E16000A9504')
|
||||
expect(artifact.checksum).toBe(
|
||||
'e897add7d94bc456a61e6f927e831dff759efa3392a4b69c720dd3debc8f947d'
|
||||
)
|
||||
|
||||
await expect(
|
||||
fetchArtifactEE(TEST_USER_AGENT, 'isBase:False', '22.1.0', '11')
|
||||
).rejects.toThrow('Found more than one GDS artifact')
|
||||
await expect(
|
||||
fetchArtifactEE(TEST_USER_AGENT, 'isBase:True', '1.0.0', '11')
|
||||
).rejects.toThrow('Unable to find JDK11-based GraalVM EE 1.0.0')
|
||||
})
|
||||
|
||||
test('errors when downloading legacy artifacts', async () => {
|
||||
await expect(
|
||||
downloadGraalVMEELegacy('invalid', '22.1.0', '11')
|
||||
).rejects.toThrow(
|
||||
'The provided "gds-token" was rejected (reason: "Invalid download token", opc-request-id: '
|
||||
)
|
||||
await expect(
|
||||
downloadGraalVMEELegacy('invalid', '1.0.0', '11')
|
||||
).rejects.toThrow('Unable to find JDK11-based GraalVM EE 1.0.0')
|
||||
await expect(
|
||||
downloadGraalVMEELegacy('invalid', '22.1.0', '1')
|
||||
).rejects.toThrow('Unable to find JDK1-based GraalVM EE 22.1.0')
|
||||
})
|
||||
104
__tests__/graalvm.test.ts
Normal file
104
__tests__/graalvm.test.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import * as path from 'path'
|
||||
import * as graalvm from '../src/graalvm'
|
||||
import {expect, test} from '@jest/globals'
|
||||
import {getTaggedRelease} from '../src/utils'
|
||||
import {
|
||||
findGraalVMVersion,
|
||||
findHighestJavaVersion,
|
||||
findLatestEABuildDownloadUrl
|
||||
} from '../src/graalvm'
|
||||
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')
|
||||
|
||||
test('request invalid version/javaVersion', async () => {
|
||||
for (var combination of [
|
||||
['22.3.0', '7'],
|
||||
['22.3', '17'],
|
||||
['22.3', '7']
|
||||
]) {
|
||||
let error = new Error('unexpected')
|
||||
try {
|
||||
await graalvm.setUpGraalVMRelease('', combination[0], combination[1])
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
fail(`Unexpected non-Error: ${err}`)
|
||||
}
|
||||
error = err
|
||||
}
|
||||
|
||||
expect(error).not.toBeUndefined()
|
||||
expect(error.message).toContain('Failed to download')
|
||||
expect(error.message).toContain('Are you sure version')
|
||||
}
|
||||
})
|
||||
|
||||
test('find version/javaVersion', async () => {
|
||||
// Make sure the action can find the latest Java version for known major versions
|
||||
for (var majorJavaVersion of ['17', '20']) {
|
||||
await graalvm.findLatestGraalVMJDKCEJavaVersion(majorJavaVersion)
|
||||
}
|
||||
|
||||
let error = new Error('unexpected')
|
||||
try {
|
||||
await graalvm.findLatestGraalVMJDKCEJavaVersion('11')
|
||||
fail('Should not find Java version for 11')
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
fail(`Unexpected non-Error: ${err}`)
|
||||
}
|
||||
error = err
|
||||
}
|
||||
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'
|
||||
)
|
||||
const latestVersion = findGraalVMVersion(latestRelease)
|
||||
expect(latestVersion).not.toBe('')
|
||||
const latestJavaVersion = findHighestJavaVersion(latestRelease, latestVersion)
|
||||
expect(latestJavaVersion).not.toBe('')
|
||||
|
||||
error = new Error('unexpected')
|
||||
try {
|
||||
const invalidRelease = {...latestRelease, tag_name: 'invalid'}
|
||||
findGraalVMVersion(invalidRelease)
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
fail(`Unexpected non-Error: ${err}`)
|
||||
}
|
||||
error = err
|
||||
}
|
||||
expect(error.message).toContain('Could not find latest GraalVM release:')
|
||||
|
||||
try {
|
||||
findHighestJavaVersion(latestRelease, 'invalid')
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
fail(`Unexpected non-Error: ${err}`)
|
||||
}
|
||||
error = err
|
||||
}
|
||||
expect(error.message).toContain('Could not find highest Java version.')
|
||||
})
|
||||
|
||||
test('find version/javaVersion', async () => {
|
||||
let url22EA = await findLatestEABuildDownloadUrl('22-ea')
|
||||
expect(url22EA).not.toBe('')
|
||||
let urlLatestEA = await findLatestEABuildDownloadUrl('latest-ea')
|
||||
expect(urlLatestEA).not.toBe('')
|
||||
|
||||
let error = new Error('unexpected')
|
||||
try {
|
||||
await findLatestEABuildDownloadUrl('8-ea')
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
fail(`Unexpected non-Error: ${err}`)
|
||||
}
|
||||
error = err
|
||||
}
|
||||
expect(error.message).toContain('Unable to resolve download URL for')
|
||||
})
|
||||
138
__tests__/liberica.test.ts
Normal file
138
__tests__/liberica.test.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import * as liberica from '../src/liberica'
|
||||
import * as c from '../src/constants'
|
||||
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('11.0.22+12', '', 'bellsoft-liberica-vm-openjdk11.0.22')
|
||||
await expectURL('17.0.10+13', 'jdk', 'bellsoft-liberica-vm-openjdk17.0.10')
|
||||
|
||||
if (!c.IS_LINUX) {
|
||||
// This check can fail on Linux because there's no `jdk+fx` package for aarch64 and/or musl
|
||||
await expectURL(
|
||||
'21.0.2+14',
|
||||
'jdk+fx',
|
||||
'bellsoft-liberica-vm-full-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,
|
||||
javaPackage: string,
|
||||
expectedPrefix: string
|
||||
) {
|
||||
const url = await liberica.findLibericaURL(javaVersion, javaPackage)
|
||||
expect(url).toBeDefined()
|
||||
const parts = url.split('/')
|
||||
const file = parts[parts.length - 1]
|
||||
expect(file.startsWith(expectedPrefix)).toBe(true)
|
||||
}
|
||||
75
__tests__/mandrel.test.ts
Normal file
75
__tests__/mandrel.test.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import * as path from 'path'
|
||||
import * as mandrel from '../src/mandrel'
|
||||
import {expect, test} from '@jest/globals'
|
||||
import {getLatestRelease} from '../src/utils'
|
||||
|
||||
process.env['RUNNER_TOOL_CACHE'] = path.join(__dirname, 'TOOL_CACHE')
|
||||
process.env['RUNNER_TEMP'] = path.join(__dirname, 'TEMP')
|
||||
|
||||
test('request invalid version/javaVersion combination', async () => {
|
||||
for (var combination of [
|
||||
['mandrel-23.1.1.0-Final', '17'],
|
||||
['mandrel-23.0.2.1-Final', '21']
|
||||
]) {
|
||||
let error = new Error('unexpected')
|
||||
try {
|
||||
await mandrel.setUpMandrel(combination[0], combination[1])
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
fail(`Unexpected non-Error: ${err}`)
|
||||
}
|
||||
error = err
|
||||
}
|
||||
|
||||
expect(error).not.toBeUndefined()
|
||||
expect(error.message).toContain('Failed to download')
|
||||
expect(error.message).toContain('Are you sure version')
|
||||
}
|
||||
})
|
||||
test('request invalid version', async () => {
|
||||
for (var combination of [
|
||||
['mandrel-23.1.1.0', '21'],
|
||||
['mandrel-23.0.2.1', '17']
|
||||
]) {
|
||||
let error = new Error('unexpected')
|
||||
try {
|
||||
await mandrel.setUpMandrel(combination[0], combination[1])
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
fail(`Unexpected non-Error: ${err}`)
|
||||
}
|
||||
error = err
|
||||
}
|
||||
|
||||
expect(error).not.toBeUndefined()
|
||||
expect(error.message).toContain('Failed to download')
|
||||
expect(error.message).toContain('Are you sure version')
|
||||
}
|
||||
})
|
||||
|
||||
test('find latest', async () => {
|
||||
// Make sure the action can find the latest Mandrel release
|
||||
const latestRelease = await getLatestRelease(mandrel.MANDREL_REPO)
|
||||
const tag_name = latestRelease.tag_name
|
||||
expect(tag_name).toContain(mandrel.MANDREL_TAG_PREFIX)
|
||||
})
|
||||
|
||||
test('get known latest Mandrel for specific JDK', async () => {
|
||||
// Test deprecated versions that won't get updates anymore
|
||||
for (var combination of [
|
||||
['11', '22.2.0.0-Final'],
|
||||
['20', '23.0.1.2-Final']
|
||||
]) {
|
||||
const latest = await mandrel.getLatestMandrelReleaseUrl(combination[0])
|
||||
expect(latest).toContain(`mandrel-java${combination[0]}`)
|
||||
expect(latest).toContain(combination[1])
|
||||
}
|
||||
})
|
||||
|
||||
test('get latest Mandrel for specific JDK', async () => {
|
||||
// Test supported versions
|
||||
for (var javaVersion of ['17', '21']) {
|
||||
const latest = await mandrel.getLatestMandrelReleaseUrl(javaVersion)
|
||||
expect(latest).toContain(`mandrel-java${javaVersion}`)
|
||||
}
|
||||
})
|
||||
36
__tests__/msvc.test.ts
Normal file
36
__tests__/msvc.test.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import * as path from 'path'
|
||||
import {expect, test} from '@jest/globals'
|
||||
import {needsWindowsEnvironmentSetup} from '../src/msvc'
|
||||
import {VERSION_DEV, VERSION_LATEST} from '../src/constants'
|
||||
|
||||
process.env['RUNNER_TOOL_CACHE'] = path.join(__dirname, 'TOOL_CACHE')
|
||||
process.env['RUNNER_TEMP'] = path.join(__dirname, 'TEMP')
|
||||
|
||||
test('decide whether Window env must be set up for GraalVM for JDK', async () => {
|
||||
for (var javaVersion of [
|
||||
'17',
|
||||
'17.0.8',
|
||||
'17.0',
|
||||
'21',
|
||||
'22',
|
||||
'22-ea',
|
||||
'23-ea',
|
||||
VERSION_DEV
|
||||
]) {
|
||||
expect(needsWindowsEnvironmentSetup(javaVersion, '', true)).toBe(false)
|
||||
}
|
||||
})
|
||||
|
||||
test('decide whether Window env must be set up for legacy GraalVM', async () => {
|
||||
for (var combination of [
|
||||
['7', '22.3.0'],
|
||||
['17', '22.3'],
|
||||
['7', '22.3'],
|
||||
['7', VERSION_DEV],
|
||||
['17', VERSION_LATEST]
|
||||
]) {
|
||||
expect(
|
||||
needsWindowsEnvironmentSetup(combination[0], combination[1], false)
|
||||
).toBe(combination[1] !== VERSION_DEV)
|
||||
}
|
||||
})
|
||||
34
__tests__/utils.test.ts
Normal file
34
__tests__/utils.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import * as path from 'path'
|
||||
import {expect, test} from '@jest/globals'
|
||||
import {toSemVer} from '../src/utils'
|
||||
|
||||
test('convert version', async () => {
|
||||
for (var inputAndExpectedOutput of [
|
||||
['22', '22.0.0'],
|
||||
['22.0', '22.0.0'],
|
||||
['22.0.0', '22.0.0'],
|
||||
['22.0.0.2', '22.0.0-2'],
|
||||
['22-ea', '22.0.0-ea'],
|
||||
['22.0-ea', '22.0.0-ea'],
|
||||
['22.0.0-ea', '22.0.0-ea']
|
||||
]) {
|
||||
expect(toSemVer(inputAndExpectedOutput[0])).toBe(inputAndExpectedOutput[1])
|
||||
}
|
||||
})
|
||||
|
||||
test('convert invalid version', async () => {
|
||||
for (var input of ['dev', 'abc', 'a.b.c']) {
|
||||
let error = new Error('unexpected')
|
||||
try {
|
||||
toSemVer(input)
|
||||
} catch (err) {
|
||||
if (!(err instanceof Error)) {
|
||||
fail(`Unexpected non-Error: ${err}`)
|
||||
}
|
||||
error = err
|
||||
}
|
||||
|
||||
expect(error).not.toBeUndefined()
|
||||
expect(error.message).toContain('Unable to convert')
|
||||
}
|
||||
})
|
||||
68
action.yml
Normal file
68
action.yml
Normal file
@@ -0,0 +1,68 @@
|
||||
name: 'GitHub Action for GraalVM'
|
||||
description: 'Set up a specific version of the GraalVM JDK and add the command-line tools to the PATH'
|
||||
author: 'GraalVM Community'
|
||||
branding:
|
||||
icon: 'terminal'
|
||||
color: 'blue'
|
||||
inputs:
|
||||
java-version:
|
||||
required: true
|
||||
description: 'Java version. See examples of supported syntax in the README file.'
|
||||
java-package:
|
||||
description: 'The package type (jdk or jdk+fx). Currently applies to Liberica only.'
|
||||
required: false
|
||||
default: 'jdk'
|
||||
distribution:
|
||||
description: 'GraalVM distribution. See the list of available distributions in the README file.'
|
||||
required: false
|
||||
default: ''
|
||||
components:
|
||||
required: false
|
||||
description: 'Comma-separated list of GraalVM components to be installed.'
|
||||
default: ''
|
||||
github-token:
|
||||
required: false
|
||||
description: 'Set it to secrets.GITHUB_TOKEN to increase rate limits when accessing the GitHub API. Defaults to github.token.'
|
||||
default: ${{ github.token }}
|
||||
set-java-home:
|
||||
required: false
|
||||
description: 'Set $JAVA_HOME to the GraalVM installation. Default: true.'
|
||||
default: 'true'
|
||||
cache:
|
||||
description: 'Name of the build platform to cache dependencies. It can be "maven", "gradle", or "sbt".'
|
||||
required: false
|
||||
check-for-updates:
|
||||
required: false
|
||||
description: 'Annotate jobs with update notifications, for example, when a new GraalVM release is available'
|
||||
default: 'true'
|
||||
native-image-musl:
|
||||
required: false
|
||||
description: 'Set up musl for static image building with GraalVM Native Image.'
|
||||
default: 'false'
|
||||
native-image-job-reports:
|
||||
required: false
|
||||
description: 'Post a job summary containing a Native Image build report.'
|
||||
default: 'false'
|
||||
native-image-pr-reports:
|
||||
required: false
|
||||
description: 'Post a comment containing a Native Image build report on pull requests.'
|
||||
default: 'false'
|
||||
native-image-pr-reports-update-existing:
|
||||
required: false
|
||||
description: 'Instead of posting another comment, update an existing PR comment with the latest Native Image build report.'
|
||||
default: 'false'
|
||||
version:
|
||||
required: false
|
||||
description: 'GraalVM version (release, latest, dev).'
|
||||
default: ''
|
||||
gds-token:
|
||||
required: false
|
||||
description: 'Download token for the GraalVM Download Service. If provided, the action will set up GraalVM Enterprise Edition.'
|
||||
outputs:
|
||||
cache-hit:
|
||||
description: 'A boolean value to indicate an exact match was found for the primary key'
|
||||
runs:
|
||||
using: 'node20'
|
||||
main: 'dist/main/index.js'
|
||||
post: 'dist/cleanup/index.js'
|
||||
post-if: 'success()'
|
||||
95828
dist/cleanup/index.js
generated
vendored
Normal file
95828
dist/cleanup/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
97848
dist/main/index.js
generated
vendored
Normal file
97848
dist/main/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
9
jest.config.js
Normal file
9
jest.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
testMatch: ['**/*.test.ts'],
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest'
|
||||
},
|
||||
verbose: true
|
||||
}
|
||||
8011
package-lock.json
generated
Normal file
8011
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
63
package.json
Normal file
63
package.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "setup-graalvm",
|
||||
"version": "1.2.5",
|
||||
"private": true,
|
||||
"description": "GitHub Action for GraalVM",
|
||||
"main": "lib/main.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"format": "prettier --write '**/*.ts'",
|
||||
"format-check": "prettier --check '**/*.ts'",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"package": "ncc build -o dist/main src/main.ts && ncc build -o dist/cleanup src/cleanup.ts",
|
||||
"test": "jest",
|
||||
"all-but-test": "npm run build && npm run format && npm run lint && npm run package",
|
||||
"all": "npm run all-but-test && npm test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/graalvm/setup-graalvm.git"
|
||||
},
|
||||
"keywords": [
|
||||
"graalvm",
|
||||
"native image",
|
||||
"actions",
|
||||
"setup"
|
||||
],
|
||||
"author": "GraalVM Community",
|
||||
"license": "UPL",
|
||||
"dependencies": {
|
||||
"@actions/cache": "^3.2.4",
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^6.0.0",
|
||||
"@actions/glob": "^0.4.0",
|
||||
"@actions/http-client": "^2.2.1",
|
||||
"@actions/io": "^1.1.3",
|
||||
"@actions/tool-cache": "^2.0.1",
|
||||
"@octokit/core": "^5.1.0",
|
||||
"@octokit/types": "^12.6.0",
|
||||
"semver": "^7.6.0",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.11.28",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||
"@typescript-eslint/parser": "^7.2.0",
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-github": "^4.10.2",
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"eslint-plugin-jsonc": "^2.14.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"jest": "^29.7.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-eslint": "^16.3.0",
|
||||
"ts-jest": "^29.1.2",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
69
src/cleanup.ts
Normal file
69
src/cleanup.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 GitHub, Inc. and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Forked from https://github.com/actions/setup-java/blob/5b36705a13905facb447b6812d613a06a07e371d/src/cleanup-java.ts
|
||||
*/
|
||||
|
||||
import * as core from '@actions/core'
|
||||
import * as constants from './constants'
|
||||
import {save} from './features/cache'
|
||||
import {generateReports} from './features/reports'
|
||||
|
||||
/**
|
||||
* Check given input and run a save process for the specified package manager
|
||||
* @returns Promise that will be resolved when the save process finishes
|
||||
*/
|
||||
async function saveCache(): Promise<void> {
|
||||
const cache = core.getInput(constants.INPUT_CACHE)
|
||||
return cache ? save(cache) : Promise.resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
* The save process is best-effort, and it should not make the workflow fail
|
||||
* even though this process throws an error.
|
||||
* @param promise the promise to ignore error from
|
||||
* @returns Promise that will ignore error reported by the given promise
|
||||
*/
|
||||
async function ignoreErrors(promise: Promise<void>): Promise<unknown> {
|
||||
/* eslint-disable github/no-then */
|
||||
return new Promise(resolve => {
|
||||
promise
|
||||
.catch(error => {
|
||||
core.warning(error)
|
||||
resolve(void 0)
|
||||
})
|
||||
.then(resolve)
|
||||
})
|
||||
}
|
||||
|
||||
export async function run(): Promise<void> {
|
||||
await ignoreErrors(generateReports())
|
||||
await ignoreErrors(saveCache())
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
run()
|
||||
} else {
|
||||
// https://nodejs.org/api/modules.html#modules_accessing_the_main_module
|
||||
core.info('the script is loaded as a module, so skipping the execution')
|
||||
}
|
||||
123
src/constants.ts
Normal file
123
src/constants.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import * as otypes from '@octokit/types'
|
||||
|
||||
export const ACTION_VERSION = '1.2.5'
|
||||
|
||||
export const INPUT_VERSION = 'version'
|
||||
export const INPUT_GDS_TOKEN = 'gds-token'
|
||||
export const INPUT_JAVA_VERSION = 'java-version'
|
||||
export const INPUT_JAVA_PACKAGE = 'java-package'
|
||||
export const INPUT_DISTRIBUTION = 'distribution'
|
||||
export const INPUT_COMPONENTS = 'components'
|
||||
export const INPUT_GITHUB_TOKEN = 'github-token'
|
||||
export const INPUT_SET_JAVA_HOME = 'set-java-home'
|
||||
export const INPUT_CACHE = 'cache'
|
||||
export const INPUT_CHECK_FOR_UPDATES = 'check-for-updates'
|
||||
export const INPUT_NI_MUSL = 'native-image-musl'
|
||||
|
||||
export const IS_LINUX = process.platform === 'linux'
|
||||
export const IS_MACOS = process.platform === 'darwin'
|
||||
export const IS_WINDOWS = process.platform === 'win32'
|
||||
|
||||
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'
|
||||
|
||||
export const JDK_ARCH = determineJDKArchitecture()
|
||||
export const JDK_PLATFORM = determineJDKPlatform()
|
||||
export const JDK_HOME_SUFFIX = IS_MACOS ? '/Contents/Home' : ''
|
||||
|
||||
export const GRAALVM_ARCH = determineGraalVMArchitecture()
|
||||
export const GRAALVM_FILE_EXTENSION = IS_WINDOWS ? '.zip' : '.tar.gz'
|
||||
export const GRAALVM_GH_USER = 'graalvm'
|
||||
export const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform
|
||||
export const GRAALVM_RELEASES_REPO = 'graalvm-ce-builds'
|
||||
|
||||
export const MANDREL_NAMESPACE = 'mandrel-'
|
||||
|
||||
export const GDS_BASE = 'https://gds.oracle.com/api/20220101'
|
||||
export const GDS_GRAALVM_PRODUCT_ID = 'D53FAE8052773FFAE0530F15000AA6C6'
|
||||
|
||||
export const ENV_GITHUB_EVENT_NAME = 'GITHUB_EVENT_NAME'
|
||||
export const EVENT_NAME_PULL_REQUEST = 'pull_request'
|
||||
|
||||
export const ERROR_REQUEST =
|
||||
'Please file an issue at: https://github.com/graalvm/setup-graalvm/issues.'
|
||||
|
||||
export const ERROR_HINT =
|
||||
'If you think this is a mistake, please file an issue at: https://github.com/graalvm/setup-graalvm/issues.'
|
||||
|
||||
export type LatestReleaseResponse =
|
||||
otypes.Endpoints['GET /repos/{owner}/{repo}/releases/latest']['response']
|
||||
|
||||
export type MatchingRefsResponse =
|
||||
otypes.Endpoints['GET /repos/{owner}/{repo}/git/matching-refs/{ref}']['response']
|
||||
|
||||
export type ReleasesResponse =
|
||||
otypes.Endpoints['GET /repos/{owner}/{repo}/releases']['response']
|
||||
|
||||
export type ContentsResponse =
|
||||
otypes.Endpoints['GET /repos/{owner}/{repo}/contents/{path}']['response']
|
||||
|
||||
export interface OracleGraalVMEAFile {
|
||||
filename: string
|
||||
arch: 'aarch64' | 'x64'
|
||||
platform: 'darwin' | 'linux' | 'windows'
|
||||
}
|
||||
|
||||
export interface OracleGraalVMEAVersion {
|
||||
version: string
|
||||
latest?: boolean
|
||||
download_base_url: string
|
||||
files: OracleGraalVMEAFile[]
|
||||
}
|
||||
|
||||
function determineJDKArchitecture(): string {
|
||||
switch (process.arch) {
|
||||
case 'x64': {
|
||||
return 'x64'
|
||||
}
|
||||
case 'arm64': {
|
||||
return 'aarch64'
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unsupported architecture: ${process.arch}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function determineJDKPlatform(): string {
|
||||
switch (process.platform) {
|
||||
case 'linux': {
|
||||
return 'linux'
|
||||
}
|
||||
case 'darwin': {
|
||||
return 'macos'
|
||||
}
|
||||
case 'win32': {
|
||||
return 'windows'
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unsupported platform: ${process.platform}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function determineGraalVMArchitecture(): string {
|
||||
switch (process.arch) {
|
||||
case 'x64': {
|
||||
return 'amd64'
|
||||
}
|
||||
case 'arm64': {
|
||||
return 'aarch64'
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unsupported architecture: ${process.arch}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/dependencies.ts
Normal file
33
src/dependencies.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import * as core from '@actions/core'
|
||||
import {GRAALVM_PLATFORM} from './constants'
|
||||
import {exec} from './utils'
|
||||
|
||||
const APT_GET_INSTALL_BASE = 'sudo apt-get -y --no-upgrade install'
|
||||
const COMPONENT_TO_DEPS = new Map<string, Map<string, string>>([
|
||||
[
|
||||
'linux',
|
||||
new Map<string, string>([
|
||||
['nodejs', `${APT_GET_INSTALL_BASE} g++ make`],
|
||||
['ruby', `${APT_GET_INSTALL_BASE} make gcc libssl-dev libz-dev`],
|
||||
[
|
||||
'R',
|
||||
`${APT_GET_INSTALL_BASE} libgomp1 build-essential gfortran libxml2 libc++-dev`
|
||||
]
|
||||
])
|
||||
],
|
||||
['darwin', new Map<string, string>([['ruby', 'brew install openssl']])]
|
||||
])
|
||||
|
||||
export async function setUpDependencies(components: string[]): Promise<void> {
|
||||
const platformDeps = COMPONENT_TO_DEPS.get(GRAALVM_PLATFORM)
|
||||
if (platformDeps) {
|
||||
for (const component of components) {
|
||||
const depCommand = platformDeps.get(component)
|
||||
if (depCommand) {
|
||||
core.startGroup(`Installing dependencies for ${component}...`)
|
||||
await exec(depCommand)
|
||||
core.endGroup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
205
src/features/cache.ts
Normal file
205
src/features/cache.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 GitHub, Inc. and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Forked from https://github.com/actions/setup-java/blob/5b36705a13905facb447b6812d613a06a07e371d/src/cache.ts
|
||||
*
|
||||
* @fileoverview this file provides methods handling dependency cache
|
||||
*/
|
||||
|
||||
import {join} from 'path'
|
||||
import os from 'os'
|
||||
import * as cache from '@actions/cache'
|
||||
import * as core from '@actions/core'
|
||||
import * as glob from '@actions/glob'
|
||||
|
||||
const STATE_CACHE_PRIMARY_KEY = 'cache-primary-key'
|
||||
const CACHE_MATCHED_KEY = 'cache-matched-key'
|
||||
const CACHE_KEY_PREFIX = 'setup-graalvm'
|
||||
|
||||
interface PackageManager {
|
||||
id: 'maven' | 'gradle' | 'sbt'
|
||||
/**
|
||||
* Paths of the file that specify the files to cache.
|
||||
*/
|
||||
path: string[]
|
||||
pattern: string[]
|
||||
}
|
||||
const supportedPackageManager: PackageManager[] = [
|
||||
{
|
||||
id: 'maven',
|
||||
path: [join(os.homedir(), '.m2', 'repository')],
|
||||
// https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven
|
||||
pattern: ['**/pom.xml']
|
||||
},
|
||||
{
|
||||
id: 'gradle',
|
||||
path: [
|
||||
join(os.homedir(), '.gradle', 'caches'),
|
||||
join(os.homedir(), '.gradle', 'wrapper')
|
||||
],
|
||||
// https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle
|
||||
pattern: [
|
||||
'**/*.gradle*',
|
||||
'**/gradle-wrapper.properties',
|
||||
'buildSrc/**/Versions.kt',
|
||||
'buildSrc/**/Dependencies.kt'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'sbt',
|
||||
path: [
|
||||
join(os.homedir(), '.ivy2', 'cache'),
|
||||
join(os.homedir(), '.sbt'),
|
||||
getCoursierCachePath(),
|
||||
// Some files should not be cached to avoid resolution problems.
|
||||
// In particular the resolution of snapshots (ideological gap between maven/ivy).
|
||||
`!${join(os.homedir(), '.sbt', '*.lock')}`,
|
||||
`!${join(os.homedir(), '**', 'ivydata-*.properties')}`
|
||||
],
|
||||
pattern: [
|
||||
'**/*.sbt',
|
||||
'**/project/build.properties',
|
||||
'**/project/**.{scala,sbt}'
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
function getCoursierCachePath(): string {
|
||||
if (os.type() === 'Linux') return join(os.homedir(), '.cache', 'coursier')
|
||||
if (os.type() === 'Darwin')
|
||||
return join(os.homedir(), 'Library', 'Caches', 'Coursier')
|
||||
return join(os.homedir(), 'AppData', 'Local', 'Coursier', 'Cache')
|
||||
}
|
||||
|
||||
function findPackageManager(id: string): PackageManager {
|
||||
const packageManager = supportedPackageManager.find(pm => pm.id === id)
|
||||
if (packageManager === undefined) {
|
||||
throw new Error(`unknown package manager specified: ${id}`)
|
||||
}
|
||||
return packageManager
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that generates a cache key to use.
|
||||
* Format of the generated key will be "${{ platform }}-${{ id }}-${{ fileHash }}"".
|
||||
* If there is no file matched to {@link PackageManager.path}, the generated key ends with a dash (-).
|
||||
* @see {@link https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#matching-a-cache-key|spec of cache key}
|
||||
*/
|
||||
async function computeCacheKey(
|
||||
packageManager: PackageManager
|
||||
): Promise<string> {
|
||||
const hash = await glob.hashFiles(packageManager.pattern.join('\n'))
|
||||
return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${packageManager.id}-${hash}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the dependency cache
|
||||
* @param id ID of the package manager, should be "maven" or "gradle"
|
||||
*/
|
||||
export async function restore(id: string): Promise<void> {
|
||||
const packageManager = findPackageManager(id)
|
||||
const primaryKey = await computeCacheKey(packageManager)
|
||||
|
||||
core.debug(`primary key is ${primaryKey}`)
|
||||
core.saveState(STATE_CACHE_PRIMARY_KEY, primaryKey)
|
||||
if (primaryKey.endsWith('-')) {
|
||||
throw new Error(
|
||||
`No file in ${process.cwd()} matched to [${
|
||||
packageManager.pattern
|
||||
}], make sure you have checked out the target repository`
|
||||
)
|
||||
}
|
||||
|
||||
// No "restoreKeys" is set, to start with a clear cache after dependency update (see https://github.com/actions/setup-java/issues/269)
|
||||
const matchedKey = await cache.restoreCache(packageManager.path, primaryKey)
|
||||
if (matchedKey) {
|
||||
core.saveState(CACHE_MATCHED_KEY, matchedKey)
|
||||
core.setOutput('cache-hit', matchedKey === primaryKey)
|
||||
core.info(`Cache restored from key: ${matchedKey}`)
|
||||
} else {
|
||||
core.setOutput('cache-hit', false)
|
||||
core.info(`${packageManager.id} cache is not found`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the dependency cache
|
||||
* @param id ID of the package manager, should be "maven" or "gradle"
|
||||
*/
|
||||
export async function save(id: string): Promise<void> {
|
||||
const packageManager = findPackageManager(id)
|
||||
const matchedKey = core.getState(CACHE_MATCHED_KEY)
|
||||
|
||||
// Inputs are re-evaluted before the post action, so we want the original key used for restore
|
||||
const primaryKey = core.getState(STATE_CACHE_PRIMARY_KEY)
|
||||
|
||||
if (!primaryKey) {
|
||||
core.warning('Error retrieving key from state.')
|
||||
return
|
||||
} else if (matchedKey === primaryKey) {
|
||||
// no change in target directories
|
||||
core.info(
|
||||
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
|
||||
)
|
||||
return
|
||||
}
|
||||
try {
|
||||
await cache.saveCache(packageManager.path, primaryKey)
|
||||
core.info(`Cache saved with the key: ${primaryKey}`)
|
||||
} catch (error) {
|
||||
if (!(error instanceof Error)) {
|
||||
core.info(`Not an Error: ${error}`)
|
||||
throw error
|
||||
}
|
||||
if (error.name === cache.ReserveCacheError.name) {
|
||||
core.info(error.message)
|
||||
} else {
|
||||
if (isProbablyGradleDaemonProblem(packageManager, error)) {
|
||||
core.warning(
|
||||
'Failed to save Gradle cache on Windows. If tar.exe reported "Permission denied", try to run Gradle with `--no-daemon` option. Refer to https://github.com/actions/cache/issues/454 for details.'
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param packageManager the specified package manager by user
|
||||
* @param error the error thrown by the saveCache
|
||||
* @returns true if the given error seems related to the {@link https://github.com/actions/cache/issues/454|running Gradle Daemon issue}.
|
||||
* @see {@link https://github.com/actions/cache/issues/454#issuecomment-840493935|why --no-daemon is necessary}
|
||||
*/
|
||||
function isProbablyGradleDaemonProblem(
|
||||
packageManager: PackageManager,
|
||||
error: Error
|
||||
): boolean {
|
||||
if (
|
||||
packageManager.id !== 'gradle' ||
|
||||
process.env['RUNNER_OS'] !== 'Windows'
|
||||
) {
|
||||
return false
|
||||
}
|
||||
const message = error.message || ''
|
||||
return message.startsWith('Tar failed with error: ')
|
||||
}
|
||||
31
src/features/check-for-updates.ts
Normal file
31
src/features/check-for-updates.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import * as core from '@actions/core'
|
||||
|
||||
export function checkForUpdates(
|
||||
graalVMVersion: string,
|
||||
javaVersion: string
|
||||
): void {
|
||||
if (javaVersion === '20') {
|
||||
core.notice(
|
||||
'A new GraalVM release is available! Please consider upgrading to GraalVM for JDK 21: https://medium.com/graalvm/graalvm-for-jdk-21-is-here-ee01177dd12d'
|
||||
)
|
||||
return
|
||||
}
|
||||
if (
|
||||
graalVMVersion.length > 0 &&
|
||||
(javaVersion === '17' || javaVersion === '19')
|
||||
) {
|
||||
const recommendedJDK = javaVersion === '17' ? '17' : '21'
|
||||
core.notice(
|
||||
`A new GraalVM release is available! Please consider upgrading to GraalVM for JDK ${recommendedJDK}. Instructions: https://github.com/graalvm/setup-graalvm#migrating-from-graalvm-223-or-earlier-to-the-new-graalvm-for-jdk-17-and-later`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (graalVMVersion.startsWith('22.3.') && javaVersion === '11') {
|
||||
core.notice(
|
||||
'Please consider upgrading your project to Java 17+. GraalVM 22.3.X releases are the last to support JDK11: https://github.com/oracle/graal/issues/5063'
|
||||
)
|
||||
return
|
||||
}
|
||||
// TODO: add support for JDK-specific update checks (e.g., 17.0.X)
|
||||
}
|
||||
74
src/features/musl.ts
Normal file
74
src/features/musl.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import * as c from '../constants'
|
||||
import * as core from '@actions/core'
|
||||
import * as tc from '@actions/tool-cache'
|
||||
import {exec} from '../utils'
|
||||
import {join} from 'path'
|
||||
import {homedir} from 'os'
|
||||
import {promises as fs} from 'fs'
|
||||
|
||||
const MUSL_NAME = 'musl-gcc'
|
||||
const MUSL_VERSION = '1.2.4'
|
||||
const ZLIB_VERSION = '1.2.13'
|
||||
|
||||
// Build instructions: https://github.com/oracle/graal/blob/6dab549194b85252f88bda4ee825762d8b02c687/docs/reference-manual/native-image/guides/build-static-and-mostly-static-executable.md?plain=1#L38-L67
|
||||
|
||||
export async function setUpNativeImageMusl(): Promise<void> {
|
||||
if (!c.IS_LINUX) {
|
||||
core.warning('musl is only supported on Linux')
|
||||
return
|
||||
}
|
||||
let toolPath = tc.find(MUSL_NAME, MUSL_VERSION)
|
||||
if (toolPath) {
|
||||
core.info(`Found musl ${MUSL_VERSION} in tool-cache @ ${toolPath}`)
|
||||
} else {
|
||||
core.startGroup(`Building musl with zlib for GraalVM Native Image...`)
|
||||
// Build musl
|
||||
const muslHome = join(homedir(), 'musl-toolchain')
|
||||
const muslDownloadPath = await tc.downloadTool(
|
||||
`https://musl.libc.org/releases/musl-${MUSL_VERSION}.tar.gz`
|
||||
)
|
||||
const muslExtractPath = await tc.extractTar(muslDownloadPath)
|
||||
const muslPath = join(muslExtractPath, `musl-${MUSL_VERSION}`)
|
||||
const muslBuildOptions = {cwd: muslPath}
|
||||
await exec(
|
||||
'./configure',
|
||||
[`--prefix=${muslHome}`, '--static'],
|
||||
muslBuildOptions
|
||||
)
|
||||
await exec('make', [], muslBuildOptions)
|
||||
await exec('make', ['install'], muslBuildOptions)
|
||||
const muslGCC = join(muslHome, 'bin', MUSL_NAME)
|
||||
await fs.symlink(
|
||||
muslGCC,
|
||||
join(muslHome, 'bin', 'x86_64-linux-musl-gcc'),
|
||||
'file'
|
||||
)
|
||||
// Build zlib
|
||||
const zlibDownloadPath = await tc.downloadTool(
|
||||
`https://zlib.net/fossils/zlib-${ZLIB_VERSION}.tar.gz`
|
||||
)
|
||||
const zlibExtractPath = await tc.extractTar(zlibDownloadPath)
|
||||
const zlibPath = join(zlibExtractPath, `zlib-${ZLIB_VERSION}`)
|
||||
const zlibBuildOptions = {
|
||||
cwd: zlibPath,
|
||||
env: {
|
||||
...process.env,
|
||||
CC: muslGCC
|
||||
}
|
||||
}
|
||||
await exec(
|
||||
'./configure',
|
||||
[`--prefix=${muslHome}`, '--static'],
|
||||
zlibBuildOptions
|
||||
)
|
||||
await exec('make', [], zlibBuildOptions)
|
||||
await exec('make', ['install'], {cwd: zlibPath})
|
||||
// Store in cache
|
||||
core.info(
|
||||
`Adding musl ${MUSL_VERSION} with zlib ${ZLIB_VERSION} to tool-cache ...`
|
||||
)
|
||||
toolPath = await tc.cacheDir(muslHome, MUSL_NAME, MUSL_VERSION)
|
||||
core.endGroup()
|
||||
}
|
||||
core.addPath(join(toolPath, 'bin'))
|
||||
}
|
||||
490
src/features/reports.ts
Normal file
490
src/features/reports.ts
Normal file
@@ -0,0 +1,490 @@
|
||||
import * as c from '../constants'
|
||||
import * as core from '@actions/core'
|
||||
import * as fs from 'fs'
|
||||
import * as github from '@actions/github'
|
||||
import * as semver from 'semver'
|
||||
import {join} from 'path'
|
||||
import {tmpdir} from 'os'
|
||||
import {
|
||||
createPRComment,
|
||||
findExistingPRCommentId,
|
||||
isPREvent,
|
||||
toSemVer,
|
||||
updatePRComment
|
||||
} from '../utils'
|
||||
|
||||
const BUILD_OUTPUT_JSON_PATH = join(tmpdir(), 'native-image-build-output.json')
|
||||
const BYTES_TO_KiB = 1024
|
||||
const BYTES_TO_MiB = 1024 * 1024
|
||||
const BYTES_TO_GiB = 1024 * 1024 * 1024
|
||||
const DOCS_BASE =
|
||||
'https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md'
|
||||
const INPUT_NI_JOB_REPORTS = 'native-image-job-reports'
|
||||
const INPUT_NI_PR_REPORTS = 'native-image-pr-reports'
|
||||
const INPUT_NI_PR_REPORTS_UPDATE = 'native-image-pr-reports-update-existing'
|
||||
const NATIVE_IMAGE_CONFIG_FILE = join(
|
||||
tmpdir(),
|
||||
'native-image-options.properties'
|
||||
)
|
||||
const NATIVE_IMAGE_OPTIONS_ENV = 'NATIVE_IMAGE_OPTIONS'
|
||||
const NATIVE_IMAGE_CONFIG_FILE_ENV = 'NATIVE_IMAGE_CONFIG_FILE'
|
||||
const PR_COMMENT_TITLE = '## GraalVM Native Image Build Report'
|
||||
|
||||
interface AnalysisResult {
|
||||
total: number
|
||||
reachable: number
|
||||
reflection: number
|
||||
jni: number
|
||||
}
|
||||
|
||||
interface BuildOutput {
|
||||
general_info: {
|
||||
name: string
|
||||
graalvm_version: string
|
||||
java_version: string | null
|
||||
vendor_version?: string
|
||||
c_compiler: string | null
|
||||
garbage_collector: string
|
||||
graal_compiler?: {
|
||||
optimization_level: string
|
||||
march: string
|
||||
pgo?: string[]
|
||||
}
|
||||
}
|
||||
analysis_results: {
|
||||
classes: AnalysisResult
|
||||
types?: AnalysisResult
|
||||
fields: AnalysisResult
|
||||
methods: AnalysisResult
|
||||
}
|
||||
image_details: {
|
||||
total_bytes: number
|
||||
code_area: {
|
||||
bytes: number
|
||||
compilation_units: number
|
||||
}
|
||||
image_heap: {
|
||||
bytes: number
|
||||
objects?: {
|
||||
count: number
|
||||
}
|
||||
resources: {
|
||||
count: number
|
||||
bytes: number
|
||||
}
|
||||
}
|
||||
debug_info?: {
|
||||
bytes: number
|
||||
}
|
||||
runtime_compiled_methods?: {
|
||||
count: number
|
||||
graph_encoding_bytes: number
|
||||
}
|
||||
}
|
||||
resource_usage: {
|
||||
cpu: {
|
||||
load: number
|
||||
total_cores: number
|
||||
}
|
||||
garbage_collection: {
|
||||
count: number
|
||||
total_secs: number
|
||||
}
|
||||
memory: {
|
||||
system_total: number
|
||||
peak_rss_bytes: number
|
||||
}
|
||||
total_secs?: number
|
||||
}
|
||||
}
|
||||
|
||||
export async function setUpNativeImageBuildReports(
|
||||
isGraalVMforJDK17OrLater: boolean,
|
||||
javaVersionOrDev: string,
|
||||
graalVMVersion: string
|
||||
): Promise<void> {
|
||||
const isRequired = areJobReportsEnabled() || arePRReportsEnabled()
|
||||
if (!isRequired) {
|
||||
return
|
||||
}
|
||||
const isSupported =
|
||||
isGraalVMforJDK17OrLater ||
|
||||
graalVMVersion === c.VERSION_LATEST ||
|
||||
graalVMVersion === c.VERSION_DEV ||
|
||||
(!graalVMVersion.startsWith(c.MANDREL_NAMESPACE) &&
|
||||
semver.gte(toSemVer(graalVMVersion), '22.2.0'))
|
||||
if (!isSupported) {
|
||||
core.warning(
|
||||
`Build reports for PRs and job summaries are only available in GraalVM 22.2.0 or later. This build job uses GraalVM ${graalVMVersion}.`
|
||||
)
|
||||
return
|
||||
}
|
||||
setNativeImageOption(
|
||||
javaVersionOrDev,
|
||||
`-H:BuildOutputJSONFile=${BUILD_OUTPUT_JSON_PATH.replace(/\\/g, '\\\\')}`
|
||||
) // Escape backslashes for Windows
|
||||
}
|
||||
|
||||
export async function generateReports(): Promise<void> {
|
||||
if (areJobReportsEnabled() || arePRReportsEnabled()) {
|
||||
if (!fs.existsSync(BUILD_OUTPUT_JSON_PATH)) {
|
||||
core.warning(
|
||||
'Unable to find build output data to create a report. Are you sure this build job has used GraalVM Native Image?'
|
||||
)
|
||||
return
|
||||
}
|
||||
const buildOutput: BuildOutput = JSON.parse(
|
||||
fs.readFileSync(BUILD_OUTPUT_JSON_PATH, 'utf8')
|
||||
)
|
||||
const report = createReport(buildOutput)
|
||||
if (areJobReportsEnabled()) {
|
||||
core.summary.addRaw(report)
|
||||
core.summary.write()
|
||||
}
|
||||
if (arePRReportsEnabled()) {
|
||||
if (arePRReportsUpdateEnabled()) {
|
||||
const commentId = await findExistingPRCommentId(PR_COMMENT_TITLE)
|
||||
if (commentId) {
|
||||
return updatePRComment(report, commentId)
|
||||
}
|
||||
}
|
||||
return createPRComment(report)
|
||||
} else if (arePRReportsUpdateEnabled()) {
|
||||
throw new Error(
|
||||
`'${INPUT_NI_PR_REPORTS_UPDATE}' option requires '${INPUT_NI_PR_REPORTS}' to be set 'true'`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function areJobReportsEnabled(): boolean {
|
||||
return core.getInput(INPUT_NI_JOB_REPORTS) === 'true'
|
||||
}
|
||||
|
||||
function arePRReportsEnabled(): boolean {
|
||||
return isPREvent() && core.getInput(INPUT_NI_PR_REPORTS) === 'true'
|
||||
}
|
||||
|
||||
function arePRReportsUpdateEnabled(): boolean {
|
||||
return isPREvent() && core.getInput(INPUT_NI_PR_REPORTS_UPDATE) === 'true'
|
||||
}
|
||||
|
||||
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[NATIVE_IMAGE_OPTIONS_ENV]
|
||||
if (existingOptions) {
|
||||
newOptionValue = `${existingOptions} ${newOptionValue}`
|
||||
}
|
||||
core.exportVariable(NATIVE_IMAGE_OPTIONS_ENV, newOptionValue)
|
||||
} else {
|
||||
const optionsFile = getNativeImageOptionsFile()
|
||||
if (fs.existsSync(optionsFile)) {
|
||||
fs.appendFileSync(optionsFile, ` ${optionValue}`)
|
||||
} else {
|
||||
fs.writeFileSync(optionsFile, `NativeImageArgs = ${optionValue}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
function createReport(data: BuildOutput): string {
|
||||
const context = github.context
|
||||
const info = data.general_info
|
||||
const analysis = data.analysis_results
|
||||
const analysisTypes = analysis.types ? analysis.types : analysis.classes
|
||||
const details = data.image_details
|
||||
let objectCount = ''
|
||||
if (details.image_heap.objects) {
|
||||
objectCount = `${details.image_heap.objects.count.toLocaleString()} objects, `
|
||||
}
|
||||
const debugInfoBytes = details.debug_info ? details.debug_info.bytes : 0
|
||||
const otherBytes =
|
||||
details.total_bytes -
|
||||
details.code_area.bytes -
|
||||
details.image_heap.bytes -
|
||||
debugInfoBytes
|
||||
let debugInfoLine = ''
|
||||
if (details.debug_info) {
|
||||
debugInfoLine = `
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-debug-info" target="_blank">Debug info</a></td>
|
||||
<td align="right">${bytesToHuman(debugInfoBytes)}</td>
|
||||
<td align="right">${toPercent(debugInfoBytes, details.total_bytes)}</td>
|
||||
<td align="left"></td>
|
||||
</tr>`
|
||||
}
|
||||
let versionLine
|
||||
if (info.vendor_version) {
|
||||
versionLine = `
|
||||
<tr>
|
||||
<td><a href="${DOCS_BASE}#glossary-java-info" target="_blank">Java version</a></td>
|
||||
<td>${info.java_version}</td>
|
||||
<td><a href="${DOCS_BASE}#glossary-java-info" target="_blank">Vendor version</a></td>
|
||||
<td>${info.vendor_version}</td>
|
||||
</tr>`
|
||||
} else {
|
||||
versionLine = `
|
||||
<tr>
|
||||
<td><a href="${DOCS_BASE}#glossary-version-info" target="_blank">GraalVM version</a></td>
|
||||
<td>${info.graalvm_version}</td>
|
||||
<td><a href="${DOCS_BASE}#glossary-java-version-info" target="_blank">Java version</a></td>
|
||||
<td>${info.java_version}</td>
|
||||
</tr>`
|
||||
}
|
||||
let graalLine
|
||||
if (info.graal_compiler) {
|
||||
let pgoSuffix = ''
|
||||
const isOracleGraalVM =
|
||||
info.vendor_version && info.vendor_version.includes('Oracle GraalVM')
|
||||
if (isOracleGraalVM) {
|
||||
const pgo = info.graal_compiler.pgo
|
||||
const pgoText = pgo ? pgo.join('+') : 'off'
|
||||
pgoSuffix = `, <a href="${DOCS_BASE}#recommendation-pgo" target="_blank">PGO</a>: ${pgoText}`
|
||||
}
|
||||
graalLine = `
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-graal-compiler" target="_blank">Graal compiler</a></td>
|
||||
<td colspan="3">
|
||||
optimization level: ${info.graal_compiler.optimization_level},
|
||||
target machine: ${info.graal_compiler.march}${pgoSuffix}
|
||||
</td>
|
||||
</tr>`
|
||||
}
|
||||
|
||||
const resources = data.resource_usage
|
||||
|
||||
let totalTime = ''
|
||||
let gcTotalTimeRatio = ''
|
||||
if (resources.total_secs) {
|
||||
totalTime = ` in ${secondsToHuman(resources.total_secs)}`
|
||||
gcTotalTimeRatio = ` (${toPercent(
|
||||
resources.garbage_collection.total_secs,
|
||||
resources.total_secs
|
||||
)} of total time)`
|
||||
}
|
||||
|
||||
return `${PR_COMMENT_TITLE}
|
||||
|
||||
\`${info.name}\` generated${totalTime} as part of the '${
|
||||
context.job
|
||||
}' job in run <a href="${context.serverUrl}/${context.repo.owner}/${
|
||||
context.repo.repo
|
||||
}/actions/runs/${context.runId}" target="_blank">#${context.runNumber}</a>.
|
||||
|
||||
#### Environment
|
||||
|
||||
<table>${versionLine}${graalLine}
|
||||
<tr>
|
||||
<td><a href="${DOCS_BASE}#glossary-ccompiler" target="_blank">C compiler</a></td>
|
||||
<td colspan="3">${info.c_compiler}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="${DOCS_BASE}#glossary-gc" target="_blank">Garbage collector</a></td>
|
||||
<td colspan="3">${info.garbage_collector}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
#### Analysis Results
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left">Category</th>
|
||||
<th align="right">Types</th>
|
||||
<th align="right">in %</th>
|
||||
<th align="right">Fields</th>
|
||||
<th align="right">in %</th>
|
||||
<th align="right">Methods</th>
|
||||
<th align="right">in %</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-reachability" target="_blank">Reachable</a></td>
|
||||
<td align="right">${analysisTypes.reachable.toLocaleString()}</td>
|
||||
<td align="right">${toPercent(
|
||||
analysisTypes.reachable,
|
||||
analysisTypes.total
|
||||
)}</td>
|
||||
<td align="right">${analysis.fields.reachable.toLocaleString()}</td>
|
||||
<td align="right">${toPercent(
|
||||
analysis.fields.reachable,
|
||||
analysis.fields.total
|
||||
)}</td>
|
||||
<td align="right">${analysis.methods.reachable.toLocaleString()}</td>
|
||||
<td align="right">${toPercent(
|
||||
analysis.methods.reachable,
|
||||
analysis.methods.total
|
||||
)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-reflection-registrations" target="_blank">Reflection</a></td>
|
||||
<td align="right">${analysisTypes.reflection.toLocaleString()}</td>
|
||||
<td align="right">${toPercent(
|
||||
analysisTypes.reflection,
|
||||
analysisTypes.total
|
||||
)}</td>
|
||||
<td align="right">${analysis.fields.reflection.toLocaleString()}</td>
|
||||
<td align="right">${toPercent(
|
||||
analysis.fields.reflection,
|
||||
analysis.fields.total
|
||||
)}</td>
|
||||
<td align="right">${analysis.methods.reflection.toLocaleString()}</td>
|
||||
<td align="right">${toPercent(
|
||||
analysis.methods.reflection,
|
||||
analysis.methods.total
|
||||
)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-jni-access-registrations" target="_blank">JNI</a></td>
|
||||
<td align="right">${analysisTypes.jni.toLocaleString()}</td>
|
||||
<td align="right">${toPercent(
|
||||
analysisTypes.jni,
|
||||
analysisTypes.total
|
||||
)}</td>
|
||||
<td align="right">${analysis.fields.jni.toLocaleString()}</td>
|
||||
<td align="right">${toPercent(
|
||||
analysis.fields.jni,
|
||||
analysis.fields.total
|
||||
)}</td>
|
||||
<td align="right">${analysis.methods.jni.toLocaleString()}</td>
|
||||
<td align="right">${toPercent(
|
||||
analysis.methods.jni,
|
||||
analysis.methods.total
|
||||
)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-reachability" target="_blank">Loaded</a></td>
|
||||
<td align="right">${analysisTypes.total.toLocaleString()}</td>
|
||||
<td align="right">100.000%</td>
|
||||
<td align="right">${analysis.fields.total.toLocaleString()}</td>
|
||||
<td align="right">100.000%</td>
|
||||
<td align="right">${analysis.methods.total.toLocaleString()}</td>
|
||||
<td align="right">100.000%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Image Details
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left">Category</th>
|
||||
<th align="right">Size</th>
|
||||
<th align="right">in %</th>
|
||||
<th align="left">Details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-code-area" target="_blank">Code area</a></td>
|
||||
<td align="right">${bytesToHuman(details.code_area.bytes)}</td>
|
||||
<td align="right">${toPercent(
|
||||
details.code_area.bytes,
|
||||
details.total_bytes
|
||||
)}</td>
|
||||
<td align="left">${details.code_area.compilation_units.toLocaleString()} compilation units</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-image-heap" target="_blank">Image heap</a></td>
|
||||
<td align="right">${bytesToHuman(details.image_heap.bytes)}</td>
|
||||
<td align="right">${toPercent(
|
||||
details.image_heap.bytes,
|
||||
details.total_bytes
|
||||
)}</td>
|
||||
<td align="left">${objectCount}${bytesToHuman(
|
||||
details.image_heap.resources.bytes
|
||||
)} for ${details.image_heap.resources.count.toLocaleString()} resources</td>
|
||||
</tr>${debugInfoLine}
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-other-data" target="_blank">Other data</a></td>
|
||||
<td align="right">${bytesToHuman(otherBytes)}</td>
|
||||
<td align="right">${toPercent(otherBytes, details.total_bytes)}</td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">Total</td>
|
||||
<td align="right"><strong>${bytesToHuman(
|
||||
details.total_bytes
|
||||
)}</strong></td>
|
||||
<td align="right">100.000%</td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Resource Usage
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-garbage-collections" target="_blank">Garbage collection</a></td>
|
||||
<td align="left">${resources.garbage_collection.total_secs.toFixed(
|
||||
2
|
||||
)}s${gcTotalTimeRatio} in ${resources.garbage_collection.count} GCs</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-peak-rss" target="_blank">Peak RSS</a></td>
|
||||
<td align="left">${bytesToHuman(
|
||||
resources.memory.peak_rss_bytes
|
||||
)} (${toPercent(
|
||||
resources.memory.peak_rss_bytes,
|
||||
resources.memory.system_total
|
||||
)} of ${bytesToHuman(resources.memory.system_total)} system memory)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="${DOCS_BASE}#glossary-cpu-load" target="_blank">CPU load</a></td>
|
||||
<td align="left">${resources.cpu.load.toFixed(3)} (${toPercent(
|
||||
resources.cpu.load,
|
||||
resources.cpu.total_cores
|
||||
)} of ${resources.cpu.total_cores} CPU cores)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<em>Report generated by <a href="https://github.com/marketplace/actions/github-action-for-graalvm" target="_blank">setup-graalvm</a>.</em>`
|
||||
}
|
||||
|
||||
function toPercent(part: number, total: number): string {
|
||||
return `${((part / total) * 100).toFixed(3)}%`
|
||||
}
|
||||
|
||||
function bytesToHuman(bytes: number): string {
|
||||
if (bytes < BYTES_TO_KiB) {
|
||||
return `${bytes.toFixed(2)}B`
|
||||
} else if (bytes < BYTES_TO_MiB) {
|
||||
return `${(bytes / BYTES_TO_KiB).toFixed(2)}KB`
|
||||
} else if (bytes < BYTES_TO_GiB) {
|
||||
return `${(bytes / BYTES_TO_MiB).toFixed(2)}MB`
|
||||
} else {
|
||||
return `${(bytes / BYTES_TO_GiB).toFixed(2)}GB`
|
||||
}
|
||||
}
|
||||
|
||||
function secondsToHuman(seconds: number): string {
|
||||
if (seconds < 60) {
|
||||
return `${seconds.toFixed(1)}s`
|
||||
} else {
|
||||
return `${Math.trunc(seconds / 60)}m ${Math.trunc(seconds % 60)}s`
|
||||
}
|
||||
}
|
||||
280
src/gds.ts
Normal file
280
src/gds.ts
Normal file
@@ -0,0 +1,280 @@
|
||||
import * as c from './constants'
|
||||
import * as core from '@actions/core'
|
||||
import * as fs from 'fs'
|
||||
import * as httpClient from '@actions/http-client'
|
||||
import * as io from '@actions/io'
|
||||
import * as path from 'path'
|
||||
import * as stream from 'stream'
|
||||
import * as util from 'util'
|
||||
import * as semver from 'semver'
|
||||
import {IncomingHttpHeaders, OutgoingHttpHeaders} from 'http'
|
||||
import {RetryHelper} from '@actions/tool-cache/lib/retry-helper'
|
||||
import {calculateSHA256} from './utils'
|
||||
import {ok} from 'assert'
|
||||
import {v4 as uuidv4} from 'uuid'
|
||||
|
||||
interface GDSArtifactsResponse {
|
||||
readonly items: GDSArtifact[]
|
||||
}
|
||||
|
||||
interface GDSArtifact {
|
||||
readonly id: string
|
||||
readonly checksum: string
|
||||
}
|
||||
|
||||
interface GDSErrorResponse {
|
||||
readonly code: string
|
||||
readonly message: string
|
||||
}
|
||||
|
||||
export async function downloadGraalVM(
|
||||
gdsToken: string,
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
const userAgent = `GraalVMGitHubAction/${c.ACTION_VERSION} (arch:${c.GRAALVM_ARCH}; os:${c.GRAALVM_PLATFORM}; java:${javaVersion})`
|
||||
const baseArtifact = await fetchArtifact(
|
||||
userAgent,
|
||||
'isBase:True',
|
||||
javaVersion
|
||||
)
|
||||
return downloadArtifact(gdsToken, userAgent, baseArtifact)
|
||||
}
|
||||
|
||||
export async function downloadGraalVMEELegacy(
|
||||
gdsToken: string,
|
||||
version: string,
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
const userAgent = `GraalVMGitHubAction/${c.ACTION_VERSION} (arch:${c.GRAALVM_ARCH}; os:${c.GRAALVM_PLATFORM}; java:${javaVersion})`
|
||||
const baseArtifact = await fetchArtifactEE(
|
||||
userAgent,
|
||||
'isBase:True',
|
||||
version,
|
||||
javaVersion
|
||||
)
|
||||
return downloadArtifact(gdsToken, userAgent, baseArtifact)
|
||||
}
|
||||
|
||||
export async function fetchArtifact(
|
||||
userAgent: string,
|
||||
metadata: string,
|
||||
javaVersion: string
|
||||
): Promise<GDSArtifact> {
|
||||
const http = new httpClient.HttpClient(userAgent)
|
||||
|
||||
let filter
|
||||
if (javaVersion.includes('.')) {
|
||||
filter = `metadata=version:${javaVersion}`
|
||||
} else {
|
||||
filter = `sortBy=timeCreated&sortOrder=DESC&limit=1` // latest and only one item
|
||||
}
|
||||
|
||||
let majorJavaVersion
|
||||
if (semver.valid(javaVersion)) {
|
||||
majorJavaVersion = semver.major(javaVersion)
|
||||
} else {
|
||||
majorJavaVersion = javaVersion
|
||||
}
|
||||
|
||||
const catalogOS = c.IS_MACOS ? 'macos' : c.GRAALVM_PLATFORM
|
||||
const requestUrl = `${c.GDS_BASE}/artifacts?productId=${c.GDS_GRAALVM_PRODUCT_ID}&displayName=Oracle%20GraalVM&${filter}&metadata=java:jdk${majorJavaVersion}&metadata=os:${catalogOS}&metadata=arch:${c.GRAALVM_ARCH}&metadata=${metadata}&status=PUBLISHED&responseFields=id&responseFields=checksum`
|
||||
core.debug(`Requesting ${requestUrl}`)
|
||||
const response = await http.get(requestUrl, {accept: 'application/json'})
|
||||
if (response.message.statusCode !== 200) {
|
||||
throw new Error(
|
||||
`Unable to find GraalVM for JDK ${javaVersion}. Are you sure java-version: '${javaVersion}' is correct?`
|
||||
)
|
||||
}
|
||||
const artifactResponse = JSON.parse(
|
||||
await response.readBody()
|
||||
) as GDSArtifactsResponse
|
||||
if (artifactResponse.items.length !== 1) {
|
||||
throw new Error(
|
||||
artifactResponse.items.length > 1
|
||||
? `Found more than one GDS artifact. ${c.ERROR_HINT}`
|
||||
: `Unable to find GDS artifact. Are you sure java-version: '${javaVersion}' is correct?`
|
||||
)
|
||||
}
|
||||
return artifactResponse.items[0]
|
||||
}
|
||||
|
||||
export async function fetchArtifactEE(
|
||||
userAgent: string,
|
||||
metadata: string,
|
||||
version: string,
|
||||
javaVersion: string
|
||||
): Promise<GDSArtifact> {
|
||||
const http = new httpClient.HttpClient(userAgent)
|
||||
|
||||
let filter
|
||||
if (version === c.VERSION_LATEST) {
|
||||
filter = `sortBy=displayName&sortOrder=DESC&limit=1` // latest and only one item
|
||||
} else {
|
||||
filter = `metadata=version:${version}`
|
||||
}
|
||||
|
||||
const catalogOS = c.IS_MACOS ? 'macos' : c.GRAALVM_PLATFORM
|
||||
const requestUrl = `${c.GDS_BASE}/artifacts?productId=${c.GDS_GRAALVM_PRODUCT_ID}&${filter}&metadata=java:jdk${javaVersion}&metadata=os:${catalogOS}&metadata=arch:${c.GRAALVM_ARCH}&metadata=${metadata}&status=PUBLISHED&responseFields=id&responseFields=checksum`
|
||||
core.debug(`Requesting ${requestUrl}`)
|
||||
const response = await http.get(requestUrl, {accept: 'application/json'})
|
||||
if (response.message.statusCode !== 200) {
|
||||
throw new Error(
|
||||
`Unable to find JDK${javaVersion}-based GraalVM EE ${version}`
|
||||
)
|
||||
}
|
||||
const artifactResponse = JSON.parse(
|
||||
await response.readBody()
|
||||
) as GDSArtifactsResponse
|
||||
if (artifactResponse.items.length !== 1) {
|
||||
throw new Error(
|
||||
artifactResponse.items.length > 1
|
||||
? `Found more than one GDS artifact. ${c.ERROR_HINT}`
|
||||
: `Unable to find GDS artifact. Are you sure version: '${version}' is correct?`
|
||||
)
|
||||
}
|
||||
return artifactResponse.items[0]
|
||||
}
|
||||
|
||||
async function downloadArtifact(
|
||||
gdsToken: string,
|
||||
userAgent: string,
|
||||
artifact: GDSArtifact
|
||||
): Promise<string> {
|
||||
let downloadPath
|
||||
try {
|
||||
downloadPath = await downloadTool(
|
||||
`${c.GDS_BASE}/artifacts/${artifact.id}/content`,
|
||||
userAgent,
|
||||
{
|
||||
accept: 'application/x-yaml',
|
||||
'x-download-token': gdsToken
|
||||
}
|
||||
)
|
||||
} catch (err) {
|
||||
if (err instanceof HTTPError && err.httpStatusCode) {
|
||||
if (err.httpStatusCode === 401) {
|
||||
throw new Error(
|
||||
`The provided "gds-token" was rejected (reason: "${err.gdsError.message}", opc-request-id: ${err.headers['opc-request-id']})`
|
||||
)
|
||||
}
|
||||
}
|
||||
throw err
|
||||
}
|
||||
const sha256 = calculateSHA256(downloadPath)
|
||||
if (sha256.toLowerCase() !== artifact.checksum.toLowerCase()) {
|
||||
throw new Error(
|
||||
`Checksum does not match (expected: "${artifact.checksum}", got: "${sha256}")`
|
||||
)
|
||||
}
|
||||
return downloadPath
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified fork of tool-cache's downloadTool [1] with the ability to set a custom user agent.
|
||||
* [1] https://github.com/actions/toolkit/blob/2f164000dcd42fb08287824a3bc3030dbed33687/packages/tool-cache/src/tool-cache.ts
|
||||
*/
|
||||
|
||||
class HTTPError extends Error {
|
||||
constructor(
|
||||
readonly httpStatusCode: number | undefined,
|
||||
readonly gdsError: GDSErrorResponse,
|
||||
readonly headers: IncomingHttpHeaders
|
||||
) {
|
||||
super(`Unexpected HTTP response: ${httpStatusCode}`)
|
||||
Object.setPrototypeOf(this, new.target.prototype)
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadTool(
|
||||
url: string,
|
||||
userAgent: string,
|
||||
headers?: OutgoingHttpHeaders
|
||||
): Promise<string> {
|
||||
const dest = path.join(getTempDirectory(), uuidv4())
|
||||
await io.mkdirP(path.dirname(dest))
|
||||
core.debug(`Downloading ${url}`)
|
||||
core.debug(`Destination ${dest}`)
|
||||
|
||||
const maxAttempts = 3
|
||||
const minSeconds = 10
|
||||
const maxSeconds = 20
|
||||
const retryHelper = new RetryHelper(maxAttempts, minSeconds, maxSeconds)
|
||||
return await retryHelper.execute(
|
||||
async () => {
|
||||
return await downloadToolAttempt(url, userAgent, dest || '', headers)
|
||||
},
|
||||
(err: Error) => {
|
||||
if (err instanceof HTTPError && err.httpStatusCode) {
|
||||
// Don't retry anything less than 500, except 408 Request Timeout and 429 Too Many Requests
|
||||
if (
|
||||
err.httpStatusCode < 500 &&
|
||||
err.httpStatusCode !== 408 &&
|
||||
err.httpStatusCode !== 429
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise retry
|
||||
return true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function downloadToolAttempt(
|
||||
url: string,
|
||||
userAgent: string,
|
||||
dest: string,
|
||||
headers?: OutgoingHttpHeaders
|
||||
): Promise<string> {
|
||||
if (fs.existsSync(dest)) {
|
||||
throw new Error(`Destination file path ${dest} already exists`)
|
||||
}
|
||||
|
||||
// Get the response headers
|
||||
const http = new httpClient.HttpClient(userAgent, [], {
|
||||
allowRetries: false
|
||||
})
|
||||
|
||||
const response: httpClient.HttpClientResponse = await http.get(url, headers)
|
||||
if (response.message.statusCode !== 200) {
|
||||
const errorResponse = JSON.parse(
|
||||
await response.readBody()
|
||||
) as GDSErrorResponse
|
||||
const err = new HTTPError(
|
||||
response.message.statusCode,
|
||||
errorResponse,
|
||||
response.message.headers
|
||||
)
|
||||
core.debug(
|
||||
`Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})`
|
||||
)
|
||||
throw err
|
||||
}
|
||||
|
||||
// Download the response body
|
||||
const pipeline = util.promisify(stream.pipeline)
|
||||
let succeeded = false
|
||||
try {
|
||||
await pipeline(response.message, fs.createWriteStream(dest))
|
||||
core.debug('Download complete')
|
||||
succeeded = true
|
||||
return dest
|
||||
} finally {
|
||||
// Error, delete dest before retry
|
||||
if (!succeeded) {
|
||||
core.debug('Download failed')
|
||||
try {
|
||||
await io.rmRF(dest)
|
||||
} catch (err) {
|
||||
core.debug(`Failed to delete '${dest}'. ${err}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTempDirectory(): string {
|
||||
const tempDirectory = process.env['RUNNER_TEMP'] || ''
|
||||
ok(tempDirectory, 'Expected RUNNER_TEMP to be defined')
|
||||
return tempDirectory
|
||||
}
|
||||
359
src/graalvm.ts
Normal file
359
src/graalvm.ts
Normal file
@@ -0,0 +1,359 @@
|
||||
import * as c from './constants'
|
||||
import * as core from '@actions/core'
|
||||
import * as semver from 'semver'
|
||||
import {
|
||||
downloadAndExtractJDK,
|
||||
downloadExtractAndCacheJDK,
|
||||
getContents,
|
||||
getLatestRelease,
|
||||
getMatchingTags,
|
||||
getTaggedRelease
|
||||
} from './utils'
|
||||
import {downloadGraalVM, downloadGraalVMEELegacy} from './gds'
|
||||
import {downloadTool} from '@actions/tool-cache'
|
||||
import {basename} from 'path'
|
||||
|
||||
const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm'
|
||||
const GRAALVM_CE_DL_BASE = `https://github.com/graalvm/${c.GRAALVM_RELEASES_REPO}/releases/download`
|
||||
const ORACLE_GRAALVM_REPO_EA_BUILDS = 'oracle-graalvm-ea-builds'
|
||||
const ORACLE_GRAALVM_REPO_EA_BUILDS_LATEST_SYMBOL = 'latest-ea'
|
||||
const GRAALVM_REPO_DEV_BUILDS = 'graalvm-ce-dev-builds'
|
||||
const GRAALVM_JDK_TAG_PREFIX = 'jdk-'
|
||||
const GRAALVM_TAG_PREFIX = 'vm-'
|
||||
|
||||
// Support for GraalVM for JDK 17 and later
|
||||
|
||||
export async function setUpGraalVMJDK(
|
||||
javaVersionOrDev: string,
|
||||
gdsToken: string
|
||||
): Promise<string> {
|
||||
if (javaVersionOrDev === c.VERSION_DEV) {
|
||||
return setUpGraalVMJDKDevBuild()
|
||||
}
|
||||
const isTokenProvided = gdsToken.length > 0
|
||||
let javaVersion = javaVersionOrDev
|
||||
const toolName = determineToolName(javaVersion, false)
|
||||
if (javaVersionOrDev === '17' && !isTokenProvided) {
|
||||
core.warning(
|
||||
'This build uses the last update of Oracle GraalVM for JDK 17 under the GFTC. More details: https://github.com/marketplace/actions/github-action-for-graalvm#notes-on-oracle-graalvm-for-jdk-17'
|
||||
)
|
||||
return setUpGraalVMJDK('17.0.12', gdsToken)
|
||||
}
|
||||
if (isTokenProvided) {
|
||||
// Download from GDS
|
||||
const downloader = async () => downloadGraalVM(gdsToken, javaVersion)
|
||||
return downloadExtractAndCacheJDK(downloader, toolName, javaVersion)
|
||||
}
|
||||
// Download from oracle.com
|
||||
let downloadName = toolName
|
||||
let downloadUrl: string
|
||||
if (javaVersion.endsWith('-ea')) {
|
||||
downloadUrl = await findLatestEABuildDownloadUrl(javaVersion)
|
||||
const filename = basename(downloadUrl)
|
||||
const resolvedVersion = semver.valid(semver.coerce(filename))
|
||||
if (!resolvedVersion) {
|
||||
throw new Error(
|
||||
`Unable to determine resolved version based on '${filename}'. ${c.ERROR_REQUEST}`
|
||||
)
|
||||
}
|
||||
javaVersion = resolvedVersion
|
||||
} else if (javaVersion.includes('.')) {
|
||||
if (semver.valid(javaVersion)) {
|
||||
const majorJavaVersion = semver.major(javaVersion)
|
||||
const minorJavaVersion = semver.minor(javaVersion)
|
||||
const patchJavaVersion = semver.patch(javaVersion)
|
||||
const isGARelease = minorJavaVersion === 0 && patchJavaVersion === 0
|
||||
if (isGARelease) {
|
||||
// For GA versions of JDKs, /archive/ does not use minor and patch version (see https://www.oracle.com/java/technologies/jdk-script-friendly-urls/)
|
||||
downloadName = determineToolName(majorJavaVersion.toString(), false)
|
||||
}
|
||||
downloadUrl = `${GRAALVM_DL_BASE}/${majorJavaVersion}/archive/${downloadName}${c.GRAALVM_FILE_EXTENSION}`
|
||||
} else {
|
||||
throw new Error(
|
||||
`java-version set to '${javaVersion}'. Please make sure the java-version is set correctly. ${c.ERROR_HINT}`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
downloadUrl = `${GRAALVM_DL_BASE}/${javaVersion}/latest/${downloadName}${c.GRAALVM_FILE_EXTENSION}`
|
||||
}
|
||||
const downloader = async () => downloadGraalVMJDK(downloadUrl, javaVersion)
|
||||
return downloadExtractAndCacheJDK(downloader, toolName, javaVersion)
|
||||
}
|
||||
|
||||
export async function findLatestEABuildDownloadUrl(
|
||||
javaEaVersion: string
|
||||
): Promise<string> {
|
||||
const filePath = `versions/${javaEaVersion}.json`
|
||||
let response
|
||||
try {
|
||||
response = await getContents(ORACLE_GRAALVM_REPO_EA_BUILDS, filePath)
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Unable to resolve download URL for '${javaEaVersion}'. Please make sure the java-version is set correctly. ${c.ERROR_HINT}`
|
||||
)
|
||||
}
|
||||
if (
|
||||
Array.isArray(response) ||
|
||||
response.type !== 'file' ||
|
||||
!response.content
|
||||
) {
|
||||
throw new Error(
|
||||
`Unexpected response when resolving download URL for '${javaEaVersion}'. ${c.ERROR_REQUEST}`
|
||||
)
|
||||
}
|
||||
const versionData = JSON.parse(
|
||||
Buffer.from(response.content, 'base64').toString('utf-8')
|
||||
)
|
||||
let latestVersion
|
||||
if (javaEaVersion === ORACLE_GRAALVM_REPO_EA_BUILDS_LATEST_SYMBOL) {
|
||||
latestVersion = versionData as c.OracleGraalVMEAVersion
|
||||
} else {
|
||||
latestVersion = (versionData as c.OracleGraalVMEAVersion[]).find(
|
||||
v => v.latest
|
||||
)
|
||||
if (!latestVersion) {
|
||||
throw new Error(
|
||||
`Unable to find latest version for '${javaEaVersion}'. ${c.ERROR_REQUEST}`
|
||||
)
|
||||
}
|
||||
}
|
||||
const file = latestVersion.files.find(
|
||||
f => f.arch === c.JDK_ARCH && f.platform === c.GRAALVM_PLATFORM
|
||||
)
|
||||
if (!file || !file.filename.startsWith('graalvm-jdk-')) {
|
||||
throw new Error(
|
||||
`Unable to find file metadata for '${javaEaVersion}'. ${c.ERROR_REQUEST}`
|
||||
)
|
||||
}
|
||||
return `${latestVersion.download_base_url}${file.filename}`
|
||||
}
|
||||
|
||||
export async function setUpGraalVMJDKCE(
|
||||
javaVersionOrDev: string
|
||||
): Promise<string> {
|
||||
if (javaVersionOrDev === c.VERSION_DEV) {
|
||||
return setUpGraalVMJDKDevBuild()
|
||||
}
|
||||
let javaVersion = javaVersionOrDev
|
||||
if (!javaVersion.includes('.')) {
|
||||
javaVersion = await findLatestGraalVMJDKCEJavaVersion(javaVersion)
|
||||
}
|
||||
if (javaVersion.split('.').length != 3) {
|
||||
throw new Error(
|
||||
`java-version set to '${javaVersionOrDev}', which was resolved to '${javaVersion}'. Please make sure the java-version is set correctly. ${c.ERROR_HINT}`
|
||||
)
|
||||
}
|
||||
const toolName = determineToolName(javaVersion, true)
|
||||
const downloadUrl = `${GRAALVM_CE_DL_BASE}/jdk-${javaVersion}/${toolName}${c.GRAALVM_FILE_EXTENSION}`
|
||||
const downloader = async () => downloadGraalVMJDK(downloadUrl, javaVersion)
|
||||
return downloadExtractAndCacheJDK(downloader, toolName, javaVersion)
|
||||
}
|
||||
|
||||
export async function findLatestGraalVMJDKCEJavaVersion(
|
||||
majorJavaVersion: string
|
||||
): Promise<string> {
|
||||
const matchingRefs = await getMatchingTags(
|
||||
c.GRAALVM_GH_USER,
|
||||
c.GRAALVM_RELEASES_REPO,
|
||||
`${GRAALVM_JDK_TAG_PREFIX}${majorJavaVersion}`
|
||||
)
|
||||
const lowestNonExistingVersion = '0.0.1'
|
||||
let highestVersion = lowestNonExistingVersion
|
||||
const versionNumberStartIndex = `refs/tags/${GRAALVM_JDK_TAG_PREFIX}`.length
|
||||
for (const matchingRef of matchingRefs) {
|
||||
const currentVersion = matchingRef.ref.substring(versionNumberStartIndex)
|
||||
if (
|
||||
semver.valid(currentVersion) &&
|
||||
semver.gt(currentVersion, highestVersion)
|
||||
) {
|
||||
highestVersion = currentVersion
|
||||
}
|
||||
}
|
||||
if (highestVersion === lowestNonExistingVersion) {
|
||||
throw new Error(
|
||||
`Unable to find the latest Java version for '${majorJavaVersion}'. Please make sure the java-version is set correctly. ${c.ERROR_HINT}`
|
||||
)
|
||||
}
|
||||
return highestVersion
|
||||
}
|
||||
|
||||
function determineToolName(javaVersion: string, isCommunity: boolean) {
|
||||
return `graalvm${isCommunity ? '-community' : ''}-jdk-${javaVersion}_${
|
||||
c.JDK_PLATFORM
|
||||
}-${c.JDK_ARCH}_bin`
|
||||
}
|
||||
|
||||
async function downloadGraalVMJDK(
|
||||
downloadUrl: string,
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
try {
|
||||
return await downloadTool(downloadUrl)
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes('404')) {
|
||||
// Not Found
|
||||
throw new Error(
|
||||
`Failed to download ${basename(
|
||||
downloadUrl
|
||||
)}. Are you sure java-version: '${javaVersion}' is correct?`
|
||||
)
|
||||
}
|
||||
throw new Error(
|
||||
`Failed to download ${basename(downloadUrl)} (error: ${error}).`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Support for GraalVM dev builds
|
||||
|
||||
export async function setUpGraalVMJDKDevBuild(): Promise<string> {
|
||||
const latestDevBuild = await getLatestRelease(GRAALVM_REPO_DEV_BUILDS)
|
||||
const resolvedJavaVersion = findHighestJavaVersion(
|
||||
latestDevBuild,
|
||||
c.VERSION_DEV
|
||||
)
|
||||
const downloadUrl = findDownloadUrl(latestDevBuild, resolvedJavaVersion)
|
||||
return downloadAndExtractJDK(downloadUrl)
|
||||
}
|
||||
|
||||
export function findHighestJavaVersion(
|
||||
release: c.LatestReleaseResponse['data'],
|
||||
version: string
|
||||
): string {
|
||||
const graalVMIdentifierPattern = determineGraalVMLegacyIdentifier(
|
||||
false,
|
||||
version,
|
||||
'(\\d+)'
|
||||
)
|
||||
const expectedFileNameRegExp = new RegExp(
|
||||
`^${graalVMIdentifierPattern}${c.GRAALVM_FILE_EXTENSION.replace(
|
||||
/\./g,
|
||||
'\\.'
|
||||
)}$`
|
||||
)
|
||||
let highestJavaVersion = 0
|
||||
for (const asset of release.assets) {
|
||||
const matches = asset.name.match(expectedFileNameRegExp)
|
||||
if (matches) {
|
||||
const javaVersion = +matches[1]
|
||||
if (javaVersion > highestJavaVersion) {
|
||||
highestJavaVersion = javaVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
if (highestJavaVersion > 0) {
|
||||
return String(highestJavaVersion)
|
||||
} else {
|
||||
throw new Error(
|
||||
'Could not find highest Java version. Please file an issue at: https://github.com/graalvm/setup-graalvm/issues.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Support for GraalVM 22.X releases and earlier
|
||||
|
||||
export async function setUpGraalVMLatest_22_X(
|
||||
gdsToken: string,
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
const lockedVersion = javaVersion === '19' ? '22.3.1' : '22.3.3'
|
||||
if (gdsToken.length > 0) {
|
||||
return setUpGraalVMRelease(gdsToken, lockedVersion, javaVersion)
|
||||
}
|
||||
const latestRelease = await getTaggedRelease(
|
||||
c.GRAALVM_GH_USER,
|
||||
c.GRAALVM_RELEASES_REPO,
|
||||
GRAALVM_TAG_PREFIX + lockedVersion
|
||||
)
|
||||
const version = findGraalVMVersion(latestRelease)
|
||||
return setUpGraalVMRelease(gdsToken, version, javaVersion)
|
||||
}
|
||||
|
||||
export function findGraalVMVersion(release: c.LatestReleaseResponse['data']) {
|
||||
const tag_name = release.tag_name
|
||||
if (!tag_name.startsWith(GRAALVM_TAG_PREFIX)) {
|
||||
throw new Error(`Could not find latest GraalVM release: ${tag_name}`)
|
||||
}
|
||||
return tag_name.substring(GRAALVM_TAG_PREFIX.length, tag_name.length)
|
||||
}
|
||||
|
||||
export async function setUpGraalVMRelease(
|
||||
gdsToken: string,
|
||||
version: string,
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
const isEE = gdsToken.length > 0
|
||||
const toolName = determineLegacyToolName(isEE, version, javaVersion)
|
||||
let downloader: () => Promise<string>
|
||||
if (isEE) {
|
||||
downloader = async () =>
|
||||
downloadGraalVMEELegacy(gdsToken, version, javaVersion)
|
||||
} else {
|
||||
downloader = async () => downloadGraalVMCELegacy(version, javaVersion)
|
||||
}
|
||||
return downloadExtractAndCacheJDK(downloader, toolName, version)
|
||||
}
|
||||
|
||||
function findDownloadUrl(
|
||||
release: c.LatestReleaseResponse['data'],
|
||||
javaVersion: string
|
||||
): string {
|
||||
const graalVMIdentifier = determineGraalVMLegacyIdentifier(
|
||||
false,
|
||||
c.VERSION_DEV,
|
||||
javaVersion
|
||||
)
|
||||
const expectedFileName = `${graalVMIdentifier}${c.GRAALVM_FILE_EXTENSION}`
|
||||
for (const asset of release.assets) {
|
||||
if (asset.name === expectedFileName) {
|
||||
return asset.browser_download_url
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`Could not find GraalVM dev build for Java ${javaVersion}. It may no longer be available, so please consider upgrading the Java version. ${c.ERROR_HINT}`
|
||||
)
|
||||
}
|
||||
|
||||
function determineGraalVMLegacyIdentifier(
|
||||
isEE: boolean,
|
||||
version: string,
|
||||
javaVersion: string
|
||||
): string {
|
||||
return `${determineLegacyToolName(isEE, version, javaVersion)}-${
|
||||
c.GRAALVM_ARCH
|
||||
}-${version}`
|
||||
}
|
||||
|
||||
function determineLegacyToolName(
|
||||
isEE: boolean,
|
||||
version: string,
|
||||
javaVersion: string
|
||||
): string {
|
||||
const infix = isEE ? 'ee' : version === c.VERSION_DEV ? 'community' : 'ce'
|
||||
return `graalvm-${infix}-java${javaVersion}-${c.GRAALVM_PLATFORM}`
|
||||
}
|
||||
|
||||
async function downloadGraalVMCELegacy(
|
||||
version: string,
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
const graalVMIdentifier = determineGraalVMLegacyIdentifier(
|
||||
false,
|
||||
version,
|
||||
javaVersion
|
||||
)
|
||||
const downloadUrl = `${GRAALVM_CE_DL_BASE}/${GRAALVM_TAG_PREFIX}${version}/${graalVMIdentifier}${c.GRAALVM_FILE_EXTENSION}`
|
||||
try {
|
||||
return await downloadTool(downloadUrl)
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes('404')) {
|
||||
// Not Found
|
||||
throw new Error(
|
||||
`Failed to download ${graalVMIdentifier}. Are you sure version: '${version}' and java-version: '${javaVersion}' are correct?`
|
||||
)
|
||||
}
|
||||
throw new Error(
|
||||
`Failed to download ${graalVMIdentifier} (error: ${error}).`
|
||||
)
|
||||
}
|
||||
}
|
||||
84
src/gu.ts
Normal file
84
src/gu.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import * as c from './constants'
|
||||
import * as core from '@actions/core'
|
||||
import * as semver from 'semver'
|
||||
import {GRAALVM_PLATFORM} from './constants'
|
||||
import {exec} from './utils'
|
||||
import {join} from 'path'
|
||||
|
||||
const BASE_FLAGS = ['--non-interactive', 'install', '--no-progress']
|
||||
const COMPONENT_TO_POST_INSTALL_HOOK = new Map<string, Map<string, string>>([
|
||||
[
|
||||
'linux',
|
||||
new Map<string, string>([
|
||||
['ruby', 'languages/ruby/lib/truffle/post_install_hook.sh']
|
||||
// ['R', 'languages/R/bin/configure_fastr'] (GR-36105: cannot be run non-interactively)
|
||||
])
|
||||
],
|
||||
[
|
||||
'darwin',
|
||||
new Map<string, string>([
|
||||
['ruby', 'languages/ruby/lib/truffle/post_install_hook.sh']
|
||||
// ['R', 'languages/R/bin/configure_fastr'] (GR-36105: cannot be run non-interactively)
|
||||
])
|
||||
]
|
||||
// No post install hooks for Windows (yet)
|
||||
])
|
||||
|
||||
export async function setUpGUComponents(
|
||||
javaVersion: string,
|
||||
graalVMVersion: string,
|
||||
graalVMHome: string,
|
||||
components: string[],
|
||||
gdsToken: string
|
||||
): Promise<void> {
|
||||
if (components.length == 0) {
|
||||
return // nothing to do
|
||||
}
|
||||
const coercedJavaVersion = semver.coerce(javaVersion)
|
||||
if (
|
||||
graalVMVersion === c.VERSION_DEV ||
|
||||
javaVersion === c.VERSION_DEV ||
|
||||
(coercedJavaVersion != null && semver.gte(coercedJavaVersion, '21.0.0'))
|
||||
) {
|
||||
if (components.length == 1 && components[0] === 'native-image') {
|
||||
core.warning(
|
||||
`Please remove "components: 'native-image'" from your workflow file. It is automatically included since GraalVM for JDK 17: https://github.com/oracle/graal/pull/5995`
|
||||
)
|
||||
} else {
|
||||
core.warning(
|
||||
`Unable to install component(s): '${components.join(
|
||||
','
|
||||
)}'. The latest GraalVM dev builds and the upcoming GraalVM for JDK 21 no longer include the GraalVM Updater: https://github.com/oracle/graal/issues/6855`
|
||||
)
|
||||
}
|
||||
} else if (graalVMVersion.startsWith(c.MANDREL_NAMESPACE)) {
|
||||
core.warning(
|
||||
`Mandrel does not support GraalVM component(s): '${components.join(',')}'`
|
||||
)
|
||||
} else {
|
||||
await installGUComponents(gdsToken, graalVMHome, components)
|
||||
}
|
||||
}
|
||||
|
||||
async function installGUComponents(
|
||||
gdsToken: string,
|
||||
graalVMHome: string,
|
||||
components: string[]
|
||||
): Promise<void> {
|
||||
await exec('gu', BASE_FLAGS.concat(components), {
|
||||
env: {
|
||||
...process.env,
|
||||
GRAAL_EE_DOWNLOAD_TOKEN: gdsToken
|
||||
}
|
||||
})
|
||||
|
||||
const platformHooks = COMPONENT_TO_POST_INSTALL_HOOK.get(GRAALVM_PLATFORM)
|
||||
if (platformHooks) {
|
||||
for (const component of components) {
|
||||
const postInstallHook = platformHooks.get(component)
|
||||
if (postInstallHook) {
|
||||
await exec(`"${join(graalVMHome, postInstallHook)}"`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
119
src/liberica.ts
Normal file
119
src/liberica.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import * as c from './constants'
|
||||
import * as semver from 'semver'
|
||||
import {
|
||||
downloadExtractAndCacheJDK,
|
||||
getTaggedRelease,
|
||||
getMatchingTags
|
||||
} from './utils'
|
||||
import {downloadTool} from '@actions/tool-cache'
|
||||
import {spawnSync} from 'child_process'
|
||||
|
||||
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,
|
||||
javaPackage: string
|
||||
): Promise<string> {
|
||||
const resolvedJavaVersion = await findLatestLibericaJavaVersion(javaVersion)
|
||||
const downloadUrl = await findLibericaURL(resolvedJavaVersion, javaPackage)
|
||||
const toolName = determineToolName(javaVersion, javaPackage)
|
||||
return downloadExtractAndCacheJDK(
|
||||
async () => downloadTool(downloadUrl),
|
||||
toolName,
|
||||
javaVersion
|
||||
)
|
||||
}
|
||||
|
||||
export async function findLatestLibericaJavaVersion(
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
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,
|
||||
javaPackage: string
|
||||
): Promise<string> {
|
||||
const release = await getTaggedRelease(
|
||||
LIBERICA_GH_USER,
|
||||
LIBERICA_RELEASES_REPO,
|
||||
LIBERICA_JDK_TAG_PREFIX + javaVersion
|
||||
)
|
||||
const platform = determinePlatformPart()
|
||||
const assetPrefix = `${LIBERICA_VM_PREFIX}${determineVariantPart(
|
||||
javaPackage
|
||||
)}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}, java-package: ${javaPackage}, platform: ${platform}. ${c.ERROR_REQUEST}`
|
||||
)
|
||||
}
|
||||
|
||||
function determineToolName(javaVersion: string, javaPackage: string) {
|
||||
const variant = determineVariantPart(javaPackage)
|
||||
const platform = determinePlatformPart()
|
||||
return `${LIBERICA_VM_PREFIX}${variant}${platform}`
|
||||
}
|
||||
|
||||
function determineVariantPart(javaPackage: string) {
|
||||
return javaPackage !== null && javaPackage.includes('+fx') ? 'full-' : ''
|
||||
}
|
||||
|
||||
function determinePlatformPart() {
|
||||
if (isMuslBasedLinux()) {
|
||||
return `linux-${c.JDK_ARCH}-musl`
|
||||
} else {
|
||||
return `${c.JDK_PLATFORM}-${c.GRAALVM_ARCH}`
|
||||
}
|
||||
}
|
||||
|
||||
function isMuslBasedLinux() {
|
||||
if (c.IS_LINUX) {
|
||||
const output = spawnSync('ldd', ['--version']).stderr.toString('utf8')
|
||||
if (output.includes('musl')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function isDigit(c: string) {
|
||||
return c.charAt(0) >= '0' && c.charAt(0) <= '9'
|
||||
}
|
||||
179
src/main.ts
Normal file
179
src/main.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import * as c from './constants'
|
||||
import * as core from '@actions/core'
|
||||
import * as graalvm from './graalvm'
|
||||
import * as semver from 'semver'
|
||||
import {isFeatureAvailable as isCacheAvailable} from '@actions/cache'
|
||||
import {basename, join} from 'path'
|
||||
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'
|
||||
import {setUpNativeImageBuildReports} from './features/reports'
|
||||
import {exec} from '@actions/exec'
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
const javaVersion = core.getInput(c.INPUT_JAVA_VERSION, {required: true})
|
||||
const javaPackage = core.getInput(c.INPUT_JAVA_PACKAGE)
|
||||
const distribution = core.getInput(c.INPUT_DISTRIBUTION)
|
||||
const graalVMVersion = core.getInput(c.INPUT_VERSION)
|
||||
const gdsToken = core.getInput(c.INPUT_GDS_TOKEN)
|
||||
const componentsString: string = core.getInput(c.INPUT_COMPONENTS)
|
||||
const components: string[] =
|
||||
componentsString.length > 0
|
||||
? componentsString.split(',').map(x => x.trim())
|
||||
: []
|
||||
const setJavaHome = core.getInput(c.INPUT_SET_JAVA_HOME) === 'true'
|
||||
const cache = core.getInput(c.INPUT_CACHE)
|
||||
const enableCheckForUpdates =
|
||||
core.getInput(c.INPUT_CHECK_FOR_UPDATES) === 'true'
|
||||
const enableNativeImageMusl = core.getInput(c.INPUT_NI_MUSL) === 'true'
|
||||
const isGraalVMforJDK17OrLater =
|
||||
distribution.length > 0 || graalVMVersion.length == 0
|
||||
|
||||
if (c.IS_WINDOWS) {
|
||||
setUpWindowsEnvironment(
|
||||
javaVersion,
|
||||
graalVMVersion,
|
||||
isGraalVMforJDK17OrLater
|
||||
)
|
||||
}
|
||||
await setUpDependencies(components)
|
||||
if (enableNativeImageMusl) {
|
||||
await setUpNativeImageMusl()
|
||||
}
|
||||
|
||||
// Download GraalVM JDK
|
||||
let graalVMHome
|
||||
if (isGraalVMforJDK17OrLater) {
|
||||
if (
|
||||
enableCheckForUpdates &&
|
||||
(distribution === c.DISTRIBUTION_GRAALVM ||
|
||||
distribution === c.DISTRIBUTION_GRAALVM_COMMUNITY)
|
||||
) {
|
||||
checkForUpdates(graalVMVersion, javaVersion)
|
||||
}
|
||||
switch (distribution) {
|
||||
case c.DISTRIBUTION_GRAALVM:
|
||||
graalVMHome = await graalvm.setUpGraalVMJDK(javaVersion, gdsToken)
|
||||
break
|
||||
case c.DISTRIBUTION_GRAALVM_COMMUNITY:
|
||||
graalVMHome = await graalvm.setUpGraalVMJDKCE(javaVersion)
|
||||
break
|
||||
case c.DISTRIBUTION_MANDREL:
|
||||
graalVMHome = await setUpMandrel(graalVMVersion, javaVersion)
|
||||
break
|
||||
case c.DISTRIBUTION_LIBERICA:
|
||||
graalVMHome = await setUpLiberica(javaVersion, javaPackage)
|
||||
break
|
||||
case '':
|
||||
if (javaVersion === c.VERSION_DEV) {
|
||||
core.info(
|
||||
`This build is using GraalVM Community Edition. To select a specific distribution, use the 'distribution' option (see https://github.com/graalvm/setup-graalvm/tree/main#options).`
|
||||
)
|
||||
graalVMHome = await graalvm.setUpGraalVMJDKDevBuild()
|
||||
} else {
|
||||
core.info(
|
||||
`This build is using the new Oracle GraalVM. To select a specific distribution, use the 'distribution' option (see https://github.com/graalvm/setup-graalvm/tree/main#options).`
|
||||
)
|
||||
graalVMHome = await graalvm.setUpGraalVMJDK(javaVersion, gdsToken)
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported distribution: ${distribution}`)
|
||||
}
|
||||
} else {
|
||||
const coercedJavaVersion = semver.coerce(javaVersion)
|
||||
switch (graalVMVersion) {
|
||||
case c.VERSION_LATEST:
|
||||
if (
|
||||
javaVersion.startsWith('17') ||
|
||||
(coercedJavaVersion !== null &&
|
||||
semver.gte(coercedJavaVersion, '20.0.0'))
|
||||
) {
|
||||
core.info(
|
||||
`This build is using the new Oracle GraalVM. To select a specific distribution, use the 'distribution' option (see https://github.com/graalvm/setup-graalvm/tree/main#options).`
|
||||
)
|
||||
graalVMHome = await graalvm.setUpGraalVMJDK(javaVersion, gdsToken)
|
||||
} else {
|
||||
graalVMHome = await graalvm.setUpGraalVMLatest_22_X(
|
||||
gdsToken,
|
||||
javaVersion
|
||||
)
|
||||
}
|
||||
break
|
||||
case c.VERSION_DEV:
|
||||
if (gdsToken.length > 0) {
|
||||
throw new Error(
|
||||
'Downloading GraalVM EE dev builds is not supported'
|
||||
)
|
||||
}
|
||||
if (
|
||||
coercedJavaVersion !== null &&
|
||||
!semver.gte(coercedJavaVersion, '21.0.0')
|
||||
) {
|
||||
core.warning(
|
||||
`GraalVM dev builds are only available for JDK 21. This build is now using a stable release of GraalVM for JDK ${javaVersion}.`
|
||||
)
|
||||
graalVMHome = await graalvm.setUpGraalVMJDK(javaVersion, gdsToken)
|
||||
} else {
|
||||
graalVMHome = await graalvm.setUpGraalVMJDKDevBuild()
|
||||
}
|
||||
break
|
||||
default:
|
||||
if (graalVMVersion.startsWith(c.MANDREL_NAMESPACE)) {
|
||||
graalVMHome = await setUpMandrel(graalVMVersion, javaVersion)
|
||||
} else {
|
||||
if (enableCheckForUpdates) {
|
||||
checkForUpdates(graalVMVersion, javaVersion)
|
||||
}
|
||||
graalVMHome = await graalvm.setUpGraalVMRelease(
|
||||
gdsToken,
|
||||
graalVMVersion,
|
||||
javaVersion
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Activate GraalVM
|
||||
core.debug(`Activating GraalVM located at '${graalVMHome}'...`)
|
||||
core.exportVariable('GRAALVM_HOME', graalVMHome)
|
||||
core.addPath(join(graalVMHome, 'bin'))
|
||||
if (setJavaHome) {
|
||||
core.exportVariable('JAVA_HOME', graalVMHome)
|
||||
}
|
||||
|
||||
await setUpGUComponents(
|
||||
javaVersion,
|
||||
graalVMVersion,
|
||||
graalVMHome,
|
||||
components,
|
||||
gdsToken
|
||||
)
|
||||
|
||||
if (cache && isCacheAvailable()) {
|
||||
await restore(cache)
|
||||
}
|
||||
setUpNativeImageBuildReports(
|
||||
isGraalVMforJDK17OrLater,
|
||||
javaVersion,
|
||||
graalVMVersion
|
||||
)
|
||||
|
||||
core.startGroup(`Successfully set up '${basename(graalVMHome)}'`)
|
||||
await exec(join(graalVMHome, 'bin', `java${c.EXECUTABLE_SUFFIX}`), [
|
||||
javaVersion.startsWith('8') ? '-version' : '--version'
|
||||
])
|
||||
core.endGroup()
|
||||
} catch (error) {
|
||||
if (error instanceof Error) core.setFailed(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
162
src/mandrel.ts
Normal file
162
src/mandrel.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import * as c from './constants'
|
||||
import * as httpClient from '@actions/http-client'
|
||||
import {downloadExtractAndCacheJDK, getLatestRelease} from './utils'
|
||||
import {downloadTool} from '@actions/tool-cache'
|
||||
import {basename} from 'path'
|
||||
|
||||
export const MANDREL_REPO = 'mandrel'
|
||||
export const MANDREL_TAG_PREFIX = c.MANDREL_NAMESPACE
|
||||
const MANDREL_DL_BASE = 'https://github.com/graalvm/mandrel/releases/download'
|
||||
const DISCO_API_BASE = 'https://api.foojay.io/disco/v3.0/packages/jdks'
|
||||
|
||||
interface JdkData {
|
||||
message: string
|
||||
result: any
|
||||
}
|
||||
|
||||
export async function setUpMandrel(
|
||||
mandrelVersion: string,
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
const version = stripMandrelNamespace(mandrelVersion)
|
||||
let mandrelHome
|
||||
switch (version) {
|
||||
case '':
|
||||
// fetch latest if no version is specified
|
||||
case 'latest':
|
||||
mandrelHome = await setUpMandrelLatest(javaVersion)
|
||||
break
|
||||
default:
|
||||
mandrelHome = await setUpMandrelRelease(version, javaVersion)
|
||||
break
|
||||
}
|
||||
|
||||
return mandrelHome
|
||||
}
|
||||
|
||||
async function setUpMandrelLatest(javaVersion: string): Promise<string> {
|
||||
const latest_release_url = await getLatestMandrelReleaseUrl(javaVersion)
|
||||
const version_tag = getTagFromURI(latest_release_url)
|
||||
const version = stripMandrelNamespace(version_tag)
|
||||
|
||||
const toolName = determineToolName(javaVersion)
|
||||
return downloadExtractAndCacheJDK(
|
||||
async () => downloadTool(latest_release_url),
|
||||
toolName,
|
||||
version
|
||||
)
|
||||
}
|
||||
|
||||
// Download URIs are of the form https://github.com/graalvm/mandrel/releases/download/<tag>/<archive-name>
|
||||
function getTagFromURI(uri: string): string {
|
||||
const parts = uri.split('/')
|
||||
try {
|
||||
return parts[parts.length - 2]
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to extract tag from URI ${uri}: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLatestMandrelReleaseUrl(
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
const url = `${DISCO_API_BASE}?jdk_version=${javaVersion}&distribution=${c.DISTRIBUTION_MANDREL}&architecture=${c.JDK_ARCH}&operating_system=${c.JDK_PLATFORM}&latest=per_distro`
|
||||
const _http = new httpClient.HttpClient()
|
||||
const response = await _http.getJson<JdkData>(url)
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error(
|
||||
`Failed to fetch latest Mandrel release for Java ${javaVersion} from DISCO API: ${response.result}`
|
||||
)
|
||||
}
|
||||
const result = response.result?.result[0]
|
||||
try {
|
||||
const pkg_info_uri = result.links.pkg_info_uri
|
||||
return await getLatestMandrelReleaseUrlHelper(
|
||||
_http,
|
||||
javaVersion,
|
||||
pkg_info_uri
|
||||
)
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to get latest Mandrel release for Java ${javaVersion} from DISCO API: ${error}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function getLatestMandrelReleaseUrlHelper(
|
||||
_http: httpClient.HttpClient,
|
||||
java_version: string,
|
||||
pkg_info_uri: string
|
||||
): Promise<string> {
|
||||
const response = await _http.getJson<JdkData>(pkg_info_uri)
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error(
|
||||
`Failed to fetch package info of latest Mandrel release for Java ${java_version} from DISCO API: ${response.result}`
|
||||
)
|
||||
}
|
||||
const result = response.result?.result[0]
|
||||
try {
|
||||
return result.direct_download_uri
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to get download URI of latest Mandrel release for Java ${java_version} from DISCO API: ${error}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function setUpMandrelRelease(
|
||||
version: string,
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
const toolName = determineToolName(javaVersion)
|
||||
return downloadExtractAndCacheJDK(
|
||||
async () => downloadMandrelJDK(version, javaVersion),
|
||||
toolName,
|
||||
version
|
||||
)
|
||||
}
|
||||
|
||||
async function downloadMandrelJDK(
|
||||
version: string,
|
||||
javaVersion: string
|
||||
): Promise<string> {
|
||||
const identifier = determineMandrelIdentifier(version, javaVersion)
|
||||
const downloadUrl = `${MANDREL_DL_BASE}/${MANDREL_TAG_PREFIX}${version}/${identifier}${c.GRAALVM_FILE_EXTENSION}`
|
||||
try {
|
||||
return await downloadTool(downloadUrl)
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes('404')) {
|
||||
// Not Found
|
||||
throw new Error(
|
||||
`Failed to download ${basename(
|
||||
downloadUrl
|
||||
)}. Are you sure version: '${version}' and java-version: '${javaVersion}' are correct?`
|
||||
)
|
||||
}
|
||||
throw new Error(
|
||||
`Failed to download ${basename(downloadUrl)} (error: ${error}).`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function determineMandrelIdentifier(
|
||||
version: string,
|
||||
javaVersion: string
|
||||
): string {
|
||||
return `mandrel-java${javaVersion}-${c.GRAALVM_PLATFORM}-${c.GRAALVM_ARCH}-${version}`
|
||||
}
|
||||
|
||||
function determineToolName(javaVersion: string): string {
|
||||
return `mandrel-java${javaVersion}-${c.GRAALVM_PLATFORM}`
|
||||
}
|
||||
|
||||
export function stripMandrelNamespace(graalVMVersion: string) {
|
||||
if (graalVMVersion.startsWith(c.MANDREL_NAMESPACE)) {
|
||||
return graalVMVersion.substring(
|
||||
c.MANDREL_NAMESPACE.length,
|
||||
graalVMVersion.length
|
||||
)
|
||||
} else {
|
||||
return graalVMVersion
|
||||
}
|
||||
}
|
||||
95
src/msvc.ts
Normal file
95
src/msvc.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as semver from 'semver'
|
||||
import {execSync} from 'child_process'
|
||||
import {existsSync} from 'fs'
|
||||
import {VERSION_DEV} from './constants'
|
||||
|
||||
// Keep in sync with https://github.com/actions/virtual-environments
|
||||
const KNOWN_VISUAL_STUDIO_INSTALLATIONS = [
|
||||
'C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise', // 'windows-2022' and 'windows-latest'
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise', // 'windows-2019'
|
||||
'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise' // 'windows-2016' (deprecated and removed)
|
||||
]
|
||||
if (process.env['VSINSTALLDIR']) {
|
||||
// if VSINSTALLDIR is set, make it the first known installation
|
||||
KNOWN_VISUAL_STUDIO_INSTALLATIONS.unshift(
|
||||
process.env['VSINSTALLDIR'].replace(/\\$/, '')
|
||||
)
|
||||
}
|
||||
const VCVARSALL_SUBPATH = 'VC\\Auxiliary\\Build\\vcvarsall.bat'
|
||||
|
||||
function findVcvarsallPath(): string {
|
||||
for (const installation of KNOWN_VISUAL_STUDIO_INSTALLATIONS) {
|
||||
const candidate = `${installation}\\${VCVARSALL_SUBPATH}`
|
||||
if (existsSync(candidate)) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
throw new Error('Failed to find vcvarsall.bat')
|
||||
}
|
||||
|
||||
export function needsWindowsEnvironmentSetup(
|
||||
javaVersion: string,
|
||||
graalVMVersion: string,
|
||||
isGraalVMforJDK17OrLater: boolean
|
||||
): boolean {
|
||||
if (javaVersion === VERSION_DEV || graalVMVersion === VERSION_DEV) {
|
||||
return false // no longer required in dev builds
|
||||
} else if (isGraalVMforJDK17OrLater) {
|
||||
return false // no longer required in GraalVM for JDK 17 and later.
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export function setUpWindowsEnvironment(
|
||||
javaVersion: string,
|
||||
graalVMVersion: string,
|
||||
isGraalVMforJDK17OrLater: boolean
|
||||
): void {
|
||||
if (
|
||||
!needsWindowsEnvironmentSetup(
|
||||
javaVersion,
|
||||
graalVMVersion,
|
||||
isGraalVMforJDK17OrLater
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
core.startGroup('Updating Windows environment...')
|
||||
|
||||
const vcvarsallPath = findVcvarsallPath()
|
||||
core.debug(`Calling "${vcvarsallPath}"...`)
|
||||
const [originalEnv, vcvarsallOutput, updatedEnv] = execSync(
|
||||
`set && cls && "${vcvarsallPath}" x64 && cls && set`,
|
||||
{shell: 'cmd'}
|
||||
)
|
||||
.toString()
|
||||
.split('\f') // form feed page break (printed by `cls`)
|
||||
core.debug(vcvarsallOutput)
|
||||
|
||||
const originalEnvMap = new Map<string, string>()
|
||||
for (const line of originalEnv.split('\r\n')) {
|
||||
if (line.includes('=')) {
|
||||
const [name, value] = line.split('=')
|
||||
originalEnvMap.set(name, value)
|
||||
} else if (line) {
|
||||
core.debug(`Skipping ${line} (does not include '=')...`)
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of updatedEnv.split('\r\n')) {
|
||||
if (line.includes('=')) {
|
||||
const [name, value] = line.split('=')
|
||||
const originalValue = originalEnvMap.get(name)
|
||||
if (value !== originalValue) {
|
||||
core.exportVariable(name, value)
|
||||
core.debug(`"${name}" set to "${value}"`)
|
||||
}
|
||||
} else if (line) {
|
||||
core.debug(`Skipping ${line} (does not include '=')...`)
|
||||
}
|
||||
}
|
||||
|
||||
core.endGroup()
|
||||
}
|
||||
249
src/utils.ts
Normal file
249
src/utils.ts
Normal file
@@ -0,0 +1,249 @@
|
||||
import * as c from './constants'
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import * as httpClient from '@actions/http-client'
|
||||
import * as semver from 'semver'
|
||||
import * as tc from '@actions/tool-cache'
|
||||
import {ExecOptions, exec as e} from '@actions/exec'
|
||||
import {readFileSync, readdirSync} from 'fs'
|
||||
import {Octokit} from '@octokit/core'
|
||||
import {createHash} from 'crypto'
|
||||
import {join} from 'path'
|
||||
|
||||
// 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({
|
||||
baseUrl,
|
||||
request: {
|
||||
agent: new httpClient.HttpClient().getAgent(baseUrl)
|
||||
}
|
||||
})
|
||||
|
||||
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}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLatestRelease(
|
||||
repo: 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/latest', {
|
||||
owner: c.GRAALVM_GH_USER,
|
||||
repo
|
||||
})
|
||||
).data
|
||||
}
|
||||
|
||||
export async function getContents(
|
||||
repo: string,
|
||||
path: string
|
||||
): Promise<c.ContentsResponse['data']> {
|
||||
const githubToken = getGitHubToken()
|
||||
const options = githubToken.length > 0 ? {auth: githubToken} : {}
|
||||
const octokit = new GitHubDotCom(options)
|
||||
return (
|
||||
await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', {
|
||||
owner: c.GRAALVM_GH_USER,
|
||||
repo,
|
||||
path
|
||||
})
|
||||
).data
|
||||
}
|
||||
|
||||
export async function getTaggedRelease(
|
||||
owner: string,
|
||||
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}', {
|
||||
owner,
|
||||
repo,
|
||||
tag
|
||||
})
|
||||
).data
|
||||
}
|
||||
|
||||
export async function getMatchingTags(
|
||||
owner: string,
|
||||
repo: string,
|
||||
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}',
|
||||
{
|
||||
owner,
|
||||
repo,
|
||||
tagPrefix
|
||||
}
|
||||
)
|
||||
).data
|
||||
}
|
||||
|
||||
export async function downloadAndExtractJDK(
|
||||
downloadUrl: string
|
||||
): Promise<string> {
|
||||
return findJavaHomeInSubfolder(
|
||||
await extract(await tc.downloadTool(downloadUrl))
|
||||
)
|
||||
}
|
||||
|
||||
export async function downloadExtractAndCacheJDK(
|
||||
downloader: () => Promise<string>,
|
||||
toolName: string,
|
||||
version: string
|
||||
): Promise<string> {
|
||||
const semVersion = toSemVer(version)
|
||||
let toolPath = tc.find(toolName, semVersion)
|
||||
if (toolPath) {
|
||||
core.info(`Found ${toolName} ${version} in tool-cache @ ${toolPath}`)
|
||||
} else {
|
||||
const extractDir = await extract(await downloader())
|
||||
core.info(`Adding ${toolName} ${version} to tool-cache ...`)
|
||||
toolPath = await tc.cacheDir(extractDir, toolName, semVersion)
|
||||
}
|
||||
return findJavaHomeInSubfolder(toolPath)
|
||||
}
|
||||
|
||||
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') {
|
||||
return await tc.extractTar(downloadPath)
|
||||
} else if (c.GRAALVM_FILE_EXTENSION === '.zip') {
|
||||
return await tc.extractZip(downloadPath)
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unexpected filetype downloaded: ${c.GRAALVM_FILE_EXTENSION}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function findJavaHomeInSubfolder(searchPath: string): string {
|
||||
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}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function toSemVer(version: string): string {
|
||||
const parts = version.split('.')
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function createPRComment(content: string): Promise<void> {
|
||||
if (!isPREvent()) {
|
||||
throw new Error('Not a PR event.')
|
||||
}
|
||||
const context = github.context
|
||||
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}`
|
||||
)
|
||||
}
|
||||
}
|
||||
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"outDir": "./lib", /* Redirect output structure to the directory. */
|
||||
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
},
|
||||
"exclude": ["node_modules", "**/*.test.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user