Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2894363
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
17 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/repository/daemon/commitdiscovery/git/__tests__/PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase.php b/src/applications/repository/daemon/commitdiscovery/git/__tests__/PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase.php
index 8ba7af1d1b..1bef77e5c1 100644
--- a/src/applications/repository/daemon/commitdiscovery/git/__tests__/PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase.php
+++ b/src/applications/repository/daemon/commitdiscovery/git/__tests__/PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase.php
@@ -1,101 +1,113 @@
<?php
/*
- * Copyright 2011 Facebook, Inc.
+ * Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorRepositoryGitCommitDiscoveryDaemonTestCase
extends PhabricatorTestCase {
public function testVerifySameGitOrigin() {
$cases = array(
array(
'ssh://user@domain.com/path.git',
'ssh://user@domain.com/path.git',
true,
'Identical paths should pass.',
),
array(
'ssh://user@domain.com/path.git',
'https://user@domain.com/path.git',
true,
'Protocol changes should pass.',
),
array(
'ssh://user@domain.com/path.git',
'git@domain.com:path.git',
true,
'Git implicit SSH should pass.',
),
array(
'ssh://user@gitserv001.com/path.git',
'ssh://user@gitserv002.com/path.git',
true,
'Domain changes should pass.',
),
array(
'ssh://alincoln@domain.com/path.git',
'ssh://htaft@domain.com/path.git',
true,
'User/auth changes should pass.',
),
array(
'ssh://user@domain.com/apples.git',
'ssh://user@domain.com/bananas.git',
false,
'Path changes should fail.',
),
array(
'ssh://user@domain.com/apples.git',
'git@domain.com:bananas.git',
false,
'Git implicit SSH path changes should fail.',
),
array(
'user@domain.com:path/repo.git',
'user@domain.com:path/repo',
true,
'Optional .git extension should not prevent matches.',
),
array(
'user@domain.com:path/repo/',
'user@domain.com:path/repo',
true,
'Optional trailing slash should not prevent matches.',
),
+ array(
+ 'file:///path/to/local/repo.git',
+ 'file:///path/to/local/repo.git',
+ true,
+ 'file:// protocol should be supported.',
+ ),
+ array(
+ '/path/to/local/repo.git',
+ 'file:///path/to/local/repo.git',
+ true,
+ 'Implicit file:// protocol should be recognized.',
+ ),
);
foreach ($cases as $case) {
list($remote, $config, $expect, $message) = $case;
$ex = null;
try {
PhabricatorRepositoryGitCommitDiscoveryDaemon::verifySameGitOrigin(
$remote,
$config,
'(a test case)');
} catch (Exception $exception) {
$ex = $exception;
}
$this->assertEqual(
$expect,
!$ex,
"Verification that '{$remote}' and '{$config}' are the same origin ".
"had a different outcome than expected: {$message}");
}
}
}
diff --git a/src/applications/repository/storage/repository/PhabricatorRepository.php b/src/applications/repository/storage/repository/PhabricatorRepository.php
index adf2fb1ade..85017c7cf8 100644
--- a/src/applications/repository/storage/repository/PhabricatorRepository.php
+++ b/src/applications/repository/storage/repository/PhabricatorRepository.php
@@ -1,345 +1,353 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorRepository extends PhabricatorRepositoryDAO {
const TABLE_PATH = 'repository_path';
const TABLE_PATHCHANGE = 'repository_pathchange';
const TABLE_FILESYSTEM = 'repository_filesystem';
const TABLE_SUMMARY = 'repository_summary';
const TABLE_BADCOMMIT = 'repository_badcommit';
protected $phid;
protected $name;
protected $callsign;
protected $uuid;
protected $versionControlSystem;
protected $details = array();
private $sshKeyfile;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'details' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPHIDConstants::PHID_TYPE_REPO);
}
public function getDetail($key, $default = null) {
return idx($this->details, $key, $default);
}
public function setDetail($key, $value) {
$this->details[$key] = $value;
return $this;
}
public function getDiffusionBrowseURIForPath($path) {
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$branch = '/'.$this->getDetail('default-branch');
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$branch = null;
break;
default:
throw new Exception("Unknown VCS.");
}
return '/diffusion/'.$this->getCallsign().'/browse'.$branch.$path;
}
public static function newPhutilURIFromGitURI($raw_uri) {
- // If there's no protocol (git implicit SSH) reformat the URI to be a
- // normal URI. These git URIs look like "user@domain.com:path" instead of
- // "ssh://user@domain/path".
-
$uri = new PhutilURI($raw_uri);
if (!$uri->getProtocol()) {
- list($domain, $path) = explode(':', $raw_uri, 2);
- $uri = new PhutilURI('ssh://'.$domain.'/'.$path);
+ if (strpos($raw_uri, '/') === 0) {
+ // If the URI starts with a '/', it's an implicit file:// URI on the
+ // local disk.
+ $uri = new PhutilURI('file://'.$raw_uri);
+ } else if (strpos($raw_uri, ':') !== false) {
+ // If there's no protocol (git implicit SSH) but the URI has a colon,
+ // it's a git implicit SSH URI. Reformat the URI to be a normal URI.
+ // These git URIs look like "user@domain.com:path" instead of
+ // "ssh://user@domain/path".
+ list($domain, $path) = explode(':', $raw_uri, 2);
+ $uri = new PhutilURI('ssh://'.$domain.'/'.$path);
+ } else {
+ throw new Exception("The Git URI '{$raw_uri}' could not be parsed.");
+ }
}
return $uri;
}
public function getRemoteURI() {
$raw_uri = $this->getDetail('remote-uri');
$vcs = $this->getVersionControlSystem();
$is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
if ($is_git) {
$uri = self::newPhutilURIFromGitURI($raw_uri);
} else {
$uri = new PhutilURI($raw_uri);
}
if ($this->isSSHProtocol($uri->getProtocol())) {
if ($this->getSSHLogin()) {
$uri->setUser($this->getSSHLogin());
}
}
return (string)$uri;
}
public function getLocalPath() {
return $this->getDetail('local-path');
}
public function execRemoteCommand($pattern /*, $arg, ... */) {
$args = func_get_args();
$args = $this->formatRemoteCommand($args);
return call_user_func_array('exec_manual', $args);
}
public function execxRemoteCommand($pattern /*, $arg, ... */) {
$args = func_get_args();
$args = $this->formatRemoteCommand($args);
return call_user_func_array('execx', $args);
}
public function getRemoteCommandFuture($pattern /*, $arg, ... */) {
$args = func_get_args();
$args = $this->formatRemoteCommand($args);
return newv('ExecFuture', $args);
}
public function passthruRemoteCommand($pattern /*, $arg, ... */) {
$args = func_get_args();
$args = $this->formatRemoteCommand($args);
return call_user_func_array('phutil_passthru', $args);
}
public function execLocalCommand($pattern /*, $arg, ... */) {
$args = func_get_args();
$args = $this->formatLocalCommand($args);
return call_user_func_array('exec_manual', $args);
}
public function execxLocalCommand($pattern /*, $arg, ... */) {
$args = func_get_args();
$args = $this->formatLocalCommand($args);
return call_user_func_array('execx', $args);
}
public function getLocalCommandFuture($pattern /*, $arg, ... */) {
$args = func_get_args();
$args = $this->formatLocalCommand($args);
return newv('ExecFuture', $args);
}
public function passthruLocalCommand($pattern /*, $arg, ... */) {
$args = func_get_args();
$args = $this->formatLocalCommand($args);
return call_user_func_array('phutil_passthru', $args);
}
private function formatRemoteCommand(array $args) {
$pattern = $args[0];
$args = array_slice($args, 1);
if ($this->shouldUseSSH()) {
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$pattern = "SVN_SSH=%s svn --non-interactive {$pattern}";
array_unshift(
$args,
csprintf(
'ssh -l %s -i %s',
$this->getSSHLogin(),
$this->getSSHKeyfile()));
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$command = call_user_func_array(
'csprintf',
array_merge(
array(
"(ssh-add %s && git {$pattern})",
$this->getSSHKeyfile(),
),
$args));
$pattern = "ssh-agent sh -c %s";
$args = array($command);
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$pattern = "hg --config ui.ssh=%s {$pattern}";
array_unshift(
$args,
csprintf(
'ssh -l %s -i %s',
$this->getSSHLogin(),
$this->getSSHKeyfile()));
break;
default:
throw new Exception("Unrecognized version control system.");
}
} else if ($this->shouldUseHTTP()) {
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$pattern =
"svn ".
"--non-interactive ".
"--no-auth-cache ".
"--trust-server-cert ".
"--username %s ".
"--password %s ".
$pattern;
array_unshift(
$args,
$this->getDetail('http-login'),
$this->getDetail('http-pass'));
break;
default:
throw new Exception(
"No support for HTTP Basic Auth in this version control system.");
}
} else {
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$pattern = "svn --non-interactive {$pattern}";
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$pattern = "git {$pattern}";
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$pattern = "hg {$pattern}";
break;
default:
throw new Exception("Unrecognized version control system.");
}
}
array_unshift($args, $pattern);
return $args;
}
private function formatLocalCommand(array $args) {
$pattern = $args[0];
$args = array_slice($args, 1);
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$pattern = "(cd %s && svn --non-interactive {$pattern})";
array_unshift($args, $this->getLocalPath());
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$pattern = "(cd %s && git {$pattern})";
array_unshift($args, $this->getLocalPath());
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$pattern = "(cd %s && hg {$pattern})";
array_unshift($args, $this->getLocalPath());
break;
default:
throw new Exception("Unrecognized version control system.");
}
array_unshift($args, $pattern);
return $args;
}
private function getSSHLogin() {
return $this->getDetail('ssh-login');
}
private function getSSHKeyfile() {
if ($this->sshKeyfile === null) {
$key = $this->getDetail('ssh-key');
$keyfile = $this->getDetail('ssh-keyfile');
if ($keyfile) {
// Make sure we can read the file, that it exists, etc.
Filesystem::readFile($keyfile);
$this->sshKeyfile = $keyfile;
} else if ($key) {
$keyfile = new TempFile('phabricator-repository-ssh-key');
chmod($keyfile, 0600);
Filesystem::writeFile($keyfile, $key);
$this->sshKeyfile = $keyfile;
} else {
$this->sshKeyfile = '';
}
}
return (string)$this->sshKeyfile;
}
public function shouldUseSSH() {
$uri = new PhutilURI($this->getRemoteURI());
$protocol = $uri->getProtocol();
if ($this->isSSHProtocol($protocol)) {
return (bool)$this->getSSHKeyfile();
} else {
return false;
}
}
public function shouldUseHTTP() {
$uri = new PhutilURI($this->getRemoteURI());
$protocol = $uri->getProtocol();
if ($this->isHTTPProtocol($protocol)) {
return (bool)$this->getDetail('http-login');
} else {
return false;
}
}
private function isSSHProtocol($protocol) {
return ($protocol == 'ssh' || $protocol == 'svn+ssh');
}
private function isHTTPProtocol($protocol) {
return ($protocol == 'http' || $protocol == 'https');
}
public function isTracked() {
return $this->getDetail('tracking-enabled', false);
}
public function shouldTrackBranch($branch) {
$vcs = $this->getVersionControlSystem();
$is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
$use_filter = ($is_git);
if ($use_filter) {
$filter = $this->getDetail('branch-filter', array());
if ($filter && !isset($filter[$branch])) {
return false;
}
}
// By default, track all branches.
return true;
}
}
diff --git a/src/applications/repository/storage/repository/__tests__/PhabricatorRepositoryTestCase.php b/src/applications/repository/storage/repository/__tests__/PhabricatorRepositoryTestCase.php
index 7af6246422..eb5d178d26 100644
--- a/src/applications/repository/storage/repository/__tests__/PhabricatorRepositoryTestCase.php
+++ b/src/applications/repository/storage/repository/__tests__/PhabricatorRepositoryTestCase.php
@@ -1,65 +1,85 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorRepositoryTestCase
extends PhabricatorTestCase {
public function testParseGitURI() {
static $map = array(
'ssh://user@domain.com/path.git' => 'ssh://user@domain.com/path.git',
'user@domain.com:path.git' => 'ssh://user@domain.com/path.git',
+ '/path/to/local/repo.git' => 'file:///path/to/local/repo.git',
);
foreach ($map as $raw => $expect) {
$uri = PhabricatorRepository::newPhutilURIFromGitURI($raw);
$this->assertEqual(
$expect,
(string)$uri,
"Normalized Git URI '{$raw}'");
}
}
+ public function testParseBadGitURI() {
+ $junk = array(
+ 'herp derp moon balloon',
+ );
+
+ foreach ($junk as $garbage) {
+ $ex = null;
+ try {
+ $uri = PhabricatorRepository::newPhutilURIFromGitURI($garbage);
+ } catch (Exception $caught) {
+ $ex = $caught;
+ }
+ $this->assertEqual(
+ true,
+ (bool)$ex,
+ 'Expect exception when parsing garbage.');
+ }
+ }
+
public function testBranchFilter() {
$git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
$repo = new PhabricatorRepository();
$repo->setVersionControlSystem($git);
$this->assertEqual(
true,
$repo->shouldTrackBranch('imaginary'),
'Track all branches by default.');
$repo->setDetail(
'branch-filter',
array(
'master' => true,
));
$this->assertEqual(
true,
$repo->shouldTrackBranch('master'),
'Track listed branches.');
$this->assertEqual(
false,
$repo->shouldTrackBranch('imaginary'),
'Do not track unlisted branches.');
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 19:46 (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1128125
Default Alt Text
(17 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment