Skip to content
13 changes: 10 additions & 3 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,6 @@ Builds your app and starts it on a connected Android emulator or device.

#### Options

#### `--install-debug`

#### `--root [string]`

Override the root directory for the Android build (which contains the android directory)'.
Expand All @@ -272,6 +270,8 @@ Override the root directory for the Android build (which contains the android di

> default: 'debug'

Specify your app's build variant.

#### `--appFolder [string]`

> default: 'app'
Expand Down Expand Up @@ -302,14 +302,21 @@ Do not launch packager while building.

#### `--port [number]`

> default: process.env.RCT_METRO_PORT || 8081,
> default: process.env.RCT_METRO_PORT || 8081

#### `--terminal [string]`

> default: process.env.REACT_TERMINAL || process.env.TERM_PROGRAM

Launches the Metro Bundler in a new window using the specified terminal path.

#### `--tasks [list]`

> default: 'installDebug'

Run custom gradle tasks. If this argument is provided, then `--variant` option is ignored.
Example: `yarn react-native run-android --tasks clean,installDebug`.

### `run-ios`

Usage:
Expand Down
7 changes: 2 additions & 5 deletions packages/cli/src/tools/copyFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function copyFile(srcPath: string, destPath: string) {
*/
function copyBinaryFile(srcPath, destPath, cb) {
let cbCalled = false;
// const {mode} = fs.statSync(srcPath);
const {mode} = fs.statSync(srcPath);
const readStream = fs.createReadStream(srcPath);
const writeStream = fs.createWriteStream(destPath);
readStream.on('error', err => {
Expand All @@ -64,10 +64,7 @@ function copyBinaryFile(srcPath, destPath, cb) {
});
readStream.on('close', () => {
done();
// We may revisit setting mode to original later, however this fn is used
// before "replace placeholder in template" step, which expects files to be
// writable.
// fs.chmodSync(destPath, mode);
fs.chmodSync(destPath, mode);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

unrelated bugfix: while testing I noticed that gradlew is not executable in newly init projects, so brought chmod back (we're not copying Pods anymore anyway, so we should be fine)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sounds good, thanks for explaining this particular change!

});
readStream.pipe(writeStream);
function done(err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,68 +21,62 @@ describe('--appFolder', () => {
jest.clearAllMocks();
});

it('uses installDebug as default if no arguments', () => {
runOnAllDevices({});
it('uses task "install[Variant]" as default task', () => {
runOnAllDevices({
variant: 'debug',
});

expect(execFileSync.mock.calls[0][1]).toContain('installDebug');
});

it('uses appFolder and default variant', () => {
runOnAllDevices({
appFolder: 'someApp',
variant: 'debug',
});

expect(execFileSync.mock.calls[0][1]).toContain('someApp:installDebug');
});

it('uses appFolder and variant', () => {
runOnAllDevices({
appFolder: 'app',
variant: 'debug',
});

expect(execFileSync.mock.calls[0][1]).toContain('app:installDebug');

runOnAllDevices({
appFolder: 'anotherApp',
variant: 'debug',
});

expect(execFileSync.mock.calls[1][1]).toContain('anotherApp:installDebug');

it('uses appFolder and custom variant', () => {
runOnAllDevices({
appFolder: 'anotherApp',
variant: 'staging',
});

expect(execFileSync.mock.calls[2][1]).toContain(
expect(execFileSync.mock.calls[0][1]).toContain(
'anotherApp:installStaging',
);
});

it('uses appFolder and flavor', () => {
it('uses only task argument', () => {
runOnAllDevices({
appFolder: 'app',
flavor: 'someFlavor',
tasks: ['someTask'],
variant: 'debug',
});

expect(execFileSync.mock.calls[0][1]).toContain('app:installSomeFlavor');
expect(execFileSync.mock.calls[0][1]).toContain('someTask');
});

it('uses only installDebug argument', () => {
it('uses appFolder and custom task argument', () => {
runOnAllDevices({
installDebug: 'someCommand',
appFolder: 'anotherApp',
tasks: ['someTask'],
variant: 'debug',
});

expect(execFileSync.mock.calls[0][1]).toContain('someCommand');
expect(execFileSync.mock.calls[0][1]).toContain('anotherApp:someTask');
});

it('uses appFolder and custom installDebug argument', () => {
it('uses multiple tasks', () => {
runOnAllDevices({
appFolder: 'anotherApp',
installDebug: 'someCommand',
appFolder: 'app',
tasks: ['clean', 'someTask'],
});

expect(execFileSync.mock.calls[0][1]).toContain('anotherApp:someCommand');
expect(execFileSync.mock.calls[0][1]).toContain(
'app:clean',
'app:someTask',
);
});
});
1 change: 0 additions & 1 deletion packages/platform-android/src/commands/runAndroid/adb.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ function getAvailableCPUs(adbPath: string, device: string): Array<string> {
}

export default {
parseDevicesResult,
getDevices,
getAvailableCPUs,
};
90 changes: 45 additions & 45 deletions packages/platform-android/src/commands/runAndroid/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,24 @@ function checkAndroid(root) {
return fs.existsSync(path.join(root, 'android/gradlew'));
}

export type FlagsT = {|
tasks?: Array<string>,
root: string,
variant: string,
appFolder: string,
appId: string,
appIdSuffix: string,
mainActivity: string,
deviceId?: string,
packager: boolean,
port: number,
terminal: string,
|};

/**
* Starts the app on a connected Android emulator or device.
*/
function runAndroid(argv: Array<string>, ctx: ConfigT, args: Object) {
function runAndroid(argv: Array<string>, config: ConfigT, args: FlagsT) {
if (!checkAndroid(args.root)) {
logger.error(
'Android project not found. Are you sure this is a React Native project?',
Expand All @@ -53,7 +67,7 @@ function runAndroid(argv: Array<string>, ctx: ConfigT, args: Object) {
} else {
// result == 'not_running'
logger.info('Starting JS server...');
startServerInNewWindow(args.port, args.terminal, ctx.reactNativePath);
startServerInNewWindow(args.port, args.terminal, config.reactNativePath);
}
return buildAndRun(args);
});
Expand Down Expand Up @@ -90,16 +104,13 @@ function buildAndRun(args) {

const adbPath = getAdbPath();
if (args.deviceId) {
if (typeof args.deviceId === 'string') {
return runOnSpecificDevice(
args,
cmd,
packageNameWithSuffix,
packageName,
adbPath,
);
}
logger.error('Argument missing for parameter --deviceId');
return runOnSpecificDevice(
args,
cmd,
packageNameWithSuffix,
packageName,
adbPath,
);
} else {
return runOnAllDevices(
args,
Expand All @@ -119,21 +130,20 @@ function runOnSpecificDevice(
adbPath,
) {
const devices = adb.getDevices(adbPath);
if (devices && devices.length > 0) {
if (devices.indexOf(args.deviceId) !== -1) {
const {deviceId} = args;
if (devices.length > 0 && deviceId) {
if (devices.indexOf(deviceId) !== -1) {
buildApk(gradlew);
installAndLaunchOnDevice(
args,
args.deviceId,
deviceId,
packageNameWithSuffix,
packageName,
adbPath,
);
} else {
logger.error(
`Could not find device with the id: "${
args.deviceId
}". Choose one of the following:`,
`Could not find device with the id: "${deviceId}". Please choose one of the following:`,
...devices,
);
}
Expand All @@ -144,17 +154,13 @@ function runOnSpecificDevice(

function buildApk(gradlew) {
try {
logger.info('Building the app...');

// using '-x lint' in order to ignore linting errors while building the apk
execFileSync(gradlew, ['build', '-x', 'lint'], {
stdio: [process.stdin, process.stdout, process.stderr],
});
const gradleArgs = ['build', '-x', 'lint'];
logger.info('Building the app...');
logger.debug(`Running command "${gradlew} ${gradleArgs.join(' ')}"`);
execFileSync(gradlew, gradleArgs, {stdio: 'inherit'});
} catch (error) {
throw new CLIError(
'Could not build the app, read the error above for details',
error,
);
throw new CLIError('Failed to build the app.', error);
}
}

Expand All @@ -174,18 +180,13 @@ function tryInstallAppOnDevice(args, adbPath, device) {

const pathToApk = `${buildDirectory}/${apkFile}`;
const adbArgs = ['-s', device, 'install', '-r', '-d', pathToApk];
logger.info(
`Installing the app on the device (cd android && adb -s ${device} install -r -d ${pathToApk}`,
);
execFileSync(adbPath, adbArgs, {
stdio: [process.stdin, process.stdout, process.stderr],
});
} catch (e) {
logger.error(
`${
e.message
}\nCould not install the app on the device, read the error above for details.`,
logger.info(`Installing the app on the device "${device}"...`);
logger.debug(
`Running command "cd android && adb -s ${device} install -r -d ${pathToApk}"`,
);
execFileSync(adbPath, adbArgs, {stdio: 'inherit'});
} catch (error) {
throw new CLIError('Failed to install the app on the device.', error);
}
}

Expand Down Expand Up @@ -305,21 +306,15 @@ export default {
'builds your app and starts it on a connected Android emulator or device',
func: runAndroid,
options: [
{
name: '--install-debug',
},
{
name: '--root [string]',
description:
'Override the root directory for the android build (which contains the android directory)',
default: '',
},
{
name: '--flavor [string]',
description: '--flavor has been deprecated. Use --variant instead',
},
{
name: '--variant [string]',
description: "Specify your app's build variant",
default: 'debug',
},
{
Expand Down Expand Up @@ -364,5 +359,10 @@ export default {
'Launches the Metro Bundler in a new window using the specified terminal path.',
default: getDefaultUserTerminal,
},
{
name: '--tasks [list]',
description: 'Run custom Gradle tasks. By default it\'s "installDebug"',
parse: (val: string) => val.split(','),
},
],
};
Loading