Compare commits

...

11 Commits

Author SHA1 Message Date
Markus Hoffrogge
d3e3b95d7c
Merge 8bb7cab8d1 into 40b9536ce5 2024-09-22 22:59:54 +02:00
Zxilly
40b9536ce5
fix: add arch to cache key (#664) 2024-09-19 08:51:30 -05:00
Fabio Niephaus
0a40ce6f61
Add support for Oracle GraalVM (#501)
* Add support for Oracle GraalVM

* Add support for EA builds of Oracle GraalVM
2024-09-18 10:17:11 -05:00
Joel Ambass
bcfbca5b71
Merge pull request #684 from actions/Jcambass-patch-1
Add workflow file for publishing releases to immutable action package
2024-09-11 09:54:38 +02:00
Joel Ambass
78eae7945c
Add workflow file for publishing releases to immutable action package
This workflow file publishes new action releases to the immutable action package of the same name as this repo.

This is part of the Immutable Actions project which is not yet fully released to the public. First party actions like this one are part of our initial testing of this feature.
2024-09-11 09:48:56 +02:00
aparnajyothi-y
2dfa2011c5
basic validation failure fix (#682) 2024-09-06 14:42:39 -05:00
Tobias
7467385c61
feat: add support for SapMachine JDK/JRE (#614)
Co-authored-by: Christian Schwaninger <christian.schwaninger@sap.com>
2024-09-05 14:04:24 -05:00
Markus Hoffrogge
8bb7cab8d1 Update compiled dist scripts after re-base to main 2024-08-23 23:50:09 +02:00
Markus Hoffrogge
d299400510 Update from npm run format to fix prettier check
- missed in initial commit
- took re-built dist from GH actions
2024-08-23 23:50:09 +02:00
Markus Hoffrogge
a368241022 Update README.md 2024-08-23 23:50:08 +02:00
Markus Hoffrogge
19a6381cef Add inputs 'update-toolchains-only', 'update-env-javahome', 'add-to-env-path'
Changes in detail:
------------------
- action.yml:
  - add inputs:
    - update-toolchains-only
    - update-env-javahome
    - add-to-env-path
  - update description for input "overwrite-settings"
  - remove default value of input "overwrite-settings",
    since the default is now propagated from input 'update-toolchains-only'

- base-models.ts:
  - extend interface JavaInstallerOptions:
    - add fields:
      - updateEnvJavaHome: boolean;
      - addToEnvPath: boolean;

- constants.ts:
  - add constant INPUT_UPDATE_TOOLCHAINS_ONLY
    = 'update-toolchains-only'

- auth.ts:
  - function configureAuthentication():
    - add parameter:
      - overwriteSettings: boolean
    - remove the now obsolete const overwriteSettings

- toolchains.ts:
  - function configureToolchains(...):
    - add parameter updateToolchains: boolean
    - remove the now obsolete const overwriteSettings
  - improve variable naming:
    - rename any occurrence of 'overwriteSettings'
        by 'updateToolchains'
    - add field updateToolchains: boolean to the parameter object
  - function writeToolchainsFileToDisk(...):
    - improve variable naming:
      - rename variable 'settingsExists'
          by 'toolchainsExists'
    - update wording of info logs to be more applicable

- setup-java.ts:
  - interface installerInputsOptions:
    - rename to IInstallerInputsOption to meet common coding convention
    - add fields:
      - updateToolchainsOnly: boolean;
      - overwriteSettings: boolean;
      - updateEnvJavaHome: boolean;
      - addToEnvPath: boolean;
  - function run():
    - add const:
      - const updateToolchainsOnly:
        - get as boolean from input 'update-toolchains-only', default: false
      - const overwriteSettings:
        - get as boolean from input 'overwrite-settings', default: !updateToolchainsOnly
      - const updateEnvJavaHome:
        - get as boolean input 'update-env-javahome', default: !updateToolchainsOnly
      - const addToEnvPath:
        - get as boolean input 'add-to-env-path', default: !updateToolchainsOnly
   - extend const installerInputsOptions to match with IInstallerInputsOption:
      - add field updateToolchainsOnly
      - add field overwriteSettings
      - add field updateEnvJavaHome
      - add field addToEnvPath
    - update call of auth.configureAuthentication()
        to auth.configureAuthentication(overwriteSettings)
  - function installVersion(...):
    - add const and init from parameter options:
      - updateToolchainsOnly, overwriteSettings,
        updateEnvJavaHome, addToEnvPath
    - init the additional fields of installerInputsOptions accordingly
    - call toolchains.configureToolchains(...):
      - with parameter updateToolchains= overwriteSettings || updateToolchainsOnly

- base-installer.ts:
  - add constants to import from constants:
    - INPUT_UPDATE_JAVA_HOME
    - INPUT_ADD_TO_PATH
  - add fields:
    - protected updateEnvJavaHome: boolean;
    - protected addToEnvPath: boolean;
  - ctor:
    - init these fields from JavaInstallerOptions accoprdingly
  - function setJavaDefault(...):
    - if updateEnvJavaHome is false:
      - SKIP updating env.JAVA_HOME
      - log info:
        `Skip updating env.JAVA_HOME according to ${INPUT_UPDATE_JAVA_HOME}`
    - if addToEnvPath is false:
      - SKIP adding toolchain path to env.PATH
      - log info:
        `Skip adding to env.PATH according to ${INPUT_ADD_TO_PATH}`
2024-08-23 23:50:08 +02:00
22 changed files with 93140 additions and 4380 deletions

View File

@ -30,7 +30,8 @@ jobs:
'microsoft', 'microsoft',
'semeru', 'semeru',
'corretto', 'corretto',
'dragonwell' 'dragonwell',
'sapmachine'
] # internally 'adopt-hotspot' is the same as 'adopt' ] # internally 'adopt-hotspot' is the same as 'adopt'
version: ['21', '11', '17'] version: ['21', '11', '17']
exclude: exclude:
@ -44,11 +45,22 @@ jobs:
version: 17 version: 17
- distribution: oracle - distribution: oracle
os: windows-latest os: windows-latest
version: 20 version: 21
- distribution: oracle - distribution: oracle
os: ubuntu-latest os: ubuntu-latest
version: 20 version: 21
- distribution: graalvm
os: macos-latest
version: 17
- distribution: graalvm
os: windows-latest
version: 21
- distribution: graalvm
os: ubuntu-latest
version: 21
- distribution: graalvm
os: ubuntu-latest
version: '24-ea'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -78,13 +90,19 @@ jobs:
include: include:
- distribution: oracle - distribution: oracle
os: ubuntu-latest os: ubuntu-latest
version: '20.0.1' version: '21.0.4'
- distribution: graalvm
os: ubuntu-latest
version: '21.0.4'
- distribution: dragonwell - distribution: dragonwell
os: ubuntu-latest os: ubuntu-latest
version: '11.0' version: '11.0'
- distribution: dragonwell - distribution: dragonwell
os: ubuntu-latest os: ubuntu-latest
version: '11.0.13+9' version: '11.0.13+9'
- distribution: sapmachine
os: ubuntu-latest
version: '17.0.7'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -106,7 +124,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-latest, windows-latest, ubuntu-latest] os: [macos-latest, windows-latest, ubuntu-latest]
distribution: ['temurin', 'zulu', 'liberica', 'dragonwell'] distribution:
['temurin', 'zulu', 'liberica', 'dragonwell', 'sapmachine']
exclude: exclude:
- distribution: dragonwell - distribution: dragonwell
os: macos-latest os: macos-latest
@ -132,7 +151,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-latest, windows-latest, ubuntu-latest] os: [macos-latest, windows-latest, ubuntu-latest]
distribution: ['temurin', 'zulu', 'liberica', 'dragonwell'] distribution:
['temurin', 'zulu', 'liberica', 'dragonwell', 'sapmachine']
exclude: exclude:
- distribution: dragonwell - distribution: dragonwell
os: macos-latest os: macos-latest
@ -154,10 +174,10 @@ jobs:
{ {
$envName = "JAVA_HOME_${version}_${env:RUNNER_ARCH}" $envName = "JAVA_HOME_${version}_${env:RUNNER_ARCH}"
$JavaVersionPath = [Environment]::GetEnvironmentVariable($envName) $JavaVersionPath = [Environment]::GetEnvironmentVariable($envName)
if (-not (Test-Path "$JavaVersionPath")) { if (-not (Test-Path "$JavaVersionPath")) {
Write-Host "$envName is not found" Write-Host "$envName is not found"
exit 1 exit 1
} }
} }
shell: pwsh shell: pwsh
- name: Verify Java - name: Verify Java
@ -208,6 +228,28 @@ jobs:
run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}"
shell: bash shell: bash
setup-java-ea-versions-sapmachine:
name: sapmachine ${{ matrix.version }} (jdk-x64) - ${{ matrix.os }}
needs: setup-java-major-minor-versions
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
version: ['17-ea', '21-ea']
steps:
- name: Checkout
uses: actions/checkout@v4
- name: setup-java
uses: ./
id: setup-java
with:
java-version: ${{ matrix.version }}
distribution: sapmachine
- name: Verify Java
run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}"
shell: bash
setup-java-custom-package-type: setup-java-custom-package-type:
name: ${{ matrix.distribution }} ${{ matrix.version }} (${{ matrix.java-package }}-x64) - ${{ matrix.os }} name: ${{ matrix.distribution }} ${{ matrix.version }} (${{ matrix.java-package }}-x64) - ${{ matrix.os }}
needs: setup-java-major-minor-versions needs: setup-java-major-minor-versions
@ -216,7 +258,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-13, windows-latest, ubuntu-latest] os: [macos-13, windows-latest, ubuntu-latest]
distribution: ['temurin', 'zulu', 'liberica', 'semeru'] distribution: ['temurin', 'zulu', 'liberica', 'semeru', 'sapmachine']
java-package: ['jre'] java-package: ['jre']
version: ['17.0'] version: ['17.0']
include: include:

View File

@ -0,0 +1,22 @@
name: 'Publish Immutable Action Version'
on:
release:
types: [created]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
packages: write
steps:
- name: Checking out
uses: actions/checkout@v4
- name: Publish
id: publish
uses: actions/publish-immutable-action@0.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -28,7 +28,7 @@ This action allows you to work with Java and Scala projects.
- `java-version`: The Java version that is going to be set up. Takes a whole or [semver](#supported-version-syntax) Java version. If not specified, the action will expect `java-version-file` input to be specified. - `java-version`: The Java version that is going to be set up. Takes a whole or [semver](#supported-version-syntax) Java version. If not specified, the action will expect `java-version-file` input to be specified.
- `java-version-file`: The path to a file containing java version. Supported file types are `.java-version` and `.tool-versions`. See more details in [about .java-version-file](docs/advanced-usage.md#Java-version-file). - `java-version-file`: The path to a file containing java version. Supported file types are `.java-version` and `.tool-versions`. See more details in [about .java-version-file](docs/advanced-usage.md#Java-version-file).
- `distribution`: _(required)_ Java [distribution](#supported-distributions). - `distribution`: _(required)_ Java [distribution](#supported-distributions).
- `java-package`: The packaging variant of the chosen distribution. Possible values: `jdk`, `jre`, `jdk+fx`, `jre+fx`. Default value: `jdk`. - `java-package`: The packaging variant of the chosen distribution. Possible values: `jdk`, `jre`, `jdk+fx`, `jre+fx`. Default value: `jdk`.
@ -46,7 +46,13 @@ This action allows you to work with Java and Scala projects.
#### Maven options #### Maven options
The action has a bunch of inputs to generate maven's [settings.xml](https://maven.apache.org/settings.html) on the fly and pass the values to Apache Maven GPG Plugin as well as Apache Maven Toolchains. See [advanced usage](docs/advanced-usage.md) for more. The action has a bunch of inputs to generate maven's [settings.xml](https://maven.apache.org/settings.html) on the fly and pass the values to Apache Maven GPG Plugin as well as Apache Maven Toolchains. See [advanced usage](docs/advanced-usage.md) for more.
- `overwrite-settings`: By default action overwrites the settings.xml. In order to skip generation of file if it exists, set this to `false`. - `overwrite-settings`: By default action overwrites the settings.xml and adds a toolchain entry to toolchains.xml. In order to skip generation of settings.xml and skip adding a toolchain entry to toolchains.xml if the according file exists, set this to `false`.
- `update-env-javahome`: By default action updates `env.JAVA_HOME` with the path of java installed. In order to skip update of JAVA_HOME, set this to `false`. The creation of the env variable JAVA_HOME_{{ MAJOR_VERSION }}_{{ ARCHITECTURE }} is NOT affected by this item. That will be created for any setup.
- `update-env-path`: By default action adds `<java_install_dir>/bin` to `env.PATH`. In order to skip this, set this to `false`.
- `update-toolchains-only`: If set to true, then `overwrite-settings`, `update-env-javahome` and `update-env-path` are propagated to `false` if the specific one is not explicitly configured to `true`. Only a toolchain entry will be added to toolchains.xml. Default is `false`.
- `server-id`: ID of the distributionManagement repository in the pom.xml file. Default is `github`. - `server-id`: ID of the distributionManagement repository in the pom.xml file. Default is `github`.
@ -108,6 +114,8 @@ Currently, the following distributions are supported:
| `semeru` | IBM Semeru Runtime Open Edition | [Link](https://developer.ibm.com/languages/java/semeru-runtimes/downloads/) | [Link](https://openjdk.java.net/legal/gplv2+ce.html) | | `semeru` | IBM Semeru Runtime Open Edition | [Link](https://developer.ibm.com/languages/java/semeru-runtimes/downloads/) | [Link](https://openjdk.java.net/legal/gplv2+ce.html) |
| `oracle` | Oracle JDK | [Link](https://www.oracle.com/java/technologies/downloads/) | [Link](https://java.com/freeuselicense) | `oracle` | Oracle JDK | [Link](https://www.oracle.com/java/technologies/downloads/) | [Link](https://java.com/freeuselicense)
| `dragonwell` | Alibaba Dragonwell JDK | [Link](https://dragonwell-jdk.io/) | [Link](https://www.aliyun.com/product/dragonwell/) | `dragonwell` | Alibaba Dragonwell JDK | [Link](https://dragonwell-jdk.io/) | [Link](https://www.aliyun.com/product/dragonwell/)
| `sapmachine` | SAP SapMachine JDK/JRE | [Link](https://sapmachine.io/) | [Link](https://github.com/SAP/SapMachine/blob/sapmachine/LICENSE)
| `graalvm` | Oracle GraalVM | [Link](https://www.graalvm.org/) | [Link](https://www.oracle.com/downloads/licenses/graal-free-license.html)
**NOTE:** The different distributors can provide discrepant list of available versions / supported configurations. Please refer to the official documentation to see the list of supported versions. **NOTE:** The different distributors can provide discrepant list of available versions / supported configurations. Please refer to the official documentation to see the list of supported versions.
@ -230,7 +238,7 @@ jobs:
### Install multiple JDKs ### Install multiple JDKs
All versions are added to the PATH. The last version will be used and available globally. Other Java versions can be accessed through env variables with such specification as 'JAVA_HOME_{{ MAJOR_VERSION }}_{{ ARCHITECTURE }}'. By default all versions are added to the PATH. The last version will be used and available globally. Other Java versions can be accessed through env variables with such specification as 'JAVA_HOME_{{ MAJOR_VERSION }}_{{ ARCHITECTURE }}'.
```yaml ```yaml
steps: steps:
@ -243,6 +251,8 @@ All versions are added to the PATH. The last version will be used and available
15 15
``` ```
**NOTE:** An alternative option is to use multiple setup-java steps. In this case the behavior can be controlled more granular by making use of the input items `overwrite-settings`, `update-env-javahome`, `update-env-path` and `update-toolchains-only`.
### Using Maven Toolchains ### Using Maven Toolchains
In the example above multiple JDKs are installed for the same job. The result after the last JDK is installed is a Maven Toolchains declaration containing references to all three JDKs. The values for `id`, `version`, and `vendor` of the individual Toolchain entries are the given input values for `distribution` and `java-version` (`vendor` being the combination of `${distribution}_${java-version}`) by default. In the example above multiple JDKs are installed for the same job. The result after the last JDK is installed is a Maven Toolchains declaration containing references to all three JDKs. The values for `id`, `version`, and `vendor` of the individual Toolchain entries are the given input values for `distribution` and `java-version` (`vendor` being the combination of `${distribution}_${java-version}`) by default.
@ -257,6 +267,8 @@ In the example above multiple JDKs are installed for the same job. The result af
- [Amazon Corretto](docs/advanced-usage.md#Amazon-Corretto) - [Amazon Corretto](docs/advanced-usage.md#Amazon-Corretto)
- [Oracle](docs/advanced-usage.md#Oracle) - [Oracle](docs/advanced-usage.md#Oracle)
- [Alibaba Dragonwell](docs/advanced-usage.md#Alibaba-Dragonwell) - [Alibaba Dragonwell](docs/advanced-usage.md#Alibaba-Dragonwell)
- [SapMachine](docs/advanced-usage.md#SapMachine)
- [GraalVM](docs/advanced-usage.md#GraalVM)
- [Installing custom Java package type](docs/advanced-usage.md#Installing-custom-Java-package-type) - [Installing custom Java package type](docs/advanced-usage.md#Installing-custom-Java-package-type)
- [Installing custom Java architecture](docs/advanced-usage.md#Installing-custom-Java-architecture) - [Installing custom Java architecture](docs/advanced-usage.md#Installing-custom-Java-architecture)
- [Installing custom Java distribution from local file](docs/advanced-usage.md#Installing-Java-from-local-file) - [Installing custom Java distribution from local file](docs/advanced-usage.md#Installing-Java-from-local-file)

87242
__tests__/data/sapmachine.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,152 @@
import {GraalVMDistribution} from '../../src/distributions/graalvm/installer';
import os from 'os';
import * as core from '@actions/core';
import {getDownloadArchiveExtension} from '../../src/util';
import {HttpClient} from '@actions/http-client';
describe('findPackageForDownload', () => {
let distribution: GraalVMDistribution;
let spyDebug: jest.SpyInstance;
let spyHttpClient: jest.SpyInstance;
beforeEach(() => {
distribution = new GraalVMDistribution({
version: '',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
});
spyDebug = jest.spyOn(core, 'debug');
spyDebug.mockImplementation(() => {});
});
it.each([
[
'21',
'21',
'https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_{{OS_TYPE}}-x64_bin.{{ARCHIVE_TYPE}}'
],
[
'21.0.4',
'21.0.4',
'https://download.oracle.com/graalvm/21/archive/graalvm-jdk-21.0.4_{{OS_TYPE}}-x64_bin.{{ARCHIVE_TYPE}}'
],
[
'17',
'17',
'https://download.oracle.com/graalvm/17/latest/graalvm-jdk-17_{{OS_TYPE}}-x64_bin.{{ARCHIVE_TYPE}}'
],
[
'17.0.12',
'17.0.12',
'https://download.oracle.com/graalvm/17/archive/graalvm-jdk-17.0.12_{{OS_TYPE}}-x64_bin.{{ARCHIVE_TYPE}}'
]
])('version is %s -> %s', async (input, expectedVersion, expectedUrl) => {
/* Needed only for this particular test because /latest/ urls tend to change */
spyHttpClient = jest.spyOn(HttpClient.prototype, 'head');
spyHttpClient.mockReturnValue(
Promise.resolve({
message: {
statusCode: 200
}
})
);
const result = await distribution['findPackageForDownload'](input);
jest.restoreAllMocks();
expect(result.version).toBe(expectedVersion);
const osType = distribution.getPlatform();
const archiveType = getDownloadArchiveExtension();
const url = expectedUrl
.replace('{{OS_TYPE}}', osType)
.replace('{{ARCHIVE_TYPE}}', archiveType);
expect(result.url).toBe(url);
});
it.each([
[
'24-ea',
/^https:\/\/github\.com\/graalvm\/oracle-graalvm-ea-builds\/releases\/download\/jdk-24\.0\.0-ea\./
]
])('version is %s -> %s', async (version, expectedUrlPrefix) => {
/* Needed only for this particular test because /latest/ urls tend to change */
spyHttpClient = jest.spyOn(HttpClient.prototype, 'head');
spyHttpClient.mockReturnValue(
Promise.resolve({
message: {
statusCode: 200
}
})
);
const eaDistro = new GraalVMDistribution({
version,
architecture: '', // to get default value
packageType: 'jdk',
checkLatest: false
});
const versionWithoutEA = version.split('-')[0];
const result = await eaDistro['findPackageForDownload'](versionWithoutEA);
jest.restoreAllMocks();
expect(result.url).toEqual(expect.stringMatching(expectedUrlPrefix));
});
it.each([
['amd64', 'x64'],
['arm64', 'aarch64']
])(
'defaults to os.arch(): %s mapped to distro arch: %s',
async (osArch: string, distroArch: string) => {
jest.spyOn(os, 'arch').mockReturnValue(osArch);
jest.spyOn(os, 'platform').mockReturnValue('linux');
const version = '21';
const distro = new GraalVMDistribution({
version,
architecture: '', // to get default value
packageType: 'jdk',
checkLatest: false
});
const osType = distribution.getPlatform();
if (osType === 'windows' && distroArch == 'aarch64') {
return; // skip, aarch64 is not available for Windows
}
const archiveType = getDownloadArchiveExtension();
const result = await distro['findPackageForDownload'](version);
const expectedUrl = `https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_${osType}-${distroArch}_bin.${archiveType}`;
expect(result.url).toBe(expectedUrl);
}
);
it('should throw an error', async () => {
await expect(distribution['findPackageForDownload']('8')).rejects.toThrow(
/GraalVM is only supported for JDK 17 and later/
);
await expect(distribution['findPackageForDownload']('11')).rejects.toThrow(
/GraalVM is only supported for JDK 17 and later/
);
await expect(distribution['findPackageForDownload']('18')).rejects.toThrow(
/Could not find GraalVM for SemVer */
);
const unavailableEADistro = new GraalVMDistribution({
version: '17-ea',
architecture: '', // to get default value
packageType: 'jdk',
checkLatest: false
});
await expect(
unavailableEADistro['findPackageForDownload']('17')
).rejects.toThrow(
/No GraalVM EA build found\. Are you sure java-version: '17-ea' is correct\?/
);
});
});

View File

@ -0,0 +1,294 @@
import {HttpClient} from '@actions/http-client';
import {SapMachineDistribution} from '../../src/distributions/sapmachine/installer';
import * as utils from '../../src/util';
import manifestData from '../data/sapmachine.json';
describe('getAvailableVersions', () => {
let spyHttpClient: jest.SpyInstance;
let spyUtilGetDownloadArchiveExtension: jest.SpyInstance;
beforeEach(() => {
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
spyHttpClient.mockReturnValue({
statusCode: 200,
headers: {},
result: manifestData
});
spyUtilGetDownloadArchiveExtension = jest.spyOn(
utils,
'getDownloadArchiveExtension'
);
spyUtilGetDownloadArchiveExtension.mockReturnValue('tar.gz');
});
afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
jest.restoreAllMocks();
});
const mockPlatform = (
distribution: SapMachineDistribution,
platform: string
) => {
distribution['getPlatformOption'] = () => platform;
const mockedExtension = platform == 'windows' ? 'zip' : 'tar.gz';
spyUtilGetDownloadArchiveExtension.mockReturnValue(mockedExtension);
};
describe('shouldFallbackToBackupUrl', () => {
it('should return correct release when the primary URL is not available', async () => {
spyHttpClient.mockReturnValueOnce({
statusCode: 404,
headers: {},
result: ''
});
spyHttpClient.mockReturnValueOnce({
statusCode: 200,
headers: {},
result: manifestData
});
const version = '17';
const distribution = new SapMachineDistribution({
version: version,
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
});
mockPlatform(distribution, 'linux');
const availableVersion = await distribution['findPackageForDownload'](
version
);
expect(availableVersion).not.toBeNull();
expect(availableVersion.url).toBe(
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jdk-17.0.10_linux-x64_bin.tar.gz'
);
});
});
describe('getAvailableVersions', () => {
it.each([
['11', 'x64', 'linux', 71],
['11', 'aarch64', 'linux', 54],
['17', 'riscv', 'linux', 0],
['16.0.1', 'x64', 'linux', 71],
['23-ea', 'x64', 'linux', 798],
['23-ea', 'aarch64', 'windows', 0],
['23-ea', 'x64', 'windows', 750]
])(
'should get right number of available versions from JSON',
async (
jdkVersion: string,
arch: string,
platform: string,
len: number
) => {
const distribution = new SapMachineDistribution({
version: jdkVersion,
architecture: arch,
packageType: 'jdk',
checkLatest: false
});
mockPlatform(distribution, platform);
const availableVersions = await distribution['getAvailableVersions']();
expect(availableVersions).not.toBeNull();
expect(availableVersions.length).toBe(len);
}
);
});
describe('findPackageForDownload', () => {
it.each([
[
'11',
'linux',
'x64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-11.0.22/sapmachine-jdk-11.0.22_linux-x64_bin.tar.gz'
],
[
'11',
'linux',
'aarch64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-11.0.22/sapmachine-jdk-11.0.22_linux-aarch64_bin.tar.gz'
],
[
'11',
'windows',
'x64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-11.0.22/sapmachine-jdk-11.0.22_windows-x64_bin.zip'
],
[
'11.0.17',
'linux',
'x64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-11.0.17/sapmachine-jdk-11.0.17_linux-x64_bin.tar.gz'
],
[
'17',
'linux',
'x64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jdk-17.0.10_linux-x64_bin.tar.gz'
],
[
'17',
'linux',
'aarch64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jdk-17.0.10_linux-aarch64_bin.tar.gz'
],
[
'17',
'windows',
'x64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jdk-17.0.10_windows-x64_bin.zip'
],
[
'17.0.4',
'linux',
'x64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.4.1/sapmachine-jdk-17.0.4.1_linux-x64_bin.tar.gz'
],
[
'17',
'linux',
'x64',
'jre',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jre-17.0.10_linux-x64_bin.tar.gz'
],
[
'17',
'linux',
'aarch64',
'jre',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jre-17.0.10_linux-aarch64_bin.tar.gz'
],
[
'17',
'windows',
'x64',
'jre',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jre-17.0.10_windows-x64_bin.zip'
],
[
'17.0.4',
'linux',
'x64',
'jre',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.4.1/sapmachine-jre-17.0.4.1_linux-x64_bin.tar.gz'
],
[
'23-ea',
'linux',
'x64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-23%2B15/sapmachine-jdk-23-ea.15_linux-x64_bin.tar.gz',
'23'
],
[
'21.0.2+2-ea',
'linux',
'x64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-21.0.2%2B2/sapmachine-jdk-21.0.2-ea.2_linux-x64_bin.tar.gz',
'21.0.2+2'
],
[
'17',
'linux-musl',
'x64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jdk-17.0.10_linux-x64-musl_bin.tar.gz'
]
])(
'should return proper link according to the specified java-version, platform and arch',
async (
version: string,
platform: string,
arch: string,
packageType: string,
expectedLink: string,
normalizedVersion: string = version
) => {
const distribution = new SapMachineDistribution({
version: version,
architecture: arch,
packageType: packageType,
checkLatest: false
});
mockPlatform(distribution, platform);
const availableVersion = await distribution['findPackageForDownload'](
normalizedVersion
);
expect(availableVersion).not.toBeNull();
expect(availableVersion.url).toBe(expectedLink);
}
);
it.each([
['8', 'linux', 'x64'],
['8', 'macos', 'aarch64'],
['23', 'macos', 'aarch64'],
['17', 'linux', 'riscv'],
['23', 'linux', 'x64'],
['25-ea', 'linux', 'x64', '25'],
['8-ea', 'linux', 'x64', '8'],
['21.0.3+7', 'linux', 'x64', '21.0.3+7'],
['21.0.3+8-ea', 'linux', 'x64', '21.0.3+8'],
['17', 'linux-muse', 'aarch64']
])(
'should throw when required version of JDK can not be found in the JSON',
async (
version: string,
platform: string,
arch: string,
normalizedVersion: string = version
) => {
const distribution = new SapMachineDistribution({
version: version,
architecture: arch,
packageType: 'jdk',
checkLatest: false
});
mockPlatform(distribution, platform);
await expect(
distribution['findPackageForDownload'](normalizedVersion)
).rejects.toThrow(
`Couldn't find any satisfied version for the specified java-version: "${normalizedVersion}" and architecture: "${arch}".`
);
}
);
it('should throw when required package type is not supported', async () => {
const jdkVersion = '17';
const arch = 'x64';
const platform = 'linux';
const distribution = new SapMachineDistribution({
version: jdkVersion,
architecture: arch,
packageType: 'jdk+fx',
checkLatest: false
});
mockPlatform(distribution, platform);
await expect(
distribution['findPackageForDownload'](jdkVersion)
).rejects.toThrow(
'SapMachine provides only the `jdk` and `jre` package type'
);
});
});
});

View File

@ -43,9 +43,21 @@ inputs:
description: 'Path to where the settings.xml file will be written. Default is ~/.m2.' description: 'Path to where the settings.xml file will be written. Default is ~/.m2.'
required: false required: false
overwrite-settings: overwrite-settings:
description: 'Overwrite the settings.xml file if it exists. Default is "true".' description: 'Overwrite the settings.xml file if it exists. Default is "!update-toolchains-only". If explcitly set "true", it will update settings.xml regardless of "update-toolchains-only"'
required: false required: false
default: true # DO NOT set a default here! The default will be propagated from input 'update-toolchains-only'!
update-toolchains-only:
description: 'Update toolchains.xml only. Default is "false". No update of settings.xml, no update of JAVA_HOME, no adding to PATH by default - unless "overwrite-settings", "update-env-javahome" or "add-to-env-path" are not explicitly set "true"'
required: false
default: false
update-env-javahome:
description: 'Update the JAVA_HOME environment variable. Default is "!update-toolchains-only"'
required: false
# DO NOT set a default here! The default will be propagated from input 'update-toolchains-only'!
add-to-env-path:
description: 'Add "<JDK home>/bin" to the PATH environment variable. Default is "!update-toolchains-only"'
required: false
# DO NOT set a default here! The default will be propagated from input 'update-toolchains-only'!
gpg-private-key: gpg-private-key:
description: 'GPG private key to import. Default is empty string.' description: 'GPG private key to import. Default is empty string.'
required: false required: false

View File

@ -88116,7 +88116,7 @@ function computeCacheKey(packageManager, cacheDependencyPath) {
if (!fileHash) { if (!fileHash) {
throw new Error(`No file in ${process.cwd()} matched to [${pattern}], make sure you have checked out the target repository`); throw new Error(`No file in ${process.cwd()} matched to [${pattern}], make sure you have checked out the target repository`);
} }
return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${packageManager.id}-${fileHash}`; return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${packageManager.id}-${fileHash}`;
}); });
} }
/** /**
@ -88311,7 +88311,7 @@ else {
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_ADD_TO_PATH = exports.INPUT_UPDATE_JAVA_HOME = exports.INPUT_UPDATE_TOOLCHAINS_ONLY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0;
exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home';
exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION = 'java-version';
exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file';
@ -88325,6 +88325,9 @@ exports.INPUT_SERVER_USERNAME = 'server-username';
exports.INPUT_SERVER_PASSWORD = 'server-password'; exports.INPUT_SERVER_PASSWORD = 'server-password';
exports.INPUT_SETTINGS_PATH = 'settings-path'; exports.INPUT_SETTINGS_PATH = 'settings-path';
exports.INPUT_OVERWRITE_SETTINGS = 'overwrite-settings'; exports.INPUT_OVERWRITE_SETTINGS = 'overwrite-settings';
exports.INPUT_UPDATE_TOOLCHAINS_ONLY = 'update-toolchains-only';
exports.INPUT_UPDATE_JAVA_HOME = 'update-env-javahome';
exports.INPUT_ADD_TO_PATH = 'add-to-env-path';
exports.INPUT_GPG_PRIVATE_KEY = 'gpg-private-key'; exports.INPUT_GPG_PRIVATE_KEY = 'gpg-private-key';
exports.INPUT_GPG_PASSPHRASE = 'gpg-passphrase'; exports.INPUT_GPG_PASSPHRASE = 'gpg-passphrase';
exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = undefined; exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = undefined;

9052
dist/setup/index.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,8 @@
- [Amazon Corretto](#Amazon-Corretto) - [Amazon Corretto](#Amazon-Corretto)
- [Oracle](#Oracle) - [Oracle](#Oracle)
- [Alibaba Dragonwell](#Alibaba-Dragonwell) - [Alibaba Dragonwell](#Alibaba-Dragonwell)
- [SapMachine](#SapMachine)
- [GraalVM](#GraalVM)
- [Installing custom Java package type](#Installing-custom-Java-package-type) - [Installing custom Java package type](#Installing-custom-Java-package-type)
- [Installing custom Java architecture](#Installing-custom-Java-architecture) - [Installing custom Java architecture](#Installing-custom-Java-architecture)
- [Installing custom Java distribution from local file](#Installing-Java-from-local-file) - [Installing custom Java distribution from local file](#Installing-Java-from-local-file)
@ -142,6 +144,33 @@ steps:
- run: java -cp java HelloWorldApp - run: java -cp java HelloWorldApp
``` ```
### SapMachine
**NOTE:** An OpenJDK release maintained and supported by SAP
```yaml
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'sapmachine'
java-version: '21'
- run: java -cp java HelloWorldApp
```
### GraalVM
**NOTE:** Oracle GraalVM is only available for JDK 17 and later.
```yaml
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'graalvm'
java-version: '21'
- run: |
java -cp java HelloWorldApp
native-image -cp java HelloWorldApp
```
## Installing custom Java package type ## Installing custom Java package type
```yaml ```yaml
steps: steps:

View File

@ -10,17 +10,13 @@ import * as constants from './constants';
import * as gpg from './gpg'; import * as gpg from './gpg';
import {getBooleanInput} from './util'; import {getBooleanInput} from './util';
export async function configureAuthentication() { export async function configureAuthentication(overwriteSettings: boolean) {
const id = core.getInput(constants.INPUT_SERVER_ID); const id = core.getInput(constants.INPUT_SERVER_ID);
const username = core.getInput(constants.INPUT_SERVER_USERNAME); const username = core.getInput(constants.INPUT_SERVER_USERNAME);
const password = core.getInput(constants.INPUT_SERVER_PASSWORD); const password = core.getInput(constants.INPUT_SERVER_PASSWORD);
const settingsDirectory = const settingsDirectory =
core.getInput(constants.INPUT_SETTINGS_PATH) || core.getInput(constants.INPUT_SETTINGS_PATH) ||
path.join(os.homedir(), constants.M2_DIR); path.join(os.homedir(), constants.M2_DIR);
const overwriteSettings = getBooleanInput(
constants.INPUT_OVERWRITE_SETTINGS,
true
);
const gpgPrivateKey = const gpgPrivateKey =
core.getInput(constants.INPUT_GPG_PRIVATE_KEY) || core.getInput(constants.INPUT_GPG_PRIVATE_KEY) ||
constants.INPUT_DEFAULT_GPG_PRIVATE_KEY; constants.INPUT_DEFAULT_GPG_PRIVATE_KEY;

View File

@ -98,7 +98,7 @@ async function computeCacheKey(
`No file in ${process.cwd()} matched to [${pattern}], make sure you have checked out the target repository` `No file in ${process.cwd()} matched to [${pattern}], make sure you have checked out the target repository`
); );
} }
return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${packageManager.id}-${fileHash}`; return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${packageManager.id}-${fileHash}`;
} }
/** /**

View File

@ -11,6 +11,9 @@ export const INPUT_SERVER_USERNAME = 'server-username';
export const INPUT_SERVER_PASSWORD = 'server-password'; export const INPUT_SERVER_PASSWORD = 'server-password';
export const INPUT_SETTINGS_PATH = 'settings-path'; export const INPUT_SETTINGS_PATH = 'settings-path';
export const INPUT_OVERWRITE_SETTINGS = 'overwrite-settings'; export const INPUT_OVERWRITE_SETTINGS = 'overwrite-settings';
export const INPUT_UPDATE_TOOLCHAINS_ONLY = 'update-toolchains-only';
export const INPUT_UPDATE_JAVA_HOME = 'update-env-javahome';
export const INPUT_ADD_TO_PATH = 'add-to-env-path';
export const INPUT_GPG_PRIVATE_KEY = 'gpg-private-key'; export const INPUT_GPG_PRIVATE_KEY = 'gpg-private-key';
export const INPUT_GPG_PASSPHRASE = 'gpg-passphrase'; export const INPUT_GPG_PASSPHRASE = 'gpg-passphrase';

View File

@ -10,7 +10,11 @@ import {
JavaInstallerOptions, JavaInstallerOptions,
JavaInstallerResults JavaInstallerResults
} from './base-models'; } from './base-models';
import {MACOS_JAVA_CONTENT_POSTFIX} from '../constants'; import {
MACOS_JAVA_CONTENT_POSTFIX,
INPUT_UPDATE_JAVA_HOME,
INPUT_ADD_TO_PATH
} from '../constants';
import os from 'os'; import os from 'os';
export abstract class JavaBase { export abstract class JavaBase {
@ -20,6 +24,8 @@ export abstract class JavaBase {
protected packageType: string; protected packageType: string;
protected stable: boolean; protected stable: boolean;
protected checkLatest: boolean; protected checkLatest: boolean;
protected updateEnvJavaHome: boolean;
protected addToEnvPath: boolean;
constructor( constructor(
protected distribution: string, protected distribution: string,
@ -36,6 +42,8 @@ export abstract class JavaBase {
this.architecture = installerOptions.architecture || os.arch(); this.architecture = installerOptions.architecture || os.arch();
this.packageType = installerOptions.packageType; this.packageType = installerOptions.packageType;
this.checkLatest = installerOptions.checkLatest; this.checkLatest = installerOptions.checkLatest;
this.updateEnvJavaHome = installerOptions.updateEnvJavaHome;
this.addToEnvPath = installerOptions.addToEnvPath;
} }
protected abstract downloadTool( protected abstract downloadTool(
@ -163,8 +171,18 @@ export abstract class JavaBase {
protected setJavaDefault(version: string, toolPath: string) { protected setJavaDefault(version: string, toolPath: string) {
const majorVersion = version.split('.')[0]; const majorVersion = version.split('.')[0];
core.exportVariable('JAVA_HOME', toolPath); if (this.updateEnvJavaHome) {
core.addPath(path.join(toolPath, 'bin')); core.exportVariable('JAVA_HOME', toolPath);
} else {
core.info(
`Skip updating env.JAVA_HOME according to ${INPUT_UPDATE_JAVA_HOME}`
);
}
if (this.addToEnvPath) {
core.addPath(path.join(toolPath, 'bin'));
} else {
core.info(`Skip adding to env.PATH according to ${INPUT_ADD_TO_PATH}`);
}
core.setOutput('distribution', this.distribution); core.setOutput('distribution', this.distribution);
core.setOutput('path', toolPath); core.setOutput('path', toolPath);
core.setOutput('version', version); core.setOutput('version', version);

View File

@ -3,6 +3,8 @@ export interface JavaInstallerOptions {
architecture: string; architecture: string;
packageType: string; packageType: string;
checkLatest: boolean; checkLatest: boolean;
updateEnvJavaHome: boolean;
addToEnvPath: boolean;
} }
export interface JavaInstallerResults { export interface JavaInstallerResults {

View File

@ -10,6 +10,8 @@ import {SemeruDistribution} from './semeru/installer';
import {CorrettoDistribution} from './corretto/installer'; import {CorrettoDistribution} from './corretto/installer';
import {OracleDistribution} from './oracle/installer'; import {OracleDistribution} from './oracle/installer';
import {DragonwellDistribution} from './dragonwell/installer'; import {DragonwellDistribution} from './dragonwell/installer';
import {SapMachineDistribution} from './sapmachine/installer';
import {GraalVMDistribution} from './graalvm/installer';
enum JavaDistribution { enum JavaDistribution {
Adopt = 'adopt', Adopt = 'adopt',
@ -23,7 +25,9 @@ enum JavaDistribution {
Semeru = 'semeru', Semeru = 'semeru',
Corretto = 'corretto', Corretto = 'corretto',
Oracle = 'oracle', Oracle = 'oracle',
Dragonwell = 'dragonwell' Dragonwell = 'dragonwell',
SapMachine = 'sapmachine',
GraalVM = 'graalvm'
} }
export function getJavaDistribution( export function getJavaDistribution(
@ -64,6 +68,10 @@ export function getJavaDistribution(
return new OracleDistribution(installerOptions); return new OracleDistribution(installerOptions);
case JavaDistribution.Dragonwell: case JavaDistribution.Dragonwell:
return new DragonwellDistribution(installerOptions); return new DragonwellDistribution(installerOptions);
case JavaDistribution.SapMachine:
return new SapMachineDistribution(installerOptions);
case JavaDistribution.GraalVM:
return new GraalVMDistribution(installerOptions);
default: default:
return null; return null;
} }

View File

@ -0,0 +1,173 @@
import * as core from '@actions/core';
import * as tc from '@actions/tool-cache';
import fs from 'fs';
import path from 'path';
import {JavaBase} from '../base-installer';
import {
JavaDownloadRelease,
JavaInstallerOptions,
JavaInstallerResults
} from '../base-models';
import {
extractJdkFile,
getDownloadArchiveExtension,
getGitHubHttpHeaders
} from '../../util';
import {HttpCodes} from '@actions/http-client';
import {GraalVMEAVersion} from './models';
const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm';
const IS_WINDOWS = process.platform === 'win32';
const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform;
export class GraalVMDistribution extends JavaBase {
constructor(installerOptions: JavaInstallerOptions) {
super('GraalVM', installerOptions);
}
protected async downloadTool(
javaRelease: JavaDownloadRelease
): Promise<JavaInstallerResults> {
core.info(
`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
);
const javaArchivePath = await tc.downloadTool(javaRelease.url);
core.info(`Extracting Java archive...`);
const extension = getDownloadArchiveExtension();
const extractedJavaPath = await extractJdkFile(javaArchivePath, extension);
const archiveName = fs.readdirSync(extractedJavaPath)[0];
const archivePath = path.join(extractedJavaPath, archiveName);
const version = this.getToolcacheVersionName(javaRelease.version);
const javaPath = await tc.cacheDir(
archivePath,
this.toolcacheFolderName,
version,
this.architecture
);
return {version: javaRelease.version, path: javaPath};
}
protected async findPackageForDownload(
range: string
): Promise<JavaDownloadRelease> {
const arch = this.distributionArchitecture();
if (arch !== 'x64' && arch !== 'aarch64') {
throw new Error(`Unsupported architecture: ${this.architecture}`);
}
if (!this.stable) {
return this.findEABuildDownloadUrl(`${range}-ea`);
}
if (this.packageType !== 'jdk') {
throw new Error('GraalVM provides only the `jdk` package type');
}
const platform = this.getPlatform();
const extension = getDownloadArchiveExtension();
let major;
let fileUrl;
if (range.includes('.')) {
major = range.split('.')[0];
fileUrl = `${GRAALVM_DL_BASE}/${major}/archive/graalvm-jdk-${range}_${platform}-${arch}_bin.${extension}`;
} else {
major = range;
fileUrl = `${GRAALVM_DL_BASE}/${range}/latest/graalvm-jdk-${range}_${platform}-${arch}_bin.${extension}`;
}
if (parseInt(major) < 17) {
throw new Error('GraalVM is only supported for JDK 17 and later');
}
const response = await this.http.head(fileUrl);
if (response.message.statusCode === HttpCodes.NotFound) {
throw new Error(`Could not find GraalVM for SemVer ${range}`);
}
if (response.message.statusCode !== HttpCodes.OK) {
throw new Error(
`Http request for GraalVM failed with status code: ${response.message.statusCode}`
);
}
return {url: fileUrl, version: range};
}
private async findEABuildDownloadUrl(
javaEaVersion: string
): Promise<JavaDownloadRelease> {
const versions = await this.fetchEAJson(javaEaVersion);
const latestVersion = versions.find(v => v.latest);
if (!latestVersion) {
throw new Error(`Unable to find latest version for '${javaEaVersion}'`);
}
const arch = this.distributionArchitecture();
const file = latestVersion.files.find(
f => f.arch === arch && f.platform === GRAALVM_PLATFORM
);
if (!file || !file.filename.startsWith('graalvm-jdk-')) {
throw new Error(`Unable to find file metadata for '${javaEaVersion}'`);
}
return {
url: `${latestVersion.download_base_url}${file.filename}`,
version: latestVersion.version
};
}
private async fetchEAJson(
javaEaVersion: string
): Promise<GraalVMEAVersion[]> {
const owner = 'graalvm';
const repository = 'oracle-graalvm-ea-builds';
const branch = 'main';
const filePath = `versions/${javaEaVersion}.json`;
const url = `https://api.github.com/repos/${owner}/${repository}/contents/${filePath}?ref=${branch}`;
const headers = getGitHubHttpHeaders();
core.debug(
`Trying to fetch available version info for GraalVM EA builds from '${url}'`
);
let fetchedJson;
try {
fetchedJson = (await this.http.getJson<GraalVMEAVersion[]>(url, headers))
.result;
} catch (err) {
throw Error(
`Fetching version info for GraalVM EA builds from '${url}' failed with the error: ${
(err as Error).message
}`
);
}
if (fetchedJson === null) {
throw Error(
`No GraalVM EA build found. Are you sure java-version: '${javaEaVersion}' is correct?`
);
}
return fetchedJson;
}
public getPlatform(platform: NodeJS.Platform = process.platform): OsVersions {
switch (platform) {
case 'darwin':
return 'macos';
case 'win32':
return 'windows';
case 'linux':
return 'linux';
default:
throw new Error(
`Platform '${platform}' is not supported. Supported platforms: 'linux', 'macos', 'windows'`
);
}
}
}

View File

@ -0,0 +1,14 @@
export type OsVersions = 'linux' | 'macos' | 'windows';
export interface GraalVMEAFile {
filename: string;
arch: 'aarch64' | 'x64';
platform: 'darwin' | 'linux' | 'windows';
}
export interface GraalVMEAVersion {
version: string;
latest?: boolean;
download_base_url: string;
files: GraalVMEAFile[];
}

View File

@ -0,0 +1,268 @@
import * as core from '@actions/core';
import * as tc from '@actions/tool-cache';
import semver from 'semver';
import fs from 'fs';
import {OutgoingHttpHeaders} from 'http';
import path from 'path';
import {
convertVersionToSemver,
extractJdkFile,
getDownloadArchiveExtension,
getGitHubHttpHeaders,
isVersionSatisfies
} from '../../util';
import {JavaBase} from '../base-installer';
import {
JavaDownloadRelease,
JavaInstallerOptions,
JavaInstallerResults
} from '../base-models';
import {ISapMachineAllVersions, ISapMachineVersions} from './models';
export class SapMachineDistribution extends JavaBase {
constructor(installerOptions: JavaInstallerOptions) {
super('SapMachine', installerOptions);
}
protected async findPackageForDownload(
version: string
): Promise<JavaDownloadRelease> {
core.debug(`Only stable versions: ${this.stable}`);
if (!['jdk', 'jre'].includes(this.packageType)) {
throw new Error(
'SapMachine provides only the `jdk` and `jre` package type'
);
}
const availableVersions = await this.getAvailableVersions();
const matchedVersions = availableVersions
.filter(item => {
return isVersionSatisfies(version, item.version);
})
.map(item => {
return {
version: item.version,
url: item.downloadLink
} as JavaDownloadRelease;
});
if (!matchedVersions.length) {
throw new Error(
`Couldn't find any satisfied version for the specified java-version: "${version}" and architecture: "${this.architecture}".`
);
}
const resolvedVersion = matchedVersions[0];
return resolvedVersion;
}
private async getAvailableVersions(): Promise<ISapMachineVersions[]> {
const platform = this.getPlatformOption();
const arch = this.distributionArchitecture();
let fetchedReleasesJson = await this.fetchReleasesFromUrl(
'https://sap.github.io/SapMachine/assets/data/sapmachine-releases-all.json'
);
if (!fetchedReleasesJson) {
fetchedReleasesJson = await this.fetchReleasesFromUrl(
'https://api.github.com/repos/SAP/SapMachine/contents/assets/data/sapmachine-releases-all.json?ref=gh-pages',
getGitHubHttpHeaders()
);
}
if (!fetchedReleasesJson) {
throw new Error(
`Couldn't fetch SapMachine versions information from both primary and backup urls`
);
}
core.debug(
'Successfully fetched information about available SapMachine versions'
);
const availableVersions = this.parseVersions(
platform,
arch,
fetchedReleasesJson
);
if (core.isDebug()) {
core.startGroup('Print information about available versions');
core.debug(availableVersions.map(item => item.version).join(', '));
core.endGroup();
}
return availableVersions;
}
protected async downloadTool(
javaRelease: JavaDownloadRelease
): Promise<JavaInstallerResults> {
core.info(
`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
);
const javaArchivePath = await tc.downloadTool(javaRelease.url);
core.info(`Extracting Java archive...`);
const extractedJavaPath = await extractJdkFile(
javaArchivePath,
getDownloadArchiveExtension()
);
const archiveName = fs.readdirSync(extractedJavaPath)[0];
const archivePath = path.join(extractedJavaPath, archiveName);
const version = this.getToolcacheVersionName(javaRelease.version);
const javaPath = await tc.cacheDir(
archivePath,
this.toolcacheFolderName,
version,
this.architecture
);
return {version: javaRelease.version, path: javaPath};
}
private parseVersions(
platform: string,
arch: string,
versions: ISapMachineAllVersions
): ISapMachineVersions[] {
const eligibleVersions: ISapMachineVersions[] = [];
for (const [, majorVersionMap] of Object.entries(versions)) {
for (const [, jdkVersionMap] of Object.entries(majorVersionMap.updates)) {
for (const [buildVersion, buildVersionMap] of Object.entries(
jdkVersionMap
)) {
let buildVersionWithoutPrefix = buildVersion.replace(
'sapmachine-',
''
);
if (!buildVersionWithoutPrefix.includes('.')) {
// replace major version with major.minor.patch and keep the remaining build identifier after the + as is with regex
buildVersionWithoutPrefix = buildVersionWithoutPrefix.replace(
/(\d+)(\+.*)?/,
'$1.0.0$2'
);
}
// replace + with . to convert to semver format if we have more than 3 version digits
if (buildVersionWithoutPrefix.split('.').length > 3) {
buildVersionWithoutPrefix = buildVersionWithoutPrefix.replace(
'+',
'.'
);
}
buildVersionWithoutPrefix = convertVersionToSemver(
buildVersionWithoutPrefix
);
// ignore invalid version
if (!semver.valid(buildVersionWithoutPrefix)) {
core.debug(`Invalid version: ${buildVersionWithoutPrefix}`);
continue;
}
// skip earlyAccessVersions if stable version requested
if (this.stable && buildVersionMap.ea === 'true') {
continue;
}
for (const [edition, editionAssets] of Object.entries(
buildVersionMap.assets
)) {
if (this.packageType !== edition) {
continue;
}
for (const [archAndPlatForm, archAssets] of Object.entries(
editionAssets
)) {
let expectedArchAndPlatform = `${platform}-${arch}`;
if (platform === 'linux-musl') {
expectedArchAndPlatform = `linux-${arch}-musl`;
}
if (archAndPlatForm !== expectedArchAndPlatform) {
continue;
}
for (const [contentType, contentTypeAssets] of Object.entries(
archAssets
)) {
// skip if not tar.gz and zip files
if (contentType !== 'tar.gz' && contentType !== 'zip') {
continue;
}
eligibleVersions.push({
os: platform,
architecture: arch,
version: buildVersionWithoutPrefix,
checksum: contentTypeAssets.checksum,
downloadLink: contentTypeAssets.url,
packageType: edition
});
}
}
}
}
}
}
const sortedVersions = this.sortParsedVersions(eligibleVersions);
return sortedVersions;
}
// Sorts versions in descending order as by default data in JSON isn't sorted
private sortParsedVersions(
eligibleVersions: ISapMachineVersions[]
): ISapMachineVersions[] {
const sortedVersions = eligibleVersions.sort((versionObj1, versionObj2) => {
const version1 = versionObj1.version;
const version2 = versionObj2.version;
return semver.compareBuild(version1, version2);
});
return sortedVersions.reverse();
}
private getPlatformOption(): string {
switch (process.platform) {
case 'win32':
return 'windows';
case 'darwin':
return 'macos';
case 'linux':
// figure out if alpine/musl
if (fs.existsSync('/etc/alpine-release')) {
return 'linux-musl';
}
return 'linux';
default:
return process.platform;
}
}
private async fetchReleasesFromUrl(
url: string,
headers: OutgoingHttpHeaders = {}
): Promise<ISapMachineAllVersions | null> {
try {
core.debug(
`Trying to fetch available SapMachine versions info from the primary url: ${url}`
);
const releases = (
await this.http.getJson<ISapMachineAllVersions>(url, headers)
).result;
return releases;
} catch (err) {
core.debug(
`Fetching SapMachine versions info from the link: ${url} ended up with the error: ${
(err as Error).message
}`
);
return null;
}
}
}

View File

@ -0,0 +1,33 @@
export interface ISapMachineAllVersions {
[major: string]: {
lts: string;
updates: {
[full_version: string]: {
[sapmachineBuild: string]: {
release_url: string;
ea: string;
assets: {
[packageType: string]: {
[arch: string]: {
[content_type: string]: {
name: string;
checksum: string;
url: string;
};
};
};
};
};
};
};
};
}
export interface ISapMachineVersions {
os: string;
architecture: string;
version: string;
checksum: string;
downloadLink: string;
packageType: string;
}

View File

@ -13,6 +13,19 @@ import * as path from 'path';
import {getJavaDistribution} from './distributions/distribution-factory'; import {getJavaDistribution} from './distributions/distribution-factory';
import {JavaInstallerOptions} from './distributions/base-models'; import {JavaInstallerOptions} from './distributions/base-models';
interface IInstallerInputsOptions {
architecture: string;
packageType: string;
checkLatest: boolean;
distributionName: string;
jdkFile: string;
toolchainIds: Array<string>;
updateToolchainsOnly: boolean;
overwriteSettings: boolean;
updateEnvJavaHome: boolean;
addToEnvPath: boolean;
}
async function run() { async function run() {
try { try {
const versions = core.getMultilineInput(constants.INPUT_JAVA_VERSION); const versions = core.getMultilineInput(constants.INPUT_JAVA_VERSION);
@ -28,6 +41,23 @@ async function run() {
constants.INPUT_CACHE_DEPENDENCY_PATH constants.INPUT_CACHE_DEPENDENCY_PATH
); );
const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, false); const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, false);
const updateToolchainsOnly = getBooleanInput(
constants.INPUT_UPDATE_TOOLCHAINS_ONLY,
false
);
const overwriteSettings = getBooleanInput(
constants.INPUT_OVERWRITE_SETTINGS,
!updateToolchainsOnly
);
const updateEnvJavaHome = getBooleanInput(
constants.INPUT_UPDATE_JAVA_HOME,
!updateToolchainsOnly
);
const addToEnvPath = getBooleanInput(
constants.INPUT_ADD_TO_PATH,
!updateToolchainsOnly
);
let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID); let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID);
core.startGroup('Installed distributions'); core.startGroup('Installed distributions');
@ -40,13 +70,17 @@ async function run() {
throw new Error('java-version or java-version-file input expected'); throw new Error('java-version or java-version-file input expected');
} }
const installerInputsOptions: installerInputsOptions = { const installerInputsOptions: IInstallerInputsOptions = {
architecture, architecture,
packageType, packageType,
checkLatest, checkLatest,
distributionName, distributionName,
jdkFile, jdkFile,
toolchainIds toolchainIds,
updateToolchainsOnly,
overwriteSettings,
updateEnvJavaHome,
addToEnvPath
}; };
if (!versions.length) { if (!versions.length) {
@ -78,7 +112,7 @@ async function run() {
const matchersPath = path.join(__dirname, '..', '..', '.github'); const matchersPath = path.join(__dirname, '..', '..', '.github');
core.info(`##[add-matcher]${path.join(matchersPath, 'java.json')}`); core.info(`##[add-matcher]${path.join(matchersPath, 'java.json')}`);
await auth.configureAuthentication(); await auth.configureAuthentication(overwriteSettings);
if (cache && isCacheFeatureAvailable()) { if (cache && isCacheFeatureAvailable()) {
await restore(cache, cacheDependencyPath); await restore(cache, cacheDependencyPath);
} }
@ -91,7 +125,7 @@ run();
async function installVersion( async function installVersion(
version: string, version: string,
options: installerInputsOptions, options: IInstallerInputsOptions,
toolchainId = 0 toolchainId = 0
) { ) {
const { const {
@ -100,14 +134,20 @@ async function installVersion(
architecture, architecture,
packageType, packageType,
checkLatest, checkLatest,
toolchainIds toolchainIds,
updateToolchainsOnly,
overwriteSettings,
updateEnvJavaHome,
addToEnvPath
} = options; } = options;
const installerOptions: JavaInstallerOptions = { const installerOptions: JavaInstallerOptions = {
version,
architecture, architecture,
packageType, packageType,
checkLatest, checkLatest,
version updateEnvJavaHome,
addToEnvPath
}; };
const distribution = getJavaDistribution( const distribution = getJavaDistribution(
@ -126,6 +166,7 @@ async function installVersion(
version, version,
distributionName, distributionName,
result.path, result.path,
overwriteSettings || updateToolchainsOnly,
toolchainIds[toolchainId] toolchainIds[toolchainId]
); );
@ -136,12 +177,3 @@ async function installVersion(
core.info(` Path: ${result.path}`); core.info(` Path: ${result.path}`);
core.info(''); core.info('');
} }
interface installerInputsOptions {
architecture: string;
packageType: string;
checkLatest: boolean;
distributionName: string;
jdkFile: string;
toolchainIds: Array<string>;
}

View File

@ -19,6 +19,7 @@ export async function configureToolchains(
version: string, version: string,
distributionName: string, distributionName: string,
jdkHome: string, jdkHome: string,
updateToolchains: boolean,
toolchainId?: string toolchainId?: string
) { ) {
const vendor = const vendor =
@ -27,10 +28,6 @@ export async function configureToolchains(
const settingsDirectory = const settingsDirectory =
core.getInput(constants.INPUT_SETTINGS_PATH) || core.getInput(constants.INPUT_SETTINGS_PATH) ||
path.join(os.homedir(), constants.M2_DIR); path.join(os.homedir(), constants.M2_DIR);
const overwriteSettings = getBooleanInput(
constants.INPUT_OVERWRITE_SETTINGS,
true
);
await createToolchainsSettings({ await createToolchainsSettings({
jdkInfo: { jdkInfo: {
@ -40,21 +37,21 @@ export async function configureToolchains(
jdkHome jdkHome
}, },
settingsDirectory, settingsDirectory,
overwriteSettings updateToolchains
}); });
} }
export async function createToolchainsSettings({ export async function createToolchainsSettings({
jdkInfo, jdkInfo,
settingsDirectory, settingsDirectory,
overwriteSettings updateToolchains
}: { }: {
jdkInfo: JdkInfo; jdkInfo: JdkInfo;
settingsDirectory: string; settingsDirectory: string;
overwriteSettings: boolean; updateToolchains: boolean;
}) { }) {
core.info( core.info(
`Creating ${constants.MVN_TOOLCHAINS_FILE} for JDK version ${jdkInfo.version} from ${jdkInfo.vendor}` `Adding a toolchain entry in ${constants.MVN_TOOLCHAINS_FILE} for JDK version ${jdkInfo.version} from ${jdkInfo.vendor}`
); );
// when an alternate m2 location is specified use only that location (no .m2 directory) // when an alternate m2 location is specified use only that location (no .m2 directory)
// otherwise use the home/.m2/ path // otherwise use the home/.m2/ path
@ -72,7 +69,7 @@ export async function createToolchainsSettings({
await writeToolchainsFileToDisk( await writeToolchainsFileToDisk(
settingsDirectory, settingsDirectory,
updatedToolchains, updatedToolchains,
overwriteSettings updateToolchains
); );
} }
@ -147,17 +144,17 @@ async function readExistingToolchainsFile(directory: string) {
async function writeToolchainsFileToDisk( async function writeToolchainsFileToDisk(
directory: string, directory: string,
settings: string, settings: string,
overwriteSettings: boolean updateToolchains: boolean
) { ) {
const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE); const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE);
const settingsExists = fs.existsSync(location); const toolchainsExists = fs.existsSync(location);
if (settingsExists && overwriteSettings) { if (toolchainsExists && updateToolchains) {
core.info(`Overwriting existing file ${location}`); core.info(`Updating existing file ${location}`);
} else if (!settingsExists) { } else if (!toolchainsExists) {
core.info(`Writing to ${location}`); core.info(`Creating file ${location}`);
} else { } else {
core.info( core.info(
`Skipping generation of ${location} because file already exists and overwriting is not enabled` `Skipping update of ${location} since file already exists and updating is not enabled`
); );
return; return;
} }