Compare commits

...

5 Commits

Author SHA1 Message Date
Antonin Delpeuch
81c3adab6f
Merge e518b2fd8d into 2dfa2011c5 2024-09-10 14:15:45 -05: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
Antonin Delpeuch
e518b2fd8d
Remove unnecessary comment 2024-03-13 09:50:09 +01:00
Antonin Delpeuch
58d36cecb0
Update recommended configuration for GPG signing
This attempts to document the new recommended configuration to sign artifacts with the maven-gpg-plugin as part of the deploy process.

It imitates this PR from the maintainer of the maven-gpg-plugin:
https://github.com/xerial/sqlite-jdbc/pull/1082/files

Notes that this requires the maven-gpg-plugin version 3.2.0 or above, not sure if this is worth adding to the documentation as I expect this guide will mostly be followed by people setting up a new project (hopefully using the latest version of the plugin by default).

@cstamas I hope I got it right, feel free to suggest any improvements
2024-03-13 09:45:33 +01:00
9 changed files with 88111 additions and 30 deletions

View File

@ -30,7 +30,8 @@ jobs:
'microsoft',
'semeru',
'corretto',
'dragonwell'
'dragonwell',
'sapmachine'
] # internally 'adopt-hotspot' is the same as 'adopt'
version: ['21', '11', '17']
exclude:
@ -85,6 +86,9 @@ jobs:
- distribution: dragonwell
os: ubuntu-latest
version: '11.0.13+9'
- distribution: sapmachine
os: ubuntu-latest
version: '17.0.7'
steps:
- name: Checkout
uses: actions/checkout@v4
@ -106,7 +110,8 @@ jobs:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
distribution: ['temurin', 'zulu', 'liberica', 'dragonwell']
distribution:
['temurin', 'zulu', 'liberica', 'dragonwell', 'sapmachine']
exclude:
- distribution: dragonwell
os: macos-latest
@ -132,7 +137,8 @@ jobs:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
distribution: ['temurin', 'zulu', 'liberica', 'dragonwell']
distribution:
['temurin', 'zulu', 'liberica', 'dragonwell', 'sapmachine']
exclude:
- distribution: dragonwell
os: macos-latest
@ -208,6 +214,28 @@ jobs:
run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}"
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:
name: ${{ matrix.distribution }} ${{ matrix.version }} (${{ matrix.java-package }}-x64) - ${{ matrix.os }}
needs: setup-java-major-minor-versions
@ -216,7 +244,7 @@ jobs:
fail-fast: false
matrix:
os: [macos-13, windows-latest, ubuntu-latest]
distribution: ['temurin', 'zulu', 'liberica', 'semeru']
distribution: ['temurin', 'zulu', 'liberica', 'semeru', 'sapmachine']
java-package: ['jre']
version: ['17.0']
include:

View File

@ -108,6 +108,7 @@ 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) |
| `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/)
| `sapmachine` | SAP SapMachine JDK/JRE | [Link](https://sapmachine.io/) | [Link](https://github.com/SAP/SapMachine/blob/sapmachine/LICENSE)
**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.
@ -257,6 +258,7 @@ In the example above multiple JDKs are installed for the same job. The result af
- [Amazon Corretto](docs/advanced-usage.md#Amazon-Corretto)
- [Oracle](docs/advanced-usage.md#Oracle)
- [Alibaba Dragonwell](docs/advanced-usage.md#Alibaba-Dragonwell)
- [SapMachine](docs/advanced-usage.md#SapMachine)
- [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 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,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'
);
});
});
});

217
dist/setup/index.js vendored
View File

@ -124049,6 +124049,7 @@ const installer_7 = __nccwpck_require__(41121);
const installer_8 = __nccwpck_require__(34750);
const installer_9 = __nccwpck_require__(64298);
const installer_10 = __nccwpck_require__(16132);
const installer_11 = __nccwpck_require__(52869);
var JavaDistribution;
(function (JavaDistribution) {
JavaDistribution["Adopt"] = "adopt";
@ -124063,6 +124064,7 @@ var JavaDistribution;
JavaDistribution["Corretto"] = "corretto";
JavaDistribution["Oracle"] = "oracle";
JavaDistribution["Dragonwell"] = "dragonwell";
JavaDistribution["SapMachine"] = "sapmachine";
})(JavaDistribution || (JavaDistribution = {}));
function getJavaDistribution(distributionName, installerOptions, jdkFile) {
switch (distributionName) {
@ -124089,6 +124091,8 @@ function getJavaDistribution(distributionName, installerOptions, jdkFile) {
return new installer_9.OracleDistribution(installerOptions);
case JavaDistribution.Dragonwell:
return new installer_10.DragonwellDistribution(installerOptions);
case JavaDistribution.SapMachine:
return new installer_11.SapMachineDistribution(installerOptions);
default:
return null;
}
@ -124874,6 +124878,219 @@ class OracleDistribution extends base_installer_1.JavaBase {
exports.OracleDistribution = OracleDistribution;
/***/ }),
/***/ 52869:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.SapMachineDistribution = void 0;
const core = __importStar(__nccwpck_require__(42186));
const tc = __importStar(__nccwpck_require__(27784));
const semver_1 = __importDefault(__nccwpck_require__(11383));
const fs_1 = __importDefault(__nccwpck_require__(57147));
const path_1 = __importDefault(__nccwpck_require__(71017));
const util_1 = __nccwpck_require__(92629);
const base_installer_1 = __nccwpck_require__(59741);
class SapMachineDistribution extends base_installer_1.JavaBase {
constructor(installerOptions) {
super('SapMachine', installerOptions);
}
findPackageForDownload(version) {
return __awaiter(this, void 0, void 0, function* () {
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 = yield this.getAvailableVersions();
const matchedVersions = availableVersions
.filter(item => {
return (0, util_1.isVersionSatisfies)(version, item.version);
})
.map(item => {
return {
version: item.version,
url: item.downloadLink
};
});
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;
});
}
getAvailableVersions() {
return __awaiter(this, void 0, void 0, function* () {
const platform = this.getPlatformOption();
const arch = this.distributionArchitecture();
let fetchedReleasesJson = yield this.fetchReleasesFromUrl('https://sap.github.io/SapMachine/assets/data/sapmachine-releases-all.json');
if (!fetchedReleasesJson) {
fetchedReleasesJson = yield this.fetchReleasesFromUrl('https://api.github.com/repos/SAP/SapMachine/contents/assets/data/sapmachine-releases-all.json?ref=gh-pages', (0, util_1.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;
});
}
downloadTool(javaRelease) {
return __awaiter(this, void 0, void 0, function* () {
core.info(`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`);
const javaArchivePath = yield tc.downloadTool(javaRelease.url);
core.info(`Extracting Java archive...`);
const extractedJavaPath = yield (0, util_1.extractJdkFile)(javaArchivePath, (0, util_1.getDownloadArchiveExtension)());
const archiveName = fs_1.default.readdirSync(extractedJavaPath)[0];
const archivePath = path_1.default.join(extractedJavaPath, archiveName);
const version = this.getToolcacheVersionName(javaRelease.version);
const javaPath = yield tc.cacheDir(archivePath, this.toolcacheFolderName, version, this.architecture);
return { version: javaRelease.version, path: javaPath };
});
}
parseVersions(platform, arch, versions) {
const eligibleVersions = [];
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 = (0, util_1.convertVersionToSemver)(buildVersionWithoutPrefix);
// ignore invalid version
if (!semver_1.default.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
sortParsedVersions(eligibleVersions) {
const sortedVersions = eligibleVersions.sort((versionObj1, versionObj2) => {
const version1 = versionObj1.version;
const version2 = versionObj2.version;
return semver_1.default.compareBuild(version1, version2);
});
return sortedVersions.reverse();
}
getPlatformOption() {
switch (process.platform) {
case 'win32':
return 'windows';
case 'darwin':
return 'macos';
case 'linux':
// figure out if alpine/musl
if (fs_1.default.existsSync('/etc/alpine-release')) {
return 'linux-musl';
}
return 'linux';
default:
return process.platform;
}
}
fetchReleasesFromUrl(url, headers = {}) {
return __awaiter(this, void 0, void 0, function* () {
try {
core.debug(`Trying to fetch available SapMachine versions info from the primary url: ${url}`);
const releases = (yield this.http.getJson(url, headers)).result;
return releases;
}
catch (err) {
core.debug(`Fetching SapMachine versions info from the link: ${url} ended up with the error: ${err.message}`);
return null;
}
});
}
}
exports.SapMachineDistribution = SapMachineDistribution;
/***/ }),
/***/ 41121:

View File

@ -8,6 +8,7 @@
- [Amazon Corretto](#Amazon-Corretto)
- [Oracle](#Oracle)
- [Alibaba Dragonwell](#Alibaba-Dragonwell)
- [SapMachine](#SapMachine)
- [Installing custom Java package type](#Installing-custom-Java-package-type)
- [Installing custom Java architecture](#Installing-custom-Java-architecture)
- [Installing custom Java distribution from local file](#Installing-Java-from-local-file)
@ -142,6 +143,18 @@ steps:
- 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
```
## Installing custom Java package type
```yaml
steps:
@ -283,14 +296,13 @@ jobs:
server-id: maven # Value of the distributionManagement/repository/id field of the pom.xml
server-username: MAVEN_USERNAME # env variable for username in deploy
server-password: MAVEN_CENTRAL_TOKEN # env variable for token in deploy
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
- name: Publish to Apache Maven Central
run: mvn deploy
run: mvn deploy -Dgpg.signer=bc
env:
MAVEN_USERNAME: maven_username123
MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
MAVEN_GPG_KEY: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
```
@ -326,10 +338,6 @@ The two `settings.xml` files created from the above example look like the follow
<username>${env.MAVEN_USERNAME}</username>
<password>${env.MAVEN_CENTRAL_TOKEN}</password>
</server>
<server>
<id>gpg.passphrase</id>
<passphrase>${env.MAVEN_GPG_PASSPHRASE}</passphrase>
</server>
</servers>
</settings>
```
@ -338,21 +346,6 @@ The two `settings.xml` files created from the above example look like the follow
If you don't want to overwrite the `settings.xml` file, you can set `overwrite-settings: false`
### Extra setup for pom.xml:
The Maven GPG Plugin configuration in the pom.xml file should contain the following structure to avoid possible issues like `Inappropriate ioctl for device` or `gpg: signing failed: No such file or directory`:
```xml
<configuration>
<!-- Prevent gpg from using pinentry programs -->
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
```
GPG 2.1 requires `--pinentry-mode` to be set to `loopback` in order to pick up the `gpg.passphrase` value defined in Maven `settings.xml`.
### GPG
If `gpg-private-key` input is provided, the private key will be written to a file in the runner's temp directory, the private key file will be imported into the GPG keychain, and then the file will be promptly removed before proceeding with the rest of the setup process. A cleanup step will remove the imported private key from the GPG keychain after the job completes regardless of the job status. This ensures that the private key is no longer accessible on self-hosted runners and cannot "leak" between jobs (hosted runners are always clean instances).

View File

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

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;
}