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:
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||||
$result = new PhabricatorVCSResponse(
|
$result = new PhabricatorVCSResponse(
|
||||||
500,
|
500,
|
||||||
pht('This is not a Git repository.'));
|
pht(
|
||||||
|
'This repository ("%s") is not a Git repository.',
|
||||||
|
$repository->getDisplayName()));
|
||||||
break;
|
break;
|
||||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
||||||
$result = new PhabricatorVCSResponse(
|
$result = new PhabricatorVCSResponse(
|
||||||
500,
|
500,
|
||||||
pht('This is not a Mercurial repository.'));
|
pht(
|
||||||
|
'This repository ("%s") is not a Mercurial repository.',
|
||||||
|
$repository->getDisplayName()));
|
||||||
break;
|
break;
|
||||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||||
$result = new PhabricatorVCSResponse(
|
$result = new PhabricatorVCSResponse(
|
||||||
500,
|
500,
|
||||||
pht('This is not a Subversion repository.'));
|
pht(
|
||||||
|
'This repository ("%s") is not a Subversion repository.',
|
||||||
|
$repository->getDisplayName()));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$result = new PhabricatorVCSResponse(
|
$result = new PhabricatorVCSResponse(
|
||||||
|
@ -480,7 +486,9 @@ final class DiffusionServeController extends DiffusionController {
|
||||||
private function getRequestDirectoryPath(PhabricatorRepository $repository) {
|
private function getRequestDirectoryPath(PhabricatorRepository $repository) {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$request_path = $request->getRequestURI()->getPath();
|
$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
|
// For Git repositories, strip an optional directory component if it
|
||||||
// isn't the name of a known Git resource. This allows users to clone
|
// 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 $repository;
|
||||||
private $hasWriteAccess;
|
private $hasWriteAccess;
|
||||||
private $proxyURI;
|
private $proxyURI;
|
||||||
|
private $baseRequestPath;
|
||||||
|
|
||||||
public function getRepository() {
|
public function getRepository() {
|
||||||
if (!$this->repository) {
|
if (!$this->repository) {
|
||||||
|
@ -45,6 +46,10 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
||||||
abstract protected function identifyRepository();
|
abstract protected function identifyRepository();
|
||||||
abstract protected function executeRepositoryOperations();
|
abstract protected function executeRepositoryOperations();
|
||||||
|
|
||||||
|
protected function getBaseRequestPath() {
|
||||||
|
return $this->baseRequestPath;
|
||||||
|
}
|
||||||
|
|
||||||
protected function writeError($message) {
|
protected function writeError($message) {
|
||||||
$this->getErrorChannel()->write($message);
|
$this->getErrorChannel()->write($message);
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -149,25 +154,29 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
||||||
protected function loadRepositoryWithPath($path) {
|
protected function loadRepositoryWithPath($path) {
|
||||||
$viewer = $this->getUser();
|
$viewer = $this->getUser();
|
||||||
|
|
||||||
$regex = '@^/?diffusion/(?P<callsign>[A-Z]+)(?:/|\z)@';
|
$info = PhabricatorRepository::parseRepositoryServicePath($path);
|
||||||
$matches = null;
|
if ($info === null) {
|
||||||
if (!preg_match($regex, $path, $matches)) {
|
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Unrecognized repository path "%s". Expected a path like "%s".',
|
'Unrecognized repository path "%s". Expected a path like "%s" '.
|
||||||
|
'or "%s".',
|
||||||
$path,
|
$path,
|
||||||
'/diffusion/X/'));
|
'/diffusion/X/',
|
||||||
|
'/diffusion/123/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$callsign = $matches[1];
|
$identifier = $info['identifier'];
|
||||||
|
$base = $info['base'];
|
||||||
|
|
||||||
|
$this->baseRequestPath = $base;
|
||||||
|
|
||||||
$repository = id(new PhabricatorRepositoryQuery())
|
$repository = id(new PhabricatorRepositoryQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withCallsigns(array($callsign))
|
->withIdentifiers(array($identifier))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|
||||||
if (!$repository) {
|
if (!$repository) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht('No repository "%s" exists!', $callsign));
|
pht('No repository "%s" exists!', $identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($repository->getServeOverSSH()) {
|
switch ($repository->getServeOverSSH()) {
|
||||||
|
@ -179,7 +188,9 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
||||||
case PhabricatorRepository::SERVE_OFF:
|
case PhabricatorRepository::SERVE_OFF:
|
||||||
default:
|
default:
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht('This repository is not available over SSH.'));
|
pht(
|
||||||
|
'This repository ("%s") is not available over SSH.',
|
||||||
|
$repository->getDisplayName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $repository;
|
return $repository;
|
||||||
|
|
|
@ -377,14 +377,12 @@ final class DiffusionSubversionServeSSHWorkflow
|
||||||
$repository = $this->getRepository();
|
$repository = $this->getRepository();
|
||||||
|
|
||||||
$path = $this->getPathFromSubversionURI($uri_string);
|
$path = $this->getPathFromSubversionURI($uri_string);
|
||||||
$path = preg_replace(
|
$external_base = $this->getBaseRequestPath();
|
||||||
'(^/diffusion/[A-Z]+)',
|
|
||||||
rtrim($repository->getLocalPath(), '/'),
|
|
||||||
$path);
|
|
||||||
|
|
||||||
if (preg_match('(^/diffusion/[A-Z]+/\z)', $path)) {
|
// Replace "/diffusion/X" in the request with the repository local path,
|
||||||
$path = rtrim($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
|
// NOTE: We are intentionally NOT removing username information from the
|
||||||
// URI. Subversion retains it over the course of the request and considers
|
// URI. Subversion retains it over the course of the request and considers
|
||||||
|
@ -398,7 +396,7 @@ final class DiffusionSubversionServeSSHWorkflow
|
||||||
if ($this->externalBaseURI === null) {
|
if ($this->externalBaseURI === null) {
|
||||||
$pre = (string)id(clone $uri)->setPath('');
|
$pre = (string)id(clone $uri)->setPath('');
|
||||||
|
|
||||||
$external_path = '/diffusion/'.$repository->getCallsign();
|
$external_path = $external_base;
|
||||||
$external_path = $this->normalizeSVNPath($external_path);
|
$external_path = $this->normalizeSVNPath($external_path);
|
||||||
$this->externalBaseURI = $pre.$external_path;
|
$this->externalBaseURI = $pre.$external_path;
|
||||||
|
|
||||||
|
|
|
@ -687,6 +687,41 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
return "/r{$callsign}{$identifier}";
|
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) {
|
public function getCanonicalPath($request_path) {
|
||||||
$standard_pattern =
|
$standard_pattern =
|
||||||
'(^'.
|
'(^'.
|
||||||
|
|
Loading…
Reference in a new issue