private async function genRunOnce()

in src/shipit/ShipItShellCommand.php [112:228]


  private async function genRunOnce(): Awaitable<ShipItShellCommandResult> {
    $fds = dict[
      0 => vec['pipe', 'r'],
      1 => vec['pipe', 'w'],
      2 => vec['pipe', 'w'],
    ];
    $stdin = $this->stdin;
    if ($stdin === null) {
      unset($fds[0]);
    }

    $env_vars = Dict\merge(ShipItEnv::getAll(), $this->environmentVariables);

    $command = $this->getCommandAsString();
    if ($this->showShellExecs) {
      (new ShipItVerboseLogger(true))->out(
        "Shell command: %s; cwd: %s",
        $command,
        $this->path ?? ".",
      );
    }
    $pipes = vec[];
    /* HH_IGNORE_ERROR[2049] __PHPStdLib */
    /* HH_IGNORE_ERROR[4107] __PHPStdLib */
    $fp = \proc_open($command, $fds, inout $pipes, $this->path, $env_vars);
    if (!$fp || !\HH\is_any_array($pipes)) {
      throw new \Exception("Failed executing $command");
    }
    if ($stdin !== null) {
      while (Str\length($stdin)) {
        $written = PHP\fwrite($pipes[0], $stdin);
        if ($written === 0) {
          $status = PHP\proc_get_status($fp);
          if ($status['running']) {
            continue;
          }
          $exitcode = $status['exitcode'];
          invariant(
            $exitcode is int && $exitcode > 0,
            'Expected non-zero exit from process, got %s',
            \var_export($exitcode, true),
          );
          break;
        }
        $stdin = Str\slice($stdin, $written);
      }
      PHP\fclose($pipes[0]);
    }

    $stdout_stream = $pipes[1];
    $stderr_stream = $pipes[2];
    PHP\stream_set_blocking($stdout_stream, false);
    PHP\stream_set_blocking($stderr_stream, false);
    $stdout = '';
    $stderr = '';
    while (true) {
      $ready_streams = vec[$stdout_stream, $stderr_stream];
      $null_byref = null;
      do {
        $result = PHP\stream_select(
          inout $ready_streams,
          /* write streams = */ inout $null_byref,
          /* exception streams = */ inout $null_byref,
          /* timeout = */ null,
        );
      } while (
        $result === false &&
        /* This **MUST NOT** be a PHP\ wrapper because `errno` is extremely volatile */
        /* HH_IGNORE_ERROR[2049] __PHPStdLib */
        /* HH_IGNORE_ERROR[4107] __PHPStdLib */
        \posix_get_last_error() === 4 // \HH\Lib\OS\__Private\Errno::EINTR
      );
      if ($result === false) {
        break;
      }
      $all_empty = true;
      foreach (($ready_streams as Container<_>) as $stream) {
        $out = PHP\fread($stream as resource, 1024) as string;
        if (Str\length($out) === 0) {
          continue;
        }
        $all_empty = false;

        if ($stream === $stdout_stream) {
          $stdout .= $out;
          $this->maybeOut($out);
          continue;
        }
        if ($stream === $stderr_stream) {
          $stderr .= $out;
          $this->maybeErr($out);
          continue;
        }

        invariant_violation('Unhandled stream!');
      }

      if ($all_empty) {
        break;
      }
    }
    $exitcode = PHP\proc_close($fp);

    $result = new ShipItShellCommandResult($exitcode, $stdout, $stderr);

    if ($exitcode !== 0) {
      $handler = $this->failureHandler;
      if ($handler) {
        await $handler($result);
      }
      if ($this->throwForNonZeroExit) {
        throw new ShipItShellCommandException($command, $result);
      }
    }

    return $result;
  }