Skip to content

Kddimitrov/aab build #4084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Nov 8, 2018
1 change: 1 addition & 0 deletions lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
24 changes: 17 additions & 7 deletions lib/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export abstract class BuildCommandBase extends ValidatePlatformCommandBase {
this.$projectData.initializeProjectData();
}

public async executeCore(args: string[]): Promise<void> {
public async executeCore(args: string[]): Promise<string> {
const platform = args[0].toLowerCase();
const appFilesUpdaterOptions: IAppFilesUpdaterOptions = {
bundle: !!this.$options.bundle,
Expand Down Expand Up @@ -41,12 +41,15 @@ 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);
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 {
Expand Down Expand Up @@ -89,7 +92,7 @@ export class BuildIosCommand extends BuildCommandBase implements ICommand {
}

public async execute(args: string[]): Promise<void> {
return this.executeCore([this.$platformsData.availablePlatforms.iOS]);
await this.executeCore([this.$platformsData.availablePlatforms.iOS]);
}

public async canExecute(args: string[]): Promise<boolean | ICanExecuteCommandOutput> {
Expand Down Expand Up @@ -117,18 +120,25 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand {
$platformsData: IPlatformsData,
$devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
$platformService: IPlatformService,
$bundleValidatorHelper: IBundleValidatorHelper) {
$bundleValidatorHelper: IBundleValidatorHelper,
protected $androidBundleValidatorHelper: IAndroidBundleValidatorHelper,
protected $logger: ILogger) {
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper);
}

public async execute(args: string[]): Promise<void> {
return this.executeCore([this.$platformsData.availablePlatforms.Android]);
const buildResult = await this.executeCore([this.$platformsData.availablePlatforms.Android]);

if (this.$options.aab) {
this.$logger.info(`Your .aab file is located at: ${buildResult}`);
Copy link
Contributor

@DimitarTachev DimitarTachev Nov 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also print the apk output

this.$logger.info("Link to documentation article");
}
}

public async canExecute(args: string[]): Promise<boolean | ICanExecuteCommandOutput> {
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)) {
Expand Down
5 changes: 4 additions & 1 deletion lib/commands/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -108,6 +109,8 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements
}

public async canExecute(args: string[]): Promise<ICanExecuteCommandOutput> {
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`);
}
Expand Down
4 changes: 3 additions & 1 deletion lib/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand All @@ -24,6 +25,7 @@ export class DeployOnDeviceCommand extends ValidatePlatformCommandBase implement
}

public async canExecute(args: string[]): Promise<boolean | ICanExecuteCommandOutput> {
this.$androidBundleValidatorHelper.validateNoAab();
this.$bundleValidatorHelper.validate();
if (!args || !args.length || args.length > 1) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/plugin/build-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 8 additions & 6 deletions lib/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
Expand All @@ -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;

Expand All @@ -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;
}
}
Expand Down Expand Up @@ -66,14 +68,14 @@ export class RunIosCommand implements ICommand {
}

public async execute(args: string[]): Promise<void> {
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<boolean> {
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;
}
Expand Down
7 changes: 7 additions & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -241,3 +243,8 @@ 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 class AndroidBundleValidatorMessages {
public static AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE = "This command does not support --aab (Android Application Bundle) parameter.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOT_SUPPORTED_AAB_OPTION maybe?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think AAB_NOT_SUPPORTED_BY_COMMNAND_MESSAGE is better at describing the message. If you see in constants NOT_SUPPORTED_AAB_OPTION and decide to use it in a service the error message will look strange for sidekick for example.

public static RUNTIME_VERSION_TOO_LOW = "Android Application Bundle (--aab) option requires NativeScript Android Runtime (tns-android) version %s and above.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOT_SUPPORTED_RUNTIME_VERSION?

}
29 changes: 27 additions & 2 deletions lib/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<IDashedOption>): void;
options: IDictionary<IDashedOption>;
Expand Down Expand Up @@ -531,7 +535,11 @@ interface IEnvOptions {
env: Object;
}

interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease { }
interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease, IAndroidBundle { }

interface IAndroidBundle {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IAndroidBundle -> IHasAndroidBundle

androidBundle?: boolean;
}

interface IAppFilesUpdaterOptions extends IBundle, IRelease, IOptionalWatchAllFiles, IHasUseHotModuleReloadOption { }

Expand Down Expand Up @@ -856,6 +864,23 @@ 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;

/**
* 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 {
/**
* Installs nativescript-cloud extension
Expand Down
6 changes: 3 additions & 3 deletions lib/definitions/android-plugin-migrator.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

interface IBuildOptions extends IAndroidBuildOptions{
interface IPluginBuildOptions extends IAndroidBuildOptions{
projectDir?: string
}

Expand All @@ -11,8 +11,8 @@ interface IAndroidBuildOptions {
}

interface IAndroidPluginBuildService {
buildAar(options: IBuildOptions): Promise<boolean>;
migrateIncludeGradle(options: IBuildOptions): boolean;
buildAar(options: IPluginBuildOptions): Promise<boolean>;
migrateIncludeGradle(options: IPluginBuildOptions): boolean;
}

/**
Expand Down
6 changes: 2 additions & 4 deletions lib/definitions/platform.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ interface IPlatformData {
normalizedPlatformName: string;
appDestinationDirectoryPath: string;
deviceBuildOutputPath: string;
bundleBuildOutputPath?: string;
emulatorBuildOutputPath?: string;
getValidBuildOutputData(buildOptions: IBuildOutputOptions): IValidBuildOutputData;
frameworkFilesExtensions: string[];
Expand All @@ -285,10 +286,7 @@ interface IValidBuildOutputData {
regexes?: RegExp[];
}

interface IBuildOutputOptions {
isReleaseBuild?: boolean;
isForDevice?: boolean;
}
interface IBuildOutputOptions extends Partial<IBuildForDevice>, IRelease, IAndroidBundle {}

interface IPlatformsData {
availablePlatforms: any;
Expand Down
2 changes: 1 addition & 1 deletion lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectS
/**
* Build native part of a nativescript plugins if necessary
*/
prebuildNativePlugin(buildOptions: IBuildOptions): Promise<void>;
prebuildNativePlugin(buildOptions: IPluginBuildOptions): Promise<void>;

/**
* Traverse through the production dependencies and find plugins that need build/rebuild
Expand Down
38 changes: 38 additions & 0 deletions lib/helpers/android-bundle-validator-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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 $projectDataService: IProjectDataService) {
}

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is duplicated with the code in bundle-validator-helper.ts. So I suggest to extract a helper method

public isVersionSupported(version: string, minSupportedVersion: string): boolean {
    const shouldSkipCheck = !semver.valid(version) && !semver.validRange(version);
    if(shouldSkipCheck) {
          return true;
    }
    
    const isVersionSupported = semver.gte(semver.coerce(version), semver.coerce(minSupportedVersion));
    return isVersionSupported;
}
const isAndroidBundleSupported = helpers.isVersionSupported(androidRuntimeVersion, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION);
if (!isAndroidBundleSupported) {
    this.$errors.failWithoutHelp(..);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have considered a similar approach, but it seemed strange to extract a method with such specific use case. By specific use case I mean - behavior if the version is not valid(someone might want to error in this case) and gte (this might be adjusted by the required version, but still requires knowledge about isVersionSupported inner working).

If you insist I will extract it in separate class and extend the others, but I don't think anyone will ever use it elsewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might extract !semver.valid(androidRuntimeVersion) && !semver.validRange(androidRuntimeVersion); as a helper method.

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));
}
}
}
}
}
}

$injector.register("androidBundleValidatorHelper", AndroidBundleValidatorHelper);
3 changes: 2 additions & 1 deletion lib/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
};
}

Expand Down
8 changes: 4 additions & 4 deletions lib/services/android-plugin-build-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean> {
public async buildAar(options: IPluginBuildOptions): Promise<boolean> {
this.validateOptions(options);
const manifestFilePath = this.getManifest(options.platformsAndroidDirPath);
const androidSourceDirectories = this.getAndroidSourceDirectories(options.platformsAndroidDirPath);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.");
}
Expand All @@ -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.");
}
Expand Down
Loading