From 4a6af32e45efe27de28a198356b615530e720aa2 Mon Sep 17 00:00:00 2001 From: Dimitar Kerezov Date: Fri, 2 Mar 2018 17:15:58 +0200 Subject: [PATCH] refactor: allow pass buildaction --- lib/bootstrap.ts | 5 +- lib/commands/build.ts | 19 +++-- lib/commands/debug.ts | 4 +- lib/commands/deploy.ts | 31 ++----- lib/commands/run.ts | 60 ++++--------- lib/declarations.d.ts | 23 +++++ lib/definitions/livesync.d.ts | 64 +++++++++++++- lib/definitions/platform.d.ts | 35 +++++--- .../bundle-validator-helper.ts} | 6 +- lib/helpers/deploy-command-helper.ts | 42 +++++++++ .../livesync-command-helper.ts | 85 +++++++++++++------ lib/services/platform-service.ts | 12 ++- test/stubs.ts | 4 +- 13 files changed, 257 insertions(+), 133 deletions(-) rename lib/{commands/base-bundler.ts => helpers/bundle-validator-helper.ts} (83%) create mode 100644 lib/helpers/deploy-command-helper.ts rename lib/{services/livesync => helpers}/livesync-command-helper.ts (52%) diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 3818707466..3f9136eaec 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -107,9 +107,12 @@ $injector.require("devicePathProvider", "./device-path-provider"); $injector.requireCommand("platform|clean", "./commands/platform-clean"); +$injector.require("bundleValidatorHelper", "./helpers/bundle-validator-helper"); +$injector.require("liveSyncCommandHelper", "./helpers/livesync-command-helper"); +$injector.require("deployCommandHelper", "./helpers/deploy-command-helper"); + $injector.requirePublicClass("localBuildService", "./services/local-build-service"); $injector.requirePublicClass("liveSyncService", "./services/livesync/livesync-service"); -$injector.require("liveSyncCommandHelper", "./services/livesync/livesync-command-helper"); $injector.require("androidLiveSyncService", "./services/livesync/android-livesync-service"); $injector.require("iOSLiveSyncService", "./services/livesync/ios-livesync-service"); $injector.require("usbLiveSyncService", "./services/livesync/livesync-service"); // The name is used in https://github.com/NativeScript/nativescript-dev-typescript diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 34ea2c9251..e87fd05edb 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -1,14 +1,13 @@ import { ANDROID_RELEASE_BUILD_ERROR_MESSAGE } from "../constants"; -import { BundleBase } from "./base-bundler"; -export class BuildCommandBase extends BundleBase { +export class BuildCommandBase { constructor(protected $options: IOptions, protected $errors: IErrors, protected $projectData: IProjectData, protected $platformsData: IPlatformsData, protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - protected $platformService: IPlatformService) { - super($projectData, $errors, $options); + protected $platformService: IPlatformService, + private $bundleValidatorHelper: IBundleValidatorHelper) { this.$projectData.initializeProjectData(); } @@ -50,7 +49,7 @@ export class BuildCommandBase extends BundleBase { this.$errors.fail(`Applications for platform ${platform} can not be built on this OS`); } - super.validateBundling(); + this.$bundleValidatorHelper.validate(); } } @@ -62,8 +61,9 @@ export class BuildIosCommand extends BuildCommandBase implements ICommand { $projectData: IProjectData, $platformsData: IPlatformsData, $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - $platformService: IPlatformService) { - super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService); + $platformService: IPlatformService, + $bundleValidatorHelper: IBundleValidatorHelper) { + super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper); } public async execute(args: string[]): Promise { @@ -86,8 +86,9 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand { $projectData: IProjectData, $platformsData: IPlatformsData, $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - $platformService: IPlatformService) { - super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService); + $platformService: IPlatformService, + $bundleValidatorHelper: IBundleValidatorHelper) { + super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper); } public async execute(args: string[]): Promise { diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index 28be7548f7..60dd61fbdc 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -38,7 +38,9 @@ export class DebugPlatformCommand implements ICommand { await this.$devicesService.detectCurrentlyAttachedDevices({ shouldReturnImmediateResult: false, platform: this.platform }); await this.$liveSyncCommandHelper.executeLiveSyncOperation([selectedDeviceForDebug], this.platform, { - [selectedDeviceForDebug.deviceInfo.identifier]: true + [selectedDeviceForDebug.deviceInfo.identifier]: true, + // This will default in the liveSyncCommandHelper + buildPlatform: undefined }); } diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index b7d9ad743b..2a044b89ed 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -7,43 +7,22 @@ export class DeployOnDeviceCommand implements ICommand { private $platformCommandParameter: ICommandParameter, private $options: IOptions, private $projectData: IProjectData, + private $deployCommandHelper: IDeployCommandHelper, private $errors: IErrors, private $mobileHelper: Mobile.IMobileHelper, - private $platformsData: IPlatformsData) { + private $platformsData: IPlatformsData, + private $bundleValidatorHelper: IBundleValidatorHelper) { this.$projectData.initializeProjectData(); } public async execute(args: string[]): Promise { - const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: !!this.$options.bundle, release: this.$options.release }; - const deployOptions: IDeployPlatformOptions = { - clean: this.$options.clean, - device: this.$options.device, - projectDir: this.$options.path, - emulator: this.$options.emulator, - platformTemplate: this.$options.platformTemplate, - release: this.$options.release, - forceInstall: true, - provision: this.$options.provision, - teamId: this.$options.teamId, - keyStoreAlias: this.$options.keyStoreAlias, - keyStoreAliasPassword: this.$options.keyStoreAliasPassword, - keyStorePassword: this.$options.keyStorePassword, - keyStorePath: this.$options.keyStorePath - }; - - const deployPlatformInfo: IDeployPlatformInfo = { - platform: args[0], - appFilesUpdaterOptions, - deployOptions, - projectData: this.$projectData, - config: this.$options, - env: this.$options.env - }; + const deployPlatformInfo = this.$deployCommandHelper.getDeployPlatformInfo(args[0]); return this.$platformService.deployPlatform(deployPlatformInfo); } public async canExecute(args: string[]): Promise { + this.$bundleValidatorHelper.validate(); if (!args || !args.length || args.length > 1) { return false; } diff --git a/lib/commands/run.ts b/lib/commands/run.ts index cae512b9ae..38365c2a59 100644 --- a/lib/commands/run.ts +++ b/lib/commands/run.ts @@ -1,27 +1,19 @@ import { ERROR_NO_VALID_SUBCOMMAND_FORMAT } from "../common/constants"; import { ANDROID_RELEASE_BUILD_ERROR_MESSAGE } from "../constants"; import { cache } from "../common/decorators"; -import { BundleBase } from "./base-bundler"; -export class RunCommandBase extends BundleBase implements ICommand { +export class RunCommandBase implements ICommand { public platform: string; - constructor(protected $platformService: IPlatformService, - protected $projectData: IProjectData, - protected $options: IOptions, - protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - protected $errors: IErrors, - protected $devicesService: Mobile.IDevicesService, - protected $platformsData: IPlatformsData, + constructor(private $projectData: IProjectData, + private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, + private $errors: IErrors, private $hostInfo: IHostInfo, - private $liveSyncCommandHelper: ILiveSyncCommandHelper - ) { - super($projectData, $errors, $options); - } + private $liveSyncCommandHelper: ILiveSyncCommandHelper) { } public allowedParameters: ICommandParameter[] = []; public async execute(args: string[]): Promise { - return this.executeCore(args); + return this.$liveSyncCommandHelper.executeCommandLiveSync(this.platform); } public async canExecute(args: string[]): Promise { @@ -36,32 +28,10 @@ export class RunCommandBase extends BundleBase implements ICommand { this.platform = this.$devicePlatformsConstants.Android; } - const availablePlatforms = this.$liveSyncCommandHelper.getPlatformsForOperation(this.platform); - for (const platform of availablePlatforms) { - const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); - const platformProjectService = platformData.platformProjectService; - await platformProjectService.validate(this.$projectData); - } - - super.validateBundling(); + this.$liveSyncCommandHelper.validatePlatform(this.platform); return true; } - - public async executeCore(args: string[]): Promise { - await this.$devicesService.initialize({ - deviceId: this.$options.device, - platform: this.platform, - emulator: this.$options.emulator, - skipDeviceDetectionInterval: true, - skipInferPlatform: !this.platform - }); - - await this.$devicesService.detectCurrentlyAttachedDevices({ shouldReturnImmediateResult: false, platform: this.platform }); - let devices = this.$devicesService.getDeviceInstances(); - devices = devices.filter(d => !this.platform || d.deviceInfo.platform.toLowerCase() === this.platform.toLowerCase()); - await this.$liveSyncCommandHelper.executeLiveSyncOperation(devices, this.platform); - } } $injector.registerCommand("run|*all", RunCommandBase); @@ -80,9 +50,9 @@ export class RunIosCommand implements ICommand { return this.$devicePlatformsConstants.iOS; } - constructor(protected $platformsData: IPlatformsData, - protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - protected $errors: IErrors, + constructor(private $platformsData: IPlatformsData, + private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, + private $errors: IErrors, private $injector: IInjector, private $platformService: IPlatformService, private $projectData: IProjectData, @@ -94,7 +64,7 @@ export class RunIosCommand implements ICommand { this.$errors.fail(`Applications for platform ${this.$devicePlatformsConstants.iOS} can not be built on this OS`); } - return this.runCommand.executeCore(args); + return this.runCommand.execute(args); } public async canExecute(args: string[]): Promise { @@ -118,9 +88,9 @@ export class RunAndroidCommand implements ICommand { return this.$devicePlatformsConstants.Android; } - constructor(protected $platformsData: IPlatformsData, - protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - protected $errors: IErrors, + constructor(private $platformsData: IPlatformsData, + private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, + private $errors: IErrors, private $injector: IInjector, private $platformService: IPlatformService, private $projectData: IProjectData, @@ -128,7 +98,7 @@ export class RunAndroidCommand implements ICommand { } public async execute(args: string[]): Promise { - return this.runCommand.executeCore(args); + return this.runCommand.execute(args); } public async canExecute(args: string[]): Promise { diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index 38d739fe80..be53399b4e 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -739,3 +739,26 @@ interface IXcprojInfo { */ xcprojAvailable: boolean; } + +/** + * Describes helper used during execution of deploy commands. + */ +interface IDeployCommandHelper { + /** + * Retrieves data needed to execute deploy command. + * @param {string} platform platform to which to deploy - could be android or ios. + * @return {IDeployPlatformInfo} data needed to execute deploy command. + */ + getDeployPlatformInfo(platform: string): IDeployPlatformInfo; +} + +/** + * Describes helper for validating bundling. + */ +interface IBundleValidatorHelper { + /** + * Validates bundling options. + * @return {void} + */ + validate(): void; +} diff --git a/lib/definitions/livesync.d.ts b/lib/definitions/livesync.d.ts index 9a69ffed78..006b916b82 100644 --- a/lib/definitions/livesync.d.ts +++ b/lib/definitions/livesync.d.ts @@ -77,6 +77,31 @@ interface IOptionalOutputPath { outputPath?: string; } +/** + * Describes action used whenever building a project. + */ +interface IBuildAction { + /** + * @returns {Promise} Path to build artifact (.ipa, .apk or .zip). + */ + (): Promise; +} + +/** + * Describes options that can be passed in order to specify the exact location of the built package. + */ +interface IOutputDirectoryOptions extends IPlatform { + /** + * Directory where the project is located. + */ + projectDir: string; + + /** + * Whether the build is for emulator or not. + */ + emulator?: boolean; +} + /** * Describes information for LiveSync on a device. */ @@ -88,9 +113,8 @@ interface ILiveSyncDeviceInfo extends IOptionalOutputPath, IOptionalDebuggingOpt /** * Action that will rebuild the application. The action must return a Promise, which is resolved with at path to build artifact. - * @returns {Promise} Path to build artifact (.ipa, .apk or .zip). */ - buildAction: () => Promise; + buildAction: IBuildAction; /** * Whether to skip preparing the native platform. @@ -357,14 +381,46 @@ interface IDevicePathProvider { getDeviceSyncZipPath(device: Mobile.IDevice): string; } +/** + * Describes additional options, that can be passed to LiveSyncCommandHelper. + */ +interface ILiveSyncCommandHelperAdditionalOptions extends IBuildPlatformAction { + /** + * A map representing devices which have debugging enabled initially. + */ + deviceDebugMap?: IDictionary; + + /** + * Returns the path to the directory where the build output may be found. + * @param {IOutputDirectoryOptions} options Options that are used to determine the build output directory. + * @returns {string} The build output directory. + */ + getOutputDirectory?(options: IOutputDirectoryOptions): string; +} + interface ILiveSyncCommandHelper { /** * Method sets up configuration, before calling livesync and expects that devices are already discovered. * @param {Mobile.IDevice[]} devices List of discovered devices * @param {string} platform The platform for which the livesync will be ran - * @param {IDictionary} deviceDebugMap @optional A map representing devices which have debugging enabled initially. + * @param {ILiveSyncCommandHelperAdditionalOptions} additionalOptions @optional Additional options to control LiveSync. * @returns {Promise} */ - executeLiveSyncOperation(devices: Mobile.IDevice[], platform: string, deviceDebugMap?: IDictionary): Promise; + executeLiveSyncOperation(devices: Mobile.IDevice[], platform: string, additionalOptions?: ILiveSyncCommandHelperAdditionalOptions): Promise; getPlatformsForOperation(platform: string): string[]; + + /** + * Validates the given platform's data - bundle identifier, etc. + * @param {string} platform The platform to be validated. + * @return {Promise} + */ + validatePlatform(platform: string): Promise; + + /** + * Executes livesync operation. Meant to be called from within a command. + * @param {string} platform @optional platform for whith to run the livesync operation + * @param {ILiveSyncCommandHelperAdditionalOptions} additionalOptions @optional Additional options to control LiveSync. + * @returns {Promise} + */ + executeCommandLiveSync(platform?: string, additionalOptions?: ILiveSyncCommandHelperAdditionalOptions): Promise; } diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index 9972aeed3e..fdf144481f 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -1,4 +1,20 @@ -interface IPlatformService extends NodeJS.EventEmitter { +/** + * Describes information about how to build the native project. + */ +interface IBuildPlatformAction { + /** + * Builds the native project for the specified platform for device or emulator. + * When finishes, build saves the .nsbuildinfo file in platform product folder. + * This file points to the prepare that was used to build the project and allows skipping unnecessary builds and deploys. + * @param {string} platform The platform to build. + * @param {IBuildConfig} buildConfig Indicates whether the build is for device or emulator. + * @param {IProjectData} projectData DTO with information about the project. + * @returns {Promise} The path to the built file. + */ + buildPlatform(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): Promise; +} + +interface IPlatformService extends IBuildPlatformAction, NodeJS.EventEmitter { cleanPlatforms(platforms: string[], platformTemplate: string, projectData: IProjectData, config: IPlatformOptions, framework?: string): Promise; addPlatforms(platforms: string[], platformTemplate: string, projectData: IProjectData, config: IPlatformOptions, frameworkPath?: string): Promise; @@ -56,17 +72,6 @@ interface IPlatformService extends NodeJS.EventEmitter { */ shouldBuild(platform: string, projectData: IProjectData, buildConfig?: IBuildConfig, outputPath?: string): Promise; - /** - * Builds the native project for the specified platform for device or emulator. - * When finishes, build saves the .nsbuildinfo file in platform product folder. - * This file points to the prepare that was used to build the project and allows skipping unnecessary builds and deploys. - * @param {string} platform The platform to build. - * @param {IBuildConfig} buildConfig Indicates whether the build is for device or emulator. - * @param {IProjectData} projectData DTO with information about the project. - * @returns {void} - */ - buildPlatform(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): Promise; - /** * Determines whether installation is necessary. It is necessary when one of the following is true: * - the application is not installed. @@ -347,11 +352,13 @@ interface IOptionalFilesToRemove { filesToRemove?: string[]; } -interface IPreparePlatformInfoBase extends IPlatform, IAppFilesUpdaterOptionsComposition, IProjectDataComposition, IEnvOptions, IOptionalFilesToSync, IOptionalFilesToRemove { +interface IPreparePlatformInfoBase extends IPlatform, IAppFilesUpdaterOptionsComposition, IProjectDataComposition, IEnvOptions, IOptionalFilesToSync, IOptionalFilesToRemove, IOptionalNativePrepareComposition { } + +interface IOptionalNativePrepareComposition { nativePrepare?: INativePrepare; } -interface IDeployPlatformInfo extends IPlatform, IAppFilesUpdaterOptionsComposition, IProjectDataComposition, IPlatformConfig, IEnvOptions { +interface IDeployPlatformInfo extends IPlatform, IAppFilesUpdaterOptionsComposition, IProjectDataComposition, IPlatformConfig, IEnvOptions, IOptionalNativePrepareComposition, IOptionalOutputPath, IBuildPlatformAction { deployOptions: IDeployPlatformOptions } diff --git a/lib/commands/base-bundler.ts b/lib/helpers/bundle-validator-helper.ts similarity index 83% rename from lib/commands/base-bundler.ts rename to lib/helpers/bundle-validator-helper.ts index fc075da7e9..de64aa5865 100644 --- a/lib/commands/base-bundler.ts +++ b/lib/helpers/bundle-validator-helper.ts @@ -1,4 +1,4 @@ -export abstract class BundleBase { +export class BundleValidatorHelper implements IBundleValidatorHelper { private bundlersMap: IStringDictionary = { webpack: "nativescript-dev-webpack" }; @@ -9,7 +9,7 @@ export abstract class BundleBase { this.$projectData.initializeProjectData(); } - protected validateBundling(): void { + public validate(): void { if (this.$options.bundle) { const bundlePluginName = this.bundlersMap[this.$options.bundle]; const hasBundlerPluginAsDependency = this.$projectData.dependencies && this.$projectData.dependencies[bundlePluginName]; @@ -20,3 +20,5 @@ export abstract class BundleBase { } } } + +$injector.register("bundleValidatorHelper", BundleValidatorHelper); diff --git a/lib/helpers/deploy-command-helper.ts b/lib/helpers/deploy-command-helper.ts new file mode 100644 index 0000000000..e03a6e2b92 --- /dev/null +++ b/lib/helpers/deploy-command-helper.ts @@ -0,0 +1,42 @@ +export class DeployCommandHelper implements IDeployCommandHelper { + + constructor(private $options: IOptions, + private $platformService: IPlatformService, + private $projectData: IProjectData) { + this.$projectData.initializeProjectData(); + } + + public getDeployPlatformInfo(platform: string): IDeployPlatformInfo { + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: !!this.$options.bundle, release: this.$options.release }; + const deployOptions: IDeployPlatformOptions = { + clean: this.$options.clean, + device: this.$options.device, + projectDir: this.$projectData.projectDir, + emulator: this.$options.emulator, + platformTemplate: this.$options.platformTemplate, + release: this.$options.release, + forceInstall: true, + provision: this.$options.provision, + teamId: this.$options.teamId, + keyStoreAlias: this.$options.keyStoreAlias, + keyStoreAliasPassword: this.$options.keyStoreAliasPassword, + keyStorePassword: this.$options.keyStorePassword, + keyStorePath: this.$options.keyStorePath + }; + + const deployPlatformInfo: IDeployPlatformInfo = { + platform, + appFilesUpdaterOptions, + deployOptions, + projectData: this.$projectData, + buildPlatform: this.$platformService.buildPlatform.bind(this.$platformService), + config: this.$options, + env: this.$options.env, + + }; + + return deployPlatformInfo; + } +} + +$injector.register("deployCommandHelper", DeployCommandHelper); diff --git a/lib/services/livesync/livesync-command-helper.ts b/lib/helpers/livesync-command-helper.ts similarity index 52% rename from lib/services/livesync/livesync-command-helper.ts rename to lib/helpers/livesync-command-helper.ts index 8624bfb7e7..64d05fe164 100644 --- a/lib/services/livesync/livesync-command-helper.ts +++ b/lib/helpers/livesync-command-helper.ts @@ -6,8 +6,10 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper { private $liveSyncService: ILiveSyncService, private $iosDeviceOperations: IIOSDeviceOperations, private $mobileHelper: Mobile.IMobileHelper, + private $devicesService: Mobile.IDevicesService, private $platformsData: IPlatformsData, private $analyticsService: IAnalyticsService, + private $bundleValidatorHelper: IBundleValidatorHelper, private $errors: IErrors) { this.$analyticsService.setShouldDispose(this.$options.justlaunch || !this.$options.watch); } @@ -17,7 +19,22 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper { return availablePlatforms; } - public async executeLiveSyncOperation(devices: Mobile.IDevice[], platform: string, deviceDebugMap?: IDictionary): Promise { + public async executeCommandLiveSync(platform?: string, additionalOptions?: ILiveSyncCommandHelperAdditionalOptions) { + await this.$devicesService.initialize({ + deviceId: this.$options.device, + platform: platform, + emulator: this.$options.emulator, + skipDeviceDetectionInterval: true, + skipInferPlatform: !platform + }); + + await this.$devicesService.detectCurrentlyAttachedDevices({ shouldReturnImmediateResult: false, platform: platform }); + const devices = this.$devicesService.getDeviceInstances() + .filter(d => !platform || d.deviceInfo.platform.toLowerCase() === platform.toLowerCase()); + await this.executeLiveSyncOperation(devices, platform, additionalOptions); + } + + public async executeLiveSyncOperation(devices: Mobile.IDevice[], platform: string, additionalOptions?: ILiveSyncCommandHelperAdditionalOptions): Promise { if (!devices || !devices.length) { if (platform) { this.$errors.failWithoutHelp("Unable to find applicable devices to execute operation. Ensure connected devices are trusted and try again."); @@ -33,38 +50,44 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper { } if (this.$options.release) { - await this.runInReleaseMode(platform); + await this.runInReleaseMode(platform, additionalOptions); return; } // Now let's take data for each device: const deviceDescriptors: ILiveSyncDeviceInfo[] = devices .map(d => { + let buildAction: IBuildAction; + + const buildConfig: IBuildConfig = { + buildForDevice: !d.isEmulator, + projectDir: this.$options.path, + clean: this.$options.clean, + teamId: this.$options.teamId, + device: this.$options.device, + provision: this.$options.provision, + release: this.$options.release, + keyStoreAlias: this.$options.keyStoreAlias, + keyStorePath: this.$options.keyStorePath, + keyStoreAliasPassword: this.$options.keyStoreAliasPassword, + keyStorePassword: this.$options.keyStorePassword + }; + + buildAction = additionalOptions && additionalOptions.buildPlatform ? + additionalOptions.buildPlatform.bind(additionalOptions.buildPlatform, d.deviceInfo.platform, buildConfig, this.$projectData) : + this.$platformService.buildPlatform.bind(this.$platformService, d.deviceInfo.platform, buildConfig, this.$projectData); + const info: ILiveSyncDeviceInfo = { identifier: d.deviceInfo.identifier, platformSpecificOptions: this.$options, - - buildAction: async (): Promise => { - const buildConfig: IBuildConfig = { - buildForDevice: !d.isEmulator, - projectDir: this.$options.path, - clean: this.$options.clean, - teamId: this.$options.teamId, - device: this.$options.device, - provision: this.$options.provision, - release: this.$options.release, - keyStoreAlias: this.$options.keyStoreAlias, - keyStorePath: this.$options.keyStorePath, - keyStoreAliasPassword: this.$options.keyStoreAliasPassword, - keyStorePassword: this.$options.keyStorePassword - }; - - await this.$platformService.buildPlatform(d.deviceInfo.platform, buildConfig, this.$projectData); - const result = await this.$platformService.lastOutputPath(d.deviceInfo.platform, buildConfig, this.$projectData); - return result; - }, - debugggingEnabled: deviceDebugMap && deviceDebugMap[d.deviceInfo.identifier], - debugOptions: this.$options + buildAction, + debugggingEnabled: additionalOptions && additionalOptions.deviceDebugMap && additionalOptions.deviceDebugMap[d.deviceInfo.identifier], + debugOptions: this.$options, + outputPath: additionalOptions && additionalOptions.getOutputDirectory && additionalOptions.getOutputDirectory({ + platform, + emulator: d.isEmulator, + projectDir: this.$projectData.projectDir + }) }; return info; @@ -83,7 +106,18 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper { await this.$liveSyncService.liveSync(deviceDescriptors, liveSyncInfo); } - private async runInReleaseMode(platform: string): Promise { + public async validatePlatform(platform: string) { + const availablePlatforms = this.getPlatformsForOperation(platform); + for (const availablePlatform of availablePlatforms) { + const platformData = this.$platformsData.getPlatformData(availablePlatform, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + } + + this.$bundleValidatorHelper.validate(); + } + + private async runInReleaseMode(platform: string, additionalOptions?: ILiveSyncCommandHelperAdditionalOptions): Promise { const runPlatformOptions: IRunPlatformOptions = { device: this.$options.device, emulator: this.$options.emulator, @@ -104,6 +138,7 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper { release: this.$options.release }, deployOptions, + buildPlatform: this.$platformService.buildPlatform.bind(this.$platformService), projectData: this.$projectData, config: this.$options, env: this.$options.env diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 7e3786518e..6fd1369884 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -385,7 +385,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { } } - public async buildPlatform(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): Promise { + public async buildPlatform(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): Promise { this.$logger.out("Building project..."); const action = constants.TrackActionNames.Build; @@ -417,6 +417,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { this.saveBuildInfoFile(platform, projectData.projectDir, buildInfoFilePath); this.$logger.out("Project successfully built."); + return this.lastOutputPath(platform, buildConfig, projectData); } public saveBuildInfoFile(platform: string, projectDir: string, buildInfoFileDirname: string): void { @@ -483,6 +484,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { platformTemplate: deployInfo.deployOptions.platformTemplate, projectData: deployInfo.projectData, config: deployInfo.config, + nativePrepare: deployInfo.nativePrepare, env: deployInfo.env }); const options: Mobile.IDevicesServicesInitializationOptions = { @@ -503,15 +505,17 @@ export class PlatformService extends EventEmitter implements IPlatformService { keyStorePath: deployInfo.deployOptions.keyStorePath, clean: deployInfo.deployOptions.clean }; - const shouldBuild = await this.shouldBuild(deployInfo.platform, deployInfo.projectData, buildConfig); + + let installPackageFile: string; + const shouldBuild = await this.shouldBuild(deployInfo.platform, deployInfo.projectData, buildConfig, deployInfo.outputPath); if (shouldBuild) { - await this.buildPlatform(deployInfo.platform, buildConfig, deployInfo.projectData); + installPackageFile = await deployInfo.buildPlatform(deployInfo.platform, buildConfig, deployInfo.projectData); } else { this.$logger.out("Skipping package build. No changes detected on the native side. This will be fast!"); } if (deployInfo.deployOptions.forceInstall || shouldBuild || (await this.shouldInstall(device, deployInfo.projectData))) { - await this.installApplication(device, buildConfig, deployInfo.projectData); + await this.installApplication(device, buildConfig, deployInfo.projectData, installPackageFile, deployInfo.outputPath); } else { this.$logger.out("Skipping install."); } diff --git a/test/stubs.ts b/test/stubs.ts index 16a7d6f0c1..2faf593fdb 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -691,8 +691,8 @@ export class PlatformServiceStub extends EventEmitter implements IPlatformServic return Promise.resolve(true); } - public buildPlatform(platform: string, buildConfig?: IBuildConfig): Promise { - return Promise.resolve(); + public buildPlatform(platform: string, buildConfig?: IBuildConfig): Promise { + return Promise.resolve(""); } public async shouldInstall(device: Mobile.IDevice): Promise {