mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-23 15:22:41 +01:00
Verify that SVN repository roots really are repository roots
Summary: Fixes T3238. Ref T4327. Although the instructions are fairly clear on this, it's easy to miss them. Make sure the root the user enters matches the real root. Test Plan: Added unit tests. Used `bin/repository discover` to hit the check explicitly. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T3238, T4327 Differential Revision: https://secure.phabricator.com/D8020
This commit is contained in:
parent
ef2c9861ef
commit
c9a0ffa1cf
6 changed files with 134 additions and 5 deletions
|
@ -473,8 +473,10 @@ final class DiffusionRepositoryCreateController
|
||||||
"| `svn://svn.example.net/svnroot/` |\n".
|
"| `svn://svn.example.net/svnroot/` |\n".
|
||||||
"| `file:///local/path/to/svnroot/` |\n".
|
"| `file:///local/path/to/svnroot/` |\n".
|
||||||
"\n\n".
|
"\n\n".
|
||||||
"Make sure you specify the root of the repository, not a ".
|
"You **MUST** specify the root of the repository, not a ".
|
||||||
"subdirectory.");
|
"subdirectory. (If you want to import only part of a Subversion ".
|
||||||
|
"repository, use the //Import Only// option at the end of this ".
|
||||||
|
"workflow.)");
|
||||||
} else {
|
} else {
|
||||||
throw new Exception("Unsupported VCS!");
|
throw new Exception("Unsupported VCS!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,12 +39,15 @@
|
||||||
final class PhabricatorRepositoryURINormalizer extends Phobject {
|
final class PhabricatorRepositoryURINormalizer extends Phobject {
|
||||||
|
|
||||||
const TYPE_GIT = 'git';
|
const TYPE_GIT = 'git';
|
||||||
|
const TYPE_SVN = 'svn';
|
||||||
|
|
||||||
private $type;
|
private $type;
|
||||||
private $uri;
|
private $uri;
|
||||||
|
|
||||||
public function __construct($type, $uri) {
|
public function __construct($type, $uri) {
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case self::TYPE_GIT:
|
case self::TYPE_GIT:
|
||||||
|
case self::TYPE_SVN:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception(pht('Unknown URI type "%s"!'));
|
throw new Exception(pht('Unknown URI type "%s"!'));
|
||||||
|
@ -74,6 +77,13 @@ final class PhabricatorRepositoryURINormalizer extends Phobject {
|
||||||
return $uri->getPath();
|
return $uri->getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this->uri;
|
||||||
|
case self::TYPE_SVN:
|
||||||
|
$uri = new PhutilURI($this->uri);
|
||||||
|
if ($uri->getProtocol()) {
|
||||||
|
return $uri->getPath();
|
||||||
|
}
|
||||||
|
|
||||||
return $this->uri;
|
return $this->uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,6 +100,8 @@ final class PhabricatorRepositoryURINormalizer extends Phobject {
|
||||||
case self::TYPE_GIT:
|
case self::TYPE_GIT:
|
||||||
$path = preg_replace('/\.git$/', '', $path);
|
$path = preg_replace('/\.git$/', '', $path);
|
||||||
break;
|
break;
|
||||||
|
case self::TYPE_SVN:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $path;
|
return $path;
|
||||||
|
|
|
@ -25,7 +25,25 @@ final class PhabricatorRepositoryURINormalizerTestCase
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
$expect,
|
$expect,
|
||||||
$normal->getNormalizedPath(),
|
$normal->getNormalizedPath(),
|
||||||
pht('Normalized path for "%s".', $input));
|
pht('Normalized Git path 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,6 +225,10 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
private function discoverSubversionCommits() {
|
private function discoverSubversionCommits() {
|
||||||
$repository = $this->getRepository();
|
$repository = $this->getRepository();
|
||||||
|
|
||||||
|
if (!$repository->isHosted()) {
|
||||||
|
$this->verifySubversionRoot($repository);
|
||||||
|
}
|
||||||
|
|
||||||
$upper_bound = null;
|
$upper_bound = null;
|
||||||
$limit = 1;
|
$limit = 1;
|
||||||
$refs = array();
|
$refs = array();
|
||||||
|
@ -289,6 +293,43 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function verifySubversionRoot(PhabricatorRepository $repository) {
|
||||||
|
list($xml) = $repository->execxRemoteCommand(
|
||||||
|
'info --xml %s',
|
||||||
|
$repository->getSubversionPathURI());
|
||||||
|
|
||||||
|
$xml = phutil_utf8ize($xml);
|
||||||
|
$xml = new SimpleXMLElement($xml);
|
||||||
|
|
||||||
|
$remote_root = (string)($xml->entry[0]->repository[0]->root[0]);
|
||||||
|
$expect_root = $repository->getSubversionPathURI();
|
||||||
|
|
||||||
|
$normal_type_svn = PhabricatorRepositoryURINormalizer::TYPE_SVN;
|
||||||
|
|
||||||
|
$remote_normal = id(new PhabricatorRepositoryURINormalizer(
|
||||||
|
$normal_type_svn,
|
||||||
|
$remote_root))->getNormalizedPath();
|
||||||
|
|
||||||
|
$expect_normal = id(new PhabricatorRepositoryURINormalizer(
|
||||||
|
$normal_type_svn,
|
||||||
|
$expect_root))->getNormalizedPath();
|
||||||
|
|
||||||
|
if ($remote_normal != $expect_normal) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Repository "%s" does not have a correctly configured remote URI. '.
|
||||||
|
'The remote URI for a Subversion repository MUST point at the '.
|
||||||
|
'repository root. The root for this repository is "%s", but the '.
|
||||||
|
'configured URI is "%s". To resolve this error, set the remote URI '.
|
||||||
|
'to point at the repository root. If you want to import only part '.
|
||||||
|
'of a Subversion repository, use the "Import Only" option.',
|
||||||
|
$repository->getCallsign(),
|
||||||
|
$remote_root,
|
||||||
|
$expect_root));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Discovering Mercurial Repositories )--------------------------------- */
|
/* -( Discovering Mercurial Repositories )--------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,14 @@ abstract class PhabricatorWorkingCopyTestCase extends PhabricatorTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildBareRepository($callsign) {
|
protected function buildBareRepository($callsign) {
|
||||||
|
$existing_repository = id(new PhabricatorRepositoryQuery())
|
||||||
|
->withCallsigns(array($callsign))
|
||||||
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
|
->executeOne();
|
||||||
|
if ($existing_repository) {
|
||||||
|
$existing_repository->delete();
|
||||||
|
}
|
||||||
|
|
||||||
$data_dir = dirname(__FILE__).'/data/';
|
$data_dir = dirname(__FILE__).'/data/';
|
||||||
|
|
||||||
$types = array(
|
$types = array(
|
||||||
|
@ -84,7 +92,7 @@ abstract class PhabricatorWorkingCopyTestCase extends PhabricatorTestCase {
|
||||||
|
|
||||||
id(new PhabricatorRepositoryDiscoveryEngine())
|
id(new PhabricatorRepositoryDiscoveryEngine())
|
||||||
->setRepository($repository)
|
->setRepository($repository)
|
||||||
->discoverCommits($repository);
|
->discoverCommits();
|
||||||
|
|
||||||
return $repository;
|
return $repository;
|
||||||
}
|
}
|
||||||
|
|
|
@ -962,7 +962,7 @@ final class PhabricatorChangeParserTestCase
|
||||||
|
|
||||||
id(new PhabricatorRepositoryDiscoveryEngine())
|
id(new PhabricatorRepositoryDiscoveryEngine())
|
||||||
->setRepository($repository)
|
->setRepository($repository)
|
||||||
->discoverCommits($repository);
|
->discoverCommits();
|
||||||
|
|
||||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||||
|
|
||||||
|
@ -1056,6 +1056,54 @@ final class PhabricatorChangeParserTestCase
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSubversionValidRootParser() {
|
||||||
|
// First, automatically configure the root correctly.
|
||||||
|
$repository = $this->buildBareRepository('CHD');
|
||||||
|
id(new PhabricatorRepositoryPullEngine())
|
||||||
|
->setRepository($repository)
|
||||||
|
->pullRepository();
|
||||||
|
|
||||||
|
$caught = null;
|
||||||
|
try {
|
||||||
|
id(new PhabricatorRepositoryDiscoveryEngine())
|
||||||
|
->setRepository($repository)
|
||||||
|
->discoverCommits();
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
false,
|
||||||
|
($caught instanceof Exception),
|
||||||
|
pht('Natural SVN root should work properly.'));
|
||||||
|
|
||||||
|
|
||||||
|
// This time, artificially break the root. We expect this to fail.
|
||||||
|
$repository = $this->buildBareRepository('CHD');
|
||||||
|
$repository->setDetail(
|
||||||
|
'remote-uri',
|
||||||
|
$repository->getDetail('remote-uri').'trunk/');
|
||||||
|
|
||||||
|
id(new PhabricatorRepositoryPullEngine())
|
||||||
|
->setRepository($repository)
|
||||||
|
->pullRepository();
|
||||||
|
|
||||||
|
$caught = null;
|
||||||
|
try {
|
||||||
|
id(new PhabricatorRepositoryDiscoveryEngine())
|
||||||
|
->setRepository($repository)
|
||||||
|
->discoverCommits();
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
true,
|
||||||
|
($caught instanceof Exception),
|
||||||
|
pht('Artificial SVN root should fail.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private function expectChanges(
|
private function expectChanges(
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
array $commits,
|
array $commits,
|
||||||
|
|
Loading…
Reference in a new issue