diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index d4a2a9d5..089fbca0 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -17,7 +17,7 @@ import {join} from 'path'; import {homedir} from 'os'; -import {Config, Log, ConsoleLog} from '@bubblewrap/core'; +import {Config, Log, ConsoleLog, JdkInstaller} from '@bubblewrap/core'; import * as inquirer from 'inquirer'; import {existsSync} from 'fs'; import {promises as fsPromises} from 'fs'; @@ -28,20 +28,44 @@ export const DEFAULT_CONFIG_FILE_PATH = join(DEFAULT_CONFIG_FOLDER, DEFAULT_CONF const LEGACY_CONFIG_FOLDER = join(homedir(), '.llama-pack'); const LEGACY_CONFIG_NAME = 'llama-pack-config.json'; const LEGACY_CONFIG_FILE_PATH = join(LEGACY_CONFIG_FOLDER, LEGACY_CONFIG_NAME); +const DEFAULT_JDK_FOLDER = join(DEFAULT_CONFIG_FOLDER, 'jdk'); async function createConfig(): Promise { - const result = await inquirer.prompt([ + const installRequest = await inquirer.prompt([ { - name: 'jdkPath', - message: 'Path to the JDK:', - validate: existsSync, - }, { - name: 'androidSdkPath', + type: 'confirm', + name: 'jdk', + message: 'Do you want Bubblewrap to install JDK? (Enter "No" to use your JDK installation)', + default: true, + }, + ]); + + let jdkPath; + if (!installRequest.jdk) { + const jdk = await inquirer.prompt([ + { + name: 'path', + message: 'Path to your existing JDK:', + validate: existsSync, + }, + ]); + jdkPath = jdk.path; + } else { + await fsPromises.mkdir(DEFAULT_JDK_FOLDER); + console.log(`Downloading JDK 8 to ${DEFAULT_JDK_FOLDER}`); + const jdkInstaller = new JdkInstaller(process); + jdkPath = await jdkInstaller.install(DEFAULT_JDK_FOLDER); + } + + const androidSdk = await inquirer.prompt([ + { + name: 'path', message: 'Path to the Android SDK:', validate: existsSync, }, ]); - return new Config(result.jdkPath, result.androidSdkPath); + + return new Config(jdkPath, androidSdk.path); } async function renameConfigIfNeeded(log: Log): Promise { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 299fc887..472e2fe8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -21,6 +21,7 @@ import {Log, ConsoleLog} from './lib/Log'; import {MockLog} from './lib/mock/MockLog'; import {JarSigner} from './lib/jdk/JarSigner'; import {JdkHelper} from './lib/jdk/JdkHelper'; +import {JdkInstaller} from './lib/jdk/JdkInstaller'; import {KeyTool} from './lib/jdk/KeyTool'; import {TwaManifest, DisplayModes, DisplayMode, asDisplayMode, SigningKeyInfo} from './lib/TwaManifest'; @@ -36,6 +37,7 @@ export {AndroidSdkTools, GradleWrapper, JarSigner, JdkHelper, + JdkInstaller, KeyTool, Log, ConsoleLog, diff --git a/packages/core/src/lib/jdk/JdkInstaller.ts b/packages/core/src/lib/jdk/JdkInstaller.ts new file mode 100644 index 00000000..2b0d1377 --- /dev/null +++ b/packages/core/src/lib/jdk/JdkInstaller.ts @@ -0,0 +1,94 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +import * as path from 'path'; +import util = require('../util'); + +const JDK_VERSION = '8u265-b01'; +const JDK_DIR = `jdk${JDK_VERSION}`; +const DOWNLOAD_JDK_BIN_ROOT = `https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk${JDK_VERSION}/`; +const DOWNLOAD_JDK_SRC_ROOT = 'https://github.com/AdoptOpenJDK/openjdk-jdk8u/archive/'; +const JDK_BIN_VERSION = JDK_VERSION.replace('-', ''); +const JDK_FILE_NAME_MAC = `OpenJDK8U-jdk_x64_mac_hotspot_${JDK_BIN_VERSION}.tar.gz`; +const JDK_FILE_NAME_WIN32 = `OpenJDK8U-jdk_x86-32_windows_hotspot_${JDK_BIN_VERSION}.zip`; +const JDK_FILE_NAME_LINUX64 = `OpenJDK8U-jdk_x64_linux_hotspot_${JDK_BIN_VERSION}.tar.gz`; +const JDK_SRC_ZIP = `jdk${JDK_VERSION}.zip`; + +/** + * Install JDK 8 by downloading the binary and source code and + * decompressing it at path given by user. Source code is required + * based on discussions with legal team about licensing. + */ +export class JdkInstaller { + private process: NodeJS.Process; + private downloadFile: string; + private unzipFunction: (srcPath: string, dstPath: string, deleteWhenDone: boolean) + => Promise; + private joinPath: (...paths: string[]) => string; + + /** + * Constructs a new instance of JdkInstaller. + * + * @param process {NodeJS.Process} process information from the OS process. + */ + constructor(process: NodeJS.Process) { + this.process = process; + this.unzipFunction = util.untar; + this.joinPath = path.posix.join; + switch (process.platform) { + case 'win32': { + this.downloadFile = JDK_FILE_NAME_WIN32; + this.unzipFunction = util.unzipFile; + this.joinPath = path.win32.join; + break; + } + case 'darwin': { + this.downloadFile = JDK_FILE_NAME_MAC; + break; + } + case 'linux': { + this.downloadFile = JDK_FILE_NAME_LINUX64; + break; + } + default: + this.downloadFile = ''; + throw new Error(`Platform not found or unsupported: ${this.process.platform}.`); + } + } + + /** + * Downloads the platform-appropriate version of JDK 8, including + * binary and source code. + * + * @param installPath {string} path to install JDK at. + */ + async install(installPath: string): Promise { + const dstPath = path.resolve(installPath); + const downloadSrcUrl = DOWNLOAD_JDK_SRC_ROOT + JDK_SRC_ZIP; + const localSrcZipPath = this.joinPath(dstPath, JDK_SRC_ZIP); + await util.downloadFile(downloadSrcUrl, localSrcZipPath); + await util.unzipFile(localSrcZipPath, dstPath, true); + + const downloadBinUrl = DOWNLOAD_JDK_BIN_ROOT + this.downloadFile; + const localBinPath = this.joinPath(dstPath, this.downloadFile); + await util.downloadFile(downloadBinUrl, localBinPath); + await this.unzipFunction(localBinPath, dstPath, true); + + return this.joinPath(dstPath, JDK_DIR); + } +}