mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-22 05:20:56 +01:00
Make serving repositories work with alternate URIs
Summary: Ref T4245. Consolidates the URI parsing/rewriting logic so that repositories can be served from either `/diffusion/XYZ/` or `/diffusion/123/`, over both HTTP and SSH. Test Plan: - Pulled a Git repository by ID and callsign over HTTP and SSH. - Pulled a Mercurial repository by ID and callsign over HTTP and SSH. - Pulled a Subversion repository by ID and callsign over SSH (no HTTP support for SVN). Reviewers: chad Reviewed By: chad Maniphest Tasks: T4245 Differential Revision: https://secure.phabricator.com/D15302
This commit is contained in:
parent
c2b8dd28d8
commit
74a79aa634
4 changed files with 74 additions and 22 deletions
|
@ -262,17 +262,23 @@ final class DiffusionServeController extends DiffusionController {
|
|||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||
$result = new PhabricatorVCSResponse(
|
||||
500,
|
||||
pht('This is not a Git repository.'));
|
||||
pht(
|
||||
'This repository ("%s") is not a Git repository.',
|
||||
$repository->getDisplayName()));
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
||||
$result = new PhabricatorVCSResponse(
|
||||
500,
|
||||
pht('This is not a Mercurial repository.'));
|
||||
pht(
|
||||
'This repository ("%s") is not a Mercurial repository.',
|
||||
$repository->getDisplayName()));
|
||||
break;
|
||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||
$result = new PhabricatorVCSResponse(
|
||||
500,
|
||||
pht('This is not a Subversion repository.'));
|
||||
pht(
|
||||
'This repository ("%s") is not a Subversion repository.',
|
||||
$repository->getDisplayName()));
|
||||
break;
|
||||
default:
|
||||
$result = new PhabricatorVCSResponse(
|
||||
|
@ -480,7 +486,9 @@ final class DiffusionServeController extends DiffusionController {
|
|||
private function getRequestDirectoryPath(PhabricatorRepository $repository) {
|
||||
$request = $this->getRequest();
|
||||
$request_path = $request->getRequestURI()->getPath();
|
||||
$base_path = preg_replace('@^/diffusion/[A-Z]+@', '', $request_path);
|
||||
|
||||
$info = PhabricatorRepository::parseRepositoryServicePath($request_path);
|
||||
$base_path = $info['path'];
|
||||
|
||||
// For Git repositories, strip an optional directory component if it
|
||||
// isn't the name of a known Git resource. This allows users to clone
|
||||
|
|
|
@ -6,6 +6,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
|||
private $repository;
|
||||
private $hasWriteAccess;
|
||||
private $proxyURI;
|
||||
private $baseRequestPath;
|
||||
|
||||
public function getRepository() {
|
||||
if (!$this->repository) {
|
||||
|
@ -45,6 +46,10 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
|||
abstract protected function identifyRepository();
|
||||
abstract protected function executeRepositoryOperations();
|
||||
|
||||
protected function getBaseRequestPath() {
|
||||
return $this->baseRequestPath;
|
||||
}
|
||||
|
||||
protected function writeError($message) {
|
||||
$this->getErrorChannel()->write($message);
|
||||
return $this;
|
||||
|
@ -149,25 +154,29 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
|||
protected function loadRepositoryWithPath($path) {
|
||||
$viewer = $this->getUser();
|
||||
|
||||
$regex = '@^/?diffusion/(?P<callsign>[A-Z]+)(?:/|\z)@';
|
||||
$matches = null;
|
||||
if (!preg_match($regex, $path, $matches)) {
|
||||
$info = PhabricatorRepository::parseRepositoryServicePath($path);
|
||||
if ($info === null) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unrecognized repository path "%s". Expected a path like "%s".',
|
||||
'Unrecognized repository path "%s". Expected a path like "%s" '.
|
||||
'or "%s".',
|
||||
$path,
|
||||
'/diffusion/X/'));
|
||||
'/diffusion/X/',
|
||||
'/diffusion/123/'));
|
||||
}
|
||||
|
||||
$callsign = $matches[1];
|
||||
$identifier = $info['identifier'];
|
||||
$base = $info['base'];
|
||||
|
||||
$this->baseRequestPath = $base;
|
||||
|
||||
$repository = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->withCallsigns(array($callsign))
|
||||
->withIdentifiers(array($identifier))
|
||||
->executeOne();
|
||||
|
||||
if (!$repository) {
|
||||
throw new Exception(
|
||||
pht('No repository "%s" exists!', $callsign));
|
||||
pht('No repository "%s" exists!', $identifier));
|
||||
}
|
||||
|
||||
switch ($repository->getServeOverSSH()) {
|
||||
|
@ -179,7 +188,9 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
|||
case PhabricatorRepository::SERVE_OFF:
|
||||
default:
|
||||
throw new Exception(
|
||||
pht('This repository is not available over SSH.'));
|
||||
pht(
|
||||
'This repository ("%s") is not available over SSH.',
|
||||
$repository->getDisplayName()));
|
||||
}
|
||||
|
||||
return $repository;
|
||||
|
|
|
@ -377,14 +377,12 @@ final class DiffusionSubversionServeSSHWorkflow
|
|||
$repository = $this->getRepository();
|
||||
|
||||
$path = $this->getPathFromSubversionURI($uri_string);
|
||||
$path = preg_replace(
|
||||
'(^/diffusion/[A-Z]+)',
|
||||
rtrim($repository->getLocalPath(), '/'),
|
||||
$path);
|
||||
$external_base = $this->getBaseRequestPath();
|
||||
|
||||
if (preg_match('(^/diffusion/[A-Z]+/\z)', $path)) {
|
||||
$path = rtrim($path, '/');
|
||||
}
|
||||
// Replace "/diffusion/X" in the request with the repository local path,
|
||||
// so "/diffusion/X/master/" becomes "/path/to/repository/X/master/".
|
||||
$local_path = rtrim($repository->getLocalPath(), '/');
|
||||
$path = $local_path.substr($path, strlen($external_base));
|
||||
|
||||
// NOTE: We are intentionally NOT removing username information from the
|
||||
// URI. Subversion retains it over the course of the request and considers
|
||||
|
@ -398,7 +396,7 @@ final class DiffusionSubversionServeSSHWorkflow
|
|||
if ($this->externalBaseURI === null) {
|
||||
$pre = (string)id(clone $uri)->setPath('');
|
||||
|
||||
$external_path = '/diffusion/'.$repository->getCallsign();
|
||||
$external_path = $external_base;
|
||||
$external_path = $this->normalizeSVNPath($external_path);
|
||||
$this->externalBaseURI = $pre.$external_path;
|
||||
|
||||
|
|
|
@ -687,6 +687,41 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
return "/r{$callsign}{$identifier}";
|
||||
}
|
||||
|
||||
public static function parseRepositoryServicePath($request_path) {
|
||||
// NOTE: In Mercurial over SSH, the path will begin without a leading "/",
|
||||
// so we're matching it optionally.
|
||||
|
||||
$patterns = array(
|
||||
'(^'.
|
||||
'(?P<base>/?diffusion/(?P<identifier>[A-Z]+|[0-9]\d*))'.
|
||||
'(?P<path>(?:/.*)?)'.
|
||||
'\z)',
|
||||
);
|
||||
|
||||
$identifier = null;
|
||||
foreach ($patterns as $pattern) {
|
||||
$matches = null;
|
||||
if (!preg_match($pattern, $request_path, $matches)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$identifier = $matches['identifier'];
|
||||
$base = $matches['base'];
|
||||
$path = $matches['path'];
|
||||
break;
|
||||
}
|
||||
|
||||
if ($identifier === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array(
|
||||
'identifier' => $identifier,
|
||||
'base' => $base,
|
||||
'path' => $path,
|
||||
);
|
||||
}
|
||||
|
||||
public function getCanonicalPath($request_path) {
|
||||
$standard_pattern =
|
||||
'(^'.
|
||||
|
|
Loading…
Reference in a new issue