diff --git a/src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php index 4dab5f2972..915ae45970 100644 --- a/src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php @@ -23,4 +23,12 @@ final class DiffusionSSHGitUploadPackWorkflow return head($args->getArg('dir')); } + protected function executeRepositoryOperations( + PhabricatorRepository $repository) { + + $future = new ExecFuture('git-upload-pack %s', $repository->getLocalPath()); + + return $this->passthruIO($future); + } + } diff --git a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php index abee152496..5c2c182c9b 100644 --- a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php @@ -10,6 +10,9 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { abstract protected function isReadOnly(); abstract protected function getRequestPath(); + abstract protected function executeRepositoryOperations( + PhabricatorRepository $repository); + protected function writeError($message) { $this->getErrorChannel()->write($message); return $this; @@ -20,15 +23,11 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { try { $repository = $this->loadRepository(); - - throw new Exception("TODO: Implement serve over SSH."); - + return $this->executeRepositoryOperations($repository); } catch (Exception $ex) { $this->writeError(get_class($ex).': '.$ex->getMessage()); return 1; } - - return 0; } private function loadRepository() { diff --git a/src/infrastructure/ssh/PhabricatorSSHWorkflow.php b/src/infrastructure/ssh/PhabricatorSSHWorkflow.php index 9f28d50c0a..60a9f03fcd 100644 --- a/src/infrastructure/ssh/PhabricatorSSHWorkflow.php +++ b/src/infrastructure/ssh/PhabricatorSSHWorkflow.php @@ -37,6 +37,50 @@ abstract class PhabricatorSSHWorkflow extends PhutilArgumentWorkflow { return $this->iochannel; } + public function passthruIO(ExecFuture $future) { + $exec_channel = new PhutilExecChannel($future); + $exec_channel->setStderrHandler(array($this, 'writeErrorIOCallback')); + + $io_channel = $this->getIOChannel(); + $error_channel = $this->getErrorChannel(); + + $channels = array($exec_channel, $io_channel, $error_channel); + + while (true) { + PhutilChannel::waitForAny($channels); + + $io_channel->update(); + $exec_channel->update(); + $error_channel->update(); + + $done = !$exec_channel->isOpen(); + + $data = $io_channel->read(); + if (strlen($data)) { + $exec_channel->write($data); + } + + $data = $exec_channel->read(); + if (strlen($data)) { + $io_channel->write($data); + } + + // If we have nothing left on stdin, close stdin on the subprocess. + if (!$io_channel->isOpenForReading()) { + // TODO: This should probably be part of PhutilExecChannel? + $future->write(''); + } + + if ($done) { + break; + } + } + + list($err) = $future->resolve(); + + return $err; + } + public function readAllInput() { $channel = $this->getIOChannel(); while ($channel->update()) { @@ -53,4 +97,13 @@ abstract class PhabricatorSSHWorkflow extends PhutilArgumentWorkflow { return $this; } + public function writeErrorIO($data) { + $this->getErrorChannel()->write($data); + return $this; + } + + public function writeErrorIOCallback(PhutilChannel $channel, $data) { + $this->writeErrorIO($data); + } + }