diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 1b7aa658..7f443954 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -5,7 +5,7 @@ jobs: runs-on: ${{ matrix.operating-system }} strategy: matrix: - operating-system: [ubuntu-latest, windows-latest] + operating-system: [ubuntu-latest, windows-latest, macOS-latest] steps: - name: Checkout uses: actions/checkout@v2 @@ -24,7 +24,7 @@ jobs: runs-on: ${{ matrix.operating-system }} strategy: matrix: - operating-system: [ubuntu-latest, windows-latest] + operating-system: [ubuntu-latest, windows-latest, macOS-latest] steps: - name: Checkout uses: actions/checkout@v2 diff --git a/README.md b/README.md index 2cd5af64..e466d8b4 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,8 @@ steps: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: - java-version: '9.0.4' # The JDK version to make available on the path. + java-version: '11' # The JDK version to make available on the path. + vendor: adoptopenjdk # (adoptopenjdk or zulu) - defaults to adoptopenjdk java-package: jdk # (jre, jdk, or jdk+fx) - defaults to jdk architecture: x64 # (x64 or x86) - defaults to x64 - run: java -cp java HelloWorldApp @@ -30,13 +31,13 @@ Examples of version specifications that the java-version parameter will accept: e.g. ```6, 7, 8, 9, 10, 11, 12, 13, ...``` -- A semver Java version specification +- A semver Java version specification (Zulu only) e.g. ```8.0.232, 7.0.181, 11.0.4``` e.g. ```8.0.x, >11.0.3, >=13.0.1, <8.0.212``` - -- An early access (EA) Java version + +- An early access (EA) Java version (Zulu only) e.g. ```14-ea, 15-ea``` @@ -46,7 +47,7 @@ Examples of version specifications that the java-version parameter will accept: Note that, per semver rules, EA builds will be matched by explicit EA version specifications. -- 1.x syntax +- 1.x syntax (Zulu only) e.g. ```1.8``` (same as ```8```) diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts index ff2d61b5..ea970567 100644 --- a/__tests__/installer.test.ts +++ b/__tests__/installer.test.ts @@ -13,12 +13,15 @@ import * as installer from '../src/installer'; let javaFilePath = ''; let javaUrl = ''; +let additionalPath = ''; if (process.platform === 'win32') { javaFilePath = path.join(javaDir, 'java_win.zip'); javaUrl = 'https://download.java.net/java/GA/jdk12/33/GPL/openjdk-12_windows-x64_bin.zip'; } else if (process.platform === 'darwin') { javaFilePath = path.join(javaDir, 'java_mac.tar.gz'); + // macOS tarballs are in bundle format + additionalPath = '/Contents/Home'; javaUrl = 'https://download.java.net/java/GA/jdk12/33/GPL/openjdk-12_osx-x64_bin.tar.gz'; } else { @@ -51,34 +54,57 @@ describe('installer tests', () => { } }, 100000); - it('Installs version of Java from jdkFile if no matching version is installed', async () => { - await installer.getJava('12', 'x64', javaFilePath, 'jdk'); + it('Installs version of Java from jdkFile if no matching version is installed AdoptOpenJDK', async () => { + await installer.getJava('12', 'adoptopenjdk', 'x64', javaFilePath, 'jdk'); const JavaDir = path.join(toolDir, 'jdk', '12.0.0', 'x64'); - expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true); - expect(fs.existsSync(path.join(JavaDir, 'bin'))).toBe(true); + expect(fs.existsSync(path.join(JavaDir, additionalPath, 'bin'))).toBe(true); }, 100000); - it('Throws if invalid directory to jdk', async () => { + it('Installs version of Java from jdkFile if no matching version is installed Zulu', async () => { + await installer.getJava('12', 'zulu', 'x64', javaFilePath, 'jdk'); + const JavaDir = path.join(toolDir, 'jdk', '12.0.0', 'x64'); + expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true); + expect(fs.existsSync(path.join(JavaDir, additionalPath, 'bin'))).toBe(true); + }, 100000); + + it('Throws if invalid directory to jdk AdoptOpenJDK', async () => { let thrown = false; try { - await installer.getJava('1000', 'x64', 'bad path', 'jdk'); + await installer.getJava('1000', 'adoptopenjdk', 'x64', 'bad path', 'jdk'); } catch { thrown = true; } expect(thrown).toBe(true); }); - it('Downloads java if no file given', async () => { - await installer.getJava('8.0.102', 'x64', '', 'jdk'); - const JavaDir = path.join(toolDir, 'jdk', '8.0.102', 'x64'); + it('Throws if invalid directory to jdk Zulu', async () => { + let thrown = false; + try { + await installer.getJava('1000', 'zulu', 'x64', 'bad path', 'jdk'); + } catch { + thrown = true; + } + expect(thrown).toBe(true); + }); + it('Downloads java if no file given AdoptOpenJDK', async () => { + await installer.getJava('8', 'adoptopenjdk', 'x64', '', 'jdk'); + const JavaDir = path.join(toolDir, 'jdk', '8.0.0', 'x64'); + + expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true); + expect(fs.existsSync(path.join(JavaDir, additionalPath, 'bin'))).toBe(true); + }, 100000); + + it('Downloads java if no file given Zulu', async () => { + await installer.getJava('8.0.102', 'zulu', 'x64', '', 'jdk'); + const JavaDir = path.join(toolDir, 'jdk', '8.0.102', 'x64'); expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true); expect(fs.existsSync(path.join(JavaDir, 'bin'))).toBe(true); }, 100000); it('Downloads java with 1.x syntax', async () => { - await installer.getJava('1.10', 'x64', '', 'jdk'); + await installer.getJava('1.10', 'zulu', 'x64', '', 'jdk'); const JavaDir = path.join(toolDir, 'jdk', '10.0.2', 'x64'); expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true); @@ -86,15 +112,23 @@ describe('installer tests', () => { }, 100000); it('Downloads java with normal semver syntax', async () => { - await installer.getJava('9.0.x', 'x64', '', 'jdk'); + await installer.getJava('9.0.x', 'zulu', 'x64', '', 'jdk'); const JavaDir = path.join(toolDir, 'jdk', '9.0.7', 'x64'); expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true); expect(fs.existsSync(path.join(JavaDir, 'bin'))).toBe(true); }, 100000); - it('Downloads java if package is jre', async () => { - await installer.getJava('8.0.222', 'x64', '', 'jre'); + it('Downloads java if package is jre AdoptOpenJDK', async () => { + await installer.getJava('11', 'adoptopenjdk', 'x64', '', 'jre'); + const JavaDir = path.join(toolDir, 'jre', '11.0.0', 'x64'); + + expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true); + expect(fs.existsSync(path.join(JavaDir, additionalPath, 'bin'))).toBe(true); + }, 100000); + + it('Downloads java if package is jre Zulu', async () => { + await installer.getJava('8.0.222', 'zulu', 'x64', '', 'jre'); const JavaDir = path.join(toolDir, 'jre', '8.0.222', 'x64'); expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true); @@ -102,17 +136,27 @@ describe('installer tests', () => { }, 100000); it('Downloads java if package is jdk+fx', async () => { - await installer.getJava('8.0.222', 'x64', '', 'jdk+fx'); + await installer.getJava('8.0.222', 'zulu', 'x64', '', 'jdk+fx'); const JavaDir = path.join(toolDir, 'jdk+fx', '8.0.222', 'x64'); expect(fs.existsSync(`${JavaDir}.complete`)).toBe(true); expect(fs.existsSync(path.join(JavaDir, 'bin'))).toBe(true); }, 100000); - it('Throws if invalid java package is specified', async () => { + it('Throws if invalid java package is specified AdoptOpenJDK', async () => { let thrown = false; try { - await installer.getJava('8.0.222', 'x64', '', 'bad jdk'); + await installer.getJava('8', 'adoptopenjdk', 'x64', '', 'bad jdk'); + } catch { + thrown = true; + } + expect(thrown).toBe(true); + }); + + it('Throws if invalid java package is specified Zulu', async () => { + let thrown = false; + try { + await installer.getJava('8.0.222', 'zulu', 'x64', '', 'bad jdk'); } catch { thrown = true; } @@ -122,7 +166,7 @@ describe('installer tests', () => { it('Throws if invalid directory to jdk', async () => { let thrown = false; try { - await installer.getJava('1000', 'x64', 'bad path', 'jdk'); + await installer.getJava('1000', 'zulu', 'x64', 'bad path', 'jdk'); } catch { thrown = true; } @@ -136,6 +180,7 @@ describe('installer tests', () => { // This will throw if it doesn't find it in the cache (because no such version exists) await installer.getJava( '250', + 'zulu', 'x64', 'path shouldnt matter, found in cache', 'jdk' @@ -149,7 +194,7 @@ describe('installer tests', () => { let thrown = false; try { // This will throw if it doesn't find it in the cache (because no such version exists) - await installer.getJava('251', 'x64', 'bad path', 'jdk'); + await installer.getJava('251', 'zulu', 'x64', 'bad path', 'jdk'); } catch { thrown = true; } diff --git a/action.yml b/action.yml index 74572687..c11bab7d 100644 --- a/action.yml +++ b/action.yml @@ -9,6 +9,11 @@ inputs: 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 + vendor: + description: 'The vendor to fetch the binary from (adoptopenjdk, zulu). + Defaults to adoptopenjdk' + required: false + default: 'adoptopenjdk' java-package: description: 'The package type (jre, jdk, jdk+fx)' required: false diff --git a/src/installer.ts b/src/installer.ts index 88a8ff18..75ec8c11 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -13,6 +13,7 @@ const IS_WINDOWS = util.isWindows(); export async function getJava( version: string, + vendor: string, arch: string, jdkFile: string, javaPackage: string @@ -23,31 +24,26 @@ export async function getJava( 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}`); + async function setupVendor(vendor: string) { + switch (vendor) { + case 'adoptopenjdk': + [jdkFile, version] = await getJavaAdoptOpenJDK( + version, + javaPackage, + arch + ); + break; + case 'zulu': + [jdkFile, version] = await getJavaZulu(version, javaPackage); + break; } - const message = `Unexpected HTTP status code '${response.message.statusCode}' when retrieving versions from '${url}'. ${body}`.trim(); - throw new Error(message); + return [jdkFile, version]; } - const contents = await response.readBody(); - const refs = contents.match(//gi) || []; - const downloadInfo = getDownloadInfo(refs, version, javaPackage); - jdkFile = await tc.downloadTool(downloadInfo.url); - version = downloadInfo.version; + [jdkFile, version] = await setupVendor(vendor); + compressedFileExtension = IS_WINDOWS ? '.zip' : '.tar.gz'; } else { core.debug('Retrieving Jdk from local path'); @@ -71,6 +67,8 @@ export async function getJava( ); } + if (process.platform === 'darwin') toolPath += '/Contents/Home'; + let extendedJavaHome = 'JAVA_HOME_' + version + '_' + arch; core.exportVariable(extendedJavaHome, toolPath); //TODO: remove for v2 // For portability reasons environment variables should only consist of @@ -85,6 +83,52 @@ export async function getJava( core.setOutput('version', version); } +async function getJavaZulu(version: string, javaPackage: string) { + 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(//gi) || []; + const downloadInfo = getDownloadInfo(refs, version, javaPackage); + const jdkFile = await tc.downloadTool(downloadInfo.url); + + version = downloadInfo.version; + return [jdkFile, version]; +} + +async function getJavaAdoptOpenJDK( + version: string, + javaPackage: string, + arch: string +) { + core.debug('Downloading JDK from AdoptOpenJDK'); + + const jdkFile = await tc.downloadTool( + `https://api.adoptopenjdk.net/v3/binary/latest/${normalizeAdoptOpenJDK( + version + )}/ga/${OS}/${arch}/${javaPackage}/hotspot/normal/adoptopenjdk` + ); + return [jdkFile, version]; +} + function getCacheVersionString(version: string) { const versionArray = version.split('.'); const major = versionArray[0]; @@ -290,3 +334,8 @@ function normalizeVersion(version: string): string { return version; } + +function normalizeAdoptOpenJDK(version: string): string { + if (version == '1.8') return '8'; + return version; +} diff --git a/src/setup-java.ts b/src/setup-java.ts index db169f29..49fea11a 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -11,13 +11,14 @@ async function run() { if (!version) { version = core.getInput(constants.INPUT_JAVA_VERSION, {required: true}); } + const vendor = core.getInput('vendor', {required: true}); const arch = core.getInput(constants.INPUT_ARCHITECTURE, {required: true}); 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); + await installer.getJava(version, vendor, arch, jdkFile, javaPackage); const matchersPath = path.join(__dirname, '..', '..', '.github'); core.info(`##[add-matcher]${path.join(matchersPath, 'java.json')}`); diff --git a/src/util.ts b/src/util.ts index 671727c4..b16a111f 100644 --- a/src/util.ts +++ b/src/util.ts @@ -12,6 +12,7 @@ export function getTempDir() { } else { if (process.platform === 'darwin') { baseLocation = '/Users'; + toolPath += '/Contents/Home'; } else { baseLocation = '/home'; }