From 0ba7d34f97f847b31780a9c38988fcf6dc2dd9c8 Mon Sep 17 00:00:00 2001 From: Nick Harper Date: Wed, 12 Dec 2012 14:43:09 -0800 Subject: [PATCH] Allow phabricator to manage repositories Summary: This adds a configuration option for repositories to be marked as managed by phabricator, which is then used by the pullLocal daemon to try to recover from some errors it runs into. Test Plan: Set a bogus uri for a repo, saw that the pullLocal daemon fixed it. Reviewers: epriestley, vrana Reviewed By: epriestley CC: aran, Korvin Differential Revision: https://secure.phabricator.com/D3680 --- conf/default.conf.php | 13 ++++ .../PhabricatorRepositoryEditController.php | 8 ++- .../PhabricatorRepositoryPullLocalDaemon.php | 70 ++++++++++++++----- 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/conf/default.conf.php b/conf/default.conf.php index 5f00978e2d..80290c3de8 100644 --- a/conf/default.conf.php +++ b/conf/default.conf.php @@ -1068,6 +1068,19 @@ return array( // revision is even older than it is marked as old. 'differential.days-stale' => 3, +// -- Repositories ---------------------------------------------------------- // + + // The default location in which to store local copies of repositories. + // Anything stored in this directory will be assumed to be under the + // control of phabricator, which means that Phabricator will try to do some + // maintenance on working copies if there are problems (such as a change + // to the remote origin url). This maintenance may include completely + // removing (and recloning) anything in this directory. + // + // When set to null, this option is ignored (i.e. Phabricator will not fully + // control any working copies). + 'repository.default-local-path' => null, + // -- Maniphest ------------------------------------------------------------- // 'maniphest.enabled' => true, diff --git a/src/applications/repository/controller/PhabricatorRepositoryEditController.php b/src/applications/repository/controller/PhabricatorRepositoryEditController.php index 16ffd4accc..c23e04507f 100644 --- a/src/applications/repository/controller/PhabricatorRepositoryEditController.php +++ b/src/applications/repository/controller/PhabricatorRepositoryEditController.php @@ -509,6 +509,12 @@ final class PhabricatorRepositoryEditController $inset->setTitle('Repository Information'); if ($has_local) { + $default_local_path = ''; + $default = + PhabricatorEnv::getEnvConfig('repository.default-local-path'); + if (!$repository->getDetail('remote-uri') && $default) { + $default_local_path = $default.strtolower($repository->getCallsign()); + } $inset->appendChild( '

Select a path on local disk '. 'which the daemons should '.$clone_command.' the repository '. @@ -519,7 +525,7 @@ final class PhabricatorRepositoryEditController id(new AphrontFormTextControl()) ->setName('path') ->setLabel('Local Path') - ->setValue($repository->getDetail('local-path')) + ->setValue($repository->getDetail('local-path', $default_local_path)) ->setError($e_path)); } else if ($is_svn) { $inset->appendChild( diff --git a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php index 8473a7051b..1e44278c5b 100644 --- a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php +++ b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php @@ -468,6 +468,12 @@ final class PhabricatorRepositoryPullLocalDaemon /* -( Git Implementation )------------------------------------------------- */ + private function canWrite($path) { + $default_path = + PhabricatorEnv::getEnvConfig('repository.default-local-path'); + return Filesystem::isDescendant($path, $default_path); + } + /** * @task git */ @@ -495,6 +501,7 @@ final class PhabricatorRepositoryPullLocalDaemon list($err, $stdout) = $repository->execLocalCommand( 'rev-parse --show-toplevel'); + $msg = ''; if ($err) { @@ -505,45 +512,74 @@ final class PhabricatorRepositoryPullLocalDaemon if (is_dir($path)) { $files = Filesystem::listDirectory($path, $include_hidden = true); if (!$files) { - throw new Exception( + $msg = "Expected to find a git repository at '{$path}', but there ". "is an empty directory there. Remove the directory: the daemon ". - "will run 'git clone' for you."); + "will run 'git clone' for you."; } - } - - throw new Exception( + } else { + $msg = "Expected to find a git repository at '{$path}', but there is ". "a non-repository directory (with other stuff in it) there. Move or ". "remove this directory (or reconfigure the repository to use a ". "different directory), and then either clone a repository yourself ". - "or let the daemon do it."); + "or let the daemon do it."; + } } else { $repo_path = rtrim($stdout, "\n"); if (empty($repo_path)) { - throw new Exception( + $err = true; + $msg = "Expected to find a git repository at '{$path}', but ". "there was no result from `git rev-parse --show-toplevel`. ". "Something is misconfigured or broken. The git repository ". - "may be inside a '.git/' directory."); - } - - if (!Filesystem::pathsAreEquivalent($repo_path, $path)) { - throw new Exception( + "may be inside a '.git/' directory."; + } else if (!Filesystem::pathsAreEquivalent($repo_path, $path)) { + $err = true; + $msg = "Expected to find repo at '{$path}', but the actual ". "git repository root for this directory is '{$repo_path}'. ". "Something is misconfigured. The repository's 'Local Path' should ". "be set to some place where the daemon can check out a working ". - "copy, and should not be inside another git repository."); + "copy, and should not be inside another git repository."; } } + if ($err && $this->canWrite($path)) { + phlog("{$path} failed sanity check; recloning. ({$msg})"); + Filesystem::remove($path); + $this->executeGitCreate($repository, $path); + } else if ($err) { + throw new Exception($msg); + } - // This is a local command, but needs credentials. - $future = $repository->getRemoteCommandFuture('fetch --all --prune'); - $future->setCWD($path); - $future->resolvex(); + $retry = false; + do { + // This is a local command, but needs credentials. + $future = $repository->getRemoteCommandFuture('fetch --all --prune'); + $future->setCWD($path); + list($err, $stdout, $stderr) = $future->resolve(); + + if ($err && !$retry && $this->canWrite($path)) { + $retry = true; + // Fix remote origin url if it doesn't match our configuration + $origin_url = + $repository->execLocalCommand('config --get remote.origin.url'); + $remote_uri = $repository->getDetail('remote-uri'); + if ($origin_url != $remote_uri) { + $repository->execLocalCommand('remote set-url origin %s', + $remote_uri); + } + } else if ($err) { + throw new Exception( + "git fetch failed with error #{$err}:\n". + "stdout:{$stdout}\n\n". + "stderr:{$stderr}\n"); + } else { + $retry = false; + } + } while ($retry); }