diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index 33e6d7a18e..922a7fba1f 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -135,6 +135,7 @@ export class DebugIOSCommand implements ICommand { private $platformService: IPlatformService, private $options: IOptions, private $injector: IInjector, + private $sysInfo: ISysInfo, private $projectData: IProjectData, $iosDeviceOperations: IIOSDeviceOperations, $iOSSimulatorLogProvider: Mobile.IiOSSimulatorLogProvider) { @@ -161,6 +162,12 @@ export class DebugIOSCommand implements ICommand { this.$errors.fail(`Timeout option specifies the seconds NativeScript CLI will wait to find the inspector socket port from device's logs. Must be a number.`); } + if (this.$options.inspector) { + const macOSWarning = await this.$sysInfo.getMacOSWarningMessage(); + if (macOSWarning && macOSWarning.severity === SystemWarningsSeverity.high) { + this.$errors.fail(`You cannot use NativeScript Inspector on this OS. To use it, please update your OS.`); + } + } const result = await this.debugPlatformCommand.canExecute(args); return result; } diff --git a/lib/common/declarations.d.ts b/lib/common/declarations.d.ts index 914a08681b..8aedd1092c 100644 --- a/lib/common/declarations.d.ts +++ b/lib/common/declarations.d.ts @@ -1143,6 +1143,12 @@ interface IDeviceLiveSyncService extends IDeviceLiveSyncServiceBase { afterInstallApplicationAction?(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise; } +interface ISystemWarning { + message: string; + severity: SystemWarningsSeverity; + toString?: () => string; +} + interface ISysInfo { getSysInfo(config?: NativeScriptDoctor.ISysInfoConfig): Promise; /** @@ -1163,15 +1169,15 @@ interface ISysInfo { /** * Gets all global warnings for the current environment, for example Node.js version compatibility, OS compatibility, etc. - * @return {Promise} All warnings. Empty array is returned in case the system is setup correctly. + * @return {Promise} All warnings. Empty array is returned in case the system is setup correctly. */ - getSystemWarnings(): Promise; + getSystemWarnings(): Promise; /** * Gets warning message for current macOS version. * @return {Promise} Message in case the current macOS version is deprecated, null otherwise. */ - getMacOSWarningMessage(): Promise; + getMacOSWarningMessage(): Promise; /** * Returns the value of engines.node key from CLI's package.json file. diff --git a/lib/common/definitions/logger.d.ts b/lib/common/definitions/logger.d.ts index 0231f4f238..62338bf6d0 100644 --- a/lib/common/definitions/logger.d.ts +++ b/lib/common/definitions/logger.d.ts @@ -16,4 +16,5 @@ interface ILogger { prepare(item: any): string; printInfoMessageOnSameLine(message: string): void; printMsgWithTimeout(message: string, timeout: number): Promise; + printOnStderr(formatStr?: any, ...args: any[]): void; } diff --git a/lib/common/host-info.ts b/lib/common/host-info.ts index accdfa74b9..9fcdd5b825 100644 --- a/lib/common/host-info.ts +++ b/lib/common/host-info.ts @@ -57,17 +57,7 @@ export class HostInfo implements IHostInfo { try { const systemProfileOutput = await this.$childProcess.exec(systemProfileCommand); - // Output of command is similar to: - /* -Software: - - System Software Overview: - - System Version: macOS 10.13.3 (17D47) - Kernel Version: Darwin 17.4.0 - Time since boot: 68 days 22:12 -*/ - const versionRegExp = /System Version:\s+?macOS\s+?(\d+\.\d+)\.\d+\s+/g; + const versionRegExp = /System Version:\s+?macOS\s+?(\d+\.\d+)(\.\d+)?\s+/g; const regExpMatchers = versionRegExp.exec(systemProfileOutput); const macOSVersion = regExpMatchers && regExpMatchers[1]; if (macOSVersion) { diff --git a/lib/common/logger.ts b/lib/common/logger.ts index 32cad3aca6..e6201a5512 100644 --- a/lib/common/logger.ts +++ b/lib/common/logger.ts @@ -148,6 +148,12 @@ export class Logger implements ILogger { this.write(formattedMessage); } + public printOnStderr(...args: string[]): void { + if (process.stderr) { + process.stderr.write(util.format.apply(null, args)); + } + } + private getPasswordEncodedArguments(args: string[]): string[] { return _.map(args, argument => { if (typeof argument === 'string' && !!argument.match(/password/i)) { diff --git a/lib/common/test/unit-tests/host-info.ts b/lib/common/test/unit-tests/host-info.ts index 784de59d83..96838ea850 100644 --- a/lib/common/test/unit-tests/host-info.ts +++ b/lib/common/test/unit-tests/host-info.ts @@ -65,6 +65,27 @@ describe("hostInfo", () => { assert.equal(calledCommand, "system_profiler SPSoftwareDataType -detailLevel mini"); }); + it("returns correct macOS version based on system_profile, when version has two numbers only", async () => { + const testInjector = createTestInjector(); + const hostInfo = testInjector.resolve("hostInfo"); + const childProcess = testInjector.resolve("childProcess"); + let calledCommand = ""; + childProcess.exec = async (command: string, options?: any, execOptions?: IExecOptions): Promise => { + calledCommand = command; + return `Software: + + System Software Overview: + + System Version: macOS 10.14 (18A391) + Kernel Version: Darwin 18.0.0 + Time since boot: 1 day 5:52`; + }; + + const macOSVersion = await hostInfo.getMacOSVersion(); + assert.deepEqual(macOSVersion, "10.14"); + assert.equal(calledCommand, "system_profiler SPSoftwareDataType -detailLevel mini"); + }); + it("returns correct macOS version when system_profile call throws", async () => { const testInjector = createTestInjector(); const hostInfo = testInjector.resolve("hostInfo"); diff --git a/lib/common/test/unit-tests/stubs.ts b/lib/common/test/unit-tests/stubs.ts index 71de7170c8..f1f9709623 100644 --- a/lib/common/test/unit-tests/stubs.ts +++ b/lib/common/test/unit-tests/stubs.ts @@ -41,6 +41,10 @@ export class CommonLoggerStub implements ILogger { printMarkdown(message: string): void { this.output += message; } + + printOnStderr(...args: string[]): void { + // nothing to do here + } } export class ErrorsStub implements IErrors { diff --git a/lib/common/verify-node-version.ts b/lib/common/verify-node-version.ts index eebfccb408..02dc71872b 100644 --- a/lib/common/verify-node-version.ts +++ b/lib/common/verify-node-version.ts @@ -48,12 +48,12 @@ export function verifyNodeVersion(): void { } var nodeWarning = getNodeWarning(); - if (nodeWarning) { - console.warn((os.EOL + nodeWarning + os.EOL).yellow.bold); + if (nodeWarning && nodeWarning.message) { + console.warn((`${os.EOL}${nodeWarning.message}${os.EOL}`).yellow.bold); } } -export function getNodeWarning(): string { +export function getNodeWarning(): ISystemWarning { var verificationOpts = getNodeVersionOpts(); var cliName = verificationOpts.cliName; var supportedVersionsRange = verificationOpts.supportedVersionsRange; @@ -78,6 +78,9 @@ export function getNodeWarning(): string { } } - return warningMessage; + return { + message: warningMessage, + severity: SystemWarningsSeverity.medium + }; } /* tslint:enable */ diff --git a/lib/constants.ts b/lib/constants.ts index 702024befb..4317535c57 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -186,9 +186,10 @@ export class AssetConstants { export class MacOSVersions { public static Sierra = "10.12"; public static HighSierra = "10.13"; + public static Mojave = "10.14"; } -export const MacOSDeprecationStringFormat = "Support for macOS %s is deprecated and will be removed in one of the next releases of NativeScript. Please, upgrade to the latest macOS version."; +export const MacOSDeprecationStringFormat = "NativeScript does not support macOS %s and some functionality may not work. Please, upgrade to the latest macOS version."; export const PROGRESS_PRIVACY_POLICY_URL = "https://www.progress.com/legal/privacy-policy"; export class SubscribeForNewsletterMessages { public static AgreeToReceiveEmailMsg = "I agree".green.bold + " to receive email communications from Progress Software or its Partners (`https://www.progress.com/partners/partner-directory`)," + diff --git a/lib/definitions/system-warnings.d.ts b/lib/definitions/system-warnings.d.ts new file mode 100644 index 0000000000..d02841e093 --- /dev/null +++ b/lib/definitions/system-warnings.d.ts @@ -0,0 +1,4 @@ +declare const enum SystemWarningsSeverity { + medium = "medium", + high = "high" +} diff --git a/lib/nativescript-cli.ts b/lib/nativescript-cli.ts index bbac0422f8..941367fad7 100644 --- a/lib/nativescript-cli.ts +++ b/lib/nativescript-cli.ts @@ -26,8 +26,12 @@ import { settlePromises } from "./common/helpers"; const $sysInfo = $injector.resolve("sysInfo"); const macOSWarning = await $sysInfo.getMacOSWarningMessage(); if (macOSWarning) { - const message = EOL + macOSWarning + EOL ; - logger.warn(message); + const message = `${EOL}${macOSWarning.message}${EOL}`; + if (macOSWarning.severity === SystemWarningsSeverity.high) { + logger.printOnStderr(message.red.bold); + } else { + logger.warn(message); + } } const commandDispatcher: ICommandDispatcher = $injector.resolve("commandDispatcher"); diff --git a/lib/sys-info.ts b/lib/sys-info.ts index 5850b92777..6a3a35714e 100644 --- a/lib/sys-info.ts +++ b/lib/sys-info.ts @@ -35,15 +35,17 @@ export class SysInfo implements ISysInfo { } @exported("sysInfo") - public async getSystemWarnings(): Promise { - const warnings: string[] = []; + public async getSystemWarnings(): Promise { + const warnings: ISystemWarning[] = []; const macOSWarningMessage = await this.getMacOSWarningMessage(); if (macOSWarningMessage) { + macOSWarningMessage.toString = function() { return this.message; }; warnings.push(macOSWarningMessage); } const nodeWarning = getNodeWarning(); if (nodeWarning) { + nodeWarning.toString = function() { return this.message; }; warnings.push(nodeWarning); } @@ -57,10 +59,13 @@ export class SysInfo implements ISysInfo { return jsonContent && jsonContent.engines && jsonContent.engines.node; } - public async getMacOSWarningMessage(): Promise { + public async getMacOSWarningMessage(): Promise { const macOSVersion = await this.$hostInfo.getMacOSVersion(); if (macOSVersion && macOSVersion < MacOSVersions.HighSierra) { - return format(MacOSDeprecationStringFormat, macOSVersion); + return { + message: format(MacOSDeprecationStringFormat, macOSVersion), + severity: SystemWarningsSeverity.high + }; } return null; diff --git a/test/stubs.ts b/test/stubs.ts index 38de63aee2..3880b3eeaf 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -42,6 +42,10 @@ export class LoggerStub implements ILogger { } printMarkdown(message: string): void { } + + printOnStderr(...args: string[]): void { + // nothing to do here + } } export class ProcessServiceStub implements IProcessService { diff --git a/test/sys-info.ts b/test/sys-info.ts index cb7dfbe6f2..5294fd7d5f 100644 --- a/test/sys-info.ts +++ b/test/sys-info.ts @@ -32,8 +32,8 @@ describe("sysInfo", () => { }; describe("getSystemWarnings", () => { - const getSystemWarnings = async (opts?: { nodeJsWarning?: string, macOSDeprecatedVersion?: string }): Promise => { - sandbox.stub(verifyNodeVersion, "getNodeWarning").returns(opts && opts.nodeJsWarning); + const getSystemWarnings = async (opts?: { nodeJsWarning?: string, macOSDeprecatedVersion?: string }): Promise => { + sandbox.stub(verifyNodeVersion, "getNodeWarning").returns(opts && opts.nodeJsWarning ? { message: opts.nodeJsWarning, severity: SystemWarningsSeverity.medium } : null); const testInjector = createTestInjector(); const $hostInfo = testInjector.resolve("hostInfo"); @@ -50,28 +50,31 @@ describe("sysInfo", () => { it("returns correct single warning when macOS version is deprecated", async () => { const macOSDeprecatedVersion = MacOSVersions.Sierra; - const macOSWarning = format(MacOSDeprecationStringFormat, macOSDeprecatedVersion); + const macOSWarning = { message: format(MacOSDeprecationStringFormat, macOSDeprecatedVersion), severity: SystemWarningsSeverity.high }; const warnings = await getSystemWarnings({ macOSDeprecatedVersion }); + _.each(warnings, warning => delete warning.toString); assert.deepEqual(warnings, [macOSWarning]); }); it("returns correct single warning when Node.js version is deprecated", async () => { - const nodeJsWarning = "Node.js Warning"; - const warnings = await getSystemWarnings({ nodeJsWarning }); + const nodeJsWarning = { message: "Node.js Warning", severity: SystemWarningsSeverity.medium }; + const warnings = await getSystemWarnings({ nodeJsWarning: nodeJsWarning.message }); + _.each(warnings, warning => delete warning.toString); assert.deepEqual(warnings, [nodeJsWarning]); }); it("returns correct warnings when both Node.js and macOS versions are deprecated", async () => { const macOSDeprecatedVersion = MacOSVersions.Sierra; - const macOSWarning = format(MacOSDeprecationStringFormat, macOSDeprecatedVersion); - const nodeJsWarning = "Node.js Warning"; - const warnings = await getSystemWarnings({ macOSDeprecatedVersion, nodeJsWarning }); + const macOSWarning = { message: format(MacOSDeprecationStringFormat, macOSDeprecatedVersion), severity: SystemWarningsSeverity.high }; + const nodeJsWarning = { message: "Node.js Warning", severity: SystemWarningsSeverity.medium }; + const warnings = await getSystemWarnings({ macOSDeprecatedVersion, nodeJsWarning: nodeJsWarning.message }); + _.each(warnings, warning => delete warning.toString); assert.deepEqual(warnings, [macOSWarning, nodeJsWarning]); }); }); describe("getMacOSWarningMessage", () => { - const getMacOSWarning = async (macOSDeprecatedVersion?: string): Promise => { + const getMacOSWarning = async (macOSDeprecatedVersion?: string): Promise => { sandbox.stub(verifyNodeVersion, "getNodeWarning").returns(null); const testInjector = createTestInjector(); @@ -89,8 +92,9 @@ describe("sysInfo", () => { it("returns correct single warning when macOS version is deprecated", async () => { const macOSDeprecatedVersion = MacOSVersions.Sierra; - const macOSWarning = format(MacOSDeprecationStringFormat, macOSDeprecatedVersion); + const macOSWarning: ISystemWarning = { message: format(MacOSDeprecationStringFormat, macOSDeprecatedVersion), severity: SystemWarningsSeverity.high }; const warning = await getMacOSWarning(macOSDeprecatedVersion); + delete warning.toString; assert.deepEqual(warning, macOSWarning); }); });