Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
NativeScript CLI Changelog
================

3.2.0 - RC.1 (2017, August 29)
3.3.1 (2017, November 17)
### Fixed
* [Fixed #3164](https://github.com/NativeScript/nativescript-cli/issues/3164): `npm run build-*-bundle` gets stuck at nativescript-unit-test-runner hook.
* [Fixed #3182](https://github.com/NativeScript/nativescript-cli/issues/3182): CLI fails when unable to start Analytics Broker process.

3.3.0 (2017, October 26)
==

### New

* [Implemented #3076](https://github.com/NativeScript/nativescript-cli/issues/3076): NativeScript setup scripts should have silent installer mode.

### Fixed
* [Fixed #3141](https://github.com/NativeScript/nativescript-cli/issues/3141): No console.log output Xcode 9 iOS 11.
* [Fixed #3016](https://github.com/NativeScript/nativescript-cli/issues/3016): tns_modules randomly appears in app folder and breaks build.
* [Fixed #2967](https://github.com/NativeScript/nativescript-cli/issues/2967): Create plugin by static static libraries error.

3.2.0 (2017, September 7)
==

### Fixed
Expand Down
42 changes: 41 additions & 1 deletion PublicAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const tns = require("nativescript");
* [disableDebugging](#disableDebugging)
* [getLiveSyncDeviceDescriptors](#getLiveSyncDeviceDescriptors)
* [events](#events)
* [analyticsSettingsService](#analyticsSettingsService)
* [getClientId](#getClientId)


## Module projectService
Expand Down Expand Up @@ -271,7 +273,7 @@ interface ISettingsService {

* Usage:
```JavaScript
tns.settingsService.setSettings({ userAgentName: "myUserAgent" });
tns.settingsService.setSettings({ userAgentName: "myUserAgent", profileDir: "customProfileDir" });
```

## npm
Expand Down Expand Up @@ -855,6 +857,44 @@ tns.liveSyncService.on("debuggerDetached", debugInfo => {
console.log(`Detached debugger for device with id ${debugInfo.deviceIdentifier}`);
});
```
## analyticsSettingsService
Provides methods for accessing the analytics settings file data.

### getClientId
The `getClientId` method allows retrieving the clientId used in the analytics tracking

* Definition:
```TypeScript
/**
* Gets the clientId used for analytics tracking
* @returns {Promise<string>} Client identifier in UUIDv4 standard.
*/
getClientId(): Promise<string>;
```

* Usage:
```JavaScript
tns.analyticsSettingsService.getClientId()
.then(clientId => console.log(clientId));
```

### getUserAgentString
The `getUserAgentString` method allows retrieving a user agent string identifying the current system

* Definition:
```TypeScript
/**
* Gets user agent string identifing the current system in the following format: `${identifier} (${systemInfo}) ${osArch}`
* @param {string} identifier The product identifier.
* @returns {string} The user agent string.
*/
getUserAgentString(identifier: string): string;
```

* Usage:
```JavaScript
const userAgentString = tns.analyticsSettingsService.getUserAgentString("tns/3.3.0");
```

## How to add a new method to Public API
CLI is designed as command line tool and when it is used as a library, it does not give you access to all of the methods. This is mainly implementation detail. Most of the CLI's code is created to work in command line, not as a library, so before adding method to public API, most probably it will require some modification.
Expand Down
2 changes: 1 addition & 1 deletion lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ $injector.require("iOSDebugService", "./services/ios-debug-service");
$injector.require("androidDebugService", "./services/android-debug-service");

$injector.require("userSettingsService", "./services/user-settings-service");
$injector.require("analyticsSettingsService", "./services/analytics-settings-service");
$injector.requirePublic("analyticsSettingsService", "./services/analytics-settings-service");
$injector.require("analyticsService", "./services/analytics/analytics-service");
$injector.require("eqatecAnalyticsProvider", "./services/analytics/eqatec-analytics-provider");
$injector.require("googleAnalyticsProvider", "./services/analytics/google-analytics-provider");
Expand Down
4 changes: 2 additions & 2 deletions lib/commands/generate-help.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export class GenerateHelpCommand implements ICommand {
public allowedParameters: ICommandParameter[] = [];

constructor(private $htmlHelpService: IHtmlHelpService) { }
constructor(private $helpService: IHelpService) { }

public async execute(args: string[]): Promise<void> {
return this.$htmlHelpService.generateHtmlPages();
return this.$helpService.generateHtmlPages();
}
}

Expand Down
6 changes: 3 additions & 3 deletions lib/commands/post-install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ export class PostInstallCliCommand extends PostInstallCommand {
private $subscriptionService: ISubscriptionService,
$staticConfig: Config.IStaticConfig,
$commandsService: ICommandsService,
$htmlHelpService: IHtmlHelpService,
$options: ICommonOptions,
$helpService: IHelpService,
$settingsService: ISettingsService,
$doctorService: IDoctorService,
$analyticsService: IAnalyticsService,
$logger: ILogger) {
super($fs, $staticConfig, $commandsService, $htmlHelpService, $options, $doctorService, $analyticsService, $logger);
super($fs, $staticConfig, $commandsService, $helpService, $settingsService, $doctorService, $analyticsService, $logger);
}

public async execute(args: string[]): Promise<void> {
Expand Down
7 changes: 5 additions & 2 deletions lib/commands/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ function RunTestCommandFactory(platform: string) {
return function RunTestCommand(
$options: IOptions,
$testExecutionService: ITestExecutionService,
$projectData: IProjectData) {
$projectData: IProjectData,
$analyticsService: IAnalyticsService) {
$projectData.initializeProjectData();
$analyticsService.setShouldDispose($options.justlaunch || !$options.watch);
const projectFilesConfig = helpers.getProjectFilesConfig({ isReleaseBuild: $options.release });
this.execute = (args: string[]): Promise<void> => $testExecutionService.startTestRunner(platform, $projectData, projectFilesConfig);
this.allowedParameters = [];
Expand All @@ -16,8 +18,9 @@ $injector.registerCommand("dev-test|android", RunTestCommandFactory('android'));
$injector.registerCommand("dev-test|ios", RunTestCommandFactory('iOS'));

function RunKarmaTestCommandFactory(platform: string) {
return function RunKarmaTestCommand($options: IOptions, $testExecutionService: ITestExecutionService, $projectData: IProjectData) {
return function RunKarmaTestCommand($options: IOptions, $testExecutionService: ITestExecutionService, $projectData: IProjectData, $analyticsService: IAnalyticsService) {
$projectData.initializeProjectData();
$analyticsService.setShouldDispose($options.justlaunch || !$options.watch);
const projectFilesConfig = helpers.getProjectFilesConfig({ isReleaseBuild: $options.release });
this.execute = (args: string[]): Promise<void> => $testExecutionService.startKarmaServer(platform, $projectData, projectFilesConfig);
this.allowedParameters = [];
Expand Down
7 changes: 3 additions & 4 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export class StaticConfig extends StaticConfigBase implements IStaticConfig {
public ERROR_REPORT_SETTING_NAME = "TrackExceptions";
public ANALYTICS_INSTALLATION_ID_SETTING_NAME = "AnalyticsInstallationID";
public INSTALLATION_SUCCESS_MESSAGE = "Installation successful. You are good to go. Connect with us on `http://twitter.com/NativeScript`.";
public get PROFILE_DIR_NAME(): string {
return ".nativescript-cli";
}

constructor($injector: IInjector) {
super($injector);
Expand Down Expand Up @@ -60,10 +63,6 @@ export class StaticConfig extends StaticConfigBase implements IStaticConfig {

public version = require("../package.json").version;

public get helpTextPath(): string {
return path.join(__dirname, "../resources/help.txt");
}

public get HTML_CLI_HELPERS_DIR(): string {
return path.join(__dirname, "../docs/helpers");
}
Expand Down
3 changes: 0 additions & 3 deletions lib/nativescript-cli-lib-bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,4 @@ $injector.requirePublic("companionAppsService", "./common/appbuilder/services/li
$injector.requirePublicClass("deviceEmitter", "./common/appbuilder/device-emitter");
$injector.requirePublicClass("deviceLogProvider", "./common/appbuilder/device-log-provider");

// We need this because some services check if (!$options.justlaunch) to start the device log after some operation.
// We don't want this behaviour when the CLI is required as library.
$injector.resolve("options").justlaunch = true;
$injector.resolve<IStaticConfig>("staticConfig").disableAnalytics = true;
8 changes: 5 additions & 3 deletions lib/node-package-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,12 @@ export class NodePackageManager implements INodePackageManager {
});

// Npm 5 return different object after performing `npm install --dry-run`.
// Considering that the dependency is already installed we should
// find it in the `updated` key as a first element of the array.
// We find the correct dependency by searching for the `userSpecifiedPackageName` in the
// `npm5Output.updated` array and as a fallback, considering that the dependency is already installed,
// we find it as the first element.
if (!name && npm5Output.updated) {
const updatedDependency = npm5Output.updated[0];
const packageNameWithoutVersion = userSpecifiedPackageName.split('@')[0];
const updatedDependency = _.find(npm5Output.updated, ['name', packageNameWithoutVersion]) || npm5Output.updated[0];
return {
name: updatedDependency.name,
originalOutput,
Expand Down
3 changes: 2 additions & 1 deletion lib/npm-installation-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export class NpmInstallationManager implements INpmInstallationManager {
private $childProcess: IChildProcess,
private $logger: ILogger,
private $options: IOptions,
private $settingsService: ISettingsService,
private $fs: IFileSystem,
private $staticConfig: IStaticConfig) {
}
Expand Down Expand Up @@ -59,7 +60,7 @@ export class NpmInstallationManager implements INpmInstallationManager {

// local installation takes precedence over cache
if (!this.inspectorAlreadyInstalled(inspectorPath)) {
const cachePath = path.join(this.$options.profileDir, constants.INSPECTOR_CACHE_DIRNAME);
const cachePath = path.join(this.$settingsService.getProfileDir(), constants.INSPECTOR_CACHE_DIRNAME);
this.prepareCacheDir(cachePath);
const pathToPackageInCache = path.join(cachePath, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName);

Expand Down
24 changes: 3 additions & 21 deletions lib/options.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import * as commonOptionsLibPath from "./common/options";
import * as osenv from "osenv";
import * as path from "path";

export class Options extends commonOptionsLibPath.OptionsBase {
constructor($errors: IErrors,
$staticConfig: IStaticConfig,
$hostInfo: IHostInfo) {
$hostInfo: IHostInfo,
$settingsService: ISettingsService) {
super({
ipa: { type: OptionType.String },
frameworkPath: { type: OptionType.String },
Expand Down Expand Up @@ -39,24 +38,7 @@ export class Options extends commonOptionsLibPath.OptionsBase {
clean: { type: OptionType.Boolean },
watch: { type: OptionType.Boolean, default: true }
},
path.join($hostInfo.isWindows ? process.env.AppData : path.join(osenv.home(), ".local/share"), ".nativescript-cli"),
$errors, $staticConfig);

// On Windows we moved settings from LocalAppData to AppData. Move the existing file to keep the existing settings
// I guess we can remove this code after some grace period, say after 1.7 is out
if ($hostInfo.isWindows) {
try {
const shelljs = require("shelljs"),
oldSettings = path.join(process.env.LocalAppData, ".nativescript-cli", "user-settings.json"),
newSettings = path.join(process.env.AppData, ".nativescript-cli", "user-settings.json");
if (shelljs.test("-e", oldSettings) && !shelljs.test("-e", newSettings)) {
shelljs.mkdir(path.join(process.env.AppData, ".nativescript-cli"));
shelljs.mv(oldSettings, newSettings);
}
} catch (err) {
// ignore the error - it is too early to use $logger here
}
}
$errors, $staticConfig, $settingsService);

const that = (<any>this);
// if justlaunch is set, it takes precedence over the --watch flag and the default true value
Expand Down
38 changes: 38 additions & 0 deletions lib/services/analytics-settings-service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { createGUID } from "../common/helpers";
import { exported } from "../common/decorators";

class AnalyticsSettingsService implements IAnalyticsSettingsService {
private static SESSIONS_STARTED_KEY_PREFIX = "SESSIONS_STARTED_";

constructor(private $userSettingsService: UserSettings.IUserSettingsService,
private $staticConfig: IStaticConfig,
private $hostInfo: IHostInfo,
private $osInfo: IOsInfo,
private $logger: ILogger) { }

public async canDoRequest(): Promise<boolean> {
Expand All @@ -15,6 +18,7 @@ class AnalyticsSettingsService implements IAnalyticsSettingsService {
return this.getSettingValueOrDefault("USER_ID");
}

@exported("analyticsSettingsService")
public getClientId(): Promise<string> {
return this.getSettingValueOrDefault(this.$staticConfig.ANALYTICS_INSTALLATION_ID_SETTING_NAME);
}
Expand All @@ -36,6 +40,40 @@ class AnalyticsSettingsService implements IAnalyticsSettingsService {
return this.$userSettingsService.saveSetting<number>(this.getSessionsProjectKey(projectName), count);
}

@exported("analyticsSettingsService")
public getUserAgentString(identifier: string): string {
let osString = "";
const osRelease = this.$osInfo.release();

if (this.$hostInfo.isWindows) {
osString = `Windows NT ${osRelease}`;
} else if (this.$hostInfo.isDarwin) {
osString = `Macintosh`;
const macRelease = this.getMacOSReleaseVersion(osRelease);
if (macRelease) {
osString += `; Intel Mac OS X ${macRelease}`;
}
} else {
osString = `Linux x86`;
if (this.$osInfo.arch() === "x64") {
osString += "_64";
}
}

const userAgent = `${identifier} (${osString}; ${this.$osInfo.arch()})`;

return userAgent;
}

private getMacOSReleaseVersion(osRelease: string): string {
// https://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history
// Each macOS version is labeled 10.<version>, where it looks like <versions> is taken from the major version returned by os.release() (16.x.x for example) and subtracting 4 from it.
// So the version becomes "10.12" in this case.
// Could be improved by spawning `system_profiler SPSoftwareDataType` and getting the System Version line from the result.
const majorVersion = osRelease && _.first(osRelease.split("."));
return majorVersion && `10.${+majorVersion - 4}`;
}

private getSessionsProjectKey(projectName: string): string {
return `${AnalyticsSettingsService.SESSIONS_STARTED_KEY_PREFIX}${projectName}`;
}
Expand Down
11 changes: 9 additions & 2 deletions lib/services/analytics/analytics-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isInteractive } from '../../common/helpers';
import { DeviceTypes, AnalyticsClients } from "../../common/constants";

export class AnalyticsService extends AnalyticsServiceBase {
private static ANALYTICS_BROKER_START_TIMEOUT = 30 * 1000;
private static ANALYTICS_BROKER_START_TIMEOUT = 10 * 1000;
private brokerProcess: ChildProcess;

constructor(protected $logger: ILogger,
Expand Down Expand Up @@ -182,7 +182,14 @@ export class AnalyticsService extends AnalyticsServiceBase {
}

private async sendMessageToBroker(message: ITrackingInformation): Promise<void> {
const broker = await this.getAnalyticsBroker();
let broker: ChildProcess;
try {
broker = await this.getAnalyticsBroker();
} catch (err) {
this.$logger.trace("Unable to get broker instance due to error: ", err);
return;
}

return new Promise<void>((resolve, reject) => {
if (broker && broker.connected) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Sync indexes with the custom dimensions of the cross client analytics project
declare const enum GoogleAnalyticsCrossClientCustomDimensions {
sessionId = "cd9",
clientId = "cd10",
crossClientId = "cd12",
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const enum GoogleAnalyticsCustomDimensions {
declare const enum GoogleAnalyticsCustomDimensions {
cliVersion = "cd1",
projectType = "cd2",
clientID = "cd3",
Expand Down
Loading