Skip to content

feat: support aab in run and deploy #5078

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 1 commit into from
Oct 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ $injector.require("androidPluginBuildService", "./services/android-plugin-build-
$injector.require("gradleCommandService", "./services/android/gradle-command-service");
$injector.require("gradleBuildService", "./services/android/gradle-build-service");
$injector.require("gradleBuildArgsService", "./services/android/gradle-build-args-service");
$injector.require("androidBundleToolService", "./services/android/android-bundle-tool-service");
$injector.require("iOSEntitlementsService", "./services/ios-entitlements-service");
$injector.require("iOSNativeTargetService", "./services/ios-native-target-service");
$injector.require("iOSExtensionsService", "./services/ios-extensions-service");
Expand Down
3 changes: 0 additions & 3 deletions lib/commands/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements
private $debugDataService: IDebugDataService,
private $debugController: IDebugController,
private $liveSyncCommandHelper: ILiveSyncCommandHelper,
private $androidBundleValidatorHelper: IAndroidBundleValidatorHelper,
private $migrateController: IMigrateController) {
super($options, $platformsDataService, $platformValidationService, $projectData);
$cleanupService.setShouldDispose(false);
Expand Down Expand Up @@ -57,8 +56,6 @@ export class DebugPlatformCommand extends ValidatePlatformCommandBase implements
await this.$migrateController.validate({ projectDir: this.$projectData.projectDir, platforms: [this.platform] });
}

this.$androidBundleValidatorHelper.validateNoAab();

if (!this.$platformValidationService.isPlatformSupportedForOS(this.platform, this.$projectData)) {
this.$errors.fail(`Applications for platform ${this.platform} can not be built on this OS`);
}
Expand Down
2 changes: 0 additions & 2 deletions lib/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export class DeployOnDeviceCommand extends ValidatePlatformCommandBase implement
private $mobileHelper: Mobile.IMobileHelper,
$platformsDataService: IPlatformsDataService,
private $deployCommandHelper: DeployCommandHelper,
private $androidBundleValidatorHelper: IAndroidBundleValidatorHelper,
private $markingModeService: IMarkingModeService,
private $migrateController: IMigrateController) {
super($options, $platformsDataService, $platformValidationService, $projectData);
Expand All @@ -40,7 +39,6 @@ export class DeployOnDeviceCommand extends ValidatePlatformCommandBase implement
await this.$migrateController.validate({ projectDir: this.$projectData.projectDir, platforms: [platform] });
}

this.$androidBundleValidatorHelper.validateNoAab();
if (!args || !args.length || args.length > 1) {
return false;
}
Expand Down
2 changes: 0 additions & 2 deletions lib/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export class RunCommandBase implements ICommand {
public platform: string;
constructor(
private $analyticsService: IAnalyticsService,
private $androidBundleValidatorHelper: IAndroidBundleValidatorHelper,
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
private $errors: IErrors,
private $hostInfo: IHostInfo,
Expand All @@ -34,7 +33,6 @@ export class RunCommandBase implements ICommand {
this.platform = this.$devicePlatformsConstants.Android;
}

this.$androidBundleValidatorHelper.validateNoAab();
this.$projectData.initializeProjectData();
const platforms = this.platform ? [this.platform] : [this.$devicePlatformsConstants.Android, this.$devicePlatformsConstants.iOS];

Expand Down
4 changes: 2 additions & 2 deletions lib/common/child-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ export class ChildProcess extends EventEmitter implements IChildProcess {

public async trySpawnFromCloseEvent(command: string, args: string[], options?: any, spawnFromEventOptions?: ISpawnFromEventOptions): Promise<ISpawnResult> {
try {
const childProcess = await this.spawnFromEvent(command, args, "close", options, spawnFromEventOptions);
return childProcess;
const childProcessResult = await this.spawnFromEvent(command, args, "close", options, spawnFromEventOptions);
return childProcessResult;
} catch (err) {
this.$logger.trace(`Error from trySpawnFromCloseEvent method. More info: ${err}`);
return Promise.resolve({ stderr: err && err.message ? err.message : err, stdout: null, exitCode: -1 });
Expand Down
5 changes: 5 additions & 0 deletions lib/common/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,11 @@ interface ISysInfo {
* @return {Promise<string>} Returns the currently installed version of Xcode or null if Xcode is not installed or executed on Linux or Windows.
*/
getXcodeVersion(): Promise<string>;
/**
* Returns the currently installed Java path based on JAVA_HOME and PATH..
* @return {Promise<string>} The currently installed Java path.
*/
getJavaPath(): Promise<string>;
/**
* Returns the currently installed Cocoapods version.
* @return {Promise<string>} Returns the currently installed Cocoapods version. It will return null if Cocoapods is not installed.
Expand Down
4 changes: 2 additions & 2 deletions lib/common/definitions/mobile.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,9 +321,9 @@ declare global {
interface IDeviceApplicationManager extends NodeJS.EventEmitter {
getInstalledApplications(): Promise<string[]>;
isApplicationInstalled(appIdentifier: string): Promise<boolean>;
installApplication(packageFilePath: string, appIdentifier?: string): Promise<void>;
installApplication(packageFilePath: string, appIdentifier?: string, buildData?: IBuildData): Promise<void>;
uninstallApplication(appIdentifier: string): Promise<void>;
reinstallApplication(appIdentifier: string, packageFilePath: string): Promise<void>;
reinstallApplication(appIdentifier: string, packageFilePath: string, buildData?: IBuildData): Promise<void>;
startApplication(appData: IStartApplicationData): Promise<void>;
stopApplication(appData: IApplicationData): Promise<void>;
restartApplication(appData: IStartApplicationData): Promise<void>;
Expand Down
35 changes: 33 additions & 2 deletions lib/common/mobile/android/android-application-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import { ApplicationManagerBase } from "../application-manager-base";
import { TARGET_FRAMEWORK_IDENTIFIERS, LiveSyncPaths } from "../../constants";
import { hook, sleep, regExpEscape } from "../../helpers";
import { cache } from "../../decorators";
import { parse, join } from "path";
import { AAB_EXTENSION_NAME, APKS_EXTENSION_NAME } from "../../../constants";

export class AndroidApplicationManager extends ApplicationManagerBase {
public PID_CHECK_INTERVAL = 100;
public PID_CHECK_TIMEOUT = 10000; // 10 secs

constructor(private adb: Mobile.IDeviceAndroidDebugBridge,
private identifier: string,
private $androidBundleToolService: IAndroidBundleToolService,
private $fs: IFileSystem,
private $options: IOptions,
private $logcatHelper: Mobile.ILogcatHelper,
private $androidProcessService: Mobile.IAndroidProcessService,
Expand All @@ -33,13 +37,29 @@ export class AndroidApplicationManager extends ApplicationManagerBase {
}

@hook('install')
public async installApplication(packageFilePath: string, appIdentifier?: string): Promise<void> {
public async installApplication(packageFilePath: string, appIdentifier?: string, buildData?: IAndroidBuildData): Promise<void> {
if (appIdentifier) {
const deviceRootPath = `${LiveSyncPaths.ANDROID_TMP_DIR_NAME}/${appIdentifier}`;
await this.adb.executeShellCommand(["rm", "-rf", deviceRootPath]);
}

return this.adb.executeCommand(["install", "-r", `${packageFilePath}`]);
const { dir, name, ext } = parse(packageFilePath);
if (ext === AAB_EXTENSION_NAME) {
const apksOutputPath = join(dir, name) + APKS_EXTENSION_NAME;
if (!this.hasValidApksFile(packageFilePath, apksOutputPath)) {
await this.$androidBundleToolService.buildApks({
aabFilePath: packageFilePath,
apksOutputPath,
signingData: <IAndroidSigningData>buildData
});
}
await this.$androidBundleToolService.installApks({
apksFilePath: apksOutputPath,
deviceId: this.identifier
});
} else {
return this.adb.executeCommand(["install", "-r", `${packageFilePath}`]);
}
}

public uninstallApplication(appIdentifier: string): Promise<void> {
Expand Down Expand Up @@ -155,4 +175,15 @@ export class AndroidApplicationManager extends ApplicationManagerBase {

return new RegExp(`${regExpEscape(appIdentifier)}${packageActivitySeparator}${fullJavaClassName}`, `m`);
}

private hasValidApksFile(aabFilaPath: string, apksFilePath: string): boolean {
let isValid = false;
if (this.$fs.exists(apksFilePath)) {
const lastUpdatedApks = this.$fs.getFsStats(apksFilePath).ctime.getTime();
const lastUpdatedAab = this.$fs.getFsStats(aabFilaPath).ctime.getTime();
isValid = lastUpdatedApks >= lastUpdatedAab;
}

return isValid;
}
}
6 changes: 3 additions & 3 deletions lib/common/mobile/application-manager-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ export abstract class ApplicationManagerBase extends EventEmitter implements Mob
}
}

public async reinstallApplication(appIdentifier: string, packageFilePath: string): Promise<void> {
public async reinstallApplication(appIdentifier: string, packageFilePath: string, buildData?: IBuildData): Promise<void> {
const isApplicationInstalled = await this.isApplicationInstalled(appIdentifier);

if (isApplicationInstalled) {
await this.uninstallApplication(appIdentifier);
}

await this.installApplication(packageFilePath, appIdentifier);
await this.installApplication(packageFilePath, appIdentifier, buildData);
}

public async restartApplication(appData: Mobile.IApplicationData): Promise<void> {
Expand Down Expand Up @@ -83,7 +83,7 @@ export abstract class ApplicationManagerBase extends EventEmitter implements Mob
}
}

public abstract async installApplication(packageFilePath: string, appIdentifier?: string): Promise<void>;
public abstract async installApplication(packageFilePath: string, appIdentifier?: string, buildData?: IBuildData): Promise<void>;
public abstract async uninstallApplication(appIdentifier: string): Promise<void>;
public abstract async startApplication(appData: Mobile.IApplicationData): Promise<void>;
public abstract async stopApplication(appData: Mobile.IApplicationData): Promise<void>;
Expand Down
65 changes: 63 additions & 2 deletions lib/common/test/unit-tests/android-application-manager.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { AndroidApplicationManager } from "../../mobile/android/android-application-manager";
import { Yok } from "../../yok";
import { assert } from "chai";
import { CommonLoggerStub, LogcatHelperStub, AndroidProcessServiceStub, DeviceLogProviderStub, ErrorsStub } from "./stubs";
import { AndroidBundleToolServiceStub, CommonLoggerStub, HooksServiceStub, LogcatHelperStub, AndroidProcessServiceStub, DeviceLogProviderStub, ErrorsStub } from "./stubs";
import { FileSystemStub } from "../../../../test/stubs";
const invalidIdentifier = "invalid.identifier";
const validDeviceIdentifier = "device.identifier";
const validIdentifier = "org.nativescript.testApp";
const validStartOptions = { appId: validIdentifier, projectName: "", projectDir: "" };
const validSigning: any = { keyStoreAlias: "string", keyStorePath: "string", keyStoreAliasPassword: "string", keyStorePassword: "string" };

class AndroidDebugBridgeStub {
public calledInstallApplication = false;
public calledStopApplication = false;
public startedWithActivityManager = false;
public validIdentifierPassed = false;
Expand Down Expand Up @@ -42,6 +45,12 @@ class AndroidDebugBridgeStub {
frontOfTask=true task=TaskRecord{fe592ac #449 A=org.nativescript.testApp U=0 StackId=1 sz=1}"
];

public async executeCommand(args: string[], options?: any): Promise<any> {
if (args[0] === "install") {
this.calledInstallApplication = true;
}
}

public async executeShellCommand(args: string[]): Promise<any> {
if (args && args.length > 0) {
if (args[0].startsWith("cat")) {
Expand Down Expand Up @@ -111,9 +120,11 @@ function createTestInjector(options?: {
testInjector.register("identifier", validDeviceIdentifier);
testInjector.register("logcatHelper", LogcatHelperStub);
testInjector.register("androidProcessService", AndroidProcessServiceStub);
testInjector.register("androidBundleToolService", AndroidBundleToolServiceStub);
testInjector.register("fs", FileSystemStub);
testInjector.register("httpClient", {});
testInjector.register("deviceLogProvider", DeviceLogProviderStub);
testInjector.register("hooksService", {});
testInjector.register("hooksService", HooksServiceStub);
return testInjector;
}

Expand All @@ -140,6 +151,7 @@ describe("android-application-manager", () => {
}

describe("startApplication", () => {

it("fires up the right application", async () => {
setup();
for (let i = 0; i < androidDebugBridge.getInputLength(); i++) {
Expand Down Expand Up @@ -244,6 +256,55 @@ describe("android-application-manager", () => {
});
});

describe("installApplication", () => {
afterEach(function () {
androidDebugBridge.calledInstallApplication = false;
const bundleToolService = testInjector.resolve<AndroidBundleToolServiceStub>("androidBundleToolService");
bundleToolService.isBuildApksCalled = false;
bundleToolService.isInstallApksCalled = false;
});

it("should install apk using adb", async () => {
const bundleToolService = testInjector.resolve<AndroidBundleToolServiceStub>("androidBundleToolService");

await androidApplicationManager.installApplication("myApp.apk");

assert.isFalse(bundleToolService.isBuildApksCalled);
assert.isFalse(bundleToolService.isInstallApksCalled);
assert.isTrue(androidDebugBridge.calledInstallApplication);
});

it("should install aab using bundletool", async () => {
const bundleToolService = testInjector.resolve<AndroidBundleToolServiceStub>("androidBundleToolService");

await androidApplicationManager.installApplication("myApp.aab");

assert.isTrue(bundleToolService.isBuildApksCalled);
assert.isTrue(bundleToolService.isInstallApksCalled);
assert.isFalse(androidDebugBridge.calledInstallApplication);
});

it("should skip aab build when already built", async () => {
const fsStub = testInjector.resolve<FileSystemStub>("fs");
const bundleToolService = testInjector.resolve<AndroidBundleToolServiceStub>("androidBundleToolService");

await androidApplicationManager.installApplication("myApp.aab", "my.app", validSigning);

assert.isTrue(bundleToolService.isBuildApksCalled);
assert.isTrue(bundleToolService.isInstallApksCalled);
assert.isFalse(androidDebugBridge.calledInstallApplication);

fsStub.fsStatCache["myApp.aab"] = fsStub.fsStatCache["myApp.apks"];
bundleToolService.isBuildApksCalled = false;
bundleToolService.isInstallApksCalled = false;
await androidApplicationManager.installApplication("myApp.aab", "my.app", validSigning);

assert.isFalse(bundleToolService.isBuildApksCalled);
assert.isTrue(bundleToolService.isInstallApksCalled);
assert.isFalse(androidDebugBridge.calledInstallApplication);
});
});

describe("stopApplication", () => {
it("should stop the logcat helper", async () => {
setup();
Expand Down
14 changes: 14 additions & 0 deletions lib/common/test/unit-tests/stubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,17 @@ export class DeviceLogProviderStub extends EventEmitter implements Mobile.IDevic
this.currentDeviceProjectDirs[deviceIdentifier] = projectDir;
}
}

export class AndroidBundleToolServiceStub implements IAndroidBundleToolService {
public isInstallApksCalled = false;
public isBuildApksCalled = false;

buildApks(options: IBuildApksOptions): Promise<void> {
this.isBuildApksCalled = true;
return;
}
installApks(options: IInstallApksOptions): Promise<void> {
this.isInstallApksCalled = true;
return;
}
}
2 changes: 2 additions & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ 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 APKS_EXTENSION_NAME = ".apks";
export const HASHES_FILE_NAME = ".nshashes";
export const TNS_NATIVE_SOURCE_GROUP_NAME = "TNSNativeSource";
export const NATIVE_SOURCE_FOLDER = "src";
Expand Down Expand Up @@ -288,6 +289,7 @@ export class BundleValidatorMessages {
export class AndroidBundleValidatorMessages {
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.";
public static NOT_SUPPORTED_ANDROID_VERSION = "Cannot use the Android App Bundle (--aab) option on device '%s' with Android '%s'. The --aab options is supported on Android '%s' and above.";
}

export class AndroidAppBundleMessages {
Expand Down
12 changes: 10 additions & 2 deletions lib/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -581,10 +581,10 @@ interface IEnvOptions {
env: Object;
}

interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease, IHasAndroidBundle { }
interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease, Partial<IHasAndroidBundle> { }

interface IHasAndroidBundle {
androidBundle?: boolean;
androidBundle: boolean;
}

interface IPlatformBuildData extends IRelease, IHasUseHotModuleReloadOption, IBuildConfig, IEnvOptions { }
Expand Down Expand Up @@ -939,6 +939,14 @@ interface IAndroidBundleValidatorHelper {
* @return {void}
*/
validateRuntimeVersion(projectData: IProjectData): void

/**
* Validates that the specified device supports aab.
* @param {Mobile.IDevice} device The device to be validated.
* @param {IBuildData} buildData The current build data.
* @return {void}
*/
validateDeviceApiLevel(device: Mobile.IDevice, buildData: IBuildData): void
}

interface IOptionsTracker {
Expand Down
15 changes: 15 additions & 0 deletions lib/definitions/android-bundle-tool-service.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
interface IAndroidBundleToolService {
buildApks(options: IBuildApksOptions): Promise<void>;
installApks(options: IInstallApksOptions): Promise<void>;
}

interface IBuildApksOptions {
aabFilePath: string;
apksOutputPath: string;
signingData: IAndroidSigningData;
}

interface IInstallApksOptions {
apksFilePath: string;
deviceId: string;
}
6 changes: 4 additions & 2 deletions lib/definitions/build.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ interface IiOSBuildData extends IBuildData {
iCloudContainerEnvironment: string;
}

interface IAndroidBuildData extends IBuildData {
interface IAndroidBuildData extends IBuildData, IAndroidSigningData, IHasAndroidBundle {
}

interface IAndroidSigningData {
keyStoreAlias: string;
keyStorePath: string;
keyStoreAliasPassword: string;
keyStorePassword: string;
androidBundle: boolean;
}

interface IBuildController {
Expand Down
2 changes: 1 addition & 1 deletion lib/definitions/platform.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ interface IValidBuildOutputData {
regexes?: RegExp[];
}

interface IBuildOutputOptions extends Partial<IBuildForDevice>, IRelease, IHasAndroidBundle {
interface IBuildOutputOptions extends Partial<IBuildForDevice>, IRelease, Partial<IHasAndroidBundle> {
outputPath?: string;
}

Expand Down
Loading