From 8545629e899e63d613d80b5c079e92bf5394c0c3 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Fri, 5 Oct 2018 14:07:55 +0300 Subject: [PATCH] feat: drop support for macOS Sierra and below Drop support for macOS Sierra and below as we are no longer testing this OS. Drop a line on stderr on each CLI command and inform the user that this OS is not supported. Change the public API (getSystemWarnings) to return objects with information about the severity of the warnings. This way the consumer can decide how to handle the warnings. Fix an issue when getting macOS version from System Profile - the code was not working when version has two numbers (10.14 for example). Add new method in logger that allows printing to stderr. It should be used instead of `console.error`, so we can make the code testable. --- lib/commands/debug.ts | 7 +++++++ lib/common/declarations.d.ts | 12 +++++++++--- lib/common/definitions/logger.d.ts | 1 + lib/common/host-info.ts | 12 +----------- lib/common/logger.ts | 6 ++++++ lib/common/test/unit-tests/host-info.ts | 21 +++++++++++++++++++++ lib/common/test/unit-tests/stubs.ts | 4 ++++ lib/common/verify-node-version.ts | 11 +++++++---- lib/constants.ts | 3 ++- lib/definitions/system-warnings.d.ts | 4 ++++ lib/nativescript-cli.ts | 8 ++++++-- lib/sys-info.ts | 13 +++++++++---- test/stubs.ts | 4 ++++ test/sys-info.ts | 24 ++++++++++++++---------- 14 files changed, 95 insertions(+), 35 deletions(-) create mode 100644 lib/definitions/system-warnings.d.ts 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); }); });