diff --git a/CHANGELOG.md b/CHANGELOG.md
index 291586b657..32ae3f35d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,43 @@
NativeScript CLI Changelog
================
+6.1.2 (2019, September 18)
+==
+
+### Fixed
+* [Fixed #5018](https://github.com/NativeScript/nativescript-cli/issues/5018): Track runtime versions on add and on build, run, deploy
+
+
+6.1.1 (2019, September 17)
+==
+
+### Fixed
+
+* [Fixed #5015](https://github.com/NativeScript/nativescript-cli/pull/5015): CLI passes `--preserve-symlinks` to the webpack itself, not to the Node.js
+* [Fixed #4893](https://github.com/NativeScript/nativescript-cli/issues/4893): `tns preview` crashes when scanning on devices with different platforms
+* [Fixed #4939](https://github.com/NativeScript/nativescript-cli/issues/4939): Xcode 11 warning: `CFBundleIdentifier value must be the same as PRODUCT_BUNDLE_IDENTIFIER`
+
+6.1.0 (2019, September 04)
+==
+
+### New
+
+* [Implemented #4229](https://github.com/NativeScript/nativescript-cli/issues/4229): Do not display command usage help after execution is started
+* [Implemented #4909](https://github.com/NativeScript/nativescript-cli/issues/4909): Support for Xcode 11 and iOS 13
+* [Implemented #4926](https://github.com/NativeScript/nativescript-cli/issues/4926): Android SDK 29 support
+* [Implemented #4947](https://github.com/NativeScript/nativescript-cli/issues/4947): Add tracking for both React NativeScript and Svelte Native projects
+* [Implemented #4966](https://github.com/NativeScript/nativescript-cli/issues/4966): Support LiveSync to iOS Wi-Fi devices
+* [Implemented #4974](https://github.com/NativeScript/nativescript-cli/issues/4974): Ask the users why they've uninstalled NativeScript CLI
+* [Implemented #4976](https://github.com/NativeScript/nativescript-cli/issues/4976): Handle changes in iOS and Android Runtime 6.1.0 logging
+* [Implemented #4980](https://github.com/NativeScript/nativescript-cli/issues/4980): Update message for subscribing to NativeScript newsletter
+* [Implemented #4992](https://github.com/NativeScript/nativescript-cli/pull/4992): Allow tns to be able to use npm configuration properly
+
+### Fixed
+
+* [Fixed #4936](https://github.com/NativeScript/nativescript-cli/issues/4936): HMR not recovering after exception in Angular lazy routes
+* [Fixed #4958](https://github.com/NativeScript/nativescript-cli/issues/4958): `tns doctor` fails when setup is not correct and user selects to fix it manually
+* [Fixed #4971](https://github.com/NativeScript/nativescript-cli/issues/4971): Not needed checks are executed on `pod install`
+
6.0.3 (2019, August 05)
==
* [Fixed #4914](https://github.com/NativeScript/nativescript-cli/issues/4914): livesync not working with command tns test android
diff --git a/README.md b/README.md
index 37062115da..2d27145292 100644
--- a/README.md
+++ b/README.md
@@ -143,9 +143,10 @@ The --insecure
flag allows you to perform insecure SSL connections
#### Limitations
* You can provide the `` and `` attributes only on Windows systems.
-* Proxy settings for the npm and the Android Gradle need to be configured separately. For more information, see the following articles:
+* Proxy settings for the npm, the Android Gradle and (optional) Docker need to be configured separately. For more information, see the following articles:
* [Configure the npm proxy](https://docs.npmjs.com/misc/config#https-proxy)
* [Configure the Android Gradle proxy](https://docs.gradle.org/3.3/userguide/build_environment.html#sec:accessing_the_web_via_a_proxy)
+ * [Configure the Docker proxy](https://docs.docker.com/network/proxy/)
### Display Current Proxy Settings
diff --git a/docs/man_pages/general/proxy-set.md b/docs/man_pages/general/proxy-set.md
index ec14d3a1fa..d894039bd0 100644
--- a/docs/man_pages/general/proxy-set.md
+++ b/docs/man_pages/general/proxy-set.md
@@ -31,9 +31,10 @@ General | `$ tns proxy set [ <% if(isWindows) {%>[ []]<
### Command Limitations
* You can set credentials only on Windows systems.
-* Proxy settings for npm and (Android) Gradle need to be set separately.
+* Proxy settings for npm, (Android) Gradle and (optional) Docker need to be set separately.
* configuring `npm` proxy - https://docs.npmjs.com/misc/config#https-proxy
* (Android) configuring Gradle proxy - set global configuration in the user directory - _/.gradle/gradle.properties_ - https://docs.gradle.org/3.3/userguide/build_environment.html#sec:accessing_the_web_via_a_proxy
+ * configuring Docker proxy - https://docs.docker.com/network/proxy/
### Related Commands
diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts
index 56d552ba6e..39393ad64c 100644
--- a/lib/bootstrap.ts
+++ b/lib/bootstrap.ts
@@ -15,6 +15,7 @@ $injector.require("gradleCommandService", "./services/android/gradle-command-ser
$injector.require("gradleBuildService", "./services/android/gradle-build-service");
$injector.require("gradleBuildArgsService", "./services/android/gradle-build-args-service");
$injector.require("iOSEntitlementsService", "./services/ios-entitlements-service");
+$injector.require("iOSNativeTargetService", "./services/ios-native-target-service");
$injector.require("iOSExtensionsService", "./services/ios-extensions-service");
$injector.require("iOSWatchAppService", "./services/ios-watch-app-service");
$injector.require("iOSProjectService", "./services/ios-project-service");
diff --git a/lib/constants.ts b/lib/constants.ts
index 1ce1a04016..877328afd0 100644
--- a/lib/constants.ts
+++ b/lib/constants.ts
@@ -186,7 +186,9 @@ export const enum TrackActionNames {
AcceptTracking = "Accept Tracking",
Performance = "Performance",
PreviewAppData = "Preview App Data",
- UninstallCLI = "Uninstall CLI"
+ UninstallCLI = "Uninstall CLI",
+ UsingRuntimeVersion = "Using Runtime Version",
+ AddPlatform = "Add Platform"
}
export const AnalyticsEventLabelDelimiter = "__";
diff --git a/lib/controllers/deploy-controller.ts b/lib/controllers/deploy-controller.ts
index 8c51ac20c3..bffb70425a 100644
--- a/lib/controllers/deploy-controller.ts
+++ b/lib/controllers/deploy-controller.ts
@@ -11,7 +11,11 @@ export class DeployController {
const executeAction = async (device: Mobile.IDevice) => {
const deviceDescriptor = _.find(deviceDescriptors, dd => dd.identifier === device.deviceInfo.identifier);
- await this.$prepareController.prepare(deviceDescriptor.buildData);
+ const prepareData = {
+ ...deviceDescriptor.buildData,
+ nativePrepare: { skipNativePrepare: !!deviceDescriptor.skipNativePrepare }
+ };
+ await this.$prepareController.prepare(prepareData);
const packageFilePath = await deviceDescriptor.buildAction();
await this.$deviceInstallAppService.installOnDevice(device, { ...deviceDescriptor.buildData, buildForDevice: !device.isEmulator }, packageFilePath);
};
diff --git a/lib/controllers/prepare-controller.ts b/lib/controllers/prepare-controller.ts
index 9d6a85195d..fe0ff9460a 100644
--- a/lib/controllers/prepare-controller.ts
+++ b/lib/controllers/prepare-controller.ts
@@ -1,10 +1,9 @@
import * as choki from "chokidar";
import { hook } from "../common/helpers";
-import { performanceLog } from "../common/decorators";
+import { performanceLog, cache } from "../common/decorators";
import { EventEmitter } from "events";
import * as path from "path";
-import { PREPARE_READY_EVENT_NAME, WEBPACK_COMPILATION_COMPLETE, PACKAGE_JSON_FILE_NAME, PLATFORMS_DIR_NAME } from "../constants";
-
+import { PREPARE_READY_EVENT_NAME, WEBPACK_COMPILATION_COMPLETE, PACKAGE_JSON_FILE_NAME, PLATFORMS_DIR_NAME, TrackActionNames, AnalyticsEventLabelDelimiter } from "../constants";
interface IPlatformWatcherData {
hasWebpackCompilerProcess: boolean;
nativeFilesWatcher: choki.FSWatcher;
@@ -27,12 +26,14 @@ export class PrepareController extends EventEmitter {
private $projectChangesService: IProjectChangesService,
private $projectDataService: IProjectDataService,
private $webpackCompilerService: IWebpackCompilerService,
- private $watchIgnoreListService: IWatchIgnoreListService
+ private $watchIgnoreListService: IWatchIgnoreListService,
+ private $analyticsService: IAnalyticsService
) { super(); }
public async prepare(prepareData: IPrepareData): Promise {
const projectData = this.$projectDataService.getProjectData(prepareData.projectDir);
+ await this.trackRuntimeVersion(prepareData.platform, projectData);
await this.$pluginsService.ensureAllDependenciesAreInstalled(projectData);
return this.prepareCore(prepareData, projectData);
@@ -127,6 +128,21 @@ export class PrepareController extends EventEmitter {
}
private async startNativeWatcherWithPrepare(platformData: IPlatformData, projectData: IProjectData, prepareData: IPrepareData): Promise {
+ let newNativeWatchStarted = false;
+ let hasNativeChanges = false;
+
+ if (prepareData.watchNative) {
+ newNativeWatchStarted = await this.startNativeWatcher(platformData, projectData);
+ }
+
+ if (newNativeWatchStarted) {
+ hasNativeChanges = await this.$prepareNativePlatformService.prepareNativePlatform(platformData, projectData, prepareData);
+ }
+
+ return hasNativeChanges;
+ }
+
+ private async startNativeWatcher(platformData: IPlatformData, projectData: IProjectData): Promise {
if (this.watchersData[projectData.projectDir][platformData.platformNameLowerCase].nativeFilesWatcher) {
return false;
}
@@ -155,9 +171,7 @@ export class PrepareController extends EventEmitter {
this.watchersData[projectData.projectDir][platformData.platformNameLowerCase].nativeFilesWatcher = watcher;
- const hasNativeChanges = await this.$prepareNativePlatformService.prepareNativePlatform(platformData, projectData, prepareData);
-
- return hasNativeChanges;
+ return true;
}
@hook('watchPatterns')
@@ -186,5 +200,24 @@ export class PrepareController extends EventEmitter {
this.persistedData.push(filesChangeEventData);
}
}
+
+ @cache()
+ private async trackRuntimeVersion(platform: string, projectData: IProjectData): Promise {
+ let runtimeVersion: string = null;
+ try {
+ const platformData = this.$platformsDataService.getPlatformData(platform, projectData);
+ const runtimeVersionData = this.$projectDataService.getNSValue(projectData.projectDir, platformData.frameworkPackageName);
+ runtimeVersion = runtimeVersionData && runtimeVersionData.version;
+ } catch (err) {
+ this.$logger.trace(`Unable to get runtime version for project directory: ${projectData.projectDir} and platform ${platform}. Error is: `, err);
+ }
+
+ if (runtimeVersion) {
+ await this.$analyticsService.trackEventActionInGoogleAnalytics({
+ action: TrackActionNames.UsingRuntimeVersion,
+ additionalData: `${platform.toLowerCase()}${AnalyticsEventLabelDelimiter}${runtimeVersion}`
+ });
+ }
+ }
}
$injector.register("prepareController", PrepareController);
diff --git a/lib/controllers/preview-app-controller.ts b/lib/controllers/preview-app-controller.ts
index 447e86f429..79fb137586 100644
--- a/lib/controllers/preview-app-controller.ts
+++ b/lib/controllers/preview-app-controller.ts
@@ -99,7 +99,7 @@ export class PreviewAppController extends EventEmitter implements IPreviewAppCon
data.env = data.env || {};
data.env.externals = this.$previewAppPluginsService.getExternalPlugins(device);
- const prepareData = this.$prepareDataService.getPrepareData(data.projectDir, device.platform.toLowerCase(), { ...data, nativePrepare: { skipNativePrepare: true }, watch: true });
+ const prepareData = this.$prepareDataService.getPrepareData(data.projectDir, device.platform.toLowerCase(), { ...data, nativePrepare: { skipNativePrepare: true }, watch: true, watchNative: false });
await this.$prepareController.prepare(prepareData);
try {
diff --git a/lib/data/prepare-data.ts b/lib/data/prepare-data.ts
index c24f6fcc0f..d13ed4d01a 100644
--- a/lib/data/prepare-data.ts
+++ b/lib/data/prepare-data.ts
@@ -5,6 +5,7 @@ export class PrepareData extends ControllerDataBase {
public hmr: boolean;
public env: any;
public watch?: boolean;
+ public watchNative: boolean = true;
constructor(public projectDir: string, public platform: string, data: any) {
super(projectDir, platform, data);
@@ -16,6 +17,9 @@ export class PrepareData extends ControllerDataBase {
hmr: data.hmr || data.useHotModuleReload
};
this.watch = data.watch;
+ if (_.isBoolean(data.watchNative)) {
+ this.watchNative = data.watchNative;
+ }
}
}
diff --git a/lib/definitions/prepare.d.ts b/lib/definitions/prepare.d.ts
index 43f93ae225..9ad8f0a3c1 100644
--- a/lib/definitions/prepare.d.ts
+++ b/lib/definitions/prepare.d.ts
@@ -7,6 +7,7 @@ declare global {
hmr: boolean;
env: any;
watch?: boolean;
+ watchNative: boolean
}
interface IiOSCodeSigningData {
diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts
index 56c9e23ee4..aae148252b 100644
--- a/lib/definitions/project.d.ts
+++ b/lib/definitions/project.d.ts
@@ -458,12 +458,26 @@ interface ICocoaPodsPlatformManager {
replacePlatformRow(podfileContent: string, podfilePath: string): { replacedContent: string, podfilePlatformData: IPodfilePlatformData };
}
+declare const enum BuildNames {
+ debug = "Debug",
+ release = "Release"
+}
+
+interface IXcodeTargetBuildConfigurationProperty {
+ name: string;
+ value: any;
+ buildNames?: BuildNames[];
+}
+
/**
* Describes a service used to add and remove iOS extension
*/
-interface IIOSExtensionsService {
- addExtensionsFromPath(options: IAddExtensionsFromPathOptions): Promise;
- removeExtensions(options: IRemoveExtensionsOptions): void;
+interface IIOSNativeTargetService {
+ addTargetToProject(targetRootPath: string, targetFolder: string, targetType: string, project: IXcode.project, platformData: IPlatformData, parentTarget?: string): IXcode.target;
+ prepareSigning(targetUuids: string[], projectData:IProjectData, projectPath: string): void;
+ getTargetDirectories(folderPath: string): string[];
+ setXcodeTargetBuildConfigurationProperties(properties: IXcodeTargetBuildConfigurationProperty[], targetName: string, project: IXcode.project): void
+ setConfigurationsFromJsonFile(jsonPath: string, targetUuid: string, targetName: string, project: IXcode.project): void
}
/**
diff --git a/lib/services/ios-extensions-service.ts b/lib/services/ios-extensions-service.ts
index fc83e100cf..4786e1dbd8 100644
--- a/lib/services/ios-extensions-service.ts
+++ b/lib/services/ios-extensions-service.ts
@@ -1,12 +1,11 @@
import * as path from "path";
-import { NativeTargetServiceBase } from "./ios-native-target-service-base";
import { IOSNativeTargetProductTypes, IOSNativeTargetTypes } from "../constants";
-export class IOSExtensionsService extends NativeTargetServiceBase implements IIOSExtensionsService {
+export class IOSExtensionsService implements IIOSExtensionsService {
constructor(protected $fs: IFileSystem,
protected $pbxprojDomXcode: IPbxprojDomXcode,
- protected $xcode: IXcode) {
- super($fs, $pbxprojDomXcode, $xcode);
+ protected $xcode: IXcode,
+ private $iOSNativeTargetService: IIOSNativeTargetService) {
}
public async addExtensionsFromPath({extensionsFolderPath, projectData, platformData, pbxProjPath}: IAddExtensionsFromPathOptions): Promise {
@@ -17,16 +16,16 @@ export class IOSExtensionsService extends NativeTargetServiceBase implements IIO
}
const project = new this.$xcode.project(pbxProjPath);
project.parseSync();
- this.getTargetDirectories(extensionsFolderPath)
+ this.$iOSNativeTargetService.getTargetDirectories(extensionsFolderPath)
.forEach(extensionFolder => {
- const target = this.addTargetToProject(extensionsFolderPath, extensionFolder, IOSNativeTargetTypes.appExtension, project, platformData);
+ const target = this.$iOSNativeTargetService.addTargetToProject(extensionsFolderPath, extensionFolder, IOSNativeTargetTypes.appExtension, project, platformData);
this.configureTarget(extensionFolder, path.join(extensionsFolderPath, extensionFolder), target, project, projectData);
targetUuids.push(target.uuid);
addedExtensions = true;
});
this.$fs.writeFile(pbxProjPath, project.writeSync({omitEmptyValues: true}));
- this.prepareSigning(targetUuids, projectData, pbxProjPath);
+ this.$iOSNativeTargetService.prepareSigning(targetUuids, projectData, pbxProjPath);
return addedExtensions;
}
@@ -34,12 +33,12 @@ export class IOSExtensionsService extends NativeTargetServiceBase implements IIO
private configureTarget(extensionName: string, extensionPath: string, target: IXcode.target, project: IXcode.project, projectData: IProjectData) {
const extJsonPath = path.join(extensionPath, "extension.json");
- this.setXcodeTargetBuildConfigurationProperties(
+ this.$iOSNativeTargetService.setXcodeTargetBuildConfigurationProperties(
[{name: "PRODUCT_BUNDLE_IDENTIFIER", value: `${projectData.projectIdentifiers.ios}.${extensionName}`}],
extensionName,
project);
- this.setConfigurationsFromJsonFile(extJsonPath, target.uuid, extensionName, project);
+ this.$iOSNativeTargetService.setConfigurationsFromJsonFile(extJsonPath, target.uuid, extensionName, project);
}
public removeExtensions({pbxProjPath}: IRemoveExtensionsOptions): void {
diff --git a/lib/services/ios-native-target-service-base.ts b/lib/services/ios-native-target-service.ts
similarity index 59%
rename from lib/services/ios-native-target-service-base.ts
rename to lib/services/ios-native-target-service.ts
index 5f203d405b..b8be774f6e 100644
--- a/lib/services/ios-native-target-service-base.ts
+++ b/lib/services/ios-native-target-service.ts
@@ -1,39 +1,27 @@
import * as path from "path";
-export enum BuildNames {
- debug = "Debug",
- release = "Release"
-}
-
-export interface IXcodeTargetBuildConfigurationProperty {
- name: string;
- value: any;
- buildNames?: BuildNames[];
-}
-
-export abstract class NativeTargetServiceBase {
+export class IOSNativeTargetService implements IIOSNativeTargetService {
constructor(protected $fs: IFileSystem,
- protected $pbxprojDomXcode: IPbxprojDomXcode,
- protected $xcode: IXcode) {
+ protected $pbxprojDomXcode: IPbxprojDomXcode) {
}
- protected addTargetToProject(extensionsFolderPath: string, extensionFolder: string, targetType: string, project: IXcode.project, platformData: IPlatformData, parentTarget?: string): IXcode.target {
- const extensionPath = path.join(extensionsFolderPath, extensionFolder);
- const extensionRelativePath = path.relative(platformData.projectRoot, extensionPath);
- const files = this.$fs.readDirectory(extensionPath)
+ public addTargetToProject(targetRootPath: string, targetFolder: string, targetType: string, project: IXcode.project, platformData: IPlatformData, parentTarget?: string): IXcode.target {
+ const targetPath = path.join(targetRootPath, targetFolder);
+ const targetRelativePath = path.relative(platformData.projectRoot, targetPath);
+ const files = this.$fs.readDirectory(targetPath)
.filter(filePath => !filePath.startsWith("."))
- .map(filePath => path.join(extensionPath, filePath));
- const target = project.addTarget(extensionFolder, targetType, extensionRelativePath, parentTarget);
+ .map(filePath => path.join(targetPath, filePath));
+ const target = project.addTarget(targetFolder, targetType, targetRelativePath, parentTarget);
project.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid);
project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid);
project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid);
- project.addPbxGroup(files, extensionFolder, extensionPath, null, { isMain: true, target: target.uuid, filesRelativeToProject: true });
- project.addToHeaderSearchPaths(extensionPath, target.pbxNativeTarget.productName);
+ project.addPbxGroup(files, targetFolder, targetPath, null, { isMain: true, target: target.uuid, filesRelativeToProject: true });
+ project.addToHeaderSearchPaths(targetPath, target.pbxNativeTarget.productName);
return target;
}
- protected prepareSigning(targetUuids: string[], projectData:IProjectData, projectPath: string) {
+ public prepareSigning(targetUuids: string[], projectData: IProjectData, projectPath: string): void {
const xcode = this.$pbxprojDomXcode.Xcode.open(projectPath);
const signing = xcode.getSigning(projectData.projectName);
if (signing !== undefined) {
@@ -52,7 +40,7 @@ export abstract class NativeTargetServiceBase {
xcode.save();
}
- protected getTargetDirectories(folderPath: string): string[] {
+ public getTargetDirectories(folderPath: string): string[] {
return this.$fs.readDirectory(folderPath)
.filter(fileName => {
const filePath = path.join(folderPath, fileName);
@@ -62,7 +50,7 @@ export abstract class NativeTargetServiceBase {
});
}
- protected setXcodeTargetBuildConfigurationProperties(properties: IXcodeTargetBuildConfigurationProperty[], targetName: string, project: IXcode.project): void {
+ public setXcodeTargetBuildConfigurationProperties(properties: IXcodeTargetBuildConfigurationProperty[], targetName: string, project: IXcode.project): void {
properties.forEach(property => {
const buildNames = property.buildNames || [BuildNames.debug, BuildNames.release];
buildNames.forEach((buildName) => {
@@ -71,7 +59,7 @@ export abstract class NativeTargetServiceBase {
});
}
- protected setConfigurationsFromJsonFile(jsonPath: string, targetUuid: string, targetName: string, project: IXcode.project) {
+ public setConfigurationsFromJsonFile(jsonPath: string, targetUuid: string, targetName: string, project: IXcode.project): void {
if (this.$fs.exists(jsonPath)) {
const configurationJson = this.$fs.readJson(jsonPath) || {};
@@ -94,3 +82,5 @@ export abstract class NativeTargetServiceBase {
}
}
}
+
+$injector.register("iOSNativeTargetService", IOSNativeTargetService);
diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts
index a7d93a4f5d..784fd90388 100644
--- a/lib/services/ios-project-service.ts
+++ b/lib/services/ios-project-service.ts
@@ -52,7 +52,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
private $xcconfigService: IXcconfigService,
private $xcodebuildService: IXcodebuildService,
private $iOSExtensionsService: IIOSExtensionsService,
- private $iOSWatchAppService: IIOSWatchAppService) {
+ private $iOSWatchAppService: IIOSWatchAppService,
+ private $iOSNativeTargetService: IIOSNativeTargetService) {
super($fs, $projectDataService);
}
@@ -432,7 +433,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
CFBundleIdentifier
- ${projectData.projectIdentifiers.ios}
+ $(PRODUCT_BUNDLE_IDENTIFIER)
`
});
@@ -529,6 +530,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
public async handleNativeDependenciesChange(projectData: IProjectData, opts: IRelease): Promise {
const platformData = this.getPlatformData(projectData);
+
+ this.setProductBundleIdentifier(projectData);
await this.$cocoapodsService.applyPodfileFromAppResources(projectData, platformData);
const projectPodfilePath = this.$cocoapodsService.getProjectPodfilePath(platformData.projectRoot);
@@ -603,6 +606,17 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
return target;
}
+ private setProductBundleIdentifier(projectData: IProjectData): void {
+ const project = this.createPbxProj(projectData);
+ this.$iOSNativeTargetService.setXcodeTargetBuildConfigurationProperties([
+ {
+ name: "PRODUCT_BUNDLE_IDENTIFIER",
+ value: `"${projectData.projectIdentifiers.ios}"`
+ }
+ ], projectData.projectName, project);
+ this.savePbxProj(project, projectData);
+ }
+
private getAllLibsForPluginWithFileExtension(pluginData: IPluginData, fileExtension: string | string[]): string[] {
const fileExtensions = _.isArray(fileExtension) ? fileExtension : [fileExtension];
const filterCallback = (fileName: string, pluginPlatformsFolderPath: string) =>
diff --git a/lib/services/ios-watch-app-service.ts b/lib/services/ios-watch-app-service.ts
index d5b6886e45..3c4dad7e5c 100644
--- a/lib/services/ios-watch-app-service.ts
+++ b/lib/services/ios-watch-app-service.ts
@@ -1,14 +1,13 @@
import * as path from "path";
-import { NativeTargetServiceBase } from "./ios-native-target-service-base";
import { IOSDeviceTargets, IOS_WATCHAPP_FOLDER, IOS_WATCHAPP_EXTENSION_FOLDER, IOSNativeTargetProductTypes, IOSNativeTargetTypes } from "../constants";
-export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSWatchAppService {
+export class IOSWatchAppService implements IIOSWatchAppService {
private static WATCH_APP_IDENTIFIER = "watchkitapp";
private static WACTCH_EXTENSION_IDENTIFIER = "watchkitextension";
constructor(protected $fs: IFileSystem,
protected $pbxprojDomXcode: IPbxprojDomXcode,
- protected $xcode: IXcode) {
- super($fs, $pbxprojDomXcode, $xcode);
+ protected $xcode: IXcode,
+ private $iOSNativeTargetService: IIOSNativeTargetService) {
}
public async addWatchAppFromPath({watchAppFolderPath, projectData, platformData, pbxProjPath}: IAddWatchAppFromPathOptions): Promise {
@@ -20,13 +19,13 @@ export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSW
return false;
}
- const appFolder = this.getTargetDirectories(appPath)[0];
- const extensionFolder = this.getTargetDirectories(extensionPath)[0];
+ const appFolder = this.$iOSNativeTargetService.getTargetDirectories(appPath)[0];
+ const extensionFolder = this.$iOSNativeTargetService.getTargetDirectories(extensionPath)[0];
const project = new this.$xcode.project(pbxProjPath);
project.parseSync();
- const watchApptarget = this.addTargetToProject(appPath, appFolder, IOSNativeTargetTypes.watchApp, project, platformData, project.getFirstTarget().uuid);
+ const watchApptarget = this.$iOSNativeTargetService.addTargetToProject(appPath, appFolder, IOSNativeTargetTypes.watchApp, project, platformData, project.getFirstTarget().uuid);
this.configureTarget(
appFolder,
path.join(appPath, appFolder),
@@ -37,7 +36,7 @@ export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSW
);
targetUuids.push(watchApptarget.uuid);
- const watchExtensionTarget = this.addTargetToProject(extensionPath, extensionFolder, IOSNativeTargetTypes.watchExtension, project, platformData, watchApptarget.uuid);
+ const watchExtensionTarget = this.$iOSNativeTargetService.addTargetToProject(extensionPath, extensionFolder, IOSNativeTargetTypes.watchExtension, project, platformData, watchApptarget.uuid);
this.configureTarget(
extensionFolder,
path.join(extensionPath, extensionFolder),
@@ -48,7 +47,7 @@ export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSW
targetUuids.push(watchExtensionTarget.uuid);
this.$fs.writeFile(pbxProjPath, project.writeSync({omitEmptyValues: true}));
- this.prepareSigning(targetUuids, projectData, pbxProjPath);
+ this.$iOSNativeTargetService.prepareSigning(targetUuids, projectData, pbxProjPath);
return true;
}
@@ -78,7 +77,7 @@ export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSW
identifierParts.pop();
const wkAppBundleIdentifier = identifierParts.join(".");
- this.setXcodeTargetBuildConfigurationProperties([
+ this.$iOSNativeTargetService.setXcodeTargetBuildConfigurationProperties([
{name: "PRODUCT_BUNDLE_IDENTIFIER", value: identifier},
{name: "SDKROOT", value: "watchos"},
{name: "TARGETED_DEVICE_FAMILY", value: IOSDeviceTargets.watchos},
@@ -86,7 +85,7 @@ export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSW
{name: "WK_APP_BUNDLE_IDENTIFIER", value: wkAppBundleIdentifier}
], targetName, project);
- this.setConfigurationsFromJsonFile(targetConfigurationJsonPath, target.uuid, targetName, project);
+ this.$iOSNativeTargetService.setConfigurationsFromJsonFile(targetConfigurationJsonPath, target.uuid, targetName, project);
project.addToHeaderSearchPaths(targetPath, target.pbxNativeTarget.productName);
}
}
diff --git a/lib/services/ip-service.ts b/lib/services/ip-service.ts
index d1a5d9e17d..9815c9bebd 100644
--- a/lib/services/ip-service.ts
+++ b/lib/services/ip-service.ts
@@ -1,9 +1,12 @@
+import { cache } from "../common/decorators";
+
export class IPService implements IIPService {
private static GET_IP_TIMEOUT = 1000;
constructor(private $config: IConfiguration,
private $httpClient: Server.IHttpClient,
private $logger: ILogger) { }
+ @cache()
public async getCurrentIPv4Address(): Promise {
const ipAddress = await this.getIPAddressFromServiceReturningJSONWithIPProperty(this.$config.WHOAMI_URL_ENDPOINT) ||
await this.getIPAddressFromServiceReturningJSONWithIPProperty("https://api.myip.com") ||
diff --git a/lib/services/platform/add-platform-service.ts b/lib/services/platform/add-platform-service.ts
index 9185127290..4f795e3076 100644
--- a/lib/services/platform/add-platform-service.ts
+++ b/lib/services/platform/add-platform-service.ts
@@ -1,6 +1,6 @@
import * as path from "path";
import * as temp from "temp";
-import { PROJECT_FRAMEWORK_FOLDER_NAME } from "../../constants";
+import { PROJECT_FRAMEWORK_FOLDER_NAME, TrackActionNames, AnalyticsEventLabelDelimiter } from "../../constants";
import { performanceLog } from "../../common/decorators";
export class AddPlatformService implements IAddPlatformService {
@@ -8,7 +8,8 @@ export class AddPlatformService implements IAddPlatformService {
private $fs: IFileSystem,
private $pacoteService: IPacoteService,
private $projectDataService: IProjectDataService,
- private $terminalSpinnerService: ITerminalSpinnerService
+ private $terminalSpinnerService: ITerminalSpinnerService,
+ private $analyticsService: IAnalyticsService
) { }
public async addPlatformSafe(projectData: IProjectData, platformData: IPlatformData, packageToInstall: string, nativePrepare: INativePrepare): Promise {
@@ -22,6 +23,7 @@ export class AddPlatformService implements IAddPlatformService {
const frameworkVersion = frameworkPackageJsonContent.version;
await this.setPlatformVersion(platformData, projectData, frameworkVersion);
+ await this.trackPlatformVersion(frameworkVersion, platformData);
if (!nativePrepare || !nativePrepare.skipNativePrepare) {
await this.addNativePlatform(platformData, projectData, frameworkDirPath, frameworkVersion);
@@ -61,5 +63,12 @@ export class AddPlatformService implements IAddPlatformService {
await platformData.platformProjectService.interpolateData(projectData);
platformData.platformProjectService.afterCreateProject(platformData.projectRoot, projectData);
}
+
+ private async trackPlatformVersion(frameworkVersion: string, platformData: IPlatformData): Promise {
+ await this.$analyticsService.trackEventActionInGoogleAnalytics({
+ action: TrackActionNames.AddPlatform,
+ additionalData: `${platformData.platformNameLowerCase}${AnalyticsEventLabelDelimiter}${frameworkVersion}`
+ });
+ }
}
$injector.register("addPlatformService", AddPlatformService);
diff --git a/lib/services/webpack/webpack-compiler-service.ts b/lib/services/webpack/webpack-compiler-service.ts
index 29f6c4c7f8..80457719b3 100644
--- a/lib/services/webpack/webpack-compiler-service.ts
+++ b/lib/services/webpack/webpack-compiler-service.ts
@@ -139,8 +139,8 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
const args = [
...additionalNodeArgs,
- path.join(projectData.projectDir, "node_modules", "webpack", "bin", "webpack.js"),
"--preserve-symlinks",
+ path.join(projectData.projectDir, "node_modules", "webpack", "bin", "webpack.js"),
`--config=${path.join(projectData.projectDir, "webpack.config.js")}`,
...envParams
];
@@ -174,11 +174,18 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
Object.assign(envData,
appPath && { appPath },
- appResourcesPath && { appResourcesPath },
+ appResourcesPath && { appResourcesPath }
);
envData.verbose = envData.verbose || this.$logger.isVerbose();
envData.production = envData.production || prepareData.release;
+ // The snapshot generation is wrongly located in the Webpack plugin.
+ // It should be moved in the Native Prepare of the CLI or a Gradle task in the Runtime.
+ // As a workaround, we skip the mksnapshot, xxd and android-ndk calls based on skipNativePrepare.
+ // In this way the plugin will prepare only the snapshot JS entry without any native prepare and
+ // we will able to execute cloud builds with snapshot without having any local snapshot or Docker setup.
+ // TODO: Remove this flag when we remove the native part from the plugin.
+ envData.skipSnapshotTools = prepareData.nativePrepare && prepareData.nativePrepare.skipNativePrepare;
if (prepareData.env && (prepareData.env.sourceMap === false || prepareData.env.sourceMap === 'false')) {
delete envData.sourceMap;
diff --git a/test/controllers/add-platform-controller.ts b/test/controllers/add-platform-controller.ts
index 4da557ea4c..2af133929d 100644
--- a/test/controllers/add-platform-controller.ts
+++ b/test/controllers/add-platform-controller.ts
@@ -16,6 +16,9 @@ function createInjector(data?: { latestFrameworkVersion: string }) {
injector.register("platformController", PlatformController);
injector.register("addPlatformService", AddPlatformService);
injector.register("pacoteService", PacoteServiceStub);
+ injector.register("analyticsService", {
+ trackEventActionInGoogleAnalytics: () => ({})
+ });
injector.register("pacoteService", {
extractPackage: async (name: string): Promise => { extractedPackageFromPacote = name; }
diff --git a/test/controllers/prepare-controller.ts b/test/controllers/prepare-controller.ts
index ef0b4a0fd0..741da72bdb 100644
--- a/test/controllers/prepare-controller.ts
+++ b/test/controllers/prepare-controller.ts
@@ -9,7 +9,8 @@ const prepareData = {
release: false,
hmr: false,
env: {},
- watch: true
+ watch: true,
+ watchNative: true
};
let isCompileWithWatchCalled = false;
@@ -54,6 +55,10 @@ function createTestInjector(data: { hasNativeChanges: boolean }): IInjector {
isFileInIgnoreList: () => false
});
+ injector.register("analyticsService", {
+ trackEventActionInGoogleAnalytics: () => ({})
+ });
+
const prepareController: PrepareController = injector.resolve("prepareController");
prepareController.emit = (eventName: string, eventData: any) => {
emittedEventNames.push(eventName);
diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts
index f980f15267..fd1b2ed35c 100644
--- a/test/ios-project-service.ts
+++ b/test/ios-project-service.ts
@@ -174,6 +174,9 @@ function createTestInjector(projectPath: string, projectName: string, xCode?: IX
testInjector.register("logSourceMapService", {
replaceWithOriginalFileLocations: (platform: string, message: string) => message
});
+ testInjector.register("iOSNativeTargetService", {
+ setXcodeTargetBuildConfigurationProperties: () => { /* */ }
+ });
return testInjector;
}
diff --git a/test/services/ip-service.ts b/test/services/ip-service.ts
index 0d91ebe0d5..c2750f6ebf 100644
--- a/test/services/ip-service.ts
+++ b/test/services/ip-service.ts
@@ -135,5 +135,25 @@ describe("ipService", () => {
assert.isTrue(logger.traceOutput.indexOf(errMsgForMyipCom) !== -1, `Trace output\n'${logger.traceOutput}'\ndoes not contain expected message:\n${errMsgForMyipCom}`);
assert.isTrue(logger.traceOutput.indexOf(errMsgForIpifyOrg) !== -1, `Trace output\n'${logger.traceOutput}'\ndoes not contain expected message:\n${errMsgForMyipCom}`);
});
+
+ it("is called only once per process", async () => {
+ const testInjector = createTestInjector();
+ const httpClient = testInjector.resolve("httpClient");
+ let httpRequestCounter = 0;
+ httpClient.httpRequest = async (options: any, proxySettings?: IProxySettings): Promise => {
+ httpRequestCounter++;
+ return { body: JSON.stringify({ ip }) };
+ };
+
+ const ipService = testInjector.resolve("ipService");
+
+ const ipAddress = await ipService.getCurrentIPv4Address();
+ assert.equal(httpRequestCounter, 1);
+ assert.equal(ipAddress, ip);
+
+ const ipAddress2 = await ipService.getCurrentIPv4Address();
+ assert.equal(httpRequestCounter, 1);
+ assert.equal(ipAddress2, ip);
+ });
});
});
diff --git a/test/services/platform/add-platform-service.ts b/test/services/platform/add-platform-service.ts
index 8a2dcc9810..6d4945f7ba 100644
--- a/test/services/platform/add-platform-service.ts
+++ b/test/services/platform/add-platform-service.ts
@@ -19,6 +19,9 @@ function createTestInjector() {
}
});
injector.register("addPlatformService", AddPlatformService);
+ injector.register("analyticsService", {
+ trackEventActionInGoogleAnalytics: () => ({})
+ });
const fs = injector.resolve("fs");
fs.exists = () => false;