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
Merged

Kddimitrov/aab build #4084

merged 12 commits into from
Nov 8, 2018

Conversation

KristianDD
Copy link
Contributor

PR Checklist

What is the current behavior?

You can't generate .aab file.

What is the new behavior?

You can generate .aab file using tns build android --aab.

Fixes/Implements/Closes #4068

@KristianDD KristianDD force-pushed the kddimitrov/aab-build branch from 8f3cc2a to cdb1ae1 Compare November 1, 2018 14:24
@KristianDD KristianDD force-pushed the kddimitrov/aab-build branch from cdb1ae1 to d5432f7 Compare November 2, 2018 09:20
const gradleArgs = this.getGradleBuildOptions(buildConfig, projectData);
const baseTask = buildConfig.androidBundle ? "bundle" : "assemble";
const platformData = this.getPlatformData(projectData);
const outputPath = buildConfig.androidBundle ? platformData.bundleBuildOutputPath : platformData.deviceBuildOutputPath;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use this._platformData here?

Copy link
Contributor Author

@KristianDD KristianDD Nov 2, 2018

Choose a reason for hiding this comment

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

No, because we can't mock it inside tests - _platformData is private. Initially I used this._platformData.

Copy link
Contributor Author

@KristianDD KristianDD Nov 2, 2018

Choose a reason for hiding this comment

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

And _platformData is not used anywhere else inside the service. Everywhere we use this.getPlatformData, but we should change that or at least cache the platformDatabased on the projectData.

if (this.$logger.getLevel() === "TRACE") {
gradleArgs.unshift("--stacktrace");
gradleArgs.unshift("--debug");
}
if (buildConfig.release) {
gradleArgs.unshift("assembleRelease");
task = baseTask + "Release";
Copy link
Contributor

Choose a reason for hiding this comment

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

task = `${baseTask}Release`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shouldn't we have a linter rule if we won't use string concatenation at all?

} else {
gradleArgs.unshift("assembleDebug");
task = baseTask + "Debug";
Copy link
Contributor

Choose a reason for hiding this comment

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

task = `${baseTask}Debug`

@@ -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<boolean> {
public async validateOptions(provision: true | string, teamId: true | string, projectData: IProjectData, platform?: string, aab?: boolean): Promise<boolean> {
if (platform && this.$mobileHelper.isiOSPlatform(platform) && aab) {
Copy link
Contributor

Choose a reason for hiding this comment

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

if (platform && !this.$mobileHelper.isAndroidPlatform(platform) && aab)

is better because if we add new platform the above check will be fine.

lib/constants.ts Outdated
@@ -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.

lib/constants.ts Outdated

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.";
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?

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.

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

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

@NativeScript NativeScript deleted a comment from DimitarTachev Nov 2, 2018
@gezn
Copy link

gezn commented Nov 6, 2018

when this will be merged ? can't wait to test it and see the app sizes for different porpouses. hope this improve size a lot. Thanks @KristianDD

1 similar comment
@gezn
Copy link

gezn commented Nov 6, 2018

when this will be merged ? can't wait to test it and see the app sizes for different porpouses. hope this improve size a lot. Thanks @KristianDD

this.$errors.failWithoutHelp(util.format(AndroidBundleValidatorMessages.RUNTIME_VERSION_TOO_LOW, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION));
}
}
if (androidRuntimeVersion &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Cool! I really like it. But we can make it even better.

const shouldThrowError = androidRuntimeVersion && this.isValidVersion(androidRuntimeVersion) && this.isVersionLowerThan(androidRuntimeVersion, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION);
if (shouldThrowError) { ... }

So no need to provide semver.gte function as a param to another function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

isVersionLowerThan is actually misleading, because it does not imply for gte comparison, but gt comparison. I prefer sending a function to be used to compare the values. This makes the helper much more versatile for future use.

Copy link
Contributor

Choose a reason for hiding this comment

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

  1. "divide and conquer" approach
isVersionLowerThan(..)
isVersionLowerOrEqualThan(..)
isVersionGreaterThan(...)
isVersionGreaterOrEqualThan(...)

If we decide to change the implementation or add additional logic to some of the above functions, the change will affect ONLY the specific function. So we will have simplification + more control over the affected parts.

compareCoerceVersions(androidRuntimeVersion, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION, semver.gte)
compareCoerceVersions(androidRuntimeVersion, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION, semver.gt)
compareCoerceVersions(androidRuntimeVersion, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION, semver.lte)
compareCoerceVersions(androidRuntimeVersion, AndroidBundleValidatorHelper.MIN_RUNTIME_VERSION, semver.lt)

If we decide to change the implementation of compareCoerceVersions it will affect ALL the calls above.
So we will wonder "how this will affect the case when the function is called for example with semver.gt" or Will it affect the case when passed function is semver.lte. So it will be harder to read + less control over the affected parts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Even if I create a wrapper for each comparison semver has, it will underneath use the same private method that wraps the same logic (getting the coerce of each version and invoking a function). So changing the private method will affect the same amount of places, but indirectly.

Copy link
Contributor Author

@KristianDD KristianDD Nov 7, 2018

Choose a reason for hiding this comment

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

@rosen-vladimirov @DimitarTachev I would appreciate your input on the matter. If the majority considers it's better to wrap semver methods I will do it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer a few but simple functions. Methods like isVersionLowerThan are more clear, simple and easy to be unit tested. It's much easier to do one thing and do it well for such functions. In other words, I prefer sticking to simple functions till we don't need more complex scenario requiring some custom comparisons.

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

Choose a reason for hiding this comment

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

I am not very familiar with the CLI errors, but if you/your is not used a lot, I would recommend changing this to "The build result is located at" instead of "Your..."

public async validateOptions(provision: true | string, teamId: true | string, projectData: IProjectData, platform?: string): Promise<boolean> {
public async validateOptions(provision: true | string, teamId: true | string, projectData: IProjectData, platform?: string, aab?: boolean): Promise<boolean> {
if (platform && !this.$mobileHelper.isAndroidPlatform(platform) && aab) {
this.$errors.failWithoutHelp("Option --aab is supported for Android platform only.");
Copy link
Contributor

Choose a reason for hiding this comment

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

I would recommend rephrasing this to "The --aab option is supported only for the Android platform" or "The --aab option is supported only for Android builds".

@@ -27,6 +27,7 @@ General | `$ tns build android [--compileSdk <API Level>] [--key-store-path <Fil
* `--copy-to` - Specifies the file path where the built `.apk` will be copied. If it points to a non-existent directory, it will be created. If the specified value is directory, the original file name will be used.
* `--bundle` - Specifies that the `webpack` bundler will be used to bundle the application.
* `--env.*` - Specifies additional flags that the bundler may process. May be passed multiple times. For example: `--env.uglify --env.snapshot`.
* `--aab` - Specifies that the build must produce an Android App Bundle(`.aab`) file.
Copy link
Contributor

Choose a reason for hiding this comment

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

You should consider replacing "must" with "will". It sounds better to say what the command will do :)

Copy link
Contributor

@ggarabedian ggarabedian left a comment

Choose a reason for hiding this comment

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

👍 for the texts

@KristianDD KristianDD force-pushed the kddimitrov/aab-build branch from df65d21 to a6dcfb9 Compare November 8, 2018 08:25
@KristianDD KristianDD merged commit cd2aad3 into master Nov 8, 2018
@KristianDD KristianDD deleted the kddimitrov/aab-build branch November 8, 2018 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants