Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import adb from '../runAndroid/adb';
import getAdbPath from '../runAndroid/getAdbPath';
import {startServerInNewWindow} from './startServerInNewWindow';
import {getTaskNames} from '../runAndroid/getTaskNames';
import {promptForTaskSelection} from '../runAndroid/listAndroidTasks';

export interface BuildFlags {
mode?: string;
Expand All @@ -21,6 +22,7 @@ export interface BuildFlags {
terminal: string;
tasks?: Array<string>;
extraParams?: Array<string>;
interactive?: boolean;
}

export async function runPackager(args: BuildFlags, config: Config) {
Expand Down Expand Up @@ -64,10 +66,22 @@ async function buildAndroid(
);
}

let {tasks} = args;

if (args.interactive) {
const selectedTask = await promptForTaskSelection(
'build',
androidProject.sourceDir,
);
if (selectedTask) {
tasks = [selectedTask];
}
}

let gradleArgs = getTaskNames(
androidProject.appName,
args.mode || args.variant,
args.tasks,
tasks,
'assemble',
);

Expand Down Expand Up @@ -156,6 +170,11 @@ export const options = [
description: 'Custom properties passed to gradle build command',
parse: (val: string) => val.split(' '),
},
{
name: '--interactive',
description:
'Explicitly select build type and flavour to use before running a build',
},
];

export default {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import chalk from 'chalk';
import execa from 'execa';
import prompts from 'prompts';
import {
parseTasksFromGradleFile,
promptForTaskSelection,
} from '../listAndroidTasks';

const gradleTaskOutput = `
> Task :tasks

------------------------------------------------------------
Tasks runnable from root project 'com.bananas'
------------------------------------------------------------

Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for the base and test modules
sourceSets - Prints out all the source sets defined in this project.

Build tasks
-----------
assemble - Assemble main outputs for all the variants.
assembleAndroidTest - Assembles all the Test applications.
assembleDebug - Assembles main outputs for all Debug variants.
assembleProduction - Assembles main outputs for all Production variants.
assembleRelease - Assembles main outputs for all Release variants.
assembleUat - Assembles main outputs for all Uat variants.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
bundle - Assemble bundles for all the variants.
bundleDebug - Assembles bundles for all Debug variants.
bundleProduction - Assembles bundles for all Production variants.
bundleRelease - Assembles bundles for all Release variants.
bundleUat - Assembles bundles for all Uat variants.
clean - Deletes the build directory.
compileProductionDebugAndroidTestSources
compileProductionDebugSources
compileProductionDebugUnitTestSources
compileProductionReleaseSources
compileProductionReleaseUnitTestSources
compileUatDebugAndroidTestSources
compileUatDebugSources
compileUatDebugUnitTestSources
compileUatReleaseSources
compileUatReleaseUnitTestSources

Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'com.bananas'.
dependencies - Displays all dependencies declared in root project 'com.bananas'.
dependencyInsight - Displays the insight into a specific dependency in root project 'com.bananas'.
help - Displays a help message.
javaToolchains - Displays the detected java toolchains.
outgoingVariants - Displays the outgoing variants of root project 'com.bananas'.
projects - Displays the sub-projects of root project 'com.bananas'.
properties - Displays the properties of root project 'com.bananas'.
resolvableConfigurations - Displays the configurations that can be resolved in root project 'com.bananas'.
tasks - Displays the tasks runnable from root project 'com.bananas' (some of the displayed tasks may belong to subprojects).

Install tasks
-------------
installProductionDebug - Installs the DebugProductionDebug build.
installProductionDebugAndroidTest - Installs the android (on device) tests for the ProductionDebug build.
installProductionRelease - Installs the ReleaseProductionRelease build.
installUatDebug - Installs the DebugUatDebug build.
installUatDebugAndroidTest - Installs the android (on device) tests for the UatDebug build.
installUatRelease - Installs the ReleaseUatRelease build.
uninstallAll - Uninstall all applications.

`;

const tasksList = [
{
description: 'Installs the DebugProductionDebug build.',
task: 'installProductionDebug',
},
{
description: 'Installs the ReleaseProductionRelease build.',
task: 'installProductionRelease',
},
{
description: 'Installs the DebugUatDebug build.',
task: 'installUatDebug',
},
{
description: 'Installs the ReleaseUatRelease build.',
task: 'installUatRelease',
},
];

jest.mock('execa', () => {
return {sync: jest.fn()};
});

jest.mock('prompts', () => jest.fn());

describe('promptForTaskSelection', () => {
it('should prompt with correct tasks', () => {
(execa.sync as jest.Mock).mockReturnValueOnce({stdout: gradleTaskOutput});
prompts.mockReturnValue({task: []});

promptForTaskSelection('install', 'sourceDir');

expect(prompts).toHaveBeenCalledWith({
choices: tasksList.map((t) => ({
title: `${chalk.bold(t.task)} - ${t.description}`,
value: t.task,
})),
message: 'Select install task you want to perform',
min: 1,
name: 'task',
type: 'select',
});
});
});

describe('parseTasksFromGradleFile', () => {
it('should correctly parse gradle tasks output for "install" taskType', () => {
const tasks = parseTasksFromGradleFile('install', gradleTaskOutput);

expect(tasks).toEqual(tasksList);
});
it('should correctly parse gradle tasks output for "build" taskType', () => {
const buildTasks = parseTasksFromGradleFile('build', gradleTaskOutput);

expect(buildTasks).toContainEqual({
description: 'Assemble main outputs for all the variants.',
task: 'assemble',
});

expect(buildTasks).not.toContainEqual({
description: 'Assembles all the Test applications.',
task: 'assembleAndroidTest',
});
});
});
18 changes: 16 additions & 2 deletions packages/cli-platform-android/src/commands/runAndroid/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import tryLaunchEmulator from './tryLaunchEmulator';
import chalk from 'chalk';
import path from 'path';
import {build, runPackager, BuildFlags, options} from '../buildAndroid';
import {promptForTaskSelection} from './listAndroidTasks';

export interface Flags extends BuildFlags {
appId: string;
Expand Down Expand Up @@ -84,6 +85,19 @@ async function buildAndRun(args: Flags, androidProject: AndroidProject) {
const cmd = process.platform.startsWith('win') ? 'gradlew.bat' : './gradlew';

const adbPath = getAdbPath();

let {tasks} = args;

if (args.interactive) {
const selectedTask = await promptForTaskSelection(
'install',
androidProject.sourceDir,
);
if (selectedTask) {
tasks = [selectedTask];
}
}

if (args.listDevices) {
if (args.deviceId) {
logger.warn(
Expand Down Expand Up @@ -123,9 +137,9 @@ async function buildAndRun(args: Flags, androidProject: AndroidProject) {
);
}
if (args.deviceId) {
return runOnSpecificDevice(args, adbPath, androidProject);
return runOnSpecificDevice({...args, tasks}, adbPath, androidProject);
} else {
return runOnAllDevices(args, cmd, adbPath, androidProject);
return runOnAllDevices({...args, tasks}, cmd, adbPath, androidProject);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {CLIError} from '@react-native-community/cli-tools';
import chalk from 'chalk';
import execa from 'execa';
import prompts from 'prompts';

type GradleTask = {
task: string;
description: string;
};

export const parseTasksFromGradleFile = (
taskType: 'install' | 'build',
text: string,
): Array<GradleTask> => {
const instalTasks: Array<GradleTask> = [];
const taskRegex = new RegExp(
taskType === 'build' ? '^assemble|^bundle' : '^install',
);
text.split('\n').forEach((line) => {
if (taskRegex.test(line) && /(?!.*?Test)^.*$/.test(line)) {
const metadata = line.split(' - ');
instalTasks.push({
task: metadata[0],
description: metadata[1],
});
}
});
return instalTasks;
};

export const promptForTaskSelection = async (
taskType: 'install' | 'build',
sourceDir: string,
) => {
const cmd = process.platform.startsWith('win') ? 'gradlew.bat' : './gradlew';

const out = execa.sync(cmd, ['tasks'], {
cwd: sourceDir,
}).stdout;
const installTasks = parseTasksFromGradleFile(taskType, out);
if (!installTasks.length) {
throw new CLIError(`No actionable ${taskType} tasks were found...`);
}
const {task} = await prompts({
type: 'select',
name: 'task',
message: `Select ${taskType} task you want to perform`,
choices: installTasks.map((t: GradleTask) => ({
title: `${chalk.bold(t.task)} - ${t.description}`,
value: t.task,
})),
min: 1,
});
return task;
};