diff --git a/scripts/repository/test_connection.php b/scripts/repository/test_connection.php
new file mode 100755
index 0000000000..3f511d40c5
--- /dev/null
+++ b/scripts/repository/test_connection.php
@@ -0,0 +1,90 @@
+#!/usr/bin/env php
+\n";
+ exit(1);
+}
+
+echo phutil_console_wrap(
+ phutil_console_format(
+ 'This script will test that you have configured valid credentials for '.
+ 'access to a repository, so the Phabricator daemons can pull from it. '.
+ 'You should run this as the **same user you will run the daemons as**, '.
+ 'from the **same machine they will run from**. Doing this will help '.
+ 'detect various problems with your configuration, such as SSH issues.'));
+
+list($whoami) = execx('whoami');
+$whoami = trim($whoami);
+
+$ok = phutil_console_confirm("Do you want to continue as '{$whoami}'?");
+if (!$ok) {
+ die(1);
+}
+
+$callsign = $argv[1];
+echo "Loading '{$callsign}' repository...\n";
+$repository = id(new PhabricatorRepository())->loadOneWhere(
+ 'callsign = %s',
+ $argv[1]);
+if (!$repository) {
+ throw new Exception("No such repository exists!");
+}
+
+$vcs = $repository->getVersionControlSystem();
+
+PhutilServiceProfiler::installEchoListener();
+
+echo "Trying to connect to the remote...\n";
+switch ($vcs) {
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+ $err = $repository->passthruRemoteCommand(
+ '--limit 1 log %s',
+ $repository->getRemoteURI());
+ break;
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+ // Do an ls-remote on a nonexistent ref, which we expect to just return
+ // nothing.
+ $err = $repository->passthruRemoteCommand(
+ 'ls-remote %s %s',
+ $repository->getRemoteURI(),
+ 'just-testing');
+ break;
+ default:
+ throw new Exception("Unsupported repository type.");
+}
+
+if ($err) {
+ echo phutil_console_format(
+ "** FAIL ** Connection failed. The credentials for this ".
+ "repository appear to be incorrectly configured.\n");
+ exit(1);
+} else {
+ echo phutil_console_format(
+ "** OKAY ** Connection successful. The credentials for ".
+ "this repository appear to be correctly configured.\n");
+}
+
diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
index 21038f4976..83d7e1f9cc 100644
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -652,6 +652,17 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-preview.js',
),
+ 0 =>
+ array(
+ 'uri' => '/res/1da00bfe/rsrc/js/javelin/lib/__tests__/URI.js',
+ 'type' => 'js',
+ 'requires' =>
+ array(
+ 0 => 'javelin-uri',
+ 1 => 'javelin-php-serializer',
+ ),
+ 'disk' => '/rsrc/js/javelin/lib/__tests__/URI.js',
+ ),
'javelin-behavior-owners-path-editor' =>
array(
'uri' => '/res/9cf78ffc/rsrc/js/application/owners/owners-path-editor.js',
@@ -1283,15 +1294,6 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/core/Prefab.js',
),
- 0 =>
- array(
- 'uri' => '/res/936e8e81/rsrc/js/javelin/docs/onload.js',
- 'type' => 'js',
- 'requires' =>
- array(
- ),
- 'disk' => '/rsrc/js/javelin/docs/onload.js',
- ),
'phabricator-profile-css' =>
array(
'uri' => '/res/ebe1ac2f/rsrc/css/application/profile/profile-view.css',
diff --git a/src/applications/repository/controller/edit/PhabricatorRepositoryEditController.php b/src/applications/repository/controller/edit/PhabricatorRepositoryEditController.php
index 159cc45c0c..91d3715d2d 100644
--- a/src/applications/repository/controller/edit/PhabricatorRepositoryEditController.php
+++ b/src/applications/repository/controller/edit/PhabricatorRepositoryEditController.php
@@ -239,6 +239,9 @@ class PhabricatorRepositoryEditController
'default-owners-path',
'/'));
+ $repository->setDetail('ssh-login', $request->getStr('ssh-login'));
+ $repository->setDetail('ssh-key', $request->getStr('ssh-key'));
+
$repository->setDetail(
'herald-disabled',
$request->getInt('herald-disabled', 0));
@@ -329,10 +332,14 @@ class PhabricatorRepositoryEditController
'Differential, Diffusion, Herald, and other services. To enable '.
'tracking for a repository, configure it here and then start (or '.
'restart) the daemons. More information is available in the '.
- ''.$user_guide_link.'.
')
+ ''.$user_guide_link.'.');
+
+ $form
+ ->appendChild(
+ 'Basics
')
->appendChild(
id(new AphrontFormStaticControl())
- ->setLabel('Repository')
+ ->setLabel('Repository Name')
->setValue($repository->getName()))
->appendChild(
id(new AphrontFormSelectControl())
@@ -345,13 +352,19 @@ class PhabricatorRepositoryEditController
->setValue(
$repository->getDetail('tracking-enabled')
? 'enabled'
- : 'disabled'));
+ : 'disabled'))
+ ->appendChild('
');
+
+ $form->appendChild(
+ 'Remote URI
'.
+ '');
+
+ $form->appendChild(
+ 'Importing Repository Information
'.
+ '');
+
+ $form->appendChild(
+ 'Application Configuration
'.
+ '');
+
if ($is_git) {
$form
->appendChild(
@@ -452,8 +503,8 @@ class PhabricatorRepositoryEditController
1 => 'Disabled - Do Not Send Email',
))
->setCaption(
- 'You can temporarily disable Herald notifications when reparsing '.
- 'a repository or importing a new repository.'));
+ 'You can temporarily disable Herald commit notifications when '.
+ 'reparsing a repository or importing a new repository.'));
$parsers = id(new PhutilSymbolLoader())
->setAncestorClass('PhabricatorRepositoryCommitMessageDetailParser')
@@ -487,10 +538,12 @@ class PhabricatorRepositoryEditController
->setCaption('Repository UUID from svn info.'));
}
+ $form->appendChild('
');
+
$form
->appendChild(
id(new AphrontFormSubmitControl())
- ->setValue('Save'));
+ ->setValue('Save Configuration'));
$panel = new AphrontPanelView();
$panel->setHeader('Repository Tracking');
diff --git a/src/applications/repository/controller/edit/__init__.php b/src/applications/repository/controller/edit/__init__.php
index 7da55db338..4f61cde518 100644
--- a/src/applications/repository/controller/edit/__init__.php
+++ b/src/applications/repository/controller/edit/__init__.php
@@ -17,6 +17,7 @@ phutil_require_module('phabricator', 'applications/repository/storage/repository
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/form/base');
+phutil_require_module('phabricator', 'view/form/control/markup');
phutil_require_module('phabricator', 'view/form/control/select');
phutil_require_module('phabricator', 'view/form/control/static');
phutil_require_module('phabricator', 'view/form/control/submit');
diff --git a/src/applications/repository/storage/repository/PhabricatorRepository.php b/src/applications/repository/storage/repository/PhabricatorRepository.php
index 0235e1a435..bb1181ceea 100644
--- a/src/applications/repository/storage/repository/PhabricatorRepository.php
+++ b/src/applications/repository/storage/repository/PhabricatorRepository.php
@@ -32,6 +32,8 @@ class PhabricatorRepository extends PhabricatorRepositoryDAO {
protected $versionControlSystem;
protected $details = array();
+ private $sshKeyfile;
+
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
@@ -55,4 +57,183 @@ class PhabricatorRepository extends PhabricatorRepositoryDAO {
return $this;
}
+ public function getRemoteURI() {
+ $raw_uri = $this->getDetail('remote-uri');
+
+ $vcs = $this->getVersionControlSystem();
+ $is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
+
+ // 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 ($is_git && !$uri->getProtocol()) {
+ list($domain, $path) = explode(':', $raw_uri, 2);
+ $uri = new PhutilURI('ssh://'.$domain.'/'.$path);
+ }
+
+ 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 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 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 {$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 {
+ switch ($this->getVersionControlSystem()) {
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+ $pattern = "svn {$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 {$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) {
+ $keyfile = new TempFile('phabricator-repository-ssh-key');
+ chmod($keyfile, 0600);
+ Filesystem::writeFile($keyfile, $this->getDetail('ssh-key'));
+ $this->sshKeyfile = $keyfile;
+ }
+
+ return (string)$this->sshKeyfile;
+ }
+
+ public function shouldUseSSH() {
+ $uri = new PhutilURI($this->getRemoteURI());
+ $protocol = $uri->getProtocol();
+ if ($this->isSSHProtocol($protocol)) {
+ return (bool)$this->getDetail('ssh-key');
+ } else {
+ return false;
+ }
+ }
+
+ private function isSSHProtocol($protocol) {
+ return ($protocol == 'ssh' || $protocol == 'svn+ssh');
+ }
+
}
diff --git a/src/applications/repository/storage/repository/__init__.php b/src/applications/repository/storage/repository/__init__.php
index 6480f38dac..e08a467030 100644
--- a/src/applications/repository/storage/repository/__init__.php
+++ b/src/applications/repository/storage/repository/__init__.php
@@ -8,9 +8,15 @@
phutil_require_module('phabricator', 'applications/phid/constants');
phutil_require_module('phabricator', 'applications/phid/storage/phid');
+phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
phutil_require_module('phabricator', 'applications/repository/storage/base');
+phutil_require_module('phutil', 'filesystem');
+phutil_require_module('phutil', 'filesystem/tempfile');
+phutil_require_module('phutil', 'future/exec');
+phutil_require_module('phutil', 'parser/uri');
phutil_require_module('phutil', 'utils');
+phutil_require_module('phutil', 'xsprintf/csprintf');
phutil_require_source('PhabricatorRepository.php');
diff --git a/webroot/index.php b/webroot/index.php
index 44159da28b..2c2154fadb 100644
--- a/webroot/index.php
+++ b/webroot/index.php
@@ -250,7 +250,7 @@ function phabricator_shutdown() {
return;
}
- if ($event['type'] != E_ERROR) {
+ if ($event['type'] != E_ERROR && $event['type'] != E_PARSE) {
return;
}