From c28745f4ec8d58785772fdfb632f81f69d1bd88c Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Thu, 18 Nov 2021 09:24:01 -0800 Subject: [PATCH] Add microsoft distribution of the JDK. --- .github/workflows/e2e-versions.yml | 16 ++- README.md | 1 + dist/setup/index.js | 164 +++++++++++++++++++++- src/distributions/distribution-factory.ts | 6 +- src/distributions/microsoft/installer.ts | 123 ++++++++++++++++ src/distributions/microsoft/models.ts | 17 +++ 6 files changed, 319 insertions(+), 8 deletions(-) create mode 100644 src/distributions/microsoft/installer.ts create mode 100644 src/distributions/microsoft/models.ts diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 1f23fbaf7..e511dd72c 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -14,13 +14,14 @@ on: workflow_dispatch: jobs: setup-java-major-versions: + if: ${{ matrix.distribution != 'microsoft' || matrix.version != '8' }} name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-x64) - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [macos-latest, windows-latest, ubuntu-latest] - distribution: ['temurin', 'adopt', 'adopt-openj9', 'zulu'] # internally 'adopt-hotspot' is the same as 'adopt' + distribution: ['temurin', 'adopt', 'adopt-openj9', 'zulu', 'microsoft' ] # internally 'adopt-hotspot' is the same as 'adopt' version: ['8', '11', '16'] steps: - name: Checkout @@ -167,16 +168,19 @@ jobs: shell: bash setup-java-custom-architecture: - name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-x86) - ${{ matrix.os }} + # Only Zulu provides x86 arch for now and only for windows / ubuntu + # Only Microsoft provides arm64 for windows / ubuntu / os x + if: ${{ (matrix.distribution == 'zulu' && matrix.architecture == 'x86' && matrix.os != 'macos-latest' ) || (matrix.distribution == 'microsoft' && matrix.architecture == 'arm64') }} + name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-${{ matrix.architecture }}) - ${{ matrix.os }} needs: setup-java-major-minor-versions runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - # Only Zulu provides x86 arch for now and only for windows / ubuntu - os: [windows-latest, ubuntu-latest] - distribution: ['zulu'] + os: [macos-latest, windows-latest, ubuntu-latest] + distribution: ['zulu', 'microsoft'] version: ['11'] + architecture: [ 'x86', 'arm64' ] steps: - name: Checkout uses: actions/checkout@v2 @@ -186,7 +190,7 @@ jobs: with: distribution: ${{ matrix.distribution }} java-version: ${{ matrix.version }} - architecture: x86 + architecture: ${{ matrix.architecture }} - name: Verify Java run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" shell: bash diff --git a/README.md b/README.md index 431beb2cb..edbdd948f 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Currently, the following distributions are supported: | `zulu` | Zulu OpenJDK | [Link](https://www.azul.com/downloads/zulu-community/?package=jdk) | [Link](https://www.azul.com/products/zulu-and-zulu-enterprise/zulu-terms-of-use/) | | `adopt` or `adopt-hotspot` | Adopt OpenJDK Hotspot | [Link](https://adoptopenjdk.net/) | [Link](https://adoptopenjdk.net/about.html) | | `adopt-openj9` | Adopt OpenJDK OpenJ9 | [Link](https://adoptopenjdk.net/) | [Link](https://adoptopenjdk.net/about.html) +| `microsoft` | Microsoft OpenJDK | [Link](https://www.microsoft.com/openjdk) | [Link](https://docs.microsoft.com/en-us/java/openjdk/faq) **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. diff --git a/dist/setup/index.js b/dist/setup/index.js index ae1012a01..d00cec53e 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -14215,7 +14215,165 @@ exports.XMLCBWriter = XMLCBWriter; /* 193 */, /* 194 */, /* 195 */, -/* 196 */, +/* 196 */ +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (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.MicrosoftDistributions = void 0; +const base_installer_1 = __webpack_require__(83); +const semver_1 = __importDefault(__webpack_require__(876)); +const util_1 = __webpack_require__(322); +const core = __importStar(__webpack_require__(470)); +const tc = __importStar(__webpack_require__(139)); +const fs_1 = __importDefault(__webpack_require__(747)); +const path_1 = __importDefault(__webpack_require__(622)); +const supportedPlatform = `'linux', 'linux-musl', 'macos', 'solaris', 'windows'`; +const supportedArchitecture = `'x86', 'x64', 'armv7', 'aarch64', 'ppc64le'`; +class MicrosoftDistributions extends base_installer_1.JavaBase { + constructor(installerOptions) { + super('Microsoft', installerOptions); + } + 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 extension = util_1.getDownloadArchiveExtension(); + const extractedJavaPath = yield util_1.extractJdkFile(javaArchivePath, extension); + const archiveName = fs_1.default.readdirSync(extractedJavaPath)[0]; + const archivePath = path_1.default.join(extractedJavaPath, archiveName); + const javaPath = yield tc.cacheDir(archivePath, this.toolcacheFolderName, this.getToolcacheVersionName(javaRelease.version), this.architecture); + return { version: javaRelease.version, path: javaPath }; + }); + } + findPackageForDownload(range) { + return __awaiter(this, void 0, void 0, function* () { + const availableVersionsRaw = yield this.getAvailableVersions(); + const availableVersions = availableVersionsRaw.map(item => ({ + url: item.downloadUrl, + version: this.convertVersionToSemver(item) + })); + const satisfiedVersion = availableVersions + .filter(item => util_1.isVersionSatisfies(range, item.version)) + .sort((a, b) => -semver_1.default.compareBuild(a.version, b.version))[0]; + if (!satisfiedVersion) { + const availableOptions = availableVersions.map(item => item.version).join(', '); + const availableOptionsMessage = availableOptions + ? `\nAvailable versions: ${availableOptions}` + : ''; + throw new Error(`Could not find satisfied version for semver ${range}. ${availableOptionsMessage}`); + } + return satisfiedVersion; + }); + } + getAvailableVersions() { + var _a; + return __awaiter(this, void 0, void 0, function* () { + console.time('microsoft-retrieve-available-versions'); + const url = this.prepareAvailableVersionsUrl(); + if (core.isDebug()) { + core.debug(`Gathering available versions from '${url}'`); + } + const availableVersions = (_a = (yield this.http.getJson(url)).result) !== null && _a !== void 0 ? _a : []; + if (core.isDebug()) { + core.startGroup('Print information about available versions'); + console.timeEnd('microsoft-retrieve-available-versions'); + console.log(`Available versions: [${availableVersions.length}]`); + console.log(availableVersions.map(item => item.version)); + core.endGroup(); + } + return availableVersions; + }); + } + prepareAvailableVersionsUrl() { + const urlOptions = Object.assign(Object.assign({ os: this.getPlatformOption(), 'bundle-type': this.getBundleType() }, this.getArchitectureOptions()), { 'build-type': this.stable ? 'all' : 'ea', 'installation-type': 'archive', fields: 'downloadUrl,version,featureVersion,interimVersion,updateVersion,buildVersion' }); + const searchParams = new URLSearchParams(urlOptions).toString(); + return `https://api.bell-sw.com/v1/liberica/releases?${searchParams}`; + } + getBundleType() { + const [bundleType, feature] = this.packageType.split('+'); + if (feature === null || feature === void 0 ? void 0 : feature.includes('fx')) { + return bundleType + '-full'; + } + return bundleType; + } + getArchitectureOptions() { + switch (this.architecture) { + case 'x86': + return { bitness: '32', arch: 'x86' }; + case 'x64': + return { bitness: '64', arch: 'x86' }; + case 'armv7': + return { bitness: '32', arch: 'arm' }; + case 'aarch64': + return { bitness: '64', arch: 'arm' }; + case 'ppc64le': + return { bitness: '64', arch: 'ppc' }; + default: + throw new Error(`Architecture '${this.architecture}' is not supported. Supported architectures: ${supportedArchitecture}`); + } + } + getPlatformOption(platform = process.platform) { + switch (platform) { + case 'darwin': + return 'macos'; + case 'win32': + case 'cygwin': + return 'windows'; + case 'linux': + return 'linux'; + case 'sunos': + return 'solaris'; + default: + throw new Error(`Platform '${platform}' is not supported. Supported platforms: ${supportedPlatform}`); + } + } + convertVersionToSemver(version) { + let { buildVersion, featureVersion, interimVersion, updateVersion } = version; + const mainVersion = [featureVersion, interimVersion, updateVersion].join('.'); + if (buildVersion != 0) { + return `${mainVersion}+${buildVersion}`; + } + return mainVersion; + } +} +exports.MicrosoftDistributions = MicrosoftDistributions; + + +/***/ }), /* 197 */ /***/ (function(__unusedmodule, exports, __webpack_require__) { @@ -55912,6 +56070,7 @@ const installer_1 = __webpack_require__(757); const installer_2 = __webpack_require__(834); const installer_3 = __webpack_require__(584); const installer_4 = __webpack_require__(439); +const installer_5 = __webpack_require__(196); var JavaDistribution; (function (JavaDistribution) { JavaDistribution["Adopt"] = "adopt"; @@ -55920,6 +56079,7 @@ var JavaDistribution; JavaDistribution["Temurin"] = "temurin"; JavaDistribution["Zulu"] = "zulu"; JavaDistribution["JdkFile"] = "jdkfile"; + JavaDistribution["Microsoft"] = "microsoft"; })(JavaDistribution || (JavaDistribution = {})); function getJavaDistribution(distributionName, installerOptions, jdkFile) { switch (distributionName) { @@ -55934,6 +56094,8 @@ function getJavaDistribution(distributionName, installerOptions, jdkFile) { return new installer_4.TemurinDistribution(installerOptions, installer_4.TemurinImplementation.Hotspot); case JavaDistribution.Zulu: return new installer_2.ZuluDistribution(installerOptions); + case JavaDistribution.Microsoft: + return new installer_5.MicrosoftDistributions(installerOptions); default: return null; } diff --git a/src/distributions/distribution-factory.ts b/src/distributions/distribution-factory.ts index e16254fe1..f667f3d2f 100644 --- a/src/distributions/distribution-factory.ts +++ b/src/distributions/distribution-factory.ts @@ -4,6 +4,7 @@ import { LocalDistribution } from './local/installer'; import { ZuluDistribution } from './zulu/installer'; import { AdoptDistribution, AdoptImplementation } from './adopt/installer'; import { TemurinDistribution, TemurinImplementation } from './temurin/installer'; +import { MicrosoftDistributions } from './microsoft/installer'; enum JavaDistribution { Adopt = 'adopt', @@ -11,7 +12,8 @@ enum JavaDistribution { AdoptOpenJ9 = 'adopt-openj9', Temurin = 'temurin', Zulu = 'zulu', - JdkFile = 'jdkfile' + JdkFile = 'jdkfile', + Microsoft = 'microsoft' } export function getJavaDistribution( @@ -31,6 +33,8 @@ export function getJavaDistribution( return new TemurinDistribution(installerOptions, TemurinImplementation.Hotspot); case JavaDistribution.Zulu: return new ZuluDistribution(installerOptions); + case JavaDistribution.Microsoft: + return new MicrosoftDistributions(installerOptions); default: return null; } diff --git a/src/distributions/microsoft/installer.ts b/src/distributions/microsoft/installer.ts new file mode 100644 index 000000000..5d510023c --- /dev/null +++ b/src/distributions/microsoft/installer.ts @@ -0,0 +1,123 @@ +import { JavaBase } from '../base-installer'; +import { JavaDownloadRelease, JavaInstallerOptions, JavaInstallerResults } from '../base-models'; +import semver from 'semver'; +import { extractJdkFile, getDownloadArchiveExtension, isVersionSatisfies } from '../../util'; +import * as core from '@actions/core'; +import { ArchitectureOptions, MicrosoftVersion, OsVersions } from './models'; +import * as tc from '@actions/tool-cache'; +import fs from 'fs'; +import path from 'path'; + +const supportedPlatform = `'linux', 'macos', 'windows'`; + +const supportedArchitecture = `'x64', 'armv7', 'aarch64'`; + +export class MicrosoftDistributions extends JavaBase { + constructor(installerOptions: JavaInstallerOptions) { + super('Microsoft', installerOptions); + } + + protected async downloadTool(javaRelease: JavaDownloadRelease): Promise { + 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 javaPath = await tc.cacheDir( + archivePath, + this.toolcacheFolderName, + this.getToolcacheVersionName(javaRelease.version), + this.architecture + ); + + return { version: javaRelease.version, path: javaPath }; + } + + protected async findPackageForDownload(range: string): Promise { + const availableVersionsRaw = await this.getAvailableVersions(); + + const availableVersions = availableVersionsRaw.map(item => ({ + url: `https://aka.ms/download-jdk/microsoft-jdk-${item.fullVersion}-${this.getPlatformOption()}-${this.architecture}.tar.gz`, + version: this.convertVersionToSemver(item) + })); + + const satisfiedVersion = availableVersions + .filter(item => isVersionSatisfies(range, item.version)) + .sort((a, b) => -semver.compareBuild(a.version, b.version))[0]; + + if (!satisfiedVersion) { + const availableOptions = availableVersions.map(item => item.version).join(', '); + const availableOptionsMessage = availableOptions + ? `\nAvailable versions: ${availableOptions}` + : ''; + throw new Error( + `Could not find satisfied version for semver ${range}. ${availableOptionsMessage}` + ); + } + + return satisfiedVersion; + } + + private async getAvailableVersions(): Promise { + console.time('microsoft-retrieve-available-versions'); + + // TODO get these dynamically! + var jdkVersions = [{ + majorVersion: 17, + minorVersion: 0, + patchVersion: 1, + fullVersion: '17.0.1.12.1' + }, { + majorVersion: 16, + minorVersion: 0, + patchVersion: 2, + fullVersion: '16.0.2.7.1' + }, { + majorVersion: 11, + minorVersion: 0, + patchVersion: 13, + fullVersion: '11.0.13.8.1' + }]; + + return jdkVersions; + } + + private getArchitectureOptions(): ArchitectureOptions { + switch (this.architecture) { + case 'x64': + return { bitness: '64', arch: 'x86' }; + case 'aarch64': + return { bitness: '64', arch: 'arm' }; + default: + throw new Error( + `Architecture '${this.architecture}' is not supported. Supported architectures: ${supportedArchitecture}` + ); + } + } + + private getPlatformOption(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: ${supportedPlatform}` + ); + } + } + + private convertVersionToSemver(version: MicrosoftVersion): string { + return `${version.majorVersion}.${version.minorVersion}.${version.patchVersion}`; + } +} \ No newline at end of file diff --git a/src/distributions/microsoft/models.ts b/src/distributions/microsoft/models.ts new file mode 100644 index 000000000..6317c5b51 --- /dev/null +++ b/src/distributions/microsoft/models.ts @@ -0,0 +1,17 @@ +export type Bitness = '32' | '64'; +export type ArchType = 'arm' | 'ppc' | 'sparc' | 'x86'; + +export type OsVersions = 'linux' | 'linux-musl' | 'macos' | 'solaris' | 'windows'; + +export interface ArchitectureOptions { + bitness: Bitness; + arch: ArchType; +} + +export interface MicrosoftVersion { + downloadUrl?: string; + majorVersion: number; + minorVersion: number; + patchVersion: number; + fullVersion: string; +} \ No newline at end of file