Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ Currently, the following distributions are supported:
| `corretto` | [Amazon Corretto Build of OpenJDK](https://aws.amazon.com/corretto/) | [`corretto` license](https://aws.amazon.com/corretto/faqs/)
| `semeru` | [IBM Semeru Runtime Open Edition](https://developer.ibm.com/languages/java/semeru-runtimes/downloads/) | [`semeru` license](https://openjdk.java.net/legal/gplv2+ce.html) |
| `oracle` | [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) | [`oracle` license](https://java.com/freeuselicense)
| `oracle-openjdk` | [Oracle OpenJDK](https://jdk.java.net/) | [`oracle-openjdk` license](https://openjdk.org/legal/gplv2+ce.html)
| `dragonwell` | [Alibaba Dragonwell JDK](https://dragonwell-jdk.io/) | [`dragonwell` license](https://www.aliyun.com/product/dragonwell/)
| `sapmachine` | [SAP SapMachine JDK/JRE](https://sapmachine.io/) | [`sapmachine` license](https://github.com/SAP/SapMachine/blob/sapmachine/LICENSE)
| `graalvm` | [Oracle GraalVM](https://www.graalvm.org/) | [`graalvm` license](https://www.oracle.com/downloads/licenses/graal-free-license.html)
Expand All @@ -159,6 +160,7 @@ Currently, the following distributions are supported:
> [!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.
> - AdoptOpenJDK got moved to Eclipse Temurin and won't be updated anymore. It is highly recommended to migrate workflows from `adopt` and `adopt-openj9`, to `temurin` and `semeru` respectively, to keep receiving software and security updates. See more details in the [Good-bye AdoptOpenJDK post](https://blog.adoptopenjdk.net/2021/08/goodbye-adoptopenjdk-hello-adoptium/).
> - Oracle OpenJDK builds are created and hosted by Oracle. After a limited number of releases, Oracle archives these builds and no longer provides security updates. To continue receiving security patches, users must move to Oracle JDK or choose a different vendor.
> - For Azul Zulu OpenJDK, architecture `arm64` is mapped to `aarch64` when querying the Azul Metadata API.
> - To comply with the GraalVM Free Terms and Conditions (GFTC) license, it is recommended to use GraalVM JDK 17 version 17.0.12, as this is the only version of GraalVM JDK 17 available under the GFTC license. Additionally, it is encouraged to consider upgrading to GraalVM JDK 21, which offers the latest features and improvements.
> - GraalVM Community is available as `distribution: 'graalvm-community'` for stable JDK 17 and later releases published on GitHub.
Expand Down
216 changes: 216 additions & 0 deletions __tests__/distributors/openjdk-installer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import {afterEach, beforeEach, describe, expect, it, jest} from '@jest/globals';
import {HttpClient} from '@actions/http-client';

jest.unstable_mockModule('@actions/core', () => ({
info: jest.fn(),
warning: jest.fn(),
debug: jest.fn(),
error: jest.fn(),
notice: jest.fn(),
setFailed: jest.fn(),
setOutput: jest.fn(),
getInput: jest.fn(),
getBooleanInput: jest.fn(),
getMultilineInput: jest.fn(),
addPath: jest.fn(),
exportVariable: jest.fn(),
saveState: jest.fn(),
getState: jest.fn(),
setSecret: jest.fn(),
isDebug: jest.fn(() => false),
startGroup: jest.fn(),
endGroup: jest.fn(),
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
toPlatformPath: jest.fn((value: string) => value),
toWin32Path: jest.fn((value: string) => value),
toPosixPath: jest.fn((value: string) => value)
}));

const {OpenJdkDistribution} =
await import('../../src/distributions/openjdk/installer.js');
const {getJavaDistribution} =
await import('../../src/distributions/distribution-factory.js');

const homePage = `
<a href="/26/">JDK 26</a>
<a href="/27/">JDK
27</a>
`;
const currentPage = `
<a href="https://download.java.net/java/GA/jdk26.0.2/hash/10/GPL/openjdk-26.0.2_linux-x64_bin.tar.gz">tar.gz</a>
<a href="https://download.java.net/java/GA/jdk26.0.2/hash/10/GPL/openjdk-26.0.2_linux-aarch64_bin.tar.gz">tar.gz</a>
`;
const earlyAccessPage = `
<a href="https://download.java.net/java/early_access/jdk27/32/GPL/openjdk-27-ea+32_linux-x64_bin.tar.gz">tar.gz</a>
`;
const archivePage = `
<a href="https://download.java.net/java/GA/jdk26.0.1/hash/8/GPL/openjdk-26.0.1_linux-x64_bin.tar.gz">tar.gz</a>
<a href="https://download.java.net/java/GA/jdk25/hash/36/GPL/openjdk-25_linux-x64_bin.tar.gz">tar.gz</a>
<a href="https://download.java.net/java/GA/jdk18.0.1.1/hash/2/GPL/openjdk-18.0.1.1_linux-x64_bin.tar.gz">tar.gz</a>
<th>9.0.4 (build 9.0.4+11)</th>
<a href="https://download.java.net/java/GA/jdk9/9.0.4/binaries/openjdk-9.0.4_linux-x64_bin.tar.gz">tar.gz</a>
`;

function createDistribution(
version = '26',
architecture = 'x64',
packageType = 'jdk',
useFixturePlatform = true
) {
const distribution = new OpenJdkDistribution({
version,
architecture,
packageType,
checkLatest: false
});
if (useFixturePlatform) {
distribution['getPlatform'] = jest.fn(() => 'linux');
}
return distribution;
}

describe('OpenJdkDistribution', () => {
let getSpy: jest.SpiedFunction<HttpClient['get']>;

beforeEach(() => {
getSpy = jest
.spyOn(HttpClient.prototype, 'get')
.mockImplementation(async url => {
const pages: Record<string, string> = {
'https://jdk.java.net/': homePage,
'https://jdk.java.net/26/': currentPage,
'https://jdk.java.net/27/': earlyAccessPage,
'https://jdk.java.net/archive/': archivePage
};
return {
readBody: async () => pages[url] ?? ''
} as Awaited<ReturnType<HttpClient['get']>>;
});
});

afterEach(() => {
jest.restoreAllMocks();
});

it('resolves the newest matching GA release', async () => {
const result = await createDistribution()['findPackageForDownload']('26');

expect(result).toEqual({
version: '26.0.2+10',
url: 'https://download.java.net/java/GA/jdk26.0.2/hash/10/GPL/openjdk-26.0.2_linux-x64_bin.tar.gz'
});
});

it('resolves an archived GA release', async () => {
const result =
await createDistribution('26.0.1')['findPackageForDownload']('26.0.1');

expect(result.version).toBe('26.0.1+8');
expect(result.url).toContain('/openjdk-26.0.1_linux-x64_bin.tar.gz');
});

it('resolves an exact GA build', async () => {
const result =
await createDistribution('26.0.2+10')['findPackageForDownload'](
'26.0.2+10'
);

expect(result.version).toBe('26.0.2+10');
});

it('resolves an exact build from a legacy archive heading', async () => {
const result =
await createDistribution('9.0.4+11')['findPackageForDownload'](
'9.0.4+11'
);

expect(result.version).toBe('9.0.4+11');
expect(result.url).toContain('/binaries/openjdk-9.0.4_linux-x64_bin');
});

it('resolves a four-field Java version', async () => {
const result =
await createDistribution('18.0.1.1')['findPackageForDownload'](
'18.0.1+1'
);

expect(result.version).toBe('18.0.1+1');
expect(result.url).toContain('/openjdk-18.0.1.1_linux-x64_bin.tar.gz');
});

it('resolves an early-access release without requesting the archive', async () => {
const result =
await createDistribution('27-ea')['findPackageForDownload']('27');

expect(result).toEqual({
version: '27.0.0+32',
url: 'https://download.java.net/java/early_access/jdk27/32/GPL/openjdk-27-ea+32_linux-x64_bin.tar.gz'
});
expect(getSpy).not.toHaveBeenCalledWith('https://jdk.java.net/archive/');
});

it('reports available versions when no release matches', async () => {
await expect(
createDistribution()['findPackageForDownload']('24')
).rejects.toThrow(
"No matching version found for SemVer '24'.\nDistribution: Oracle OpenJDK"
);
});

it.each([
['jre', 'Oracle OpenJDK provides only the `jdk` package type'],
['jdk+fx', 'Oracle OpenJDK provides only the `jdk` package type']
])('rejects the %s package type', async (packageType, message) => {
await expect(
createDistribution('26', 'x64', packageType)['findPackageForDownload'](
'26'
)
).rejects.toThrow(message);
});

it('rejects unsupported architectures', async () => {
await expect(
createDistribution('26', 'x86')['findPackageForDownload']('26')
).rejects.toThrow('Unsupported architecture: x86');
});

it('maps supported platforms', () => {
const distribution = createDistribution('26', 'x64', 'jdk', false);

expect(distribution['getPlatform']('linux')).toBe('linux');
expect(distribution['getPlatform']('darwin')).toBe('macos');
expect(distribution['getPlatform']('win32')).toBe('windows');
expect(() => distribution['getPlatform']('freebsd')).toThrow(
"Platform 'freebsd' is not supported"
);
});

it('parses legacy platform names and archive formats', () => {
const distribution = createDistribution();
const macRelease = distribution['parseReleases'](
'<a href="https://download.java.net/java/GA/jdk16/hash/7/GPL/openjdk-16_osx-x64_bin.tar.gz">tar.gz</a>',
'macos',
'x64'
);
const windowsRelease = distribution['parseReleases'](
'<a href="https://download.java.net/java/GA/jdk10/hash/13/openjdk-10.0.2_windows-x64_bin.tar.gz">tar.gz</a>',
'windows',
'x64'
);

expect(macRelease[0].version).toBe('16.0.0+7');
expect(windowsRelease[0].version).toBe('10.0.2+13');
expect(windowsRelease[0].url.endsWith('.tar.gz')).toBe(true);
});

it('is registered in the distribution factory', () => {
const distribution = getJavaDistribution('oracle-openjdk', {
version: '26',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
});

expect(distribution).toBeInstanceOf(OpenJdkDistribution);
});
});
107 changes: 107 additions & 0 deletions dist/setup/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -131882,6 +131882,109 @@ class KonaDistribution extends JavaBase {
}
}

;// CONCATENATED MODULE: ./src/distributions/openjdk/installer.ts







const OPENJDK_BASE_URL = 'https://jdk.java.net';
class OpenJdkDistribution extends JavaBase {
constructor(installerOptions) {
super('Oracle OpenJDK', installerOptions);
}
async findPackageForDownload(range) {
if (this.packageType !== 'jdk') {
throw new Error('Oracle OpenJDK provides only the `jdk` package type');
}
const arch = this.distributionArchitecture();
if (!['x64', 'aarch64'].includes(arch)) {
throw new Error(`Unsupported architecture: ${this.architecture}`);
}
const platform = this.getPlatform();
const releases = await this.getAvailableVersions(platform, arch);
const matchingReleases = releases
.filter(release => isVersionSatisfies(range, release.version))
.sort((left, right) => -semver_default().compareBuild(left.version, right.version));
if (!matchingReleases.length) {
throw this.createVersionNotFoundError(range, releases.map(release => release.version), `Platform: ${platform}`);
}
return matchingReleases[0];
}
async downloadTool(javaRelease) {
info(`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`);
let javaArchivePath = await downloadTool(javaRelease.url);
info(`Extracting Java archive...`);
const extension = javaRelease.url.endsWith('.zip') ? 'zip' : 'tar.gz';
if (extension === 'zip') {
javaArchivePath = renameWinArchive(javaArchivePath);
}
const extractedJavaPath = await extractJdkFile(javaArchivePath, extension);
const archiveName = external_fs_default().readdirSync(extractedJavaPath)[0];
const archivePath = external_path_default().join(extractedJavaPath, archiveName);
const javaPath = await cacheDir(archivePath, this.toolcacheFolderName, this.getToolcacheVersionName(javaRelease.version), this.architecture);
return { version: javaRelease.version, path: javaPath };
}
async getAvailableVersions(platform, arch) {
const homePage = await this.fetchPage(`${OPENJDK_BASE_URL}/`);
const releasePageUrls = Array.from(homePage.matchAll(/href="\/(\d+)\/">JDK\s+\d+/g), match => `${OPENJDK_BASE_URL}/${match[1]}/`);
const pages = await Promise.all(releasePageUrls.map(url => this.fetchPage(url)));
if (this.stable) {
pages.push(await this.fetchPage(`${OPENJDK_BASE_URL}/archive/`));
}
const releases = pages.flatMap(page => this.parseReleases(page, platform, arch));
return releases.filter(release => release.url.includes('/early_access/') !== this.stable);
}
async fetchPage(url) {
const response = await this.http.get(url);
return response.readBody();
}
parseReleases(html, platform, arch) {
const platformPattern = platform === 'macos' ? '(?:macos|osx)' : platform;
const extensionPattern = platform === 'windows' ? '(?:zip|tar\\.gz)' : 'tar\\.gz';
const pattern = new RegExp(`href="(https://download\\.java\\.net/[^"]+/openjdk-([^"_]+)_${platformPattern}-${arch}_bin\\.${extensionPattern})"`, 'g');
return Array.from(html.matchAll(pattern), match => {
const url = match[1];
const build = url.match(/\/(\d+)\/(?:GPL\/)?openjdk-/)?.[1] ??
this.findBuildInArchiveHeading(html, match.index, match[2]);
return {
version: this.toSemver(match[2], build),
url
};
});
}
findBuildInArchiveHeading(html, assetIndex, version) {
const headings = Array.from(html.slice(0, assetIndex).matchAll(/\(build\s+([^)]+)\)/g));
const headingVersion = headings.at(-1)?.[1];
if (!headingVersion) {
return undefined;
}
const [javaVersion, build] = headingVersion.split('+');
return javaVersion === version ? build : undefined;
}
toSemver(version, urlBuild) {
const [javaVersion, filenameBuild] = version.replace('-ea', '').split('+');
const versionParts = javaVersion.split('.');
const normalizedVersion = convertVersionToSemver(versionParts.length === 1 ? `${javaVersion}.0.0` : javaVersion);
const build = filenameBuild ?? (versionParts.length <= 3 ? urlBuild : undefined);
return build ? `${normalizedVersion}+${build}` : normalizedVersion;
}
getPlatform(platform = process.platform) {
switch (platform) {
case 'darwin':
return 'macos';
case 'linux':
return 'linux';
case 'win32':
return 'windows';
default:
throw new Error(`Platform '${platform}' is not supported. Supported platforms: 'linux', 'macos', 'windows'`);
}
}
}

;// CONCATENATED MODULE: ./src/distributions/distribution-factory.ts


Expand All @@ -131898,6 +132001,7 @@ class KonaDistribution extends JavaBase {




var JavaDistribution;
(function (JavaDistribution) {
JavaDistribution["Adopt"] = "adopt";
Expand All @@ -131918,6 +132022,7 @@ var JavaDistribution;
JavaDistribution["GraalVMCommunity"] = "graalvm-community";
JavaDistribution["JetBrains"] = "jetbrains";
JavaDistribution["Kona"] = "kona";
JavaDistribution["OracleOpenJdk"] = "oracle-openjdk";
})(JavaDistribution || (JavaDistribution = {}));
function getJavaDistribution(distributionName, installerOptions, jdkFile) {
switch (distributionName) {
Expand Down Expand Up @@ -131956,6 +132061,8 @@ function getJavaDistribution(distributionName, installerOptions, jdkFile) {
return new JetBrainsDistribution(installerOptions);
case JavaDistribution.Kona:
return new KonaDistribution(installerOptions);
case JavaDistribution.OracleOpenJdk:
return new OpenJdkDistribution(installerOptions);
default:
return null;
}
Expand Down
Loading
Loading