'use strict' ; import { ExtensionUtil } from '../util/ExtensionUtil' ; import * as path from 'path' ; import * as fs from 'fs-extra' ; import * as vscode from 'vscode' ; import * as semver from 'semver' ; import { CommandUtil } from '../util/CommandUtil' ; import { GlobalState , ExtensionData } from '../util/GlobalState' ; import { RequiredDependencies , OptionalDependencies , Dependencies , defaultDependencies , DependencyProperties } from './Dependencies' ; import OS = require ( 'os' ) ; export class DependencyManager { public static instance ( ) { return this . _instance ; } private static _instance : DependencyManager = new DependencyManager ( ) ; private constructor ( ) { } public isValidDependency ( dependency ) { const name = dependency . name ; const needsToBeInstalledWithSemver = [ defaultDependencies . optional . node . name , defaultDependencies . optional . java . name , defaultDependencies . optional . npm . name , defaultDependencies . required . docker . name , defaultDependencies . optional . go . name , defaultDependencies . optional . openssl . name ] ; const needsToBeInstalled = [ defaultDependencies . optional . goExtension . name , defaultDependencies . optional . javaLanguageExtension . name , defaultDependencies . optional . javaDebuggerExtension . name , defaultDependencies . optional . javaTestRunnerExtension . name , defaultDependencies . optional . nodeTestRunnerExtension . name , defaultDependencies . optional . ibmCloudAccountExtension . name , ] ; const needsToBeComplete = [ defaultDependencies . required . dockerForWindows . name , defaultDependencies . required . systemRequirements . name ] ; if ( needsToBeInstalledWithSemver . includes ( name ) ) { if ( dependency . version ) { return semver . satisfies ( dependency . version , dependency . requiredVersion ) ; } else { return false ; } } else if ( needsToBeInstalled . includes ( name ) ) { if ( dependency . version ) { return true ; } else { return false ; } } else if ( needsToBeComplete . includes ( name ) ) { if ( ! dependency . complete ) { dependency . complete = false ; } return dependency . complete ; } return false ; } public areAllValidDependencies ( properties , dependencies ) { const isWindows = ( process . platform === 'win32' ) ; for ( const property of properties ) { if ( dependencies . hasOwnProperty ( property ) ) { if ( ! isWindows && dependencies [ property ] . windowsOnly ) { continue ; } const isValid = this . isValidDependency ( dependencies [ property ] ) ; if ( ! isValid ) { return false ; } } } return true ; } public async hasPreReqsInstalled ( dependencies ? , optionalInstalled = false ) { if ( ! dependencies ) { dependencies = await this . getPreReqVersions ( ) ; } const localFabricEnabled = ExtensionUtil . getExtensionLocalFabricSetting ( ) ; if ( localFabricEnabled ) { const localFabricProperties = [ 'docker' , 'systemRequirements' ] ; if ( ! this . areAllValidDependencies ( localFabricProperties , dependencies ) ) { return false ; } } if ( process . platform === 'win32' ) { if ( localFabricEnabled ) { const windowsProperties = [ 'dockerForWindows' ] ; if ( ! this . areAllValidDependencies ( windowsProperties , dependencies ) ) { return false ; } } } if ( optionalInstalled ) { const optionalInstallProperties = Object . getOwnPropertyNames ( defaultDependencies . optional ) ; if ( ! this . areAllValidDependencies ( optionalInstallProperties , dependencies ) ) { return false ; } } return true ; } public async getPreReqVersions ( ) { const extensionData = GlobalState . get ( ) ; const getMultipleVersions = [ this . getRequiredDependencies ( extensionData . dockerForWindows ) , this . getOptionalDependencies ( ) , ] ; const [ requiredDependencies , optionalDependencies ] = await Promise . all ( getMultipleVersions ) ; const dependencies = { ... requiredDependencies , ... optionalDependencies , } ; return dependencies ; } public async clearExtensionCache ( ) { const extensionPath = ExtensionUtil . getExtensionPath ( ) ; const extensionsPath = path . resolve ( extensionPath , '..' ) ; const currentDate = new Date ( ) ; await fs . utimes ( extensionsPath , currentDate , currentDate ) ; } public getPackageJsonPath ( ) { return path . resolve ( ExtensionUtil . getExtensionPath ( ) , 'package.json' ) ; } public async getRawPackageJson ( ) { const fileContents = await fs . readFile ( this . getPackageJsonPath ( ) , 'utf8' ) ; return JSON . parse ( fileContents ) ; } public async writePackageJson ( packageJson ) { const packageJsonString = JSON . stringify ( packageJson , null , 4 ) ; return fs . writeFile ( this . getPackageJsonPath ( ) , packageJsonString , 'utf8' ) ; } private isCommandFound ( output ) { if ( output . toLowerCase ( ) . includes ( 'not found' ) || output . toLowerCase ( ) . includes ( 'not recognized' ) || output . toLowerCase ( ) . includes ( 'no such file or directory' ) || output . toLowerCase ( ) . includes ( 'unable to get active developer directory' ) ) { return false ; } else { return true ; } } private async getRequiredDependencies ( dockerForWindows ) { const localFabricEnabled = ExtensionUtil . getExtensionLocalFabricSetting ( ) ; const isWindows = process . platform === 'win32' ; const dependencies = { } ; if ( localFabricEnabled ) { const getDockerVersions = [ this . getDockerVersion ( ) ] ; const [ dockerVersion ] = await Promise . all ( getDockerVersions ) ; const systemRequirementsVersion = OS . totalmem ( ) / 1073741824 ; dependencies . docker = { ... defaultDependencies . required . docker , version : dockerVersion , } ; dependencies . systemRequirements = { ... defaultDependencies . required . systemRequirements , version : systemRequirementsVersion , complete : systemRequirementsVersion >= 4 , } ; if ( isWindows ) { dependencies . dockerForWindows = { ... defaultDependencies . required . dockerForWindows , complete : ! ! dockerForWindows , } ; } return dependencies ; } return { } ; } private async getOptionalDependencies ( ) { const getOptionalVersions = [ this . getNodeVersion ( ) , this . getNPMVersion ( ) , this . getGoVersion ( ) , this . getJavaVersion ( ) , ] ; const [ nodeVersion , npmVersion , goVersion , javaVersion ] = await Promise . all ( getOptionalVersions ) ; const goExtensionVersion = this . getExtensionVersion ( DependencyProperties . GO_LANGUAGE_EXTENSION ) ; const javaLanguageExtensionVersion = this . getExtensionVersion ( DependencyProperties . JAVA_LANGUAGE_EXTENSION ) ; const javaDebuggerExtensionVersion = this . getExtensionVersion ( DependencyProperties . JAVA_DEBUG_EXTENSION ) ; const javaTestRunnerExtensionVersion = this . getExtensionVersion ( DependencyProperties . JAVA_TEST_RUNNER_EXTENSION ) ; const nodeTestRunnerExtensionVersion = this . getExtensionVersion ( DependencyProperties . NODEJS_TEST_RUNNER_EXTENSION ) ; const ibmCloudAccountExtensionVersion = this . getExtensionVersion ( DependencyProperties . IBM_CLOUD_ACCOUNT_EXTENSION ) ; const isWindows = process . platform === 'win32' ; const optionalDependencies = { ... defaultDependencies . optional } ; if ( isWindows ) { optionalDependencies . openssl . version = await this . getOpensslVersion ( ) ; } else { for ( const [ property , value ] of Object . entries ( optionalDependencies ) ) { if ( value . windowsOnly ) { delete optionalDependencies [ property ] ; } } } optionalDependencies . node . version = nodeVersion ; optionalDependencies . npm . version = npmVersion ; optionalDependencies . go . version = goVersion ; optionalDependencies . goExtension . version = goExtensionVersion ; optionalDependencies . java . version = javaVersion ; optionalDependencies . javaLanguageExtension . version = javaLanguageExtensionVersion ; optionalDependencies . javaDebuggerExtension . version = javaDebuggerExtensionVersion ; optionalDependencies . javaTestRunnerExtension . version = javaTestRunnerExtensionVersion ; optionalDependencies . nodeTestRunnerExtension . version = nodeTestRunnerExtensionVersion ; optionalDependencies . ibmCloudAccountExtension . version = ibmCloudAccountExtensionVersion ; return optionalDependencies ; } private async getDockerVersion ( ) { try { const dockerResult = await CommandUtil . sendCommand ( 'docker -v' ) ; if ( this . isCommandFound ( dockerResult ) ) { const dockerMatchedVersion = dockerResult . match ( /version (.*),/ ) [ 1 ] ; const dockerCleaned = semver . clean ( dockerMatchedVersion , { loose : true } ) ; const dockerVersionCoerced = semver . coerce ( dockerCleaned ) ; const dockerVersion = semver . valid ( dockerVersionCoerced ) ; return dockerVersion ; } } catch ( error ) { return ; } } private async getOpensslVersion ( ) { try { const libPath = path . win32 . join ( 'C:\\OpenSSL-Win64' , 'lib' , 'libeay32.lib' ) ; const libExists = await fs . pathExists ( libPath ) ; if ( libExists ) { return '1.0.2' ; } } catch ( error ) { } } private async getNodeVersion ( ) { try { const nodeResult = await CommandUtil . sendCommand ( 'node -v' ) ; if ( this . isCommandFound ( nodeResult ) ) { const nodeVersion = nodeResult . substr ( 1 ) ; const nodeValid = semver . valid ( nodeVersion ) ; return nodeValid ; } } catch ( error ) { } } private async getNPMVersion ( ) { try { const npmResult = await CommandUtil . sendCommand ( 'npm -v' ) ; if ( this . isCommandFound ( npmResult ) ) { const npmVersion = semver . valid ( npmResult ) ; return npmVersion ; } } catch ( error ) { } } private async getGoVersion ( ) { try { const goResult = await CommandUtil . sendCommand ( 'go version' ) ; if ( this . isCommandFound ( goResult ) ) { const goMatchedVersion = goResult . match ( /go version go(.*) / ) [ 1 ] ; const goVersionCoerced = semver . coerce ( goMatchedVersion ) ; const goVersion = semver . valid ( goVersionCoerced ) ; return goVersion ; } } catch ( error ) { } } private async getJavaVersion ( ) { try { let getVersion = true ; if ( process . platform === 'darwin' ) { const javaPath = '/Library/Java/JavaVirtualMachines' ; const javaDirExists = await fs . pathExists ( javaPath ) ; getVersion = javaDirExists ; } if ( getVersion ) { const javaResult = await CommandUtil . sendCommand ( 'java -version 2>&1' ) ; if ( this . isCommandFound ( javaResult ) ) { const javaMatchedVersion = javaResult . match ( /(openjdk|java) version "(.*)"/ ) [ 2 ] ; const javaVersionCoerced = semver . coerce ( javaMatchedVersion ) ; const javaVersion = semver . valid ( javaVersionCoerced ) ; return javaVersion ; } } } catch ( error ) { } } private getExtensionVersion ( extension ) { try { const nextensionResult = vscode . extensions . getExtension ( extension ) ; if ( nextensionResult ) { return nextensionResult . packageJSON . version ; } } catch ( error ) { } } }