mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-09 22:31:03 +01:00
Collapse repository URI normalization code into Arcanist
Summary: Ref T13546. Companion change to D21372. Move URI normalization code to Arcanist to we can more-often resolve remote URIs correctly. Test Plan: Grepped for affected symbols. Maniphest Tasks: T13546 Differential Revision: https://secure.phabricator.com/D21373
This commit is contained in:
parent
a7b3ba5a6f
commit
7d496f2c6d
6 changed files with 39 additions and 260 deletions
|
@ -4653,8 +4653,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryType' => 'applications/repository/constants/PhabricatorRepositoryType.php',
|
'PhabricatorRepositoryType' => 'applications/repository/constants/PhabricatorRepositoryType.php',
|
||||||
'PhabricatorRepositoryURI' => 'applications/repository/storage/PhabricatorRepositoryURI.php',
|
'PhabricatorRepositoryURI' => 'applications/repository/storage/PhabricatorRepositoryURI.php',
|
||||||
'PhabricatorRepositoryURIIndex' => 'applications/repository/storage/PhabricatorRepositoryURIIndex.php',
|
'PhabricatorRepositoryURIIndex' => 'applications/repository/storage/PhabricatorRepositoryURIIndex.php',
|
||||||
'PhabricatorRepositoryURINormalizer' => 'applications/repository/data/PhabricatorRepositoryURINormalizer.php',
|
|
||||||
'PhabricatorRepositoryURINormalizerTestCase' => 'applications/repository/data/__tests__/PhabricatorRepositoryURINormalizerTestCase.php',
|
|
||||||
'PhabricatorRepositoryURIPHIDType' => 'applications/repository/phid/PhabricatorRepositoryURIPHIDType.php',
|
'PhabricatorRepositoryURIPHIDType' => 'applications/repository/phid/PhabricatorRepositoryURIPHIDType.php',
|
||||||
'PhabricatorRepositoryURIQuery' => 'applications/repository/query/PhabricatorRepositoryURIQuery.php',
|
'PhabricatorRepositoryURIQuery' => 'applications/repository/query/PhabricatorRepositoryURIQuery.php',
|
||||||
'PhabricatorRepositoryURITestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php',
|
'PhabricatorRepositoryURITestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php',
|
||||||
|
@ -11407,8 +11405,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorConduitResultInterface',
|
'PhabricatorConduitResultInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorRepositoryURIIndex' => 'PhabricatorRepositoryDAO',
|
'PhabricatorRepositoryURIIndex' => 'PhabricatorRepositoryDAO',
|
||||||
'PhabricatorRepositoryURINormalizer' => 'Phobject',
|
|
||||||
'PhabricatorRepositoryURINormalizerTestCase' => 'PhabricatorTestCase',
|
|
||||||
'PhabricatorRepositoryURIPHIDType' => 'PhabricatorPHIDType',
|
'PhabricatorRepositoryURIPHIDType' => 'PhabricatorPHIDType',
|
||||||
'PhabricatorRepositoryURIQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorRepositoryURIQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhabricatorRepositoryURITestCase' => 'PhabricatorTestCase',
|
'PhabricatorRepositoryURITestCase' => 'PhabricatorTestCase',
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize repository URIs. For example, these URIs are generally equivalent
|
|
||||||
* and all point at the same repository:
|
|
||||||
*
|
|
||||||
* ssh://user@host/repo
|
|
||||||
* ssh://user@host/repo/
|
|
||||||
* ssh://user@host:22/repo
|
|
||||||
* user@host:/repo
|
|
||||||
* ssh://user@host/repo.git
|
|
||||||
*
|
|
||||||
* This class can be used to normalize URIs like this, in order to detect
|
|
||||||
* alternate spellings of the same repository URI. In particular, the
|
|
||||||
* @{method:getNormalizedPath} method will return:
|
|
||||||
*
|
|
||||||
* repo
|
|
||||||
*
|
|
||||||
* ...for all of these URIs. Generally, usage looks like this:
|
|
||||||
*
|
|
||||||
* $norm_a = new PhabricatorRepositoryURINormalizer($type, $uri_a);
|
|
||||||
* $norm_b = new PhabricatorRepositoryURINormalizer($type, $uri_b);
|
|
||||||
*
|
|
||||||
* if ($norm_a->getNormalizedPath() == $norm_b->getNormalizedPath()) {
|
|
||||||
* // URIs appear to point at the same repository.
|
|
||||||
* } else {
|
|
||||||
* // URIs are very unlikely to be the same repository.
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* Because a repository can be hosted at arbitrarily many arbitrary URIs, there
|
|
||||||
* is no way to completely prevent false negatives by only examining URIs
|
|
||||||
* (that is, repositories with totally different URIs could really be the same).
|
|
||||||
* However, normalization is relatively aggressive and false negatives should
|
|
||||||
* be rare: if normalization says two URIs are different repositories, they
|
|
||||||
* probably are.
|
|
||||||
*
|
|
||||||
* @task normal Normalizing URIs
|
|
||||||
*/
|
|
||||||
final class PhabricatorRepositoryURINormalizer extends Phobject {
|
|
||||||
|
|
||||||
const TYPE_GIT = 'git';
|
|
||||||
const TYPE_SVN = 'svn';
|
|
||||||
const TYPE_MERCURIAL = 'hg';
|
|
||||||
|
|
||||||
private $type;
|
|
||||||
private $uri;
|
|
||||||
|
|
||||||
public function __construct($type, $uri) {
|
|
||||||
switch ($type) {
|
|
||||||
case self::TYPE_GIT:
|
|
||||||
case self::TYPE_SVN:
|
|
||||||
case self::TYPE_MERCURIAL:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception(pht('Unknown URI type "%s"!', $type));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->type = $type;
|
|
||||||
$this->uri = $uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getAllURITypes() {
|
|
||||||
return array(
|
|
||||||
self::TYPE_GIT,
|
|
||||||
self::TYPE_SVN,
|
|
||||||
self::TYPE_MERCURIAL,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -( Normalizing URIs )--------------------------------------------------- */
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @task normal
|
|
||||||
*/
|
|
||||||
public function getPath() {
|
|
||||||
switch ($this->type) {
|
|
||||||
case self::TYPE_GIT:
|
|
||||||
$uri = new PhutilURI($this->uri);
|
|
||||||
return $uri->getPath();
|
|
||||||
case self::TYPE_SVN:
|
|
||||||
case self::TYPE_MERCURIAL:
|
|
||||||
$uri = new PhutilURI($this->uri);
|
|
||||||
if ($uri->getProtocol()) {
|
|
||||||
return $uri->getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->uri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getNormalizedURI() {
|
|
||||||
return $this->getNormalizedDomain().'/'.$this->getNormalizedPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @task normal
|
|
||||||
*/
|
|
||||||
public function getNormalizedPath() {
|
|
||||||
$path = $this->getPath();
|
|
||||||
$path = trim($path, '/');
|
|
||||||
|
|
||||||
switch ($this->type) {
|
|
||||||
case self::TYPE_GIT:
|
|
||||||
$path = preg_replace('/\.git$/', '', $path);
|
|
||||||
break;
|
|
||||||
case self::TYPE_SVN:
|
|
||||||
case self::TYPE_MERCURIAL:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a Phabricator URI, strip it down to the callsign. We mutably
|
|
||||||
// allow you to clone repositories as "/diffusion/X/anything.git", for
|
|
||||||
// example.
|
|
||||||
|
|
||||||
$matches = null;
|
|
||||||
if (preg_match('@^(diffusion/(?:[A-Z]+|\d+))@', $path, $matches)) {
|
|
||||||
$path = $matches[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getNormalizedDomain() {
|
|
||||||
$domain = null;
|
|
||||||
|
|
||||||
$uri = new PhutilURI($this->uri);
|
|
||||||
$domain = $uri->getDomain();
|
|
||||||
|
|
||||||
if (!strlen($domain)) {
|
|
||||||
return '<void>';
|
|
||||||
}
|
|
||||||
|
|
||||||
$domain = phutil_utf8_strtolower($domain);
|
|
||||||
|
|
||||||
// See T13435. If the domain for a repository URI is same as the install
|
|
||||||
// base URI, store it as a "<base-uri>" token instead of the actual domain
|
|
||||||
// so that the index does not fall out of date if the install moves.
|
|
||||||
|
|
||||||
$base_uri = PhabricatorEnv::getURI('/');
|
|
||||||
$base_uri = new PhutilURI($base_uri);
|
|
||||||
$base_domain = $base_uri->getDomain();
|
|
||||||
$base_domain = phutil_utf8_strtolower($base_domain);
|
|
||||||
if ($domain === $base_domain) {
|
|
||||||
return '<base-uri>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Likewise, store a token for the "SSH Host" domain so it can be changed
|
|
||||||
// without requiring an index rebuild.
|
|
||||||
|
|
||||||
$ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host');
|
|
||||||
if (strlen($ssh_host)) {
|
|
||||||
$ssh_host = phutil_utf8_strtolower($ssh_host);
|
|
||||||
if ($domain === $ssh_host) {
|
|
||||||
return '<ssh-host>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorRepositoryURINormalizerTestCase
|
|
||||||
extends PhabricatorTestCase {
|
|
||||||
|
|
||||||
public function testGitURINormalizer() {
|
|
||||||
$cases = array(
|
|
||||||
'ssh://user@domain.com/path.git' => 'path',
|
|
||||||
'https://user@domain.com/path.git' => 'path',
|
|
||||||
'git@domain.com:path.git' => 'path',
|
|
||||||
'ssh://user@gitserv002.com/path.git' => 'path',
|
|
||||||
'ssh://htaft@domain.com/path.git' => 'path',
|
|
||||||
'ssh://user@domain.com/bananas.git' => 'bananas',
|
|
||||||
'git@domain.com:bananas.git' => 'bananas',
|
|
||||||
'user@domain.com:path/repo' => 'path/repo',
|
|
||||||
'user@domain.com:path/repo/' => 'path/repo',
|
|
||||||
'file:///path/to/local/repo.git' => 'path/to/local/repo',
|
|
||||||
'/path/to/local/repo.git' => 'path/to/local/repo',
|
|
||||||
'ssh://something.com/diffusion/X/anything.git' => 'diffusion/X',
|
|
||||||
'ssh://something.com/diffusion/X/' => 'diffusion/X',
|
|
||||||
);
|
|
||||||
|
|
||||||
$type_git = PhabricatorRepositoryURINormalizer::TYPE_GIT;
|
|
||||||
|
|
||||||
foreach ($cases as $input => $expect) {
|
|
||||||
$normal = new PhabricatorRepositoryURINormalizer($type_git, $input);
|
|
||||||
$this->assertEqual(
|
|
||||||
$expect,
|
|
||||||
$normal->getNormalizedPath(),
|
|
||||||
pht('Normalized Git path for "%s".', $input));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDomainURINormalizer() {
|
|
||||||
$base_domain = 'base.phabricator.example.com';
|
|
||||||
$ssh_domain = 'ssh.phabricator.example.com';
|
|
||||||
|
|
||||||
$env = PhabricatorEnv::beginScopedEnv();
|
|
||||||
$env->overrideEnvConfig('phabricator.base-uri', 'http://'.$base_domain);
|
|
||||||
$env->overrideEnvConfig('diffusion.ssh-host', $ssh_domain);
|
|
||||||
|
|
||||||
$cases = array(
|
|
||||||
'/' => '<void>',
|
|
||||||
'/path/to/local/repo.git' => '<void>',
|
|
||||||
'ssh://user@domain.com/path.git' => 'domain.com',
|
|
||||||
'ssh://user@DOMAIN.COM/path.git' => 'domain.com',
|
|
||||||
'http://'.$base_domain.'/diffusion/X/' => '<base-uri>',
|
|
||||||
'ssh://'.$ssh_domain.'/diffusion/X/' => '<ssh-host>',
|
|
||||||
'git@'.$ssh_domain.':bananas.git' => '<ssh-host>',
|
|
||||||
);
|
|
||||||
|
|
||||||
$type_git = PhabricatorRepositoryURINormalizer::TYPE_GIT;
|
|
||||||
|
|
||||||
foreach ($cases as $input => $expect) {
|
|
||||||
$normal = new PhabricatorRepositoryURINormalizer($type_git, $input);
|
|
||||||
|
|
||||||
$this->assertEqual(
|
|
||||||
$expect,
|
|
||||||
$normal->getNormalizedDomain(),
|
|
||||||
pht('Normalized domain for "%s".', $input));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSVNURINormalizer() {
|
|
||||||
$cases = array(
|
|
||||||
'file:///path/to/repo' => 'path/to/repo',
|
|
||||||
'file:///path/to/repo/' => 'path/to/repo',
|
|
||||||
);
|
|
||||||
|
|
||||||
$type_svn = PhabricatorRepositoryURINormalizer::TYPE_SVN;
|
|
||||||
|
|
||||||
foreach ($cases as $input => $expect) {
|
|
||||||
$normal = new PhabricatorRepositoryURINormalizer($type_svn, $input);
|
|
||||||
$this->assertEqual(
|
|
||||||
$expect,
|
|
||||||
$normal->getNormalizedPath(),
|
|
||||||
pht('Normalized SVN path for "%s".', $input));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -290,13 +290,13 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
$remote_root = (string)($xml->entry[0]->repository[0]->root[0]);
|
$remote_root = (string)($xml->entry[0]->repository[0]->root[0]);
|
||||||
$expect_root = $repository->getSubversionPathURI();
|
$expect_root = $repository->getSubversionPathURI();
|
||||||
|
|
||||||
$normal_type_svn = PhabricatorRepositoryURINormalizer::TYPE_SVN;
|
$normal_type_svn = ArcanistRepositoryURINormalizer::TYPE_SVN;
|
||||||
|
|
||||||
$remote_normal = id(new PhabricatorRepositoryURINormalizer(
|
$remote_normal = id(new ArcanistRepositoryURINormalizer(
|
||||||
$normal_type_svn,
|
$normal_type_svn,
|
||||||
$remote_root))->getNormalizedPath();
|
$remote_root))->getNormalizedPath();
|
||||||
|
|
||||||
$expect_normal = id(new PhabricatorRepositoryURINormalizer(
|
$expect_normal = id(new ArcanistRepositoryURINormalizer(
|
||||||
$normal_type_svn,
|
$normal_type_svn,
|
||||||
$expect_root))->getNormalizedPath();
|
$expect_root))->getNormalizedPath();
|
||||||
|
|
||||||
|
|
|
@ -689,10 +689,13 @@ final class PhabricatorRepositoryQuery
|
||||||
// or an `svn+ssh` URI, we could deduce how to normalize it. However, this
|
// or an `svn+ssh` URI, we could deduce how to normalize it. However, this
|
||||||
// would be more complicated and it's not clear if it matters in practice.
|
// would be more complicated and it's not clear if it matters in practice.
|
||||||
|
|
||||||
$types = PhabricatorRepositoryURINormalizer::getAllURITypes();
|
$domain_map = PhabricatorRepositoryURI::getURINormalizerDomainMap();
|
||||||
|
|
||||||
|
$types = ArcanistRepositoryURINormalizer::getAllURITypes();
|
||||||
foreach ($this->uris as $uri) {
|
foreach ($this->uris as $uri) {
|
||||||
foreach ($types as $type) {
|
foreach ($types as $type) {
|
||||||
$normalized_uri = new PhabricatorRepositoryURINormalizer($type, $uri);
|
$normalized_uri = new ArcanistRepositoryURINormalizer($type, $uri);
|
||||||
|
$normalized_uri->setDomainMap($domain_map);
|
||||||
$normalized_uris[] = $normalized_uri->getNormalizedURI();
|
$normalized_uris[] = $normalized_uri->getNormalizedURI();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,19 +196,22 @@ final class PhabricatorRepositoryURI
|
||||||
|
|
||||||
$map = array(
|
$map = array(
|
||||||
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT =>
|
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT =>
|
||||||
PhabricatorRepositoryURINormalizer::TYPE_GIT,
|
ArcanistRepositoryURINormalizer::TYPE_GIT,
|
||||||
PhabricatorRepositoryType::REPOSITORY_TYPE_SVN =>
|
PhabricatorRepositoryType::REPOSITORY_TYPE_SVN =>
|
||||||
PhabricatorRepositoryURINormalizer::TYPE_SVN,
|
ArcanistRepositoryURINormalizer::TYPE_SVN,
|
||||||
PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL =>
|
PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL =>
|
||||||
PhabricatorRepositoryURINormalizer::TYPE_MERCURIAL,
|
ArcanistRepositoryURINormalizer::TYPE_MERCURIAL,
|
||||||
);
|
);
|
||||||
|
|
||||||
$type = $map[$vcs];
|
$type = $map[$vcs];
|
||||||
$display = (string)$this->getDisplayURI();
|
$display = (string)$this->getDisplayURI();
|
||||||
|
|
||||||
$normal_uri = new PhabricatorRepositoryURINormalizer($type, $display);
|
$normalizer = new ArcanistRepositoryURINormalizer($type, $display);
|
||||||
|
|
||||||
return $normal_uri->getNormalizedURI();
|
$domain_map = self::getURINormalizerDomainMap();
|
||||||
|
$normalizer->setDomainMap($domain_map);
|
||||||
|
|
||||||
|
return $normalizer->getNormalizedURI();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDisplayURI() {
|
public function getDisplayURI() {
|
||||||
|
@ -735,4 +738,27 @@ final class PhabricatorRepositoryURI
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getURINormalizerDomainMap() {
|
||||||
|
$domain_map = array();
|
||||||
|
|
||||||
|
// See T13435. If the domain for a repository URI is same as the install
|
||||||
|
// base URI, store it as a "<base-uri>" token instead of the actual domain
|
||||||
|
// so that the index does not fall out of date if the install moves.
|
||||||
|
|
||||||
|
$base_uri = PhabricatorEnv::getURI('/');
|
||||||
|
$base_uri = new PhutilURI($base_uri);
|
||||||
|
$base_domain = $base_uri->getDomain();
|
||||||
|
$domain_map['<base-uri>'] = $base_domain;
|
||||||
|
|
||||||
|
// Likewise, store a token for the "SSH Host" domain so it can be changed
|
||||||
|
// without requiring an index rebuild.
|
||||||
|
|
||||||
|
$ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host');
|
||||||
|
if (strlen($ssh_host)) {
|
||||||
|
$domain_map['<ssh-host>'] = $ssh_host;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $domain_map;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue