From 1860059b1db0ff881547ed44a93c7c657cfb62c6 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Thu, 25 Oct 2018 16:28:42 +0300 Subject: [PATCH 01/12] feat: inital work on Android Application Bundle Build (.aab) --- lib/commands/build.ts | 3 ++- lib/constants.ts | 2 ++ lib/declarations.d.ts | 12 ++++++++++-- lib/definitions/platform.d.ts | 3 ++- lib/options.ts | 3 ++- lib/services/android-project-service.ts | 18 +++++++++++++++-- lib/services/platform-service.ts | 26 +++++++++++++++---------- 7 files changed, 50 insertions(+), 17 deletions(-) diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 331930802a..d1d8b5fbec 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -41,7 +41,8 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase { keyStoreAlias: this.$options.keyStoreAlias, keyStorePath: this.$options.keyStorePath, keyStoreAliasPassword: this.$options.keyStoreAliasPassword, - keyStorePassword: this.$options.keyStorePassword + keyStorePassword: this.$options.keyStorePassword, + androidBundle: this.$options.aab }; await this.$platformService.buildPlatform(platform, buildConfig, this.$projectData); if (this.$options.copyTo) { diff --git a/lib/constants.ts b/lib/constants.ts index d5509ffbea..a4e58d7feb 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -33,12 +33,14 @@ export const BUILD_XCCONFIG_FILE_NAME = "build.xcconfig"; export const BUILD_DIR = "build"; export const OUTPUTS_DIR = "outputs"; export const APK_DIR = "apk"; +export const BUNDLE_DIR = "bundle"; export const RESOURCES_DIR = "res"; export const CONFIG_NS_FILE_NAME = "nsconfig.json"; export const CONFIG_NS_APP_RESOURCES_ENTRY = "appResourcesPath"; export const CONFIG_NS_APP_ENTRY = "appPath"; export const DEPENDENCIES_JSON_NAME = "dependencies.json"; export const APK_EXTENSION_NAME = ".apk"; +export const AAB_EXTENSION_NAME = ".aab" export const HASHES_FILE_NAME = ".nshashes"; export class PackageVersion { diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index 175fa8ec4b..f05e9f0fca 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -458,7 +458,11 @@ interface IPluginSeedOptions { pluginName: string; } -interface IOptions extends IRelease, IDeviceIdentifier, IJustLaunch, IAvd, IAvailableDevices, IProfileDir, IHasEmulatorOption, IBundleString, IPlatformTemplate, IHasEmulatorOption, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, INpmInstallConfigurationOptions, IPort, IEnvOptions, IPluginSeedOptions, IGenerateOptions { +interface IAndroidBundleOptions { + aab: boolean; +} + +interface IOptions extends IRelease, IDeviceIdentifier, IJustLaunch, IAvd, IAvailableDevices, IProfileDir, IHasEmulatorOption, IBundleString, IPlatformTemplate, IHasEmulatorOption, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, IAndroidBundleOptions, INpmInstallConfigurationOptions, IPort, IEnvOptions, IPluginSeedOptions, IGenerateOptions { argv: IYargArgv; validateOptions(commandSpecificDashedOptions?: IDictionary): void; options: IDictionary; @@ -531,7 +535,11 @@ interface IEnvOptions { env: Object; } -interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease { } +interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease, IAndroidBundle { } + + interface IAndroidBundle { + androidBundle?: boolean; + } interface IAppFilesUpdaterOptions extends IBundle, IRelease, IOptionalWatchAllFiles, IHasUseHotModuleReloadOption { } diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index c1a05fa5c1..33d2faedf8 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -268,6 +268,7 @@ interface IPlatformData { normalizedPlatformName: string; appDestinationDirectoryPath: string; deviceBuildOutputPath: string; + bundleBuildOutputPath?: string; emulatorBuildOutputPath?: string; getValidBuildOutputData(buildOptions: IBuildOutputOptions): IValidBuildOutputData; frameworkFilesExtensions: string[]; @@ -285,7 +286,7 @@ interface IValidBuildOutputData { regexes?: RegExp[]; } -interface IBuildOutputOptions { +interface IBuildOutputOptions extends IAndroidBundle { isReleaseBuild?: boolean; isForDevice?: boolean; } diff --git a/lib/options.ts b/lib/options.ts index 20d428cade..59e6a78798 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -115,7 +115,8 @@ export class Options { default: { type: OptionType.Boolean }, count: { type: OptionType.Number }, hooks: { type: OptionType.Boolean, default: true }, - link: { type: OptionType.Boolean, default: false } + link: { type: OptionType.Boolean, default: false }, + aab: { type: OptionType.Boolean } }; } diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index f03926a4e4..4d5030eb8b 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -74,15 +74,25 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject platformProjectService: this, projectRoot: projectRoot, deviceBuildOutputPath: path.join(...deviceBuildOutputArr), + bundleBuildOutputPath: path.join(projectRoot, constants.APP_FOLDER_NAME, constants.BUILD_DIR, constants.OUTPUTS_DIR, constants.BUNDLE_DIR), getValidBuildOutputData: (buildOptions: IBuildOutputOptions): IValidBuildOutputData => { const buildMode = buildOptions.isReleaseBuild ? Configurations.Release.toLowerCase() : Configurations.Debug.toLowerCase(); + if(buildOptions.androidBundle) { + return { + packageNames: [ + `${constants.APP_FOLDER_NAME}${constants.AAB_EXTENSION_NAME}` + ] + } + } + return { packageNames: [ `${packageName}-${buildMode}${constants.APK_EXTENSION_NAME}`, `${projectData.projectName}-${buildMode}${constants.APK_EXTENSION_NAME}`, `${projectData.projectName}${constants.APK_EXTENSION_NAME}`, `${constants.APP_FOLDER_NAME}-${buildMode}${constants.APK_EXTENSION_NAME}` + ], regexes: [new RegExp(`${constants.APP_FOLDER_NAME}-.*-(${Configurations.Debug}|${Configurations.Release})${constants.APK_EXTENSION_NAME}`, "i"), new RegExp(`${packageName}-.*-(${Configurations.Debug}|${Configurations.Release})${constants.APK_EXTENSION_NAME}`, "i")] }; @@ -316,17 +326,21 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } public async buildProject(projectRoot: string, projectData: IProjectData, buildConfig: IBuildConfig): Promise { + let task; const gradleArgs = this.getGradleBuildOptions(buildConfig, projectData); + const baseTask = buildConfig.androidBundle ? "bundle" : "assemble"; if (this.$logger.getLevel() === "TRACE") { gradleArgs.unshift("--stacktrace"); gradleArgs.unshift("--debug"); } if (buildConfig.release) { - gradleArgs.unshift("assembleRelease"); + task = baseTask + "Release"; } else { - gradleArgs.unshift("assembleDebug"); + task = baseTask + "Debug"; } + gradleArgs.unshift(task); + const handler = (data: any) => { this.emit(constants.BUILD_OUTPUT_EVENT_NAME, data); }; diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 022b6957f4..7fed940c9a 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -373,7 +373,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { return true; } - const validBuildOutputData = platformData.getValidBuildOutputData({ isForDevice: forDevice }); + const validBuildOutputData = platformData.getValidBuildOutputData({ isForDevice: forDevice, isReleaseBuild: buildConfig.release }); const packages = this.getApplicationPackages(outputPath, validBuildOutputData); if (packages.length === 0) { return true; @@ -449,7 +449,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { await attachAwaitDetach(constants.BUILD_OUTPUT_EVENT_NAME, platformData.platformProjectService, handler, platformData.platformProjectService.buildProject(platformData.projectRoot, projectData, buildConfig)); - const buildInfoFilePath = this.getBuildOutputPath(platform, platformData, buildConfig); + const buildInfoFilePath = this.getBuildOutputPath(platform, platformData, { isForDevice: buildConfig.buildForDevice, isReleaseBuild: buildConfig.release, androidBundle: buildConfig.androidBundle }); this.saveBuildInfoFile(platform, projectData.projectDir, buildInfoFilePath); this.$logger.out("Project successfully built."); @@ -468,7 +468,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { this.$fs.writeJson(buildInfoFile, buildInfo); } - public async shouldInstall(device: Mobile.IDevice, projectData: IProjectData, release: IBuildConfig, outputPath?: string): Promise { + public async shouldInstall(device: Mobile.IDevice, projectData: IProjectData, release: IRelease, outputPath?: string): Promise { const platform = device.deviceInfo.platform; if (!(await device.applicationManager.isApplicationInstalled(projectData.projectIdentifiers[platform.toLowerCase()]))) { return true; @@ -476,7 +476,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { const platformData = this.$platformsData.getPlatformData(platform, projectData); const deviceBuildInfo: IBuildInfo = await this.getDeviceBuildInfo(device, projectData); - const localBuildInfo = this.getBuildInfo(platform, platformData, { buildForDevice: !device.isEmulator }, outputPath); + const localBuildInfo = this.getBuildInfo(platform, platformData, { isForDevice: !device.isEmulator, isReleaseBuild: release.release }, outputPath); return !localBuildInfo || !deviceBuildInfo || deviceBuildInfo.buildTime !== localBuildInfo.buildTime; } @@ -514,7 +514,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { const deviceFilePath = await this.getDeviceBuildInfoFilePath(device, projectData); const options = buildConfig; options.buildForDevice = !device.isEmulator; - const buildInfoFilePath = outputFilePath || this.getBuildOutputPath(device.deviceInfo.platform, platformData, options); + const buildInfoFilePath = outputFilePath || this.getBuildOutputPath(device.deviceInfo.platform, platformData, { isForDevice: buildConfig.buildForDevice, isReleaseBuild: buildConfig.release, androidBundle: buildConfig.androidBundle }); const appIdentifier = projectData.projectIdentifiers[platform]; await device.fileSystem.putFile(path.join(buildInfoFilePath, buildInfoFileName), deviceFilePath, appIdentifier); @@ -611,9 +611,13 @@ export class PlatformService extends EventEmitter implements IPlatformService { await this.$devicesService.execute(action, this.getCanExecuteAction(platform, runOptions)); } - private getBuildOutputPath(platform: string, platformData: IPlatformData, options: IBuildForDevice): string { + private getBuildOutputPath(platform: string, platformData: IPlatformData, options: IBuildOutputOptions): string { + if(options.androidBundle) { + return platformData.bundleBuildOutputPath; + } + if (platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { - return options.buildForDevice ? platformData.deviceBuildOutputPath : platformData.emulatorBuildOutputPath; + return options.isForDevice ? platformData.deviceBuildOutputPath : platformData.emulatorBuildOutputPath; } return platformData.deviceBuildOutputPath; @@ -637,7 +641,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { } } - private getBuildInfo(platform: string, platformData: IPlatformData, options: IBuildForDevice, buildOutputPath?: string): IBuildInfo { + private getBuildInfo(platform: string, platformData: IPlatformData, options: IBuildOutputOptions, buildOutputPath?: string): IBuildInfo { buildOutputPath = buildOutputPath || this.getBuildOutputPath(platform, platformData, options); const buildInfoFile = path.join(buildOutputPath, buildInfoFileName); if (this.$fs.exists(buildInfoFile)) { @@ -899,11 +903,13 @@ export class PlatformService extends EventEmitter implements IPlatformService { } public getLatestApplicationPackageForDevice(platformData: IPlatformData, buildConfig: IBuildConfig, outputPath?: string): IApplicationPackage { - return this.getLatestApplicationPackage(outputPath || platformData.deviceBuildOutputPath, platformData.getValidBuildOutputData({ isForDevice: true, isReleaseBuild: buildConfig.release })); + return this.getLatestApplicationPackage(outputPath || platformData.deviceBuildOutputPath, platformData.getValidBuildOutputData({ isForDevice: true, isReleaseBuild: buildConfig.release, androidBundle: buildConfig.androidBundle })); } public getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildConfig: IBuildConfig, outputPath?: string): IApplicationPackage { - return this.getLatestApplicationPackage(outputPath || platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.getValidBuildOutputData({ isForDevice: false, isReleaseBuild: buildConfig.release })); + const buildOutputOptions: IBuildOutputOptions = { isForDevice: false, isReleaseBuild: buildConfig.release, androidBundle: buildConfig.androidBundle }; + outputPath = outputPath || this.getBuildOutputPath(platformData.normalizedPlatformName.toLowerCase(), platformData, buildOutputOptions); + return this.getLatestApplicationPackage(outputPath || platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.getValidBuildOutputData(buildOutputOptions)); } private async updatePlatform(platform: string, version: string, platformTemplate: string, projectData: IProjectData, config: IPlatformOptions): Promise { From 58d57fd6bb5ab4a878983b9f898b3dd450d96e1d Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Thu, 25 Oct 2018 16:29:38 +0300 Subject: [PATCH 02/12] chore: unify interfaces --- lib/commands/plugin/build-plugin.ts | 2 +- lib/definitions/android-plugin-migrator.d.ts | 6 ++--- lib/definitions/platform.d.ts | 5 +--- lib/definitions/project.d.ts | 2 +- lib/services/android-plugin-build-service.ts | 8 +++--- lib/services/android-project-service.ts | 6 ++--- lib/services/ios-project-service.ts | 5 ++-- lib/services/platform-service.ts | 14 +++++----- test/services/android-plugin-build-service.ts | 26 +++++++++---------- test/stubs.ts | 6 ++--- 10 files changed, 39 insertions(+), 41 deletions(-) diff --git a/lib/commands/plugin/build-plugin.ts b/lib/commands/plugin/build-plugin.ts index 5acfc022ed..27be1fed5f 100644 --- a/lib/commands/plugin/build-plugin.ts +++ b/lib/commands/plugin/build-plugin.ts @@ -32,7 +32,7 @@ export class BuildPluginCommand implements ICommand { temp.track(); const tempAndroidProject = temp.mkdirSync("android-project"); - const options: IBuildOptions = { + const options: IPluginBuildOptions = { aarOutputDir: platformsAndroidPath, platformsAndroidDirPath: platformsAndroidPath, pluginName: pluginName, diff --git a/lib/definitions/android-plugin-migrator.d.ts b/lib/definitions/android-plugin-migrator.d.ts index 363108ee6a..fc09a6fb51 100644 --- a/lib/definitions/android-plugin-migrator.d.ts +++ b/lib/definitions/android-plugin-migrator.d.ts @@ -1,5 +1,5 @@ -interface IBuildOptions extends IAndroidBuildOptions{ +interface IPluginBuildOptions extends IAndroidBuildOptions{ projectDir?: string } @@ -11,8 +11,8 @@ interface IAndroidBuildOptions { } interface IAndroidPluginBuildService { - buildAar(options: IBuildOptions): Promise; - migrateIncludeGradle(options: IBuildOptions): boolean; + buildAar(options: IPluginBuildOptions): Promise; + migrateIncludeGradle(options: IPluginBuildOptions): boolean; } /** diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index 33d2faedf8..f579570a98 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -286,10 +286,7 @@ interface IValidBuildOutputData { regexes?: RegExp[]; } -interface IBuildOutputOptions extends IAndroidBundle { - isReleaseBuild?: boolean; - isForDevice?: boolean; -} +interface IBuildOutputOptions extends Partial, IRelease, IAndroidBundle {} interface IPlatformsData { availablePlatforms: any; diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 83e6e360eb..7a21340ad7 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -437,7 +437,7 @@ interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectS /** * Build native part of a nativescript plugins if necessary */ - prebuildNativePlugin(buildOptions: IBuildOptions): Promise; + prebuildNativePlugin(buildOptions: IPluginBuildOptions): Promise; /** * Traverse through the production dependencies and find plugins that need build/rebuild diff --git a/lib/services/android-plugin-build-service.ts b/lib/services/android-plugin-build-service.ts index d19e50f344..7d09fbaf7d 100644 --- a/lib/services/android-plugin-build-service.ts +++ b/lib/services/android-plugin-build-service.ts @@ -169,7 +169,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService { * @param {string} options.aarOutputDir - The path where the aar should be copied after a successful build. * @param {string} options.tempPluginDirPath - The path where the android plugin will be built. */ - public async buildAar(options: IBuildOptions): Promise { + public async buildAar(options: IPluginBuildOptions): Promise { this.validateOptions(options); const manifestFilePath = this.getManifest(options.platformsAndroidDirPath); const androidSourceDirectories = this.getAndroidSourceDirectories(options.platformsAndroidDirPath); @@ -381,7 +381,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService { * @param {Object} options * @param {string} options.platformsAndroidDirPath - The path to the 'plugin/src/platforms/android' directory. */ - public migrateIncludeGradle(options: IBuildOptions): boolean { + public migrateIncludeGradle(options: IPluginBuildOptions): boolean { this.validatePlatformsAndroidDirPathOption(options); const includeGradleFilePath = path.join(options.platformsAndroidDirPath, INCLUDE_GRADLE_NAME); @@ -435,7 +435,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService { } } - private validateOptions(options: IBuildOptions): void { + private validateOptions(options: IPluginBuildOptions): void { if (!options) { this.$errors.failWithoutHelp("Android plugin cannot be built without passing an 'options' object."); } @@ -455,7 +455,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService { this.validatePlatformsAndroidDirPathOption(options); } - private validatePlatformsAndroidDirPathOption(options: IBuildOptions): void { + private validatePlatformsAndroidDirPathOption(options: IPluginBuildOptions): void { if (!options) { this.$errors.failWithoutHelp("Android plugin cannot be built without passing an 'options' object."); } diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 4d5030eb8b..9d99c12db2 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -76,7 +76,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject deviceBuildOutputPath: path.join(...deviceBuildOutputArr), bundleBuildOutputPath: path.join(projectRoot, constants.APP_FOLDER_NAME, constants.BUILD_DIR, constants.OUTPUTS_DIR, constants.BUNDLE_DIR), getValidBuildOutputData: (buildOptions: IBuildOutputOptions): IValidBuildOutputData => { - const buildMode = buildOptions.isReleaseBuild ? Configurations.Release.toLowerCase() : Configurations.Debug.toLowerCase(); + const buildMode = buildOptions.release ? Configurations.Release.toLowerCase() : Configurations.Debug.toLowerCase(); if(buildOptions.androidBundle) { return { @@ -445,7 +445,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject // build Android plugins which contain AndroidManifest.xml and/or resources const pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, AndroidProjectService.ANDROID_PLATFORM_NAME); if (this.$fs.exists(pluginPlatformsFolderPath)) { - const options: IBuildOptions = { + const options: IPluginBuildOptions = { projectDir: projectData.projectDir, pluginName: pluginData.name, platformsAndroidDirPath: pluginPlatformsFolderPath, @@ -514,7 +514,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return item.endsWith(constants.MANIFEST_FILE_NAME) || item.endsWith(constants.RESOURCES_DIR); } - public async prebuildNativePlugin(options: IBuildOptions): Promise { + public async prebuildNativePlugin(options: IPluginBuildOptions): Promise { if (await this.$androidPluginBuildService.buildAar(options)) { this.$logger.info(`Built aar for ${options.pluginName}`); } diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 655b3dabea..0b5b847609 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -70,7 +70,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ deviceBuildOutputPath: path.join(projectRoot, constants.BUILD_DIR, "device"), emulatorBuildOutputPath: path.join(projectRoot, constants.BUILD_DIR, "emulator"), getValidBuildOutputData: (buildOptions: IBuildOutputOptions): IValidBuildOutputData => { - if (buildOptions.isForDevice) { + const forDevice = !buildOptions || !!buildOptions.buildForDevice; + if (forDevice) { return { packageNames: [`${projectData.projectName}.ipa`] }; @@ -1024,7 +1025,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } } - public async prebuildNativePlugin(options: IBuildOptions): Promise { /** */ } + public async prebuildNativePlugin(options: IPluginBuildOptions): Promise { /** */ } public async checkIfPluginsNeedBuild(projectData: IProjectData): Promise> { return []; diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 7fed940c9a..ee70f545b7 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -373,7 +373,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { return true; } - const validBuildOutputData = platformData.getValidBuildOutputData({ isForDevice: forDevice, isReleaseBuild: buildConfig.release }); + const validBuildOutputData = platformData.getValidBuildOutputData(buildConfig); const packages = this.getApplicationPackages(outputPath, validBuildOutputData); if (packages.length === 0) { return true; @@ -449,7 +449,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { await attachAwaitDetach(constants.BUILD_OUTPUT_EVENT_NAME, platformData.platformProjectService, handler, platformData.platformProjectService.buildProject(platformData.projectRoot, projectData, buildConfig)); - const buildInfoFilePath = this.getBuildOutputPath(platform, platformData, { isForDevice: buildConfig.buildForDevice, isReleaseBuild: buildConfig.release, androidBundle: buildConfig.androidBundle }); + const buildInfoFilePath = this.getBuildOutputPath(platform, platformData, buildConfig); this.saveBuildInfoFile(platform, projectData.projectDir, buildInfoFilePath); this.$logger.out("Project successfully built."); @@ -476,7 +476,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { const platformData = this.$platformsData.getPlatformData(platform, projectData); const deviceBuildInfo: IBuildInfo = await this.getDeviceBuildInfo(device, projectData); - const localBuildInfo = this.getBuildInfo(platform, platformData, { isForDevice: !device.isEmulator, isReleaseBuild: release.release }, outputPath); + const localBuildInfo = this.getBuildInfo(platform, platformData, { buildForDevice: !device.isEmulator, release: release.release }, outputPath); return !localBuildInfo || !deviceBuildInfo || deviceBuildInfo.buildTime !== localBuildInfo.buildTime; } @@ -514,7 +514,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { const deviceFilePath = await this.getDeviceBuildInfoFilePath(device, projectData); const options = buildConfig; options.buildForDevice = !device.isEmulator; - const buildInfoFilePath = outputFilePath || this.getBuildOutputPath(device.deviceInfo.platform, platformData, { isForDevice: buildConfig.buildForDevice, isReleaseBuild: buildConfig.release, androidBundle: buildConfig.androidBundle }); + const buildInfoFilePath = outputFilePath || this.getBuildOutputPath(device.deviceInfo.platform, platformData, buildConfig); const appIdentifier = projectData.projectIdentifiers[platform]; await device.fileSystem.putFile(path.join(buildInfoFilePath, buildInfoFileName), deviceFilePath, appIdentifier); @@ -617,7 +617,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { } if (platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { - return options.isForDevice ? platformData.deviceBuildOutputPath : platformData.emulatorBuildOutputPath; + return options.buildForDevice ? platformData.deviceBuildOutputPath : platformData.emulatorBuildOutputPath; } return platformData.deviceBuildOutputPath; @@ -903,11 +903,11 @@ export class PlatformService extends EventEmitter implements IPlatformService { } public getLatestApplicationPackageForDevice(platformData: IPlatformData, buildConfig: IBuildConfig, outputPath?: string): IApplicationPackage { - return this.getLatestApplicationPackage(outputPath || platformData.deviceBuildOutputPath, platformData.getValidBuildOutputData({ isForDevice: true, isReleaseBuild: buildConfig.release, androidBundle: buildConfig.androidBundle })); + return this.getLatestApplicationPackage(outputPath || platformData.deviceBuildOutputPath, platformData.getValidBuildOutputData({ buildForDevice: true, release: buildConfig.release, androidBundle: buildConfig.androidBundle })); } public getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildConfig: IBuildConfig, outputPath?: string): IApplicationPackage { - const buildOutputOptions: IBuildOutputOptions = { isForDevice: false, isReleaseBuild: buildConfig.release, androidBundle: buildConfig.androidBundle }; + const buildOutputOptions: IBuildOutputOptions = { buildForDevice: false, release: buildConfig.release, androidBundle: buildConfig.androidBundle }; outputPath = outputPath || this.getBuildOutputPath(platformData.normalizedPlatformName.toLowerCase(), platformData, buildOutputOptions); return this.getLatestApplicationPackage(outputPath || platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.getValidBuildOutputData(buildOutputOptions)); } diff --git a/test/services/android-plugin-build-service.ts b/test/services/android-plugin-build-service.ts index 590c9425df..29c7e303f3 100644 --- a/test/services/android-plugin-build-service.ts +++ b/test/services/android-plugin-build-service.ts @@ -32,7 +32,7 @@ describe('androidPluginBuildService', () => { projectRuntimeGradleAndroidVersion?: string, addPreviousBuildInfo?: boolean, hasChangesInShasums?: boolean, - }): IBuildOptions { + }): IPluginBuildOptions { options = options || {}; spawnFromEventCalled = false; tempFolder = temp.mkdirSync("androidPluginBuildService-temp"); @@ -182,7 +182,7 @@ dependencies { describe('buildAar', () => { it('builds aar when all supported files are in the plugin', async () => { - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addManifest: true, addResFolder: true, addAssetsFolder: true @@ -194,7 +194,7 @@ dependencies { }); it('does not build aar when there are no supported files in the plugin', async () => { - const config: IBuildOptions = setup(); + const config: IPluginBuildOptions = setup(); await androidBuildPluginService.buildAar(config); @@ -202,7 +202,7 @@ dependencies { }); it('builds aar when there are res and assets folders', async () => { - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addResFolder: true, addAssetsFolder: true }); @@ -213,7 +213,7 @@ dependencies { }); it('builds aar when there is an android manifest', async () => { - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addManifest: true }); @@ -223,7 +223,7 @@ dependencies { }); it('builds aar when plugin is already build and source files have changed since last buid', async () => { - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addManifest: true, addPreviousBuildInfo: true, hasChangesInShasums: true @@ -235,7 +235,7 @@ dependencies { }); it('does not build aar when plugin is already build and source files have not changed', async () => { - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addManifest: true, addPreviousBuildInfo: true }); @@ -248,7 +248,7 @@ dependencies { it('builds aar with the latest runtime gradle versions when no project dir is specified', async () => { const expectedGradleVersion = "1.2.3"; const expectedAndroidVersion = "4.5.6"; - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addManifest: true, latestRuntimeGradleVersion: expectedGradleVersion, latestRuntimeGradleAndroidVersion: expectedAndroidVersion @@ -266,7 +266,7 @@ dependencies { it('builds aar with the latest runtime gradle versions when a project dir without runtime versions is specified', async () => { const expectedGradleVersion = "4.4.4"; const expectedAndroidVersion = "5.5.5"; - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addManifest: true, addProjectDir: true, latestRuntimeGradleVersion: expectedGradleVersion, @@ -288,7 +288,7 @@ dependencies { it('builds aar with the specified runtime gradle versions when the project runtime has gradle versions', async () => { const expectedGradleVersion = "2.2.2"; const expectedAndroidVersion = "3.3"; - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addManifest: true, addProjectDir: true, latestRuntimeGradleVersion: "4.4.4", @@ -308,7 +308,7 @@ dependencies { }); it('builds aar with the hardcoded gradle versions when the project runtime and the latest runtime do not have versions specified', async () => { - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addManifest: true, addProjectDir: true, latestRuntimeGradleVersion: null, @@ -330,7 +330,7 @@ dependencies { describe('migrateIncludeGradle', () => { it('if there is a legacy include.gradle file', async () => { - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addLegacyIncludeGradle: true }); @@ -343,7 +343,7 @@ dependencies { }); it('if there is an already migrated include.gradle file', async () => { - const config: IBuildOptions = setup({ + const config: IPluginBuildOptions = setup({ addIncludeGradle: true }); diff --git a/test/stubs.ts b/test/stubs.ts index a37d28ea20..ac05debb56 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -305,10 +305,10 @@ export class ProjectDataStub implements IProjectData { } export class AndroidPluginBuildServiceStub implements IAndroidPluginBuildService { - buildAar(options: IBuildOptions): Promise { + buildAar(options: IPluginBuildOptions): Promise { return Promise.resolve(true); } - migrateIncludeGradle(options: IBuildOptions): boolean { + migrateIncludeGradle(options: IPluginBuildOptions): boolean { return true; } } @@ -328,7 +328,7 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor fastLivesyncFileExtensions: [] }; } - prebuildNativePlugin(options: IBuildOptions): Promise { + prebuildNativePlugin(options: IPluginBuildOptions): Promise { return Promise.resolve(); } From dac46b7ace538e938f3a9b4a3bb53d2ad949ea3c Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Fri, 26 Oct 2018 11:29:58 +0300 Subject: [PATCH 03/12] feat: add validations for --aab command --- lib/bootstrap.ts | 1 + lib/commands/build.ts | 11 +++++++++-- lib/commands/debug.ts | 5 ++++- lib/commands/deploy.ts | 4 +++- lib/commands/run.ts | 14 ++++++++------ lib/constants.ts | 2 ++ lib/declarations.d.ts | 10 ++++++++++ lib/helpers/android-bundle-validator-helper.ts | 16 ++++++++++++++++ lib/services/platform-service.ts | 6 +++++- 9 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 lib/helpers/android-bundle-validator-helper.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 590d72a1e6..9b6c53f22b 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -125,6 +125,7 @@ $injector.require("devicePathProvider", "./device-path-provider"); $injector.requireCommand("platform|clean", "./commands/platform-clean"); $injector.require("bundleValidatorHelper", "./helpers/bundle-validator-helper"); +$injector.require("androidBundleValidatorHelper", "./helpers/android-bundle-validator-helper"); $injector.require("liveSyncCommandHelper", "./helpers/livesync-command-helper"); $injector.require("deployCommandHelper", "./helpers/deploy-command-helper"); diff --git a/lib/commands/build.ts b/lib/commands/build.ts index d1d8b5fbec..ee10a917af 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -118,12 +118,19 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand { $platformsData: IPlatformsData, $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, $platformService: IPlatformService, - $bundleValidatorHelper: IBundleValidatorHelper) { + $bundleValidatorHelper: IBundleValidatorHelper, + protected $logger: ILogger) { super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper); } public async execute(args: string[]): Promise { - return this.executeCore([this.$platformsData.availablePlatforms.Android]); + const buildResult = await this.executeCore([this.$platformsData.availablePlatforms.Android]); + + if(this.$options.aab) { + this.$logger.info("Link to documentation article"); + } + + return buildResult } public async canExecute(args: string[]): Promise { diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index 922a7fba1f..0129c33f05 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -19,7 +19,8 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements private $debugDataService: IDebugDataService, private $liveSyncService: IDebugLiveSyncService, private $prompter: IPrompter, - private $liveSyncCommandHelper: ILiveSyncCommandHelper) { + private $liveSyncCommandHelper: ILiveSyncCommandHelper, + private $androidBundleValidatorHelper: IAndroidBundleValidatorHelper) { super($options, $platformsData, $platformService, $projectData); } @@ -108,6 +109,8 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements } public async canExecute(args: string[]): Promise { + this.$androidBundleValidatorHelper.validateNoAab(); + if (!this.$platformService.isPlatformSupportedForOS(this.platform, this.$projectData)) { this.$errors.fail(`Applications for platform ${this.platform} can not be built on this OS`); } diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index 4d23e661aa..c83b357ca0 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -12,7 +12,8 @@ export class DeployOnDeviceCommand extends ValidatePlatformCommandBase implement private $errors: IErrors, private $mobileHelper: Mobile.IMobileHelper, $platformsData: IPlatformsData, - private $bundleValidatorHelper: IBundleValidatorHelper) { + private $bundleValidatorHelper: IBundleValidatorHelper, + private $androidBundleValidatorHelper: IAndroidBundleValidatorHelper) { super($options, $platformsData, $platformService, $projectData); this.$projectData.initializeProjectData(); } @@ -24,6 +25,7 @@ export class DeployOnDeviceCommand extends ValidatePlatformCommandBase implement } public async canExecute(args: string[]): Promise { + this.$androidBundleValidatorHelper.validateNoAab(); 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 9877ff9740..dfc1f98fc8 100644 --- a/lib/commands/run.ts +++ b/lib/commands/run.ts @@ -10,7 +10,8 @@ export class RunCommandBase implements ICommand { private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, private $errors: IErrors, private $hostInfo: IHostInfo, - private $liveSyncCommandHelper: ILiveSyncCommandHelper) { } + private $liveSyncCommandHelper: ILiveSyncCommandHelper, + private $androidBundleValidatorHelper: IAndroidBundleValidatorHelper) { } public allowedParameters: ICommandParameter[] = []; public async execute(args: string[]): Promise { @@ -22,6 +23,8 @@ export class RunCommandBase implements ICommand { this.$errors.fail(ERROR_NO_VALID_SUBCOMMAND_FORMAT, "run"); } + this.$androidBundleValidatorHelper.validateNoAab(); + this.$projectData.initializeProjectData(); this.platform = args[0] || this.platform; @@ -35,7 +38,6 @@ export class RunCommandBase implements ICommand { const checkEnvironmentRequirementsOutput = validatePlatformOutput[this.platform.toLowerCase()].checkEnvironmentRequirementsOutput; this.liveSyncCommandHelperAdditionalOptions.syncToPreviewApp = checkEnvironmentRequirementsOutput && checkEnvironmentRequirementsOutput.selectedOption === "Sync to Playground"; } - return true; } } @@ -66,14 +68,14 @@ export class RunIosCommand implements ICommand { } public async execute(args: string[]): Promise { - if (!this.$platformService.isPlatformSupportedForOS(this.$devicePlatformsConstants.iOS, this.$projectData)) { - this.$errors.fail(`Applications for platform ${this.$devicePlatformsConstants.iOS} can not be built on this OS`); - } - return this.runCommand.execute(args); } public async canExecute(args: string[]): Promise { + if (!this.$platformService.isPlatformSupportedForOS(this.$devicePlatformsConstants.iOS, this.$projectData)) { + this.$errors.fail(`Applications for platform ${this.$devicePlatformsConstants.iOS} can not be built on this OS`); + } + const result = await this.runCommand.canExecute(args) && await this.$platformService.validateOptions(this.$options.provision, this.$options.teamId, this.$projectData, this.$platformsData.availablePlatforms.iOS); return result; } diff --git a/lib/constants.ts b/lib/constants.ts index a4e58d7feb..3c5c2229cf 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -243,3 +243,5 @@ export class BundleValidatorMessages { public static MissingBundlePlugin = "Passing --bundle requires a bundling plugin. No bundling plugin found or the specified bundling plugin is invalid."; public static NotSupportedVersion = `The NativeScript CLI requires nativescript-dev-webpack %s or later to work properly. After updating nativescript-dev-webpack you need to ensure "webpack.config.js" file is up to date with the one in the new version of nativescript-dev-webpack. You can automatically update it using "./node_modules/.bin/update-ns-webpack --configs" command.`; } + +export const AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE = "This command does not support --aab (Android Application Bundle) parameter."; diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index f05e9f0fca..45642943dc 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -864,6 +864,16 @@ interface IBundleValidatorHelper { validate(minSupportedVersion?: string): void; } + +interface IAndroidBundleValidatorHelper { + /** + * Validates android bundling option is not provided. + * Commands that require deploy of the application must not be called with --aab option + * @return {void} + */ + validateNoAab(): void; +} + interface INativeScriptCloudExtensionService { /** * Installs nativescript-cloud extension diff --git a/lib/helpers/android-bundle-validator-helper.ts b/lib/helpers/android-bundle-validator-helper.ts new file mode 100644 index 0000000000..82f05fc3ec --- /dev/null +++ b/lib/helpers/android-bundle-validator-helper.ts @@ -0,0 +1,16 @@ +import { AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE } from "../constants"; + +export class AndroidBundleValidatorHelper implements IAndroidBundleValidatorHelper { + constructor(protected $projectData: IProjectData, + protected $errors: IErrors, + protected $options: IOptions) { + } + + public validateNoAab(minSupportedVersion?: string): void { + if(this.$options.aab) { + this.$errors.fail(AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE); + } + } +} + +$injector.register("androidBundleValidatorHelper", AndroidBundleValidatorHelper); diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index ee70f545b7..9a14956526 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -256,7 +256,11 @@ export class PlatformService extends EventEmitter implements IPlatformService { return true; } - public async validateOptions(provision: true | string, teamId: true | string, projectData: IProjectData, platform?: string): Promise { + public async validateOptions(provision: true | string, teamId: true | string, projectData: IProjectData, platform?: string, aab?: boolean): Promise { + if (platform && this.$mobileHelper.isiOSPlatform(platform) && aab) { + this.$errors.failWithoutHelp("Option --aab is supported for Android platform only."); + } + if (platform) { platform = this.$mobileHelper.normalizePlatformName(platform); this.$logger.trace("Validate options for platform: " + platform); From 61d984307a2b4f156dc09162778b61b9d1b05374 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Tue, 30 Oct 2018 16:03:13 +0200 Subject: [PATCH 04/12] fix: hashes with --aab outputed in wrong directory --- lib/services/android-project-service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 9d99c12db2..c1d1e72e55 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -329,6 +329,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject let task; const gradleArgs = this.getGradleBuildOptions(buildConfig, projectData); const baseTask = buildConfig.androidBundle ? "bundle" : "assemble"; + const outputPath = buildConfig.androidBundle ? this._platformData.bundleBuildOutputPath : this._platformData.deviceBuildOutputPath; if (this.$logger.getLevel() === "TRACE") { gradleArgs.unshift("--stacktrace"); gradleArgs.unshift("--debug"); @@ -357,7 +358,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject }) ); - await this.$filesHashService.saveHashesForProject(this._platformData, this._platformData.deviceBuildOutputPath); + await this.$filesHashService.saveHashesForProject(this._platformData, outputPath); } private getGradleBuildOptions(settings: IAndroidBuildOptionsSettings, projectData: IProjectData): Array { From 4bb3bdcbfd9b6177f903779dce7ef9e7971a2d45 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Wed, 31 Oct 2018 11:57:51 +0200 Subject: [PATCH 05/12] feat: validation for runtime version for --aab, linting, tests fix --- lib/commands/build.ts | 7 ++-- lib/constants.ts | 7 ++-- lib/declarations.d.ts | 7 ++++ .../android-bundle-validator-helper.ts | 32 ++++++++++++++++--- lib/services/android-project-service.ts | 4 +-- lib/services/platform-service.ts | 4 +-- test/debug.ts | 1 + test/platform-service.ts | 1 + test/stubs.ts | 10 ++++++ 9 files changed, 59 insertions(+), 14 deletions(-) diff --git a/lib/commands/build.ts b/lib/commands/build.ts index ee10a917af..0a4f55fd65 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -119,6 +119,7 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand { $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, $platformService: IPlatformService, $bundleValidatorHelper: IBundleValidatorHelper, + protected $androidBundleValidatorHelper: IAndroidBundleValidatorHelper, protected $logger: ILogger) { super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper); } @@ -126,17 +127,17 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand { public async execute(args: string[]): Promise { const buildResult = await this.executeCore([this.$platformsData.availablePlatforms.Android]); - if(this.$options.aab) { + if (this.$options.aab) { this.$logger.info("Link to documentation article"); } - return buildResult + return buildResult; } public async canExecute(args: string[]): Promise { const platform = this.$devicePlatformsConstants.Android; super.validatePlatform(platform); - + this.$androidBundleValidatorHelper.validateRuntimeVersion(this.$projectData); let result = await super.canExecuteCommandBase(platform, { notConfiguredEnvOptions: { hideSyncToPreviewAppOption: true }}); if (result.canExecute) { if (this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) { diff --git a/lib/constants.ts b/lib/constants.ts index 3c5c2229cf..1dda478626 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -40,7 +40,7 @@ export const CONFIG_NS_APP_RESOURCES_ENTRY = "appResourcesPath"; export const CONFIG_NS_APP_ENTRY = "appPath"; export const DEPENDENCIES_JSON_NAME = "dependencies.json"; export const APK_EXTENSION_NAME = ".apk"; -export const AAB_EXTENSION_NAME = ".aab" +export const AAB_EXTENSION_NAME = ".aab"; export const HASHES_FILE_NAME = ".nshashes"; export class PackageVersion { @@ -244,4 +244,7 @@ export class BundleValidatorMessages { public static NotSupportedVersion = `The NativeScript CLI requires nativescript-dev-webpack %s or later to work properly. After updating nativescript-dev-webpack you need to ensure "webpack.config.js" file is up to date with the one in the new version of nativescript-dev-webpack. You can automatically update it using "./node_modules/.bin/update-ns-webpack --configs" command.`; } -export const AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE = "This command does not support --aab (Android Application Bundle) parameter."; +export class AndroidBundleValidatorMessages { + public static AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE = "This command does not support --aab (Android Application Bundle) parameter."; + public static RUNTIME_VERSION_TOO_LOW = "Android Application Bundle (--aab) option requires NativeScript Android Runtime (tns-android) version %s and above."; +} diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index 45642943dc..f079622e01 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -872,6 +872,13 @@ interface IAndroidBundleValidatorHelper { * @return {void} */ validateNoAab(): void; + + /** + * Validates android runtime version is sufficient to support bundling option --aab. + * @param {IProjectData} projectData DTO with information about the project. + * @return {void} + */ + validateRuntimeVersion(projectData: IProjectData): void } interface INativeScriptCloudExtensionService { diff --git a/lib/helpers/android-bundle-validator-helper.ts b/lib/helpers/android-bundle-validator-helper.ts index 82f05fc3ec..46821997c9 100644 --- a/lib/helpers/android-bundle-validator-helper.ts +++ b/lib/helpers/android-bundle-validator-helper.ts @@ -1,14 +1,36 @@ -import { AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE } from "../constants"; +import * as semver from "semver"; +import * as util from "util"; +import { AndroidBundleValidatorMessages, TNS_ANDROID_RUNTIME_NAME } from "../constants"; export class AndroidBundleValidatorHelper implements IAndroidBundleValidatorHelper { + public static MIN_RUNTIME_VERSION = "5.0.0"; + constructor(protected $projectData: IProjectData, protected $errors: IErrors, - protected $options: IOptions) { + protected $options: IOptions, + protected $projectDataService: IProjectDataService) { } - public validateNoAab(minSupportedVersion?: string): void { - if(this.$options.aab) { - this.$errors.fail(AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE); + public validateNoAab(): void { + if (this.$options.aab) { + this.$errors.fail(AndroidBundleValidatorMessages.AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE); + } + } + + public validateRuntimeVersion(projectData: IProjectData): void { + if (this.$options.aab) { + const androidRuntimeInfo = this.$projectDataService.getNSValue(projectData.projectDir, TNS_ANDROID_RUNTIME_NAME); + const androidRuntimeVersion = androidRuntimeInfo ? androidRuntimeInfo.version : ""; + + if (androidRuntimeVersion) { + const shouldSkipCheck = !semver.valid(androidRuntimeVersion) && !semver.validRange(androidRuntimeVersion); + if (!shouldSkipCheck) { + const isAndroidBundleSupported = semver.gte(semver.coerce(androidRuntimeVersion), semver.coerce(AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION)); + if (!isAndroidBundleSupported) { + this.$errors.failWithoutHelp(util.format(AndroidBundleValidatorMessages.RUNTIME_VERSION_TOO_LOW, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION)); + } + } + } } } } diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index c1d1e72e55..27f4f0415d 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -78,12 +78,12 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject getValidBuildOutputData: (buildOptions: IBuildOutputOptions): IValidBuildOutputData => { const buildMode = buildOptions.release ? Configurations.Release.toLowerCase() : Configurations.Debug.toLowerCase(); - if(buildOptions.androidBundle) { + if (buildOptions.androidBundle) { return { packageNames: [ `${constants.APP_FOLDER_NAME}${constants.AAB_EXTENSION_NAME}` ] - } + }; } return { diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 9a14956526..5e84795879 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -616,10 +616,10 @@ export class PlatformService extends EventEmitter implements IPlatformService { } private getBuildOutputPath(platform: string, platformData: IPlatformData, options: IBuildOutputOptions): string { - if(options.androidBundle) { + if (options.androidBundle) { return platformData.bundleBuildOutputPath; } - + if (platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { return options.buildForDevice ? platformData.deviceBuildOutputPath : platformData.emulatorBuildOutputPath; } diff --git a/test/debug.ts b/test/debug.ts index 0f1ae8d38c..be29e08028 100644 --- a/test/debug.ts +++ b/test/debug.ts @@ -29,6 +29,7 @@ function createTestInjector(): IInjector { testInjector.register('fs', FileSystem); testInjector.register('errors', stubs.ErrorsStub); testInjector.register('hostInfo', {}); + testInjector.register("androidBundleValidatorHelper", stubs.AndroidBundleValidatorHelper); testInjector.register("analyticsService", { trackException: async (): Promise => undefined, checkConsent: async (): Promise => undefined, diff --git a/test/platform-service.ts b/test/platform-service.ts index becab6da49..b4b5abe26d 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -937,6 +937,7 @@ describe('Platform Service Tests', () => { platformsData.getPlatformData = (platform: string) => { return { deviceBuildOutputPath: "", + normalizedPlatformName: "", platformProjectService: { buildProject: () => Promise.resolve(), on: () => ({}), diff --git a/test/stubs.ts b/test/stubs.ts index ac05debb56..ce7f7744f6 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -849,6 +849,16 @@ export class AndroidResourcesMigrationServiceStub implements IAndroidResourcesMi } } +export class AndroidBundleValidatorHelper implements IAndroidBundleValidatorHelper { + validateNoAab() { + return true; + } + + validateRuntimeVersion() { + + } +} + export class InjectorStub extends Yok implements IInjector { constructor() { super(); From d40fc5a1c658bd7d668fdd9819d5fd2dd8fe09d5 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Wed, 31 Oct 2018 19:46:53 +0200 Subject: [PATCH 06/12] test: add tests for androidBundle --- lib/services/android-project-service.ts | 3 +- test/services/android-project-service.ts | 135 +++++++++++++++++++++++ test/stubs.ts | 2 +- 3 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 test/services/android-project-service.ts diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 27f4f0415d..e5311a4304 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -329,7 +329,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject let task; const gradleArgs = this.getGradleBuildOptions(buildConfig, projectData); const baseTask = buildConfig.androidBundle ? "bundle" : "assemble"; - const outputPath = buildConfig.androidBundle ? this._platformData.bundleBuildOutputPath : this._platformData.deviceBuildOutputPath; + const platformData = this.getPlatformData(projectData); + const outputPath = buildConfig.androidBundle ? platformData.bundleBuildOutputPath : platformData.deviceBuildOutputPath; if (this.$logger.getLevel() === "TRACE") { gradleArgs.unshift("--stacktrace"); gradleArgs.unshift("--debug"); diff --git a/test/services/android-project-service.ts b/test/services/android-project-service.ts new file mode 100644 index 0000000000..881e97ff01 --- /dev/null +++ b/test/services/android-project-service.ts @@ -0,0 +1,135 @@ +import { AndroidProjectService } from "../../lib/services/android-project-service"; +import { Yok } from "../../lib/common/yok"; +import * as stubs from "../stubs"; +import { assert } from "chai"; +import * as sinon from "sinon"; + + + +const createTestInjector = (): IInjector => { + const testInjector = new Yok(); + testInjector.register("androidProjectService", AndroidProjectService); + testInjector.register("childProcess", stubs.ChildProcessStub); + testInjector.register("hostInfo", {}); + testInjector.register("projectDataService", stubs.ProjectDataService); + testInjector.register("pluginVariablesService", {}); + testInjector.register("fs", stubs.FileSystemStub); + testInjector.register("injector", testInjector); + testInjector.register("devicePlatformsConstants", {}); + testInjector.register("npm", {}); + testInjector.register("platformEnvironmentRequirements", {}); + testInjector.register("androidResourcesMigrationService", {}); + testInjector.register("androidPluginBuildService", {}); + testInjector.register("filesHashService", { + saveHashesForProject: () => {} + }); + testInjector.register("androidPluginBuildService", {}); + testInjector.register("errors", stubs.ErrorsStub); + testInjector.register("logger", stubs.LoggerStub); + testInjector.register("projectData", stubs.ProjectDataStub); + testInjector.register("androidToolsInfo", { + getToolsInfo: () => { + return { + androidHomeEnvVar: true + }; + }, + validateInfo: () => { + + } + }); + + return testInjector; +}; + +const getDefautlBuildConfig = (): IBuildConfig => { + return { + release: true, + buildForDevice: false, + device: "testDevice", + provision: null, + teamId: "", + projectDir: "location/location", + keyStorePath: "" + } +} + +describe.only("androidDebugService", () => { + let injector: IInjector; + let androidProjectService: IPlatformProjectService; + let sandbox: sinon.SinonSandbox = null; + + beforeEach(() => { + sandbox = sinon.sandbox.create(); + injector = createTestInjector(); + androidProjectService = injector.resolve("androidProjectService"); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe("buildPlatform", () => { + let projectData: IProjectData; + let childProcess: stubs.ChildProcessStub; + + beforeEach(() => { + projectData = injector.resolve("projectData"); + childProcess = injector.resolve("childProcess"); + const getPlatformDataStub: sinon.SinonStub = sandbox.stub(androidProjectService, "getPlatformData"); + getPlatformDataStub.callsFake(() => { + return { + configurationFilePath: "" + }; + }); + + }); + + it("release no bundle", async () => { + //arrange + const buildConfig = getDefautlBuildConfig(); + + //act + await androidProjectService.buildProject("local/local", projectData, buildConfig, ); + + //assert + assert.include(childProcess.lastCommandArgs, "assembleRelease"); + }) + + it("debug no bundle", async () => { + //arrange + const buildConfig = getDefautlBuildConfig(); + buildConfig.release = false; + + //act + await androidProjectService.buildProject("local/local", projectData, buildConfig, ); + + //assert + assert.include(childProcess.lastCommandArgs, "assembleDebug"); + }) + + it("release bundle", async () => { + //arrange + const buildConfig = getDefautlBuildConfig(); + buildConfig.androidBundle = true; + + //act + await androidProjectService.buildProject("local/local", projectData, buildConfig, ); + + //assert + assert.include(childProcess.lastCommandArgs, "bundleRelease"); + }) + + it("debug bundle", async () => { + //arrange + const buildConfig = getDefautlBuildConfig(); + buildConfig.androidBundle = true; + buildConfig.release = false; + + //act + await androidProjectService.buildProject("local/local", projectData, buildConfig, ); + + //assert + assert.include(childProcess.lastCommandArgs, "bundleDebug"); + }) + }); +}); diff --git a/test/stubs.ts b/test/stubs.ts index ce7f7744f6..c5b61417bf 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -647,7 +647,7 @@ export class AndroidToolsInfoStub implements IAndroidToolsInfo { } } -export class ChildProcessStub { +export class ChildProcessStub extends EventEmitter { public spawnCount = 0; public execCount = 0; public spawnFromEventCount = 0; From 2cb70130ced423259e232ceaf38fb61b9ad6e525 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Thu, 1 Nov 2018 15:00:02 +0200 Subject: [PATCH 07/12] feat: print output path of .aab file --- lib/commands/build.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 0a4f55fd65..6cc43b25a9 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -13,7 +13,7 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase { this.$projectData.initializeProjectData(); } - public async executeCore(args: string[]): Promise { + public async executeCore(args: string[]): Promise { const platform = args[0].toLowerCase(); const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: !!this.$options.bundle, @@ -44,10 +44,12 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase { keyStorePassword: this.$options.keyStorePassword, androidBundle: this.$options.aab }; - await this.$platformService.buildPlatform(platform, buildConfig, this.$projectData); + const outputPath = await this.$platformService.buildPlatform(platform, buildConfig, this.$projectData); if (this.$options.copyTo) { this.$platformService.copyLastOutput(platform, this.$options.copyTo, buildConfig, this.$projectData); } + + return outputPath } protected validatePlatform(platform: string): void { @@ -90,7 +92,7 @@ export class BuildIosCommand extends BuildCommandBase implements ICommand { } public async execute(args: string[]): Promise { - return this.executeCore([this.$platformsData.availablePlatforms.iOS]); + await this.executeCore([this.$platformsData.availablePlatforms.iOS]); } public async canExecute(args: string[]): Promise { @@ -128,10 +130,9 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand { const buildResult = await this.executeCore([this.$platformsData.availablePlatforms.Android]); if (this.$options.aab) { + this.$logger.info(`Your .aab file is located at: ${buildResult}`); this.$logger.info("Link to documentation article"); } - - return buildResult; } public async canExecute(args: string[]): Promise { From d5432f7417102ca319db9abf71a38b2ee5e814d6 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Thu, 1 Nov 2018 16:14:41 +0200 Subject: [PATCH 08/12] chore: lint and remove only in tests --- lib/commands/build.ts | 2 +- test/services/android-project-service.ts | 21 +++++++++------------ test/stubs.ts | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 6cc43b25a9..2ed300be3c 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -49,7 +49,7 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase { this.$platformService.copyLastOutput(platform, this.$options.copyTo, buildConfig, this.$projectData); } - return outputPath + return outputPath; } protected validatePlatform(platform: string): void { diff --git a/test/services/android-project-service.ts b/test/services/android-project-service.ts index 881e97ff01..2945514d66 100644 --- a/test/services/android-project-service.ts +++ b/test/services/android-project-service.ts @@ -4,8 +4,6 @@ import * as stubs from "../stubs"; import { assert } from "chai"; import * as sinon from "sinon"; - - const createTestInjector = (): IInjector => { const testInjector = new Yok(); testInjector.register("androidProjectService", AndroidProjectService); @@ -16,12 +14,12 @@ const createTestInjector = (): IInjector => { testInjector.register("fs", stubs.FileSystemStub); testInjector.register("injector", testInjector); testInjector.register("devicePlatformsConstants", {}); - testInjector.register("npm", {}); + testInjector.register("packageManager", {}); testInjector.register("platformEnvironmentRequirements", {}); testInjector.register("androidResourcesMigrationService", {}); testInjector.register("androidPluginBuildService", {}); testInjector.register("filesHashService", { - saveHashesForProject: () => {} + saveHashesForProject: () => ({}) }); testInjector.register("androidPluginBuildService", {}); testInjector.register("errors", stubs.ErrorsStub); @@ -34,7 +32,7 @@ const createTestInjector = (): IInjector => { }; }, validateInfo: () => { - + return true; } }); @@ -50,8 +48,8 @@ const getDefautlBuildConfig = (): IBuildConfig => { teamId: "", projectDir: "location/location", keyStorePath: "" - } -} + }; +}; describe.only("androidDebugService", () => { let injector: IInjector; @@ -81,7 +79,6 @@ describe.only("androidDebugService", () => { configurationFilePath: "" }; }); - }); it("release no bundle", async () => { @@ -93,7 +90,7 @@ describe.only("androidDebugService", () => { //assert assert.include(childProcess.lastCommandArgs, "assembleRelease"); - }) + }); it("debug no bundle", async () => { //arrange @@ -105,7 +102,7 @@ describe.only("androidDebugService", () => { //assert assert.include(childProcess.lastCommandArgs, "assembleDebug"); - }) + }); it("release bundle", async () => { //arrange @@ -117,7 +114,7 @@ describe.only("androidDebugService", () => { //assert assert.include(childProcess.lastCommandArgs, "bundleRelease"); - }) + }); it("debug bundle", async () => { //arrange @@ -130,6 +127,6 @@ describe.only("androidDebugService", () => { //assert assert.include(childProcess.lastCommandArgs, "bundleDebug"); - }) + }); }); }); diff --git a/test/stubs.ts b/test/stubs.ts index c5b61417bf..9c38431f4b 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -855,7 +855,7 @@ export class AndroidBundleValidatorHelper implements IAndroidBundleValidatorHelp } validateRuntimeVersion() { - + return; } } From ba7cac4c7554512f3711837b02d1bc9bd9d942ae Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Tue, 6 Nov 2018 17:10:20 +0200 Subject: [PATCH 09/12] chore: fix comments --- lib/commands/build.ts | 25 +++++++++++++------ lib/constants.ts | 9 +++++-- lib/declarations.d.ts | 4 +-- lib/definitions/platform.d.ts | 2 +- .../android-bundle-validator-helper.ts | 16 ++++++------ lib/helpers/bundle-validator-helper.ts | 16 +++++------- lib/helpers/version-validator-helper.ts | 12 +++++++++ lib/services/android-project-service.ts | 4 +-- lib/services/platform-service.ts | 2 +- test/services/android-project-service.ts | 2 +- 10 files changed, 56 insertions(+), 36 deletions(-) create mode 100644 lib/helpers/version-validator-helper.ts diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 2ed300be3c..7f3f2e72d8 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -1,4 +1,4 @@ -import { ANDROID_RELEASE_BUILD_ERROR_MESSAGE } from "../constants"; +import { ANDROID_RELEASE_BUILD_ERROR_MESSAGE, AndroidAppBundleMessages } from "../constants"; import { ValidatePlatformCommandBase } from "./command-base"; export abstract class BuildCommandBase extends ValidatePlatformCommandBase { @@ -8,7 +8,8 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase { $platformsData: IPlatformsData, protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, $platformService: IPlatformService, - private $bundleValidatorHelper: IBundleValidatorHelper) { + private $bundleValidatorHelper: IBundleValidatorHelper, + protected $logger: ILogger) { super($options, $platformsData, $platformService, $projectData); this.$projectData.initializeProjectData(); } @@ -44,9 +45,13 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase { keyStorePassword: this.$options.keyStorePassword, androidBundle: this.$options.aab }; + const outputPath = await this.$platformService.buildPlatform(platform, buildConfig, this.$projectData); + if (this.$options.copyTo) { this.$platformService.copyLastOutput(platform, this.$options.copyTo, buildConfig, this.$projectData); + } else { + this.$logger.info(`Your build result is located at: ${outputPath}`); } return outputPath; @@ -87,8 +92,9 @@ export class BuildIosCommand extends BuildCommandBase implements ICommand { $platformsData: IPlatformsData, $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, $platformService: IPlatformService, - $bundleValidatorHelper: IBundleValidatorHelper) { - super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper); + $bundleValidatorHelper: IBundleValidatorHelper, + $logger: ILogger) { + super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper, $logger); } public async execute(args: string[]): Promise { @@ -123,15 +129,18 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand { $bundleValidatorHelper: IBundleValidatorHelper, protected $androidBundleValidatorHelper: IAndroidBundleValidatorHelper, protected $logger: ILogger) { - super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper); + super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper, $logger); } public async execute(args: string[]): Promise { - const buildResult = await this.executeCore([this.$platformsData.availablePlatforms.Android]); + await this.executeCore([this.$platformsData.availablePlatforms.Android]); if (this.$options.aab) { - this.$logger.info(`Your .aab file is located at: ${buildResult}`); - this.$logger.info("Link to documentation article"); + this.$logger.info(AndroidAppBundleMessages.ANDROID_APP_BUNDLE_DOCS_MESSAGE); + + if (this.$options.release) { + this.$logger.info(AndroidAppBundleMessages.ANDROID_APP_BUNDLE_PUBLISH_DOCS_MESSAGE); + } } } diff --git a/lib/constants.ts b/lib/constants.ts index 1dda478626..8b55dd1238 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -245,6 +245,11 @@ export class BundleValidatorMessages { } export class AndroidBundleValidatorMessages { - public static AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE = "This command does not support --aab (Android Application Bundle) parameter."; - public static RUNTIME_VERSION_TOO_LOW = "Android Application Bundle (--aab) option requires NativeScript Android Runtime (tns-android) version %s and above."; + public static AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE = "This command does not support --aab (Android App Bundle) parameter."; + public static NOT_SUPPORTED_RUNTIME_VERSION = "Android App Bundle (--aab) option requires NativeScript Android Runtime (tns-android) version %s and above."; +} + +export class AndroidAppBundleMessages { + public static ANDROID_APP_BUNDLE_DOCS_MESSAGE = "What is Android App Bundle: https://docs.nativescript.org/tooling/publishing/android-app-bundle"; + public static ANDROID_APP_BUNDLE_PUBLISH_DOCS_MESSAGE = "How to use Android App Bundle for publishing: https://docs.nativescript.org/tooling/publishing/publishing-android-apps#android-app-bundle"; } diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index f079622e01..ce7a0aea4d 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -535,9 +535,9 @@ interface IEnvOptions { env: Object; } -interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease, IAndroidBundle { } +interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease, IHasAndroidBundle { } - interface IAndroidBundle { + interface IHasAndroidBundle { androidBundle?: boolean; } diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index f579570a98..018a740eea 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -286,7 +286,7 @@ interface IValidBuildOutputData { regexes?: RegExp[]; } -interface IBuildOutputOptions extends Partial, IRelease, IAndroidBundle {} +interface IBuildOutputOptions extends Partial, IRelease, IHasAndroidBundle {} interface IPlatformsData { availablePlatforms: any; diff --git a/lib/helpers/android-bundle-validator-helper.ts b/lib/helpers/android-bundle-validator-helper.ts index 46821997c9..4f0f266c29 100644 --- a/lib/helpers/android-bundle-validator-helper.ts +++ b/lib/helpers/android-bundle-validator-helper.ts @@ -1,14 +1,16 @@ import * as semver from "semver"; import * as util from "util"; import { AndroidBundleValidatorMessages, TNS_ANDROID_RUNTIME_NAME } from "../constants"; +import { VersionValidatorHelper } from "./version-validator-helper"; -export class AndroidBundleValidatorHelper implements IAndroidBundleValidatorHelper { +export class AndroidBundleValidatorHelper extends VersionValidatorHelper implements IAndroidBundleValidatorHelper { public static MIN_RUNTIME_VERSION = "5.0.0"; constructor(protected $projectData: IProjectData, protected $errors: IErrors, protected $options: IOptions, protected $projectDataService: IProjectDataService) { + super(); } public validateNoAab(): void { @@ -22,14 +24,10 @@ export class AndroidBundleValidatorHelper implements IAndroidBundleValidatorHelp const androidRuntimeInfo = this.$projectDataService.getNSValue(projectData.projectDir, TNS_ANDROID_RUNTIME_NAME); const androidRuntimeVersion = androidRuntimeInfo ? androidRuntimeInfo.version : ""; - if (androidRuntimeVersion) { - const shouldSkipCheck = !semver.valid(androidRuntimeVersion) && !semver.validRange(androidRuntimeVersion); - if (!shouldSkipCheck) { - const isAndroidBundleSupported = semver.gte(semver.coerce(androidRuntimeVersion), semver.coerce(AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION)); - if (!isAndroidBundleSupported) { - this.$errors.failWithoutHelp(util.format(AndroidBundleValidatorMessages.RUNTIME_VERSION_TOO_LOW, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION)); - } - } + if (androidRuntimeVersion && + this.isValidVersion(androidRuntimeVersion) && + !this.compareCoerceVersions(androidRuntimeVersion, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION, semver.gte)) { + this.$errors.failWithoutHelp(util.format(AndroidBundleValidatorMessages.NOT_SUPPORTED_RUNTIME_VERSION, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION)); } } } diff --git a/lib/helpers/bundle-validator-helper.ts b/lib/helpers/bundle-validator-helper.ts index 831d50f66d..abc3c2dce7 100644 --- a/lib/helpers/bundle-validator-helper.ts +++ b/lib/helpers/bundle-validator-helper.ts @@ -1,8 +1,9 @@ import * as semver from "semver"; import * as util from "util"; import { BundleValidatorMessages } from "../constants"; +import { VersionValidatorHelper } from "./version-validator-helper"; -export class BundleValidatorHelper implements IBundleValidatorHelper { +export class BundleValidatorHelper extends VersionValidatorHelper implements IBundleValidatorHelper { private bundlersMap: IStringDictionary = { webpack: "nativescript-dev-webpack" }; @@ -10,6 +11,7 @@ export class BundleValidatorHelper implements IBundleValidatorHelper { constructor(protected $projectData: IProjectData, protected $errors: IErrors, protected $options: IOptions) { + super(); this.$projectData.initializeProjectData(); } @@ -22,15 +24,9 @@ export class BundleValidatorHelper implements IBundleValidatorHelper { this.$errors.fail(BundleValidatorMessages.MissingBundlePlugin); } - if (minSupportedVersion) { - const currentVersion = bundlerVersionInDependencies || bundlerVersionInDevDependencies; - const shouldSkipCheck = !semver.valid(currentVersion) && !semver.validRange(currentVersion); - if (!shouldSkipCheck) { - const isBundleSupported = semver.gte(semver.coerce(currentVersion), semver.coerce(minSupportedVersion)); - if (!isBundleSupported) { - this.$errors.failWithoutHelp(util.format(BundleValidatorMessages.NotSupportedVersion, minSupportedVersion)); - } - } + const currentVersion = bundlerVersionInDependencies || bundlerVersionInDevDependencies; + if (minSupportedVersion && this.isValidVersion(currentVersion) && !this.compareCoerceVersions(currentVersion, minSupportedVersion, semver.gte)) { + this.$errors.failWithoutHelp(util.format(BundleValidatorMessages.NotSupportedVersion, minSupportedVersion)); } } } diff --git a/lib/helpers/version-validator-helper.ts b/lib/helpers/version-validator-helper.ts new file mode 100644 index 0000000000..bb46213cb1 --- /dev/null +++ b/lib/helpers/version-validator-helper.ts @@ -0,0 +1,12 @@ + +import * as semver from "semver"; + +export class VersionValidatorHelper { + protected isValidVersion(version: string): string { + return semver.valid(version) || semver.validRange(version); + } + + protected compareCoerceVersions(version: string, minVersion: string, comperator: Function): boolean { + return comperator(semver.coerce(version), semver.coerce(minVersion)); + } +} diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index e5311a4304..5c7a3f1ea3 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -336,9 +336,9 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject gradleArgs.unshift("--debug"); } if (buildConfig.release) { - task = baseTask + "Release"; + task = `${baseTask}Release`; } else { - task = baseTask + "Debug"; + task = `${baseTask}Debug`; } gradleArgs.unshift(task); diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 5e84795879..39b8aad9ae 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -257,7 +257,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { } public async validateOptions(provision: true | string, teamId: true | string, projectData: IProjectData, platform?: string, aab?: boolean): Promise { - if (platform && this.$mobileHelper.isiOSPlatform(platform) && aab) { + if (platform && !this.$mobileHelper.isAndroidPlatform(platform) && aab) { this.$errors.failWithoutHelp("Option --aab is supported for Android platform only."); } diff --git a/test/services/android-project-service.ts b/test/services/android-project-service.ts index 2945514d66..9c69115a31 100644 --- a/test/services/android-project-service.ts +++ b/test/services/android-project-service.ts @@ -51,7 +51,7 @@ const getDefautlBuildConfig = (): IBuildConfig => { }; }; -describe.only("androidDebugService", () => { +describe("androidDebugService", () => { let injector: IInjector; let androidProjectService: IPlatformProjectService; let sandbox: sinon.SinonSandbox = null; From 11bd5ba2247294515655d2fdd48287bbd781e4d7 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Wed, 7 Nov 2018 13:35:34 +0200 Subject: [PATCH 10/12] chore: add help for aab flag --- docs/man_pages/project/testing/build-android.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/man_pages/project/testing/build-android.md b/docs/man_pages/project/testing/build-android.md index ddc277c821..ce06a7ae8d 100644 --- a/docs/man_pages/project/testing/build-android.md +++ b/docs/man_pages/project/testing/build-android.md @@ -13,7 +13,7 @@ Builds the project for Android and produces an APK that you can manually deploy Usage | Synopsis ---|--- -General | `$ tns build android [--compileSdk ] [--key-store-path --key-store-password --key-store-alias --key-store-alias-password ] [--release] [--static-bindings] [--copy-to ] [--bundle [] [--env.*]]` +General | `$ tns build android [--compileSdk ] [--key-store-path --key-store-password --key-store-alias --key-store-alias-password ] [--release] [--static-bindings] [--copy-to ] [--bundle [] [--env.*]] [--aab]` ### Options @@ -27,6 +27,7 @@ General | `$ tns build android [--compileSdk ] [--key-store-path From a29ced30f9643bf1095075826f176cacfbc0859d Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Wed, 7 Nov 2018 17:27:17 +0200 Subject: [PATCH 11/12] chore: fix aab messages after review --- docs/man_pages/project/testing/build-android.md | 2 +- lib/commands/build.ts | 2 +- lib/services/platform-service.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/man_pages/project/testing/build-android.md b/docs/man_pages/project/testing/build-android.md index ce06a7ae8d..344c32b1da 100644 --- a/docs/man_pages/project/testing/build-android.md +++ b/docs/man_pages/project/testing/build-android.md @@ -27,7 +27,7 @@ General | `$ tns build android [--compileSdk ] [--key-store-path diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 7f3f2e72d8..0c4f091f19 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -51,7 +51,7 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase { if (this.$options.copyTo) { this.$platformService.copyLastOutput(platform, this.$options.copyTo, buildConfig, this.$projectData); } else { - this.$logger.info(`Your build result is located at: ${outputPath}`); + this.$logger.info(`The build result is located at: ${outputPath}`); } return outputPath; diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 39b8aad9ae..4f3ff14d1f 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -258,7 +258,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { public async validateOptions(provision: true | string, teamId: true | string, projectData: IProjectData, platform?: string, aab?: boolean): Promise { if (platform && !this.$mobileHelper.isAndroidPlatform(platform) && aab) { - this.$errors.failWithoutHelp("Option --aab is supported for Android platform only."); + this.$errors.failWithoutHelp("The --aab option is supported only for the Android platform."); } if (platform) { From a6dcfb964bcc12009868a5246bdca4ffb5a7408d Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Wed, 7 Nov 2018 19:35:54 +0200 Subject: [PATCH 12/12] chore: fix comments aab --- .../android-bundle-validator-helper.ts | 8 ++++---- lib/helpers/bundle-validator-helper.ts | 4 ++-- lib/helpers/version-validator-helper.ts | 20 +++++++++++++++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/lib/helpers/android-bundle-validator-helper.ts b/lib/helpers/android-bundle-validator-helper.ts index 4f0f266c29..fbcbd3698b 100644 --- a/lib/helpers/android-bundle-validator-helper.ts +++ b/lib/helpers/android-bundle-validator-helper.ts @@ -1,4 +1,3 @@ -import * as semver from "semver"; import * as util from "util"; import { AndroidBundleValidatorMessages, TNS_ANDROID_RUNTIME_NAME } from "../constants"; import { VersionValidatorHelper } from "./version-validator-helper"; @@ -23,10 +22,11 @@ export class AndroidBundleValidatorHelper extends VersionValidatorHelper impleme if (this.$options.aab) { const androidRuntimeInfo = this.$projectDataService.getNSValue(projectData.projectDir, TNS_ANDROID_RUNTIME_NAME); const androidRuntimeVersion = androidRuntimeInfo ? androidRuntimeInfo.version : ""; - - if (androidRuntimeVersion && + const shouldThrowError = androidRuntimeVersion && this.isValidVersion(androidRuntimeVersion) && - !this.compareCoerceVersions(androidRuntimeVersion, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION, semver.gte)) { + this.isVersionLowerThan(androidRuntimeVersion, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION); + + if (shouldThrowError) { this.$errors.failWithoutHelp(util.format(AndroidBundleValidatorMessages.NOT_SUPPORTED_RUNTIME_VERSION, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION)); } } diff --git a/lib/helpers/bundle-validator-helper.ts b/lib/helpers/bundle-validator-helper.ts index abc3c2dce7..41336b2846 100644 --- a/lib/helpers/bundle-validator-helper.ts +++ b/lib/helpers/bundle-validator-helper.ts @@ -1,4 +1,3 @@ -import * as semver from "semver"; import * as util from "util"; import { BundleValidatorMessages } from "../constants"; import { VersionValidatorHelper } from "./version-validator-helper"; @@ -25,7 +24,8 @@ export class BundleValidatorHelper extends VersionValidatorHelper implements IBu } const currentVersion = bundlerVersionInDependencies || bundlerVersionInDevDependencies; - if (minSupportedVersion && this.isValidVersion(currentVersion) && !this.compareCoerceVersions(currentVersion, minSupportedVersion, semver.gte)) { + const shouldThrowError = minSupportedVersion && this.isValidVersion(currentVersion) && this.isVersionLowerThan(currentVersion, minSupportedVersion); + if (shouldThrowError) { this.$errors.failWithoutHelp(util.format(BundleValidatorMessages.NotSupportedVersion, minSupportedVersion)); } } diff --git a/lib/helpers/version-validator-helper.ts b/lib/helpers/version-validator-helper.ts index bb46213cb1..1531dc96ce 100644 --- a/lib/helpers/version-validator-helper.ts +++ b/lib/helpers/version-validator-helper.ts @@ -6,7 +6,23 @@ export class VersionValidatorHelper { return semver.valid(version) || semver.validRange(version); } - protected compareCoerceVersions(version: string, minVersion: string, comperator: Function): boolean { - return comperator(semver.coerce(version), semver.coerce(minVersion)); + protected isVersionGreaterThan(v1: string, v2: string): boolean { + return this.compareCoerceVersions(v1, v2, semver.gt); + } + + protected isVersionGreaterOrEqualThan(v1: string, v2: string): boolean { + return this.compareCoerceVersions(v1, v2, semver.gte); + } + + protected isVersionLowerThan(v1: string, v2: string): boolean { + return this.compareCoerceVersions(v1, v2, semver.lt); + } + + protected isVersionLowerOrEqualThan(v1: string, v2: string): boolean { + return this.compareCoerceVersions(v1, v2, semver.lte); + } + + private compareCoerceVersions(version: string, minVersion: string, condition: Function): boolean { + return condition(semver.coerce(version), semver.coerce(minVersion)); } }