mirror of
https://github.com/actions/setup-java.git
synced 2025-04-19 09:26:46 +00:00
Implement support for custom vendors in setup-java
This commit is contained in:
parent
e73e96a93b
commit
b2da088220
21 changed files with 34331 additions and 21391 deletions
23
action.yml
23
action.yml
|
@ -4,22 +4,21 @@ description: 'Set up a specific version of the Java JDK and add the
|
|||
author: 'GitHub'
|
||||
inputs:
|
||||
java-version:
|
||||
description: 'The Java version to make available on the path. Takes a whole
|
||||
or semver Java version, or 1.x syntax (e.g. 1.8 => Java 8.x).
|
||||
Early access versions can be specified in the form of e.g. 14-ea,
|
||||
14.0.0-ea, or 14.0.0-ea.28'
|
||||
required: true
|
||||
description: 'The Java version to set up. Takes a whole or semver Java version. See examples of supported syntax in README file'
|
||||
required: false
|
||||
distribution:
|
||||
description: 'Java distribution. See the list of supported distributions in README file'
|
||||
required: false
|
||||
java-package:
|
||||
description: 'The package type (jre, jdk, jdk+fx)'
|
||||
description: 'The package type (jdk, jre)'
|
||||
required: false
|
||||
default: 'jdk'
|
||||
architecture:
|
||||
description: 'The architecture (x86, x64) of the package.'
|
||||
description: 'The architecture of the package'
|
||||
required: false
|
||||
default: 'x64'
|
||||
jdkFile:
|
||||
description: 'Path to where the compressed JDK is located. The path could
|
||||
be in your source repository or a local path on the agent.'
|
||||
description: 'Path to where the compressed JDK is located'
|
||||
required: false
|
||||
server-id:
|
||||
description: 'ID of the distributionManagement repository in the pom.xml
|
||||
|
@ -47,10 +46,12 @@ inputs:
|
|||
$GPG_PASSPHRASE.'
|
||||
required: false
|
||||
outputs:
|
||||
path:
|
||||
description: 'Path to where the java environment has been installed (same as $JAVA_HOME)'
|
||||
distribution:
|
||||
description: 'Distribution of Java that has been installed'
|
||||
version:
|
||||
description: 'Actual version of the java environment that has been installed'
|
||||
path:
|
||||
description: 'Path to where the java environment has been installed (same as $JAVA_HOME)'
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'dist/setup/index.js'
|
||||
|
|
3647
dist/cleanup/index.js
vendored
3647
dist/cleanup/index.js
vendored
File diff suppressed because it is too large
Load diff
46646
dist/setup/index.js
vendored
46646
dist/setup/index.js
vendored
File diff suppressed because it is too large
Load diff
4242
package-lock.json
generated
4242
package-lock.json
generated
File diff suppressed because it is too large
Load diff
36
package.json
36
package.json
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
"name": "setup-java",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.0",
|
||||
"private": true,
|
||||
"description": "setup java action",
|
||||
"main": "dist/setup/index.js",
|
||||
"scripts": {
|
||||
"build": "ncc build -o dist/setup src/setup-java.ts && ncc build -o dist/cleanup src/cleanup-java.ts",
|
||||
"format": "prettier --write **/*.ts",
|
||||
"format-check": "prettier --check **/*.ts",
|
||||
"format": "prettier --write \"{,!(node_modules)/**/}*.ts\"",
|
||||
"format-check": "prettier --check \"{,!(node_modules)/**/}*.ts\"",
|
||||
"prerelease": "npm run-script build",
|
||||
"release": "git add -f dist/setup/index.js dist/cleanup/index.js",
|
||||
"test": "jest"
|
||||
|
@ -18,30 +18,30 @@
|
|||
},
|
||||
"keywords": [
|
||||
"actions",
|
||||
"node",
|
||||
"java",
|
||||
"setup"
|
||||
],
|
||||
"author": "GitHub",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.0.0",
|
||||
"@actions/exec": "^1.0.0",
|
||||
"@actions/http-client": "^1.0.8",
|
||||
"@actions/io": "^1.0.0",
|
||||
"@actions/tool-cache": "^1.3.1",
|
||||
"semver": "^6.1.1",
|
||||
"xmlbuilder2": "^2.1.2"
|
||||
"@actions/core": "^1.2.6",
|
||||
"@actions/exec": "^1.0.4",
|
||||
"@actions/http-client": "^1.0.9",
|
||||
"@actions/io": "^1.0.2",
|
||||
"@actions/tool-cache": "^1.6.1",
|
||||
"semver": "^7.3.4",
|
||||
"xmlbuilder2": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.13",
|
||||
"@types/node": "^12.0.4",
|
||||
"@types/semver": "^6.0.0",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/node": "^12.19.13",
|
||||
"@types/semver": "^7.3.4",
|
||||
"@zeit/ncc": "^0.20.5",
|
||||
"jest": "^24.8.0",
|
||||
"jest-circus": "^24.7.1",
|
||||
"jest": "^26.6.3",
|
||||
"jest-circus": "^26.6.3",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-jest": "^24.0.2",
|
||||
"typescript": "^3.5.1"
|
||||
"ts-jest": "^26.5.3",
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"husky": {
|
||||
"skipCI": true,
|
||||
|
|
66
src/auth.ts
66
src/auth.ts
|
@ -1,26 +1,52 @@
|
|||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import {create as xmlCreate} from 'xmlbuilder2';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
|
||||
import { create as xmlCreate } from 'xmlbuilder2';
|
||||
import * as constants from './constants';
|
||||
import * as gpg from './gpg';
|
||||
|
||||
export const M2_DIR = '.m2';
|
||||
export const SETTINGS_FILE = 'settings.xml';
|
||||
|
||||
export async function configAuthentication(
|
||||
export async function configureAuthentication() {
|
||||
const id = core.getInput(constants.INPUT_SERVER_ID);
|
||||
const username = core.getInput(constants.INPUT_SERVER_USERNAME);
|
||||
const password = core.getInput(constants.INPUT_SERVER_PASSWORD);
|
||||
const gpgPrivateKey =
|
||||
core.getInput(constants.INPUT_GPG_PRIVATE_KEY) || constants.INPUT_DEFAULT_GPG_PRIVATE_KEY;
|
||||
const gpgPassphrase =
|
||||
core.getInput(constants.INPUT_GPG_PASSPHRASE) ||
|
||||
(gpgPrivateKey ? constants.INPUT_DEFAULT_GPG_PASSPHRASE : undefined);
|
||||
|
||||
if (gpgPrivateKey) {
|
||||
core.setSecret(gpgPrivateKey);
|
||||
}
|
||||
|
||||
await createAuthenticationSettings(id, username, password, gpgPassphrase);
|
||||
|
||||
if (gpgPrivateKey) {
|
||||
core.info('Importing private gpg key');
|
||||
const keyFingerprint = (await gpg.importKey(gpgPrivateKey)) || '';
|
||||
core.saveState(constants.STATE_GPG_PRIVATE_KEY_FINGERPRINT, keyFingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
export async function createAuthenticationSettings(
|
||||
id: string,
|
||||
username: string,
|
||||
password: string,
|
||||
gpgPassphrase: string | undefined = undefined
|
||||
) {
|
||||
console.log(
|
||||
`creating ${SETTINGS_FILE} with server-id: ${id};`,
|
||||
'environment variables:',
|
||||
`username=\$${username},`,
|
||||
`password=\$${password},`,
|
||||
`and gpg-passphrase=${gpgPassphrase ? '$' + gpgPassphrase : null}`
|
||||
core.info(
|
||||
`Creating ${SETTINGS_FILE} with server-id: ${id};
|
||||
environment variables:
|
||||
username=\$${username},
|
||||
password=\$${password},
|
||||
and gpg-passphrase=${gpgPassphrase ? '$' + gpgPassphrase : null}`
|
||||
);
|
||||
// when an alternate m2 location is specified use only that location (no .m2 directory)
|
||||
// otherwise use the home/.m2/ path
|
||||
|
@ -29,11 +55,7 @@ export async function configAuthentication(
|
|||
core.getInput(constants.INPUT_SETTINGS_PATH) ? '' : M2_DIR
|
||||
);
|
||||
await io.mkdirP(settingsDirectory);
|
||||
core.debug(`created directory ${settingsDirectory}`);
|
||||
await write(
|
||||
settingsDirectory,
|
||||
generate(id, username, password, gpgPassphrase)
|
||||
);
|
||||
await write(settingsDirectory, generate(id, username, password, gpgPassphrase));
|
||||
}
|
||||
|
||||
// only exported for testing purposes
|
||||
|
@ -41,9 +63,9 @@ export function generate(
|
|||
id: string,
|
||||
username: string,
|
||||
password: string,
|
||||
gpgPassphrase: string | undefined = undefined
|
||||
gpgPassphrase?: string | undefined
|
||||
) {
|
||||
const xmlObj: {[key: string]: any} = {
|
||||
const xmlObj: { [key: string]: any } = {
|
||||
settings: {
|
||||
'@xmlns': 'http://maven.apache.org/SETTINGS/1.0.0',
|
||||
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||
|
@ -69,15 +91,19 @@ export function generate(
|
|||
xmlObj.settings.servers.server.push(gpgServer);
|
||||
}
|
||||
|
||||
return xmlCreate(xmlObj).end({headless: true, prettyPrint: true, width: 80});
|
||||
return xmlCreate(xmlObj).end({
|
||||
headless: true,
|
||||
prettyPrint: true,
|
||||
width: 80
|
||||
});
|
||||
}
|
||||
|
||||
async function write(directory: string, settings: string) {
|
||||
const location = path.join(directory, SETTINGS_FILE);
|
||||
if (fs.existsSync(location)) {
|
||||
console.warn(`overwriting existing file ${location}`);
|
||||
core.warning(`Overwriting existing file ${location}`);
|
||||
} else {
|
||||
console.log(`writing ${location}`);
|
||||
core.info(`Writing ${location}`);
|
||||
}
|
||||
|
||||
return fs.writeFileSync(location, settings, {
|
||||
|
|
|
@ -3,15 +3,13 @@ import * as gpg from './gpg';
|
|||
import * as constants from './constants';
|
||||
|
||||
async function run() {
|
||||
if (core.getInput(constants.INPUT_GPG_PRIVATE_KEY, {required: false})) {
|
||||
core.info('removing private key from keychain');
|
||||
if (core.getInput(constants.INPUT_GPG_PRIVATE_KEY, { required: false })) {
|
||||
core.info('Removing private key from keychain');
|
||||
try {
|
||||
const keyFingerprint = core.getState(
|
||||
constants.STATE_GPG_PRIVATE_KEY_FINGERPRINT
|
||||
);
|
||||
const keyFingerprint = core.getState(constants.STATE_GPG_PRIVATE_KEY_FINGERPRINT);
|
||||
await gpg.deleteKey(keyFingerprint);
|
||||
} catch (error) {
|
||||
core.setFailed('failed to remove private key');
|
||||
core.setFailed('Failed to remove private key');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
export const INPUT_VERSION = 'version';
|
||||
export const macOSJavaContentDir = 'Contents/Home';
|
||||
export const INPUT_JAVA_VERSION = 'java-version';
|
||||
export const INPUT_ARCHITECTURE = 'architecture';
|
||||
export const INPUT_JAVA_PACKAGE = 'java-package';
|
||||
export const INPUT_DISTRIBUTION = 'distribution';
|
||||
export const INPUT_JDK_FILE = 'jdkFile';
|
||||
export const INPUT_SERVER_ID = 'server-id';
|
||||
export const INPUT_SERVER_USERNAME = 'server-username';
|
||||
|
|
146
src/distributions/adoptium/installer.ts
Normal file
146
src/distributions/adoptium/installer.ts
Normal file
|
@ -0,0 +1,146 @@
|
|||
import * as core from '@actions/core';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import semver from 'semver';
|
||||
|
||||
import { JavaBase } from '../base-installer';
|
||||
import { IAdoptiumAvailableVersions } from './models';
|
||||
import { JavaInstallerOptions, JavaDownloadRelease, JavaInstallerResults } from '../base-models';
|
||||
import { macOSJavaContentDir } from '../../constants';
|
||||
import { extractJdkFile, getDownloadArchiveExtension } from '../../util';
|
||||
|
||||
export class AdoptiumDistribution extends JavaBase {
|
||||
constructor(installerOptions: JavaInstallerOptions) {
|
||||
super('Adoptium', installerOptions);
|
||||
}
|
||||
|
||||
protected async findPackageForDownload(version: semver.Range): Promise<JavaDownloadRelease> {
|
||||
const availableVersionsRaw = await this.getAvailableVersions();
|
||||
const availableVersionsWithBinaries = availableVersionsRaw
|
||||
.filter(item => item.binaries.length > 0)
|
||||
.map(item => {
|
||||
return {
|
||||
version: item.version_data.semver,
|
||||
url: item.binaries[0].package.link
|
||||
} as JavaDownloadRelease;
|
||||
});
|
||||
|
||||
const satisfiedVersions = availableVersionsWithBinaries
|
||||
.filter(item => semver.satisfies(item.version, version))
|
||||
.sort((a, b) => {
|
||||
return -semver.compareBuild(a.version, b.version);
|
||||
});
|
||||
|
||||
const resolvedFullVersion = satisfiedVersions.length > 0 ? satisfiedVersions[0] : null;
|
||||
if (!resolvedFullVersion) {
|
||||
const availableOptions = availableVersionsWithBinaries.map(item => item.version).join(', ');
|
||||
const availableOptionsMessage = availableOptions
|
||||
? `\nAvailable versions: ${availableOptions}`
|
||||
: '';
|
||||
throw new Error(
|
||||
`Could not find satisfied version for SemVer '${version.raw}'. ${availableOptionsMessage}`
|
||||
);
|
||||
}
|
||||
|
||||
return resolvedFullVersion;
|
||||
}
|
||||
|
||||
protected async downloadTool(javaRelease: JavaDownloadRelease): Promise<JavaInstallerResults> {
|
||||
let javaPath: string;
|
||||
let extractedJavaPath: string;
|
||||
|
||||
core.info(
|
||||
`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
|
||||
);
|
||||
const javaArchivePath = await tc.downloadTool(javaRelease.url);
|
||||
|
||||
core.info(`Extracting Java archive...`);
|
||||
let extension = getDownloadArchiveExtension();
|
||||
|
||||
extractedJavaPath = await extractJdkFile(javaArchivePath, extension);
|
||||
|
||||
const archiveName = fs.readdirSync(extractedJavaPath)[0];
|
||||
const archivePath = path.join(extractedJavaPath, archiveName);
|
||||
const version = this.getToolcacheVersionName(javaRelease.version);
|
||||
|
||||
javaPath = await tc.cacheDir(archivePath, this.toolcacheFolderName, version, this.architecture);
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
javaPath = path.join(javaPath, macOSJavaContentDir);
|
||||
}
|
||||
|
||||
return { version: javaRelease.version, path: javaPath };
|
||||
}
|
||||
|
||||
private async getAvailableVersions(): Promise<IAdoptiumAvailableVersions[]> {
|
||||
const platform = this.getPlatformOption();
|
||||
const arch = this.architecture;
|
||||
const imageType = this.packageType;
|
||||
const versionRange = '[1.0,100.0]'; // retrieve all available versions
|
||||
const encodedVersionRange = encodeURI(versionRange);
|
||||
const releaseType = this.stable ? 'ga' : 'ea';
|
||||
|
||||
console.time('adopt-retrieve-available-versions');
|
||||
|
||||
const baseRequestArguments = [
|
||||
`project=jdk`,
|
||||
'vendor=adoptopenjdk',
|
||||
`heap_size=normal`,
|
||||
`jvm_impl=hotspot`,
|
||||
'sort_method=DEFAULT',
|
||||
'sort_order=DESC',
|
||||
`os=${platform}`,
|
||||
`architecture=${arch}`,
|
||||
`image_type=${imageType}`,
|
||||
`release_type=${releaseType}`
|
||||
].join('&');
|
||||
|
||||
// need to iterate through all pages to retrieve the list of all versions
|
||||
// Adopt API doesn't provide way to retrieve the count of pages to iterate so infinity loop
|
||||
let page_index = 0;
|
||||
const availableVersions: IAdoptiumAvailableVersions[] = [];
|
||||
while (true) {
|
||||
const requestArguments = `${baseRequestArguments}&page_size=20&page=${page_index}`;
|
||||
const availableVersionsUrl = `https://api.adoptopenjdk.net/v3/assets/version/${encodedVersionRange}?${requestArguments}`;
|
||||
if (core.isDebug() && page_index === 0) {
|
||||
// url is identical except page_index so print it once for debug
|
||||
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
||||
}
|
||||
|
||||
const paginationPage = (
|
||||
await this.http.getJson<IAdoptiumAvailableVersions[]>(availableVersionsUrl)
|
||||
).result;
|
||||
if (paginationPage === null || paginationPage.length === 0) {
|
||||
// break infinity loop because we have reached end of pagination
|
||||
break;
|
||||
}
|
||||
|
||||
availableVersions.push(...paginationPage);
|
||||
page_index++;
|
||||
}
|
||||
|
||||
if (core.isDebug()) {
|
||||
core.startGroup('Print information about available versions');
|
||||
console.timeEnd('adopt-retrieve-available-versions');
|
||||
console.log(`Available versions: [${availableVersions.length}]`);
|
||||
console.log(availableVersions.map(item => item.version_data.semver).join(', '));
|
||||
core.endGroup();
|
||||
}
|
||||
|
||||
return availableVersions;
|
||||
}
|
||||
|
||||
private getPlatformOption(): string {
|
||||
// Adopt has own platform names so need to map them
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
return 'mac';
|
||||
case 'win32':
|
||||
return 'windows';
|
||||
default:
|
||||
return process.platform;
|
||||
}
|
||||
}
|
||||
}
|
36
src/distributions/adoptium/models.ts
Normal file
36
src/distributions/adoptium/models.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
export interface IAdoptiumAvailableVersions {
|
||||
binaries: [
|
||||
{
|
||||
architecture: string;
|
||||
heap_size: string;
|
||||
image_type: string;
|
||||
jvm_impl: string;
|
||||
os: string;
|
||||
package: {
|
||||
checksum: string;
|
||||
checksum_link: string;
|
||||
download_count: number;
|
||||
link: string;
|
||||
metadata_link: string;
|
||||
name: string;
|
||||
size: string;
|
||||
};
|
||||
project: string;
|
||||
scm_ref: string;
|
||||
updated_at: string;
|
||||
}
|
||||
];
|
||||
id: string;
|
||||
release_link: string;
|
||||
release_name: string;
|
||||
release_type: string;
|
||||
vendor: string;
|
||||
version_data: {
|
||||
build: number;
|
||||
major: number;
|
||||
minor: number;
|
||||
openjdk_version: string;
|
||||
security: string;
|
||||
semver: string;
|
||||
};
|
||||
}
|
115
src/distributions/base-installer.ts
Normal file
115
src/distributions/base-installer.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
import * as tc from '@actions/tool-cache';
|
||||
import * as core from '@actions/core';
|
||||
import semver from 'semver';
|
||||
import path from 'path';
|
||||
import * as httpm from '@actions/http-client';
|
||||
import { getVersionFromToolcachePath } from '../util';
|
||||
import { JavaDownloadRelease, JavaInstallerOptions, JavaInstallerResults } from './base-models';
|
||||
|
||||
export abstract class JavaBase {
|
||||
protected http: httpm.HttpClient;
|
||||
protected version: semver.Range;
|
||||
protected architecture: string;
|
||||
protected packageType: string;
|
||||
protected stable: boolean;
|
||||
|
||||
constructor(protected distribution: string, installerOptions: JavaInstallerOptions) {
|
||||
this.http = new httpm.HttpClient('setup-java', undefined, {
|
||||
allowRetries: true,
|
||||
maxRetries: 3
|
||||
});
|
||||
|
||||
({ version: this.version, stable: this.stable } = this.normalizeVersion(
|
||||
installerOptions.version
|
||||
));
|
||||
this.architecture = installerOptions.arch;
|
||||
this.packageType = installerOptions.packageType;
|
||||
}
|
||||
|
||||
protected abstract downloadTool(javaRelease: JavaDownloadRelease): Promise<JavaInstallerResults>;
|
||||
protected abstract findPackageForDownload(range: semver.Range): Promise<JavaDownloadRelease>;
|
||||
|
||||
public async setupJava(): Promise<JavaInstallerResults> {
|
||||
let foundJava = this.findInToolcache();
|
||||
if (foundJava) {
|
||||
core.info(`Resolved Java ${foundJava.version} from tool-cache`);
|
||||
} else {
|
||||
core.info(`Java ${this.version.raw} is not found in tool-cache. Trying to download...`);
|
||||
const javaRelease = await this.findPackageForDownload(this.version);
|
||||
foundJava = await this.downloadTool(javaRelease);
|
||||
core.info(`Java ${foundJava.version} was downloaded`);
|
||||
}
|
||||
|
||||
core.info(`Setting Java ${foundJava.version} as default`);
|
||||
this.setJavaDefault(foundJava.version, foundJava.path);
|
||||
|
||||
return foundJava;
|
||||
}
|
||||
|
||||
protected get toolcacheFolderName(): string {
|
||||
return `Java_${this.distribution}_${this.packageType}`;
|
||||
}
|
||||
|
||||
protected getToolcacheVersionName(resolvedVersion: string): string {
|
||||
let version = resolvedVersion;
|
||||
if (!this.stable) {
|
||||
const cleanVersion = semver.clean(version);
|
||||
return `${cleanVersion}-ea`;
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
protected findInToolcache(): JavaInstallerResults | null {
|
||||
// we can't use tc.find directly because firstly, we need to filter versions by stability
|
||||
// if *-ea is provided, take only ea versions from toolcache, otherwise - only stable versions
|
||||
const availableVersions = tc
|
||||
.findAllVersions(this.toolcacheFolderName, this.architecture)
|
||||
.filter(item => item.endsWith('-ea') === !this.stable);
|
||||
|
||||
const satisfiedVersions = availableVersions
|
||||
.filter(item => semver.satisfies(item.replace(/-ea$/, ''), this.version))
|
||||
.sort(semver.rcompare);
|
||||
if (!satisfiedVersions || satisfiedVersions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const javaPath = tc.find(this.toolcacheFolderName, satisfiedVersions[0], this.architecture);
|
||||
if (!javaPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
version: getVersionFromToolcachePath(javaPath),
|
||||
path: javaPath
|
||||
};
|
||||
}
|
||||
|
||||
protected setJavaDefault(version: string, toolPath: string) {
|
||||
core.exportVariable('JAVA_HOME', toolPath);
|
||||
core.addPath(path.join(toolPath, 'bin'));
|
||||
core.setOutput('distribution', this.distribution);
|
||||
core.setOutput('path', toolPath);
|
||||
core.setOutput('version', version);
|
||||
}
|
||||
|
||||
// this function validates and parse java version to its normal semver notation
|
||||
protected normalizeVersion(version: string) {
|
||||
let stable = true;
|
||||
|
||||
if (version.endsWith('-ea')) {
|
||||
version = version.replace('-ea', '');
|
||||
stable = false;
|
||||
}
|
||||
|
||||
if (!semver.validRange(version)) {
|
||||
throw new Error(
|
||||
`The string '${version}' is not valid SemVer notation for Java version. Please check README file for code snippets and more detailed information`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
version: new semver.Range(version),
|
||||
stable
|
||||
};
|
||||
}
|
||||
}
|
15
src/distributions/base-models.ts
Normal file
15
src/distributions/base-models.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export interface JavaInstallerOptions {
|
||||
version: string;
|
||||
arch: string;
|
||||
packageType: string;
|
||||
}
|
||||
|
||||
export interface JavaInstallerResults {
|
||||
version: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface JavaDownloadRelease {
|
||||
version: string;
|
||||
url: string;
|
||||
}
|
28
src/distributions/distribution-factory.ts
Normal file
28
src/distributions/distribution-factory.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { AdoptiumDistribution } from './adoptium/installer';
|
||||
import { JavaBase } from './base-installer';
|
||||
import { JavaInstallerOptions } from './base-models';
|
||||
import { LocalDistribution } from './local/installer';
|
||||
import { ZuluDistribution } from './zulu/installer';
|
||||
|
||||
enum JavaDistribution {
|
||||
Adoptium = 'adoptium',
|
||||
Zulu = 'zulu',
|
||||
JdkFile = 'jdkfile'
|
||||
}
|
||||
|
||||
export function getJavaDistribution(
|
||||
distributionName: string,
|
||||
installerOptions: JavaInstallerOptions,
|
||||
jdkFile?: string
|
||||
): JavaBase | null {
|
||||
switch (distributionName) {
|
||||
case JavaDistribution.JdkFile:
|
||||
return new LocalDistribution(installerOptions, jdkFile);
|
||||
case JavaDistribution.Adoptium:
|
||||
return new AdoptiumDistribution(installerOptions);
|
||||
case JavaDistribution.Zulu:
|
||||
return new ZuluDistribution(installerOptions);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
77
src/distributions/local/installer.ts
Normal file
77
src/distributions/local/installer.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import * as tc from '@actions/tool-cache';
|
||||
import * as core from '@actions/core';
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import semver from 'semver';
|
||||
|
||||
import { JavaBase } from '../base-installer';
|
||||
import { JavaInstallerOptions, JavaDownloadRelease, JavaInstallerResults } from '../base-models';
|
||||
import { extractJdkFile } from '../../util';
|
||||
import { macOSJavaContentDir } from '../../constants';
|
||||
|
||||
export class LocalDistribution extends JavaBase {
|
||||
constructor(installerOptions: JavaInstallerOptions, private jdkFile?: string) {
|
||||
super('jdkfile', installerOptions);
|
||||
}
|
||||
|
||||
public async setupJava(): Promise<JavaInstallerResults> {
|
||||
let foundJava = this.findInToolcache();
|
||||
|
||||
if (foundJava) {
|
||||
core.info(`Resolved Java ${foundJava.version} from tool-cache`);
|
||||
} else {
|
||||
core.info(
|
||||
`Java ${this.version.raw} is not found in tool-cache. Trying to unpack JDK file...`
|
||||
);
|
||||
if (!this.jdkFile) {
|
||||
throw new Error("'jdkFile' is not specified");
|
||||
}
|
||||
const jdkFilePath = path.resolve(this.jdkFile);
|
||||
const stats = fs.statSync(jdkFilePath);
|
||||
|
||||
if (!stats.isFile()) {
|
||||
throw new Error(`JDK file is not found in path '${jdkFilePath}'`);
|
||||
}
|
||||
|
||||
core.info(`Extracting Java from '${jdkFilePath}'`);
|
||||
|
||||
const extractedJavaPath = await extractJdkFile(jdkFilePath);
|
||||
const archiveName = fs.readdirSync(extractedJavaPath)[0];
|
||||
const archivePath = path.join(extractedJavaPath, archiveName);
|
||||
const javaVersion = this.version.raw;
|
||||
|
||||
let javaPath = await tc.cacheDir(
|
||||
archivePath,
|
||||
this.toolcacheFolderName,
|
||||
this.getToolcacheVersionName(javaVersion),
|
||||
this.architecture
|
||||
);
|
||||
|
||||
if (
|
||||
process.platform === 'darwin' &&
|
||||
fs.existsSync(path.join(javaPath, macOSJavaContentDir))
|
||||
) {
|
||||
javaPath = path.join(javaPath, macOSJavaContentDir);
|
||||
}
|
||||
|
||||
foundJava = {
|
||||
version: javaVersion,
|
||||
path: javaPath
|
||||
};
|
||||
}
|
||||
|
||||
core.info(`Setting Java ${foundJava.version} as default`);
|
||||
|
||||
this.setJavaDefault(foundJava.version, foundJava.path);
|
||||
return foundJava;
|
||||
}
|
||||
|
||||
protected async findPackageForDownload(version: semver.Range): Promise<JavaDownloadRelease> {
|
||||
throw new Error('Should not be implemented');
|
||||
}
|
||||
|
||||
protected async downloadTool(javaRelease: JavaDownloadRelease): Promise<JavaInstallerResults> {
|
||||
throw new Error('Should not be implemented');
|
||||
}
|
||||
}
|
163
src/distributions/zulu/installer.ts
Normal file
163
src/distributions/zulu/installer.ts
Normal file
|
@ -0,0 +1,163 @@
|
|||
import * as core from '@actions/core';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import semver from 'semver';
|
||||
|
||||
import { JavaBase } from '../base-installer';
|
||||
import { IZuluVersions } from './models';
|
||||
import { extractJdkFile, getDownloadArchiveExtension } from '../../util';
|
||||
import { JavaDownloadRelease, JavaInstallerOptions, JavaInstallerResults } from '../base-models';
|
||||
|
||||
export class ZuluDistribution extends JavaBase {
|
||||
constructor(installerOptions: JavaInstallerOptions) {
|
||||
super('Zulu', installerOptions);
|
||||
}
|
||||
|
||||
protected async findPackageForDownload(version: semver.Range): Promise<JavaDownloadRelease> {
|
||||
const availableVersionsRaw = await this.getAvailableVersions();
|
||||
const availableVersions = availableVersionsRaw.map(item => {
|
||||
return {
|
||||
version: this.convertVersionToSemver(item.jdk_version),
|
||||
url: item.url,
|
||||
zuluVersion: this.convertVersionToSemver(item.zulu_version)
|
||||
};
|
||||
});
|
||||
|
||||
const satisfiedVersions = availableVersions
|
||||
.filter(item => semver.satisfies(item.version, version))
|
||||
.sort((a, b) => {
|
||||
// Azul provides two versions: jdk_version and azul_version
|
||||
// we should sort by both fields by descending
|
||||
return (
|
||||
-semver.compareBuild(a.version, b.version) ||
|
||||
-semver.compareBuild(a.zuluVersion, b.zuluVersion)
|
||||
);
|
||||
})
|
||||
.map(item => {
|
||||
return {
|
||||
version: item.version,
|
||||
url: item.url
|
||||
} as JavaDownloadRelease;
|
||||
});
|
||||
|
||||
const resolvedFullVersion = satisfiedVersions.length > 0 ? satisfiedVersions[0] : null;
|
||||
if (!resolvedFullVersion) {
|
||||
const availableOptions = availableVersions.map(item => item.version).join(', ');
|
||||
const availableOptionsMessage = availableOptions
|
||||
? `\nAvailable versions: ${availableOptions}`
|
||||
: '';
|
||||
throw new Error(
|
||||
`Could not find satisfied version for semver ${version.raw}. ${availableOptionsMessage}`
|
||||
);
|
||||
}
|
||||
|
||||
return resolvedFullVersion;
|
||||
}
|
||||
|
||||
protected async downloadTool(javaRelease: JavaDownloadRelease): Promise<JavaInstallerResults> {
|
||||
let extractedJavaPath: string;
|
||||
|
||||
core.info(
|
||||
`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
|
||||
);
|
||||
const javaArchivePath = await tc.downloadTool(javaRelease.url);
|
||||
|
||||
core.info(`Extracting Java archive...`);
|
||||
let extension = getDownloadArchiveExtension();
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
private async getAvailableVersions(): Promise<IZuluVersions[]> {
|
||||
const { arch, hw_bitness, abi } = this.getArchitectureOptions();
|
||||
const [bundleType, features] = this.packageType.split('+');
|
||||
const platform = this.getPlatformOption();
|
||||
const extension = getDownloadArchiveExtension();
|
||||
const javafx = features?.includes('fx') ?? false;
|
||||
const releaseStatus = this.stable ? 'ga' : 'ea';
|
||||
|
||||
console.time('azul-retrieve-available-versions');
|
||||
const requestArguments = [
|
||||
`os=${platform}`,
|
||||
`ext=${extension}`,
|
||||
`bundle_type=${bundleType}`,
|
||||
`javafx=${javafx}`,
|
||||
`arch=${arch}`,
|
||||
`hw_bitness=${hw_bitness}`,
|
||||
`release_status=${releaseStatus}`,
|
||||
abi ? `abi=${abi}` : null,
|
||||
features ? `features=${features}` : null
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('&');
|
||||
|
||||
const availableVersionsUrl = `https://api.azul.com/zulu/download/community/v1.0/bundles/?${requestArguments}`;
|
||||
if (core.isDebug()) {
|
||||
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
||||
}
|
||||
|
||||
const availableVersions =
|
||||
(await this.http.getJson<Array<IZuluVersions>>(availableVersionsUrl)).result ?? [];
|
||||
|
||||
if (core.isDebug()) {
|
||||
core.startGroup('Print information about available versions');
|
||||
console.timeEnd('azul-retrieve-available-versions');
|
||||
console.log(`Available versions: [${availableVersions.length}]`);
|
||||
console.log(availableVersions.map(item => item.jdk_version.join('.')).join(', '));
|
||||
core.endGroup();
|
||||
}
|
||||
|
||||
return availableVersions;
|
||||
}
|
||||
|
||||
private getArchitectureOptions(): {
|
||||
arch: string;
|
||||
hw_bitness: string;
|
||||
abi: string;
|
||||
} {
|
||||
if (this.architecture == 'x64') {
|
||||
return { arch: 'x86', hw_bitness: '64', abi: '' };
|
||||
} else if (this.architecture == 'x86') {
|
||||
return { arch: 'x86', hw_bitness: '32', abi: '' };
|
||||
} else {
|
||||
return { arch: this.architecture, hw_bitness: '', abi: '' };
|
||||
}
|
||||
}
|
||||
|
||||
private getPlatformOption(): string {
|
||||
// Azul has own platform names so need to map them
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
return 'macos';
|
||||
case 'win32':
|
||||
return 'windows';
|
||||
default:
|
||||
return process.platform;
|
||||
}
|
||||
}
|
||||
|
||||
// Azul API returns jdk_version as array of digits like [11, 0, 2, 1]
|
||||
private convertVersionToSemver(version_array: number[]) {
|
||||
const mainVersion = version_array.slice(0, 3).join('.');
|
||||
if (version_array.length > 3) {
|
||||
// intentionally ignore more than 4 numbers because it is invalid semver
|
||||
return `${mainVersion}+${version_array[3]}`;
|
||||
}
|
||||
|
||||
return mainVersion;
|
||||
}
|
||||
}
|
7
src/distributions/zulu/models.ts
Normal file
7
src/distributions/zulu/models.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export interface IZuluVersions {
|
||||
id: number;
|
||||
name: string;
|
||||
url: string;
|
||||
jdk_version: Array<number>;
|
||||
zulu_version: Array<number>;
|
||||
}
|
24
src/gpg.ts
24
src/gpg.ts
|
@ -3,7 +3,7 @@ import * as path from 'path';
|
|||
import * as io from '@actions/io';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as util from './util';
|
||||
import {ExecOptions} from '@actions/exec/lib/interfaces';
|
||||
import { ExecOptions } from '@actions/exec/lib/interfaces';
|
||||
|
||||
export const PRIVATE_KEY_FILE = path.join(util.getTempDir(), 'private-key.asc');
|
||||
|
||||
|
@ -28,13 +28,7 @@ export async function importKey(privateKey: string) {
|
|||
|
||||
await exec.exec(
|
||||
'gpg',
|
||||
[
|
||||
'--batch',
|
||||
'--import-options',
|
||||
'import-show',
|
||||
'--import',
|
||||
PRIVATE_KEY_FILE
|
||||
],
|
||||
['--batch', '--import-options', 'import-show', '--import', PRIVATE_KEY_FILE],
|
||||
options
|
||||
);
|
||||
|
||||
|
@ -45,14 +39,8 @@ export async function importKey(privateKey: string) {
|
|||
}
|
||||
|
||||
export async function deleteKey(keyFingerprint: string) {
|
||||
await exec.exec(
|
||||
'gpg',
|
||||
['--batch', '--yes', '--delete-secret-keys', keyFingerprint],
|
||||
{silent: true}
|
||||
);
|
||||
await exec.exec(
|
||||
'gpg',
|
||||
['--batch', '--yes', '--delete-keys', keyFingerprint],
|
||||
{silent: true}
|
||||
);
|
||||
await exec.exec('gpg', ['--batch', '--yes', '--delete-secret-keys', keyFingerprint], {
|
||||
silent: true
|
||||
});
|
||||
await exec.exec('gpg', ['--batch', '--yes', '--delete-keys', keyFingerprint], { silent: true });
|
||||
}
|
||||
|
|
298
src/installer.ts
298
src/installer.ts
|
@ -1,298 +0,0 @@
|
|||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as httpm from '@actions/http-client';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
import * as util from './util';
|
||||
|
||||
const tempDirectory = util.getTempDir();
|
||||
const IS_WINDOWS = util.isWindows();
|
||||
|
||||
export async function getJava(
|
||||
version: string,
|
||||
arch: string,
|
||||
jdkFile: string,
|
||||
javaPackage: string
|
||||
): Promise<void> {
|
||||
let toolPath = tc.find(javaPackage, version);
|
||||
|
||||
if (toolPath) {
|
||||
core.debug(`Tool found in cache ${toolPath}`);
|
||||
} else {
|
||||
let compressedFileExtension = '';
|
||||
if (!jdkFile) {
|
||||
core.debug('Downloading JDK from Azul');
|
||||
const http = new httpm.HttpClient('setup-java', undefined, {
|
||||
allowRetries: true,
|
||||
maxRetries: 3
|
||||
});
|
||||
const url = 'https://static.azul.com/zulu/bin/';
|
||||
const response = await http.get(url);
|
||||
const statusCode = response.message.statusCode || 0;
|
||||
if (statusCode < 200 || statusCode > 299) {
|
||||
let body = '';
|
||||
try {
|
||||
body = await response.readBody();
|
||||
} catch (err) {
|
||||
core.debug(`Unable to read body: ${err.message}`);
|
||||
}
|
||||
const message = `Unexpected HTTP status code '${response.message.statusCode}' when retrieving versions from '${url}'. ${body}`.trim();
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const contents = await response.readBody();
|
||||
const refs = contents.match(/<a href.*\">/gi) || [];
|
||||
const downloadInfo = getDownloadInfo(refs, version, arch, javaPackage);
|
||||
jdkFile = await tc.downloadTool(downloadInfo.url);
|
||||
version = downloadInfo.version;
|
||||
compressedFileExtension = IS_WINDOWS ? '.zip' : '.tar.gz';
|
||||
} else {
|
||||
core.debug('Retrieving Jdk from local path');
|
||||
}
|
||||
compressedFileExtension = compressedFileExtension || getFileEnding(jdkFile);
|
||||
let tempDir: string = path.join(
|
||||
tempDirectory,
|
||||
'temp_' + Math.floor(Math.random() * 2000000000)
|
||||
);
|
||||
const jdkDir = await unzipJavaDownload(
|
||||
jdkFile,
|
||||
compressedFileExtension,
|
||||
tempDir
|
||||
);
|
||||
core.debug(`jdk extracted to ${jdkDir}`);
|
||||
toolPath = await tc.cacheDir(
|
||||
jdkDir,
|
||||
javaPackage,
|
||||
getCacheVersionString(version),
|
||||
arch
|
||||
);
|
||||
}
|
||||
|
||||
let extendedJavaHome = 'JAVA_HOME_' + version + '_' + arch;
|
||||
core.exportVariable(extendedJavaHome, toolPath); //TODO: remove for v2
|
||||
// For portability reasons environment variables should only consist of
|
||||
// uppercase letters, digits, and the underscore. Therefore we convert
|
||||
// the extendedJavaHome variable to upper case and replace '.' symbols and
|
||||
// any other non-alphanumeric characters with an underscore.
|
||||
extendedJavaHome = extendedJavaHome.toUpperCase().replace(/[^0-9A-Z_]/g, '_');
|
||||
core.exportVariable('JAVA_HOME', toolPath);
|
||||
core.exportVariable(extendedJavaHome, toolPath);
|
||||
core.addPath(path.join(toolPath, 'bin'));
|
||||
core.setOutput('path', toolPath);
|
||||
core.setOutput('version', version);
|
||||
}
|
||||
|
||||
function getCacheVersionString(version: string) {
|
||||
const versionArray = version.split('.');
|
||||
const major = versionArray[0];
|
||||
const minor = versionArray.length > 1 ? versionArray[1] : '0';
|
||||
const patch = versionArray.length > 2 ? versionArray[2] : '0';
|
||||
return `${major}.${minor}.${patch}`;
|
||||
}
|
||||
|
||||
function getFileEnding(file: string): string {
|
||||
let fileEnding = '';
|
||||
|
||||
if (file.endsWith('.tar')) {
|
||||
fileEnding = '.tar';
|
||||
} else if (file.endsWith('.tar.gz')) {
|
||||
fileEnding = '.tar.gz';
|
||||
} else if (file.endsWith('.zip')) {
|
||||
fileEnding = '.zip';
|
||||
} else if (file.endsWith('.7z')) {
|
||||
fileEnding = '.7z';
|
||||
} else {
|
||||
throw new Error(`${file} has an unsupported file extension`);
|
||||
}
|
||||
|
||||
return fileEnding;
|
||||
}
|
||||
|
||||
async function extractFiles(
|
||||
file: string,
|
||||
fileEnding: string,
|
||||
destinationFolder: string
|
||||
): Promise<void> {
|
||||
const stats = fs.statSync(file);
|
||||
if (!stats) {
|
||||
throw new Error(`Failed to extract ${file} - it doesn't exist`);
|
||||
} else if (stats.isDirectory()) {
|
||||
throw new Error(`Failed to extract ${file} - it is a directory`);
|
||||
}
|
||||
|
||||
if ('.tar' === fileEnding || '.tar.gz' === fileEnding) {
|
||||
await tc.extractTar(file, destinationFolder);
|
||||
} else if ('.zip' === fileEnding) {
|
||||
await tc.extractZip(file, destinationFolder);
|
||||
} else {
|
||||
// fall through and use sevenZip
|
||||
await tc.extract7z(file, destinationFolder);
|
||||
}
|
||||
}
|
||||
|
||||
// This method recursively finds all .pack files under fsPath and unpacks them with the unpack200 tool
|
||||
async function unpackJars(fsPath: string, javaBinPath: string) {
|
||||
if (fs.existsSync(fsPath)) {
|
||||
if (fs.lstatSync(fsPath).isDirectory()) {
|
||||
for (const file in fs.readdirSync(fsPath)) {
|
||||
const curPath = path.join(fsPath, file);
|
||||
await unpackJars(curPath, javaBinPath);
|
||||
}
|
||||
} else if (path.extname(fsPath).toLowerCase() === '.pack') {
|
||||
// Unpack the pack file synchonously
|
||||
const p = path.parse(fsPath);
|
||||
const toolName = IS_WINDOWS ? 'unpack200.exe' : 'unpack200';
|
||||
const args = IS_WINDOWS ? '-r -v -l ""' : '';
|
||||
const name = path.join(p.dir, p.name);
|
||||
await exec.exec(`"${path.join(javaBinPath, toolName)}"`, [
|
||||
`${args} "${name}.pack" "${name}.jar"`
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function unzipJavaDownload(
|
||||
repoRoot: string,
|
||||
fileEnding: string,
|
||||
destinationFolder: string,
|
||||
extension?: string
|
||||
): Promise<string> {
|
||||
// Create the destination folder if it doesn't exist
|
||||
await io.mkdirP(destinationFolder);
|
||||
|
||||
const jdkFile = path.normalize(repoRoot);
|
||||
const stats = fs.statSync(jdkFile);
|
||||
if (stats.isFile()) {
|
||||
await extractFiles(jdkFile, fileEnding, destinationFolder);
|
||||
const jdkDirectory = path.join(
|
||||
destinationFolder,
|
||||
fs.readdirSync(destinationFolder)[0]
|
||||
);
|
||||
await unpackJars(jdkDirectory, path.join(jdkDirectory, 'bin'));
|
||||
return jdkDirectory;
|
||||
} else {
|
||||
throw new Error(`Jdk argument ${jdkFile} is not a file`);
|
||||
}
|
||||
}
|
||||
|
||||
function getDownloadInfo(
|
||||
refs: string[],
|
||||
version: string,
|
||||
arch: string,
|
||||
javaPackage: string
|
||||
): {version: string; url: string} {
|
||||
version = normalizeVersion(version);
|
||||
|
||||
const archExtension = arch === 'x86' ? 'i686' : 'x64';
|
||||
|
||||
let extension = '';
|
||||
if (IS_WINDOWS) {
|
||||
extension = `-win_${archExtension}.zip`;
|
||||
} else {
|
||||
if (process.platform === 'darwin') {
|
||||
extension = `-macosx_${archExtension}.tar.gz`;
|
||||
} else {
|
||||
extension = `-linux_${archExtension}.tar.gz`;
|
||||
}
|
||||
}
|
||||
|
||||
core.debug(`Searching for files with extension: ${extension}`);
|
||||
|
||||
let pkgRegexp = new RegExp('');
|
||||
let pkgTypeLength = 0;
|
||||
if (javaPackage === 'jdk') {
|
||||
pkgRegexp = /jdk.*-/gi;
|
||||
pkgTypeLength = 'jdk'.length;
|
||||
} else if (javaPackage == 'jre') {
|
||||
pkgRegexp = /jre.*-/gi;
|
||||
pkgTypeLength = 'jre'.length;
|
||||
} else if (javaPackage == 'jdk+fx') {
|
||||
pkgRegexp = /fx-jdk.*-/gi;
|
||||
pkgTypeLength = 'fx-jdk'.length;
|
||||
} else {
|
||||
throw new Error(
|
||||
`package argument ${javaPackage} is not in [jdk | jre | jdk+fx]`
|
||||
);
|
||||
}
|
||||
|
||||
// Maps version to url
|
||||
let versionMap = new Map();
|
||||
|
||||
// Filter by platform
|
||||
refs.forEach(ref => {
|
||||
if (!ref.endsWith(extension + '">')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we haven't returned, means we're looking at the correct platform
|
||||
let versions = ref.match(pkgRegexp) || [];
|
||||
if (versions.length > 1) {
|
||||
throw new Error(
|
||||
`Invalid ref received from https://static.azul.com/zulu/bin/: ${ref}`
|
||||
);
|
||||
}
|
||||
if (versions.length == 0) {
|
||||
return;
|
||||
}
|
||||
const refVersion = versions[0].slice(pkgTypeLength, versions[0].length - 1);
|
||||
|
||||
if (semver.satisfies(refVersion, version)) {
|
||||
versionMap.set(
|
||||
refVersion,
|
||||
'https://static.azul.com/zulu/bin/' +
|
||||
ref.slice('<a href="'.length, ref.length - '">'.length)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Choose the most recent satisfying version
|
||||
let curVersion = '0.0.0';
|
||||
let curUrl = '';
|
||||
for (const entry of versionMap.entries()) {
|
||||
const entryVersion = entry[0];
|
||||
const entryUrl = entry[1];
|
||||
if (semver.gt(entryVersion, curVersion)) {
|
||||
curUrl = entryUrl;
|
||||
curVersion = entryVersion;
|
||||
}
|
||||
}
|
||||
|
||||
if (curUrl == '') {
|
||||
throw new Error(
|
||||
`No valid download found for version ${version} and package ${javaPackage}. Check https://static.azul.com/zulu/bin/ for a list of valid versions or download your own jdk file and add the jdkFile argument`
|
||||
);
|
||||
}
|
||||
|
||||
return {version: curVersion, url: curUrl};
|
||||
}
|
||||
|
||||
function normalizeVersion(version: string): string {
|
||||
if (version.slice(0, 2) === '1.') {
|
||||
// Trim leading 1. for versions like 1.8
|
||||
version = version.slice(2);
|
||||
if (!version) {
|
||||
throw new Error('1. is not a valid version');
|
||||
}
|
||||
}
|
||||
|
||||
if (version.endsWith('-ea')) {
|
||||
// convert e.g. 14-ea to 14.0.0-ea
|
||||
if (version.indexOf('.') == -1) {
|
||||
version = version.slice(0, version.length - 3) + '.0.0-ea';
|
||||
}
|
||||
// match anything in -ea.X (semver won't do .x matching on pre-release versions)
|
||||
if (version[0] >= '0' && version[0] <= '9') {
|
||||
version = '>=' + version;
|
||||
}
|
||||
} else if (version.split('.').length < 3) {
|
||||
// For non-ea versions, add trailing .x if it is missing
|
||||
if (version[version.length - 1] != 'x') {
|
||||
version = version + '.x';
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
|
@ -1,60 +1,51 @@
|
|||
import * as core from '@actions/core';
|
||||
import * as installer from './installer';
|
||||
import * as auth from './auth';
|
||||
import * as gpg from './gpg';
|
||||
|
||||
import * as constants from './constants';
|
||||
import * as path from 'path';
|
||||
import { getJavaDistribution } from './distributions/distribution-factory';
|
||||
import { JavaInstallerOptions } from './distributions/base-models';
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
let version = core.getInput(constants.INPUT_VERSION);
|
||||
if (!version) {
|
||||
version = core.getInput(constants.INPUT_JAVA_VERSION, {required: true});
|
||||
const version = core.getInput(constants.INPUT_JAVA_VERSION);
|
||||
const arch = core.getInput(constants.INPUT_ARCHITECTURE);
|
||||
const distributionName = core.getInput(constants.INPUT_DISTRIBUTION);
|
||||
const packageType = core.getInput(constants.INPUT_JAVA_PACKAGE);
|
||||
const jdkFile = core.getInput(constants.INPUT_JDK_FILE);
|
||||
|
||||
if (version || distributionName) {
|
||||
if (!version || !distributionName) {
|
||||
throw new Error(
|
||||
`Both ‘${constants.INPUT_JAVA_VERSION}’ and ‘${constants.INPUT_DISTRIBUTION}’ inputs are required if one of them is specified`
|
||||
);
|
||||
}
|
||||
|
||||
const installerOptions: JavaInstallerOptions = {
|
||||
arch,
|
||||
packageType,
|
||||
version
|
||||
};
|
||||
|
||||
const distribution = getJavaDistribution(distributionName, installerOptions, jdkFile);
|
||||
if (!distribution) {
|
||||
throw new Error(`No supported distribution was found for input ${distributionName}`);
|
||||
}
|
||||
|
||||
const result = await distribution.setupJava();
|
||||
|
||||
core.info('');
|
||||
core.info('Java configuration:');
|
||||
core.info(` Distribution: ${distributionName}`);
|
||||
core.info(` Version: ${result.version}`);
|
||||
core.info(` Path: ${result.path}`);
|
||||
core.info('');
|
||||
}
|
||||
|
||||
const arch = core.getInput(constants.INPUT_ARCHITECTURE, {required: true});
|
||||
if (!['x86', 'x64'].includes(arch)) {
|
||||
throw new Error(`architecture "${arch}" is not in [x86 | x64]`);
|
||||
}
|
||||
|
||||
const javaPackage = core.getInput(constants.INPUT_JAVA_PACKAGE, {
|
||||
required: true
|
||||
});
|
||||
const jdkFile = core.getInput(constants.INPUT_JDK_FILE, {required: false});
|
||||
|
||||
await installer.getJava(version, arch, jdkFile, javaPackage);
|
||||
|
||||
const matchersPath = path.join(__dirname, '..', '..', '.github');
|
||||
core.info(`##[add-matcher]${path.join(matchersPath, 'java.json')}`);
|
||||
|
||||
const id = core.getInput(constants.INPUT_SERVER_ID, {required: false});
|
||||
const username = core.getInput(constants.INPUT_SERVER_USERNAME, {
|
||||
required: false
|
||||
});
|
||||
const password = core.getInput(constants.INPUT_SERVER_PASSWORD, {
|
||||
required: false
|
||||
});
|
||||
const gpgPrivateKey =
|
||||
core.getInput(constants.INPUT_GPG_PRIVATE_KEY, {required: false}) ||
|
||||
constants.INPUT_DEFAULT_GPG_PRIVATE_KEY;
|
||||
const gpgPassphrase =
|
||||
core.getInput(constants.INPUT_GPG_PASSPHRASE, {required: false}) ||
|
||||
(gpgPrivateKey ? constants.INPUT_DEFAULT_GPG_PASSPHRASE : undefined);
|
||||
|
||||
if (gpgPrivateKey) {
|
||||
core.setSecret(gpgPrivateKey);
|
||||
}
|
||||
|
||||
await auth.configAuthentication(id, username, password, gpgPassphrase);
|
||||
|
||||
if (gpgPrivateKey) {
|
||||
core.info('importing private key');
|
||||
const keyFingerprint = (await gpg.importKey(gpgPrivateKey)) || '';
|
||||
core.saveState(
|
||||
constants.STATE_GPG_PRIVATE_KEY_FINGERPRINT,
|
||||
keyFingerprint
|
||||
);
|
||||
}
|
||||
await auth.configureAuthentication();
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
|
|
55
src/util.ts
55
src/util.ts
|
@ -1,26 +1,41 @@
|
|||
import * as path from 'path';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import * as tc from '@actions/tool-cache';
|
||||
|
||||
export function getTempDir() {
|
||||
let tempDirectory = process.env.RUNNER_TEMP;
|
||||
if (tempDirectory === undefined) {
|
||||
let baseLocation;
|
||||
if (isWindows()) {
|
||||
// On windows use the USERPROFILE env variable
|
||||
baseLocation = process.env['USERPROFILE']
|
||||
? process.env['USERPROFILE']
|
||||
: 'C:\\';
|
||||
} else {
|
||||
if (process.platform === 'darwin') {
|
||||
baseLocation = '/Users';
|
||||
} else {
|
||||
baseLocation = '/home';
|
||||
}
|
||||
}
|
||||
tempDirectory = path.join(baseLocation, 'actions', 'temp');
|
||||
}
|
||||
let tempDirectory = process.env['RUNNER_TEMP'] || os.tmpdir();
|
||||
|
||||
return tempDirectory;
|
||||
}
|
||||
|
||||
export function isWindows() {
|
||||
return process.platform === 'win32';
|
||||
export function getVersionFromToolcachePath(toolPath: string) {
|
||||
if (toolPath) {
|
||||
return path.basename(path.dirname(toolPath));
|
||||
}
|
||||
|
||||
return toolPath;
|
||||
}
|
||||
|
||||
export async function extractJdkFile(toolPath: string, extension?: string) {
|
||||
if (!extension) {
|
||||
extension = toolPath.endsWith('.tar.gz') ? 'tar.gz' : path.extname(toolPath);
|
||||
if (extension.startsWith('.')) {
|
||||
extension = extension.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
switch (extension) {
|
||||
case 'tar.gz':
|
||||
case 'tar':
|
||||
return await tc.extractTar(toolPath);
|
||||
case 'zip':
|
||||
return await tc.extractZip(toolPath);
|
||||
default:
|
||||
return await tc.extract7z(toolPath);
|
||||
}
|
||||
}
|
||||
|
||||
export function getDownloadArchiveExtension() {
|
||||
return process.platform === 'win32' ? 'zip' : 'tar.gz';
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
extends: default
|
||||
|
||||
rules:
|
||||
# 80 chars should be enough, but don't fail if a line is longer
|
||||
# 100 chars should be enough, but don't fail if a line is longer
|
||||
line-length:
|
||||
max: 80
|
||||
max: 100
|
||||
level: warning
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue