run method

Run a process and wait for it to complete.

Implementation

Future<ProcessOutput> run(ProcessConfig config) async {
  final sw = Stopwatch()..start();
  final process = await Process.start(
    config.command,
    config.args,
    workingDirectory: config.workDir,
    environment: config.env,
    runInShell: config.runInShell,
  );

  final pid = process.pid;
  _processes[pid] = process;
  _processInfo[pid] = ProcessInfo(
    pid: pid,
    command: config.fullCommand,
    startTime: DateTime.now(),
  );
  _eventController.add(ProcessStarted(pid, config.fullCommand));

  // Send stdin if provided
  if (config.stdin != null) {
    process.stdin.write(config.stdin);
    await process.stdin.close();
  }

  final stdoutBuf = StringBuffer();
  final stderrBuf = StringBuffer();

  final stdoutSub = process.stdout
      .transform(const SystemEncoding().decoder)
      .listen((data) {
        if (config.captureStdout) stdoutBuf.write(data);
        _eventController.add(StdoutData(pid, data));
      });

  final stderrSub = process.stderr
      .transform(const SystemEncoding().decoder)
      .listen((data) {
        if (config.captureStderr) stderrBuf.write(data);
        _eventController.add(StderrData(pid, data));
      });

  int exitCode;
  var killed = false;

  if (config.timeout != null) {
    exitCode = await process.exitCode.timeout(
      config.timeout!,
      onTimeout: () {
        process.kill(ProcessSignal.sigterm);
        killed = true;
        return -1;
      },
    );
  } else {
    exitCode = await process.exitCode;
  }

  await stdoutSub.cancel();
  await stderrSub.cancel();
  sw.stop();

  _processInfo[pid]?.status = killed
      ? ProcessStatus.killed
      : ProcessStatus.exited;
  _processes.remove(pid);
  _eventController.add(ProcessExited(pid, exitCode));

  return ProcessOutput(
    exitCode: exitCode,
    stdout: stdoutBuf.toString(),
    stderr: stderrBuf.toString(),
    duration: sw.elapsed,
    pid: pid,
    killed: killed,
  );
}