Skip to content

Getting started improvements #3426

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 7 commits into from
Mar 12, 2018
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
18 changes: 18 additions & 0 deletions docs/man_pages/cloud/cloud-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<% if (isJekyll) { %>---
title: tns cloud setup
position: 5
---<% } %>
# tns cloud setup
==========

Usage | Synopsis
------|-------
Install the `nativescript-cloud extension` | `$ tns cloud setup`

Install the `nativescript-cloud extension` to configure your environment for cloud builds.

### Related Commands

Command | Description
----------|----------
[setup](setup.html) | Run the setup script to try to automatically configure your environment for local builds.
18 changes: 18 additions & 0 deletions docs/man_pages/env-configuration/setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<% if (isJekyll) { %>---
title: tns setup
position: 5
---<% } %>
# tns setup
==========

Usage | Synopsis
------|-------
Run the setup script | `$ tns setup`

Run the setup script to try to automatically configure your environment for local builds.

### Related Commands

Command | Description
----------|----------
[setup cloud](setup-cloud.html) | Install the nativescript-cloud extension to configure your environment for cloud builds.
6 changes: 6 additions & 0 deletions docs/man_pages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ Command | Description
[device run](device/device-run.html) | Runs the selected application on a connected device.
[device list-applications](device/device-list-applications.html) | Lists the installed applications on all connected devices.

## Environment Configuration Commands
Command | Description
---|---
[setup](env-configuration/setup.html) | Run the setup script to try to automatically configure your environment for local builds.
[setup cloud](cloud/cloud-setup.html) | Install the `nativescript-cloud extension` to configure your environment for cloud builds.

## Global Options
Option | Description
-------|---------
Expand Down
157 changes: 35 additions & 122 deletions lib/android-tools-info.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import * as path from "path";
import * as semver from "semver";
import { EOL } from "os";
import { cache } from "./common/decorators";
import { appendZeroesToVersion } from './common/helpers';
import { androidToolsInfo } from "nativescript-doctor";

export class AndroidToolsInfo implements IAndroidToolsInfo {
private static ANDROID_TARGET_PREFIX = "android";
private static SUPPORTED_TARGETS = ["android-17", "android-18", "android-19", "android-21", "android-22", "android-23", "android-24", "android-25", "android-26", "android-27"];
private static MIN_REQUIRED_COMPILE_TARGET = 22;
private static REQUIRED_BUILD_TOOLS_RANGE_PREFIX = ">=23";
private static VERSION_REGEX = /((\d+\.){2}\d+)/;
private static MIN_JAVA_VERSION = "1.8.0";
private static MAX_JAVA_VERSION = "1.9.0";

private showWarningsAsErrors: boolean;
private toolsInfo: IAndroidToolsInfoData;
Expand All @@ -20,10 +17,8 @@ export class AndroidToolsInfo implements IAndroidToolsInfo {
return process.env["ANDROID_HOME"];
}

constructor(private $childProcess: IChildProcess,
private $errors: IErrors,
constructor(private $errors: IErrors,
private $fs: IFileSystem,
private $hostInfo: IHostInfo,
private $logger: ILogger,
private $options: IOptions,
protected $staticConfig: Config.IStaticConfig) { }
Expand All @@ -45,151 +40,69 @@ export class AndroidToolsInfo implements IAndroidToolsInfo {
return this.toolsInfo;
}

public validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): boolean {
public validateInfo(options?: IAndroidToolsInfoValidateInput): boolean {
let detectedErrors = false;
this.showWarningsAsErrors = options && options.showWarningsAsErrors;
const toolsInfoData = this.getToolsInfo();
const isAndroidHomeValid = this.validateAndroidHomeEnvVariable();
if (!toolsInfoData.compileSdkVersion) {
this.printMessage(`Cannot find a compatible Android SDK for compilation. To be able to build for Android, install Android SDK ${AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET} or later.`,
`Run \`\$ ${this.getPathToSdkManagementTool()}\` to manage your Android SDK versions.`);
detectedErrors = true;
}

if (!toolsInfoData.buildToolsVersion) {
const buildToolsRange = this.getBuildToolsRange();
const versionRangeMatches = buildToolsRange.match(/^.*?([\d\.]+)\s+.*?([\d\.]+)$/);
let message = `You can install any version in the following range: '${buildToolsRange}'.`;

// Improve message in case buildToolsRange is something like: ">=22.0.0 <=22.0.0" - same numbers on both sides
if (versionRangeMatches && versionRangeMatches[1] && versionRangeMatches[2] && versionRangeMatches[1] === versionRangeMatches[2]) {
message = `You have to install version ${versionRangeMatches[1]}.`;
}
detectedErrors = androidToolsInfo.validateInfo().map(warning => this.printMessage(warning.warning)).length > 0;

let invalidBuildToolsAdditionalMsg = `Run \`\$ ${this.getPathToSdkManagementTool()}\` from your command-line to install required \`Android Build Tools\`.`;
if (!isAndroidHomeValid) {
invalidBuildToolsAdditionalMsg += ' In case you already have them installed, make sure `ANDROID_HOME` environment variable is set correctly.';
}

this.printMessage("You need to have the Android SDK Build-tools installed on your system. " + message, invalidBuildToolsAdditionalMsg);
detectedErrors = true;
if (options && options.validateTargetSdk) {
detectedErrors = this.validateTargetSdk();
}

if (!toolsInfoData.supportRepositoryVersion) {
let invalidSupportLibAdditionalMsg = `Run \`\$ ${this.getPathToSdkManagementTool()}\` to manage the Android Support Repository.`;
if (!isAndroidHomeValid) {
invalidSupportLibAdditionalMsg += ' In case you already have it installed, make sure `ANDROID_HOME` environment variable is set correctly.';
}
this.printMessage(`You need to have Android SDK ${AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET} or later and the latest Android Support Repository installed on your system.`, invalidSupportLibAdditionalMsg);
detectedErrors = true;
}
return detectedErrors || !isAndroidHomeValid;
}

if (options && options.validateTargetSdk) {
const targetSdk = toolsInfoData.targetSdkVersion;
const newTarget = `${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-${targetSdk}`;
if (!_.includes(AndroidToolsInfo.SUPPORTED_TARGETS, newTarget)) {
const supportedVersions = AndroidToolsInfo.SUPPORTED_TARGETS.sort();
const minSupportedVersion = this.parseAndroidSdkString(_.first(supportedVersions));

if (targetSdk && (targetSdk < minSupportedVersion)) {
this.printMessage(`The selected Android target SDK ${newTarget} is not supported. You must target ${minSupportedVersion} or later.`);
detectedErrors = true;
} else if (!targetSdk || targetSdk > this.getMaxSupportedVersion()) {
this.$logger.warn(`Support for the selected Android target SDK ${newTarget} is not verified. Your Android app might not work as expected.`);
}
public validateTargetSdk(options?: IAndroidToolsInfoOptions): boolean {
this.showWarningsAsErrors = options && options.showWarningsAsErrors;

const toolsInfoData = this.getToolsInfo();
const targetSdk = toolsInfoData.targetSdkVersion;
const newTarget = `${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-${targetSdk}`;

if (!_.includes(AndroidToolsInfo.SUPPORTED_TARGETS, newTarget)) {
const supportedVersions = AndroidToolsInfo.SUPPORTED_TARGETS.sort();
const minSupportedVersion = this.parseAndroidSdkString(_.first(supportedVersions));

if (targetSdk && (targetSdk < minSupportedVersion)) {
this.printMessage(`The selected Android target SDK ${newTarget} is not supported. You must target ${minSupportedVersion} or later.`);
return true;
} else if (!targetSdk || targetSdk > this.getMaxSupportedVersion()) {
this.$logger.warn(`Support for the selected Android target SDK ${newTarget} is not verified. Your Android app might not work as expected.`);
}
}

return detectedErrors || !isAndroidHomeValid;
return false;
}

public validateJavacVersion(installedJavacVersion: string, options?: { showWarningsAsErrors: boolean }): boolean {
let hasProblemWithJavaVersion = false;
public validateJavacVersion(installedJavacVersion: string, options?: IAndroidToolsInfoOptions): boolean {
if (options) {
this.showWarningsAsErrors = options.showWarningsAsErrors;
}

const additionalMessage = "You will not be able to build your projects for Android." + EOL
+ "To be able to build for Android, verify that you have installed The Java Development Kit (JDK) and configured it according to system requirements as" + EOL +
" described in " + this.$staticConfig.SYS_REQUIREMENTS_LINK;

const matchingVersion = appendZeroesToVersion(installedJavacVersion || "", 3).match(AndroidToolsInfo.VERSION_REGEX);
const installedJavaCompilerVersion = matchingVersion && matchingVersion[1];
if (installedJavaCompilerVersion) {
if (semver.lt(installedJavaCompilerVersion, AndroidToolsInfo.MIN_JAVA_VERSION)) {
hasProblemWithJavaVersion = true;
this.printMessage(`Javac version ${installedJavacVersion} is not supported. You have to install at least ${AndroidToolsInfo.MIN_JAVA_VERSION}.`, additionalMessage);
} else if (semver.gte(installedJavaCompilerVersion, AndroidToolsInfo.MAX_JAVA_VERSION)) {
hasProblemWithJavaVersion = true;
this.printMessage(`Javac version ${installedJavacVersion} is not supported. You have to install version ${AndroidToolsInfo.MIN_JAVA_VERSION}.`, additionalMessage);
}
} else {
hasProblemWithJavaVersion = true;
this.printMessage("Error executing command 'javac'. Make sure you have installed The Java Development Kit (JDK) and set JAVA_HOME environment variable.", additionalMessage);
}

return hasProblemWithJavaVersion;
return androidToolsInfo.validateJavacVersion(installedJavacVersion).map(warning => this.printMessage(warning.warning)).length > 0;
}

public async getPathToAdbFromAndroidHome(): Promise<string> {
if (this.androidHome) {
const pathToAdb = path.join(this.androidHome, "platform-tools", "adb");
try {
await this.$childProcess.execFile(pathToAdb, ["help"]);
return pathToAdb;
} catch (err) {
// adb does not exist, so ANDROID_HOME is not set correctly
// try getting default adb path (included in CLI package)
this.$logger.trace(`Error while executing '${pathToAdb} help'. Error is: ${err.message}`);
}
try {
return androidToolsInfo.getPathToAdbFromAndroidHome();
} catch (err) {
// adb does not exist, so ANDROID_HOME is not set correctly
// try getting default adb path (included in CLI package)
this.$logger.trace(`Error while executing '${path.join(this.androidHome, "platform-tools", "adb")} help'. Error is: ${err.message}`);
}

return null;
}

@cache()
public validateAndroidHomeEnvVariable(options?: { showWarningsAsErrors: boolean }): boolean {
public validateAndroidHomeEnvVariable(options?: IAndroidToolsInfoOptions): boolean {
if (options) {
this.showWarningsAsErrors = options.showWarningsAsErrors;
}

const expectedDirectoriesInAndroidHome = ["build-tools", "tools", "platform-tools", "extras"];
let androidHomeValidationResult = true;

if (!this.androidHome || !this.$fs.exists(this.androidHome)) {
this.printMessage("The ANDROID_HOME environment variable is not set or it points to a non-existent directory. You will not be able to perform any build-related operations for Android.",
"To be able to perform Android build-related operations, set the `ANDROID_HOME` variable to point to the root of your Android SDK installation directory.");
androidHomeValidationResult = false;
} else if (!_.some(expectedDirectoriesInAndroidHome.map(dir => this.$fs.exists(path.join(this.androidHome, dir))))) {
this.printMessage("The ANDROID_HOME environment variable points to incorrect directory. You will not be able to perform any build-related operations for Android.",
"To be able to perform Android build-related operations, set the `ANDROID_HOME` variable to point to the root of your Android SDK installation directory, " +
"where you will find `tools` and `platform-tools` directories.");
androidHomeValidationResult = false;
}

return androidHomeValidationResult;
}

@cache()
private getPathToSdkManagementTool(): string {
const sdkManagerName = "sdkmanager";
let sdkManagementToolPath = sdkManagerName;

const isAndroidHomeValid = this.validateAndroidHomeEnvVariable();

if (isAndroidHomeValid) {
// In case ANDROID_HOME is correct, check if sdkmanager exists and if not it means the SDK has not been updated.
// In this case user shoud use `android` from the command-line instead of sdkmanager.
const pathToSdkManager = path.join(this.androidHome, "tools", "bin", sdkManagerName);
const pathToAndroidExecutable = path.join(this.androidHome, "tools", "android");
const pathToExecutable = this.$fs.exists(pathToSdkManager) ? pathToSdkManager : pathToAndroidExecutable;

this.$logger.trace(`Path to Android SDK Management tool is: ${pathToExecutable}`);

sdkManagementToolPath = pathToExecutable.replace(this.androidHome, this.$hostInfo.isWindows ? "%ANDROID_HOME%" : "$ANDROID_HOME");
}

return sdkManagementToolPath;
return androidToolsInfo.validateAndroidHomeEnvVariable().map(warning => this.printMessage(warning.warning)).length > 0;
}

private shouldGenerateTypings(): boolean {
Expand Down
7 changes: 6 additions & 1 deletion lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,15 @@ $injector.requireCommand("appstore|upload", "./commands/appstore-upload");
$injector.requireCommand("publish|ios", "./commands/appstore-upload");
$injector.require("itmsTransporterService", "./services/itmstransporter-service");

$injector.requireCommand("setup|*", "./commands/setup");
$injector.requireCommand(["setup|cloud", "cloud|setup"], "./commands/setup");

$injector.requirePublic("npm", "./node-package-manager");
$injector.require("npmInstallationManager", "./npm-installation-manager");
$injector.require("dynamicHelpProvider", "./dynamic-help-provider");
$injector.require("mobilePlatformsCapabilities", "./mobile-platforms-capabilities");
$injector.require("commandsServiceProvider", "./providers/commands-service-provider");
$injector.require("deviceAppDataProvider", "./providers/device-app-data-provider");
$injector.require("AppDataProvider", "./providers/device-app-data-provider");

$injector.require("deviceLogProvider", "./common/mobile/device-log-provider");
$injector.require("projectFilesProvider", "./providers/project-files-provider");
Expand Down Expand Up @@ -152,3 +155,5 @@ $injector.require("subscriptionService", "./services/subscription-service");
$injector.require("terminalSpinnerService", "./services/terminal-spinner-service");

$injector.require('playgroundService', './services/playground-service');
$injector.require("platformEnvironmentRequirements", "./services/platform-environment-requirements");
$injector.require("nativescriptCloudExtensionService", "./services/nativescript-cloud-extension-service");
22 changes: 11 additions & 11 deletions lib/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class BuildCommandBase {
protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
protected $platformService: IPlatformService,
private $bundleValidatorHelper: IBundleValidatorHelper) {
this.$projectData.initializeProjectData();
this.$projectData.initializeProjectData();
}

public async executeCore(args: string[]): Promise<void> {
Expand Down Expand Up @@ -44,12 +44,16 @@ export class BuildCommandBase {
}
}

protected validatePlatform(platform: string): void {
protected async validatePlatform(platform: string): Promise<void> {
if (!this.$platformService.isPlatformSupportedForOS(platform, this.$projectData)) {
this.$errors.fail(`Applications for platform ${platform} can not be built on this OS`);
}

this.$bundleValidatorHelper.validate();

const platformData = this.$platformsData.getPlatformData(platform, this.$projectData);
const platformProjectService = platformData.platformProjectService;
await platformProjectService.validate(this.$projectData);
}
}

Expand All @@ -63,15 +67,15 @@ export class BuildIosCommand extends BuildCommandBase implements ICommand {
$devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
$platformService: IPlatformService,
$bundleValidatorHelper: IBundleValidatorHelper) {
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper);
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper);
}

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

public canExecute(args: string[]): Promise<boolean> {
super.validatePlatform(this.$devicePlatformsConstants.iOS);
public async canExecute(args: string[]): Promise<boolean> {
await super.validatePlatform(this.$devicePlatformsConstants.iOS);
return args.length === 0 && this.$platformService.validateOptions(this.$options.provision, this.$options.teamId, this.$projectData, this.$platformsData.availablePlatforms.iOS);
}
}
Expand All @@ -88,23 +92,19 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand {
$devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
$platformService: IPlatformService,
$bundleValidatorHelper: IBundleValidatorHelper) {
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper);
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService, $bundleValidatorHelper);
}

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

public async canExecute(args: string[]): Promise<boolean> {
super.validatePlatform(this.$devicePlatformsConstants.Android);
await super.validatePlatform(this.$devicePlatformsConstants.Android);
if (this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) {
this.$errors.fail(ANDROID_RELEASE_BUILD_ERROR_MESSAGE);
}

const platformData = this.$platformsData.getPlatformData(this.$devicePlatformsConstants.Android, this.$projectData);
const platformProjectService = platformData.platformProjectService;
await platformProjectService.validate(this.$projectData);

return args.length === 0 && await this.$platformService.validateOptions(this.$options.provision, this.$options.teamId, this.$projectData, this.$platformsData.availablePlatforms.Android);
}
}
Expand Down
21 changes: 21 additions & 0 deletions lib/commands/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export class SetupCommand implements ICommand {
public allowedParameters: ICommandParameter[] = [];

constructor(private $doctorService: IDoctorService) { }

public execute(args: string[]): Promise<any> {
return this.$doctorService.runSetupScript();
}
}
$injector.registerCommand("setup|*", SetupCommand);

export class CloudSetupCommand implements ICommand {
public allowedParameters: ICommandParameter[] = [];

constructor(private $nativescriptCloudExtensionService: INativescriptCloudExtensionService) { }

public execute(args: string[]): Promise<any> {
return this.$nativescriptCloudExtensionService.install();
}
}
$injector.registerCommand(["setup|cloud", "cloud|setup"], CloudSetupCommand);
2 changes: 1 addition & 1 deletion lib/common
2 changes: 2 additions & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,5 @@ export const enum BuildStates {
Clean = "Clean",
Incremental = "Incremental"
}

export const NATIVESCRIPT_CLOUD_EXTENSION_NAME = "nativescript-cloud";
Loading