Add async flavors for runCommand and runBuck2 utilities (#48371)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48371

Changelog: [internal]

Adds a new favor for `runCommand` and `runBuck2` that works asynchronously and support parsing their output in real time.

Reviewed By: javache

Differential Revision: D67600614

fbshipit-source-id: 99dd2ce9bff11036829f214bf19208b10c9c1b25
This commit is contained in:
Rubén Norte
2025-01-03 04:57:43 -08:00
committed by Facebook GitHub Bot
parent 8f096ab4f8
commit f1ba4ef131
2 changed files with 71 additions and 9 deletions
+2 -2
View File
@@ -10,7 +10,7 @@
*/
import type {TestSuiteResult} from '../runtime/setup';
import type {ConsoleLogMessage} from './utils';
import type {ConsoleLogMessage, SyncCommandResult} from './utils';
import entrypointTemplate from './entrypoint-template';
import getFantomTestConfig from './getFantomTestConfig';
@@ -43,7 +43,7 @@ const BUILD_OUTPUT_PATH = fs.mkdtempSync(
const PRINT_FANTOM_OUTPUT: false = false;
function parseRNTesterCommandResult(result: ReturnType<typeof runBuck2Sync>): {
function parseRNTesterCommandResult(result: SyncCommandResult): {
logs: $ReadOnlyArray<ConsoleLogMessage>,
testResult: TestSuiteResult,
} {
+69 -7
View File
@@ -9,7 +9,7 @@
* @oncall react_native
*/
import {spawnSync} from 'child_process';
import {spawn, spawnSync} from 'child_process';
import crypto from 'crypto';
import fs from 'fs';
import os from 'os';
@@ -44,12 +44,61 @@ export function getBuckModesForPlatform(
return ['@//xplat/mode/react-force-cxx-platform', osPlatform];
}
type SyncCommandResult = {
...ReturnType<typeof spawnSync>,
export type AsyncCommandResult = {
originalCommand: string,
...
childProcess: ReturnType<typeof spawn>,
done: Promise<AsyncCommandResult>,
pid: number,
status: ?number,
signal: ?string,
error: ?Error,
};
export type SyncCommandResult = {
originalCommand: string,
pid: number,
status: number,
signal: ?string,
error: ?Error,
stdout: string,
stderr: string,
};
export function runCommand(
command: string,
args: Array<string>,
): AsyncCommandResult {
const childProcess = spawn(command, args, {
encoding: 'utf8',
env: {
...process.env,
PATH: `/usr/local/bin:${process.env.PATH ?? ''}`,
},
});
const result: AsyncCommandResult = {
childProcess,
done: new Promise(resolve => {
childProcess.on('close', (code: number, signal: string) => {
result.status = code;
result.signal = signal;
resolve(result);
});
}),
originalCommand: `${command} ${args.join(' ')}`,
pid: childProcess.pid,
status: null,
signal: null,
error: null,
};
childProcess.on('error', error => {
result.error = error;
});
return result;
}
export function runCommandSync(
command: string,
args: Array<string>,
@@ -63,8 +112,13 @@ export function runCommandSync(
});
return {
...result,
originalCommand: `${command} ${args.join(' ')}`,
pid: result.pid,
status: result.status,
signal: result.signal,
error: result.error,
stdout: result.stdout.toString(),
stderr: result.stderr.toString(),
};
}
@@ -95,7 +149,15 @@ export function getDebugInfoFromCommandResult(
return logLines.join('\n');
}
export function runBuck2(args: Array<string>): AsyncCommandResult {
return runCommand('buck2', processArgsForBuck(args));
}
export function runBuck2Sync(args: Array<string>): SyncCommandResult {
return runCommandSync('buck2', processArgsForBuck(args));
}
function processArgsForBuck(args: Array<string>): Array<string> {
// If these tests are already running from withing a buck2 process, e.g. when
// they are scheduled by a `buck2 test` wrapper, calling `buck2` again would
// cause a daemon-level deadlock.
@@ -103,10 +165,10 @@ export function runBuck2Sync(args: Array<string>): SyncCommandResult {
// dir across tests (even running in different jest processes) to properly
// employ caching.
if (process.env.BUCK2_WRAPPER != null) {
args.unshift('--isolation-dir', BUCK_ISOLATION_DIR);
return ['--isolation-dir', BUCK_ISOLATION_DIR].concat(args);
}
return runCommandSync('buck2', args);
return args;
}
export function getShortHash(contents: string): string {