@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Make repository pulls testable

Summary:
Ref T2784. This moves us toward being able to test the background and Conduit pipelines for repositories. In particular:

- Separate the logic for pulling repositories (`git pull`, `hg pull`) out of `PhabricatorRepositoryPullLocalDaemon` and put it in `PhabricatorRepositoryPullEngine`. This allows repositories to be pulled directly without invoking the daemons.
- Add tests for the engine, including a future-looking base test case.
- Add basic `PhutilDirectoryFixture`-based repositories.

Next steps:

# Do the same for repo discovery.
# Then we can start writing tests against specific Conduit methods.

Test Plan: Ran unit tests. Ran `bin/repository pull` on SVN, Hg and Git repositories. Ran `bin/phd debug pulllocal`.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran, nh

Maniphest Tasks: T2784

Differential Revision: https://secure.phabricator.com/D5904

+405 -225
+5
src/__phutil_library_map__.php
··· 1300 1300 'PhabricatorRepositoryManagementWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementWorkflow.php', 1301 1301 'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryMercurialCommitChangeParserWorker.php', 1302 1302 'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php', 1303 + 'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php', 1303 1304 'PhabricatorRepositoryPullLocalDaemon' => 'applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php', 1304 1305 'PhabricatorRepositoryPullLocalDaemonTestCase' => 'applications/repository/daemon/__tests__/PhabricatorRepositoryPullLocalDaemonTestCase.php', 1305 1306 'PhabricatorRepositoryQuery' => 'applications/repository/query/PhabricatorRepositoryQuery.php', ··· 1487 1488 'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/PhabricatorWorkerTaskDetailController.php', 1488 1489 'PhabricatorWorkerTaskUpdateController' => 'applications/daemon/controller/PhabricatorWorkerTaskUpdateController.php', 1489 1490 'PhabricatorWorkerTestCase' => 'infrastructure/daemon/workers/__tests__/PhabricatorWorkerTestCase.php', 1491 + 'PhabricatorWorkingCopyPullTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyPullTestCase.php', 1492 + 'PhabricatorWorkingCopyTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php', 1490 1493 'PhabricatorWorkpanelView' => 'view/layout/PhabricatorWorkpanelView.php', 1491 1494 'PhabricatorXHPASTViewController' => 'applications/phpast/controller/PhabricatorXHPASTViewController.php', 1492 1495 'PhabricatorXHPASTViewDAO' => 'applications/phpast/storage/PhabricatorXHPASTViewDAO.php', ··· 3194 3197 'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController', 3195 3198 'PhabricatorWorkerTaskUpdateController' => 'PhabricatorDaemonController', 3196 3199 'PhabricatorWorkerTestCase' => 'PhabricatorTestCase', 3200 + 'PhabricatorWorkingCopyPullTestCase' => 'PhabricatorWorkingCopyTestCase', 3201 + 'PhabricatorWorkingCopyTestCase' => 'PhabricatorTestCase', 3197 3202 'PhabricatorWorkpanelView' => 'AphrontView', 3198 3203 'PhabricatorXHPASTViewController' => 'PhabricatorController', 3199 3204 'PhabricatorXHPASTViewDAO' => 'PhabricatorLiskDAO',
+3 -218
src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
··· 120 120 $callsign = $repository->getCallsign(); 121 121 $this->log("Updating repository '{$callsign}'."); 122 122 123 - $this->pullRepository($repository); 123 + id(new PhabricatorRepositoryPullEngine()) 124 + ->setRepository($repository) 125 + ->pullRepository(); 124 126 125 127 if (!$no_discovery) { 126 128 // TODO: It would be nice to discover only if we pulled something, ··· 172 174 return id(new PhabricatorRepository())->loadAll(); 173 175 } else { 174 176 return PhabricatorRepository::loadAllByPHIDOrCallsign($names); 175 - } 176 - } 177 - 178 - 179 - /** 180 - * @task pull 181 - */ 182 - public function pullRepository(PhabricatorRepository $repository) { 183 - $vcs = $repository->getVersionControlSystem(); 184 - 185 - $is_svn = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN); 186 - $is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT); 187 - $is_hg = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL); 188 - 189 - if ($is_svn) { 190 - return; 191 - } 192 - 193 - $callsign = $repository->getCallsign(); 194 - 195 - if (!$is_git && !$is_hg) { 196 - throw new Exception( 197 - "Unknown VCS '{$vcs}' for repository '{$callsign}'!"); 198 - } 199 - 200 - $local_path = $repository->getDetail('local-path'); 201 - if (!$local_path) { 202 - throw new Exception( 203 - "No local path is available for repository '{$callsign}'."); 204 - } 205 - 206 - if (!Filesystem::pathExists($local_path)) { 207 - $dirname = dirname($local_path); 208 - if (!Filesystem::pathExists($dirname)) { 209 - Filesystem::createDirectory($dirname, 0755, $recursive = true); 210 - } 211 - 212 - if ($is_git) { 213 - return $this->executeGitCreate($repository, $local_path); 214 - } else if ($is_hg) { 215 - return $this->executeHgCreate($repository, $local_path); 216 - } 217 - } else { 218 - if ($is_git) { 219 - return $this->executeGitUpdate($repository, $local_path); 220 - } else if ($is_hg) { 221 - return $this->executeHgUpdate($repository, $local_path); 222 - } 223 177 } 224 178 } 225 179 ··· 468 422 /* -( Git Implementation )------------------------------------------------- */ 469 423 470 424 471 - private function canWrite($path) { 472 - $default_path = 473 - PhabricatorEnv::getEnvConfig('repository.default-local-path'); 474 - return Filesystem::isDescendant($path, $default_path); 475 - } 476 - 477 - /** 478 - * @task git 479 - */ 480 - private function executeGitCreate( 481 - PhabricatorRepository $repository, 482 - $path) { 483 - 484 - $repository->execxRemoteCommand( 485 - 'clone --origin origin %s %s', 486 - $repository->getRemoteURI(), 487 - rtrim($path, '/')); 488 - } 489 - 490 - 491 - /** 492 - * @task git 493 - */ 494 - private function executeGitUpdate( 495 - PhabricatorRepository $repository, 496 - $path) { 497 - 498 - // Run a bunch of sanity checks to detect people checking out repositories 499 - // inside other repositories, making empty directories, pointing the local 500 - // path at some random file or path, etc. 501 - 502 - list($err, $stdout) = $repository->execLocalCommand( 503 - 'rev-parse --show-toplevel'); 504 - $msg = ''; 505 - 506 - if ($err) { 507 - 508 - // Try to raise a more tailored error message in the more common case 509 - // of the user creating an empty directory. (We could try to remove it, 510 - // but might not be able to, and it's much simpler to raise a good 511 - // message than try to navigate those waters.) 512 - if (is_dir($path)) { 513 - $files = Filesystem::listDirectory($path, $include_hidden = true); 514 - if (!$files) { 515 - $msg = 516 - "Expected to find a git repository at '{$path}', but there ". 517 - "is an empty directory there. Remove the directory: the daemon ". 518 - "will run 'git clone' for you."; 519 - } 520 - } else { 521 - $msg = 522 - "Expected to find a git repository at '{$path}', but there is ". 523 - "a non-repository directory (with other stuff in it) there. Move or ". 524 - "remove this directory (or reconfigure the repository to use a ". 525 - "different directory), and then either clone a repository yourself ". 526 - "or let the daemon do it."; 527 - } 528 - } else { 529 - $repo_path = rtrim($stdout, "\n"); 530 - 531 - if (empty($repo_path)) { 532 - $err = true; 533 - $msg = 534 - "Expected to find a git repository at '{$path}', but ". 535 - "there was no result from `git rev-parse --show-toplevel`. ". 536 - "Something is misconfigured or broken. The git repository ". 537 - "may be inside a '.git/' directory."; 538 - } else if (!Filesystem::pathsAreEquivalent($repo_path, $path)) { 539 - $err = true; 540 - $msg = 541 - "Expected to find repo at '{$path}', but the actual ". 542 - "git repository root for this directory is '{$repo_path}'. ". 543 - "Something is misconfigured. The repository's 'Local Path' should ". 544 - "be set to some place where the daemon can check out a working ". 545 - "copy, and should not be inside another git repository."; 546 - } 547 - } 548 - 549 - if ($err && $this->canWrite($path)) { 550 - phlog("{$path} failed sanity check; recloning. ({$msg})"); 551 - Filesystem::remove($path); 552 - $this->executeGitCreate($repository, $path); 553 - } else if ($err) { 554 - throw new Exception($msg); 555 - } 556 - 557 - $retry = false; 558 - do { 559 - // This is a local command, but needs credentials. 560 - $future = $repository->getRemoteCommandFuture('fetch --all --prune'); 561 - $future->setCWD($path); 562 - list($err, $stdout, $stderr) = $future->resolve(); 563 - 564 - if ($err && !$retry && $this->canWrite($path)) { 565 - $retry = true; 566 - // Fix remote origin url if it doesn't match our configuration 567 - $origin_url = 568 - $repository->execLocalCommand('config --get remote.origin.url'); 569 - $remote_uri = $repository->getDetail('remote-uri'); 570 - if ($origin_url != $remote_uri) { 571 - $repository->execLocalCommand('remote set-url origin %s', 572 - $remote_uri); 573 - } 574 - } else if ($err) { 575 - throw new Exception( 576 - "git fetch failed with error #{$err}:\n". 577 - "stdout:{$stdout}\n\n". 578 - "stderr:{$stderr}\n"); 579 - } else { 580 - $retry = false; 581 - } 582 - } while ($retry); 583 - } 584 - 585 - 586 425 /** 587 426 * @task git 588 427 */ ··· 786 625 787 626 /* -( Mercurial Implementation )------------------------------------------- */ 788 627 789 - 790 - /** 791 - * @task hg 792 - */ 793 - private function executeHgCreate( 794 - PhabricatorRepository $repository, 795 - $path) { 796 - 797 - $repository->execxRemoteCommand( 798 - 'clone %s %s', 799 - $repository->getRemoteURI(), 800 - rtrim($path, '/')); 801 - } 802 - 803 - 804 - /** 805 - * @task hg 806 - */ 807 - private function executeHgUpdate( 808 - PhabricatorRepository $repository, 809 - $path) { 810 - 811 - // This is a local command, but needs credentials. 812 - $future = $repository->getRemoteCommandFuture('pull -u'); 813 - $future->setCWD($path); 814 - 815 - try { 816 - $future->resolvex(); 817 - } catch (CommandException $ex) { 818 - $err = $ex->getError(); 819 - $stdout = $ex->getStdOut(); 820 - 821 - // NOTE: Between versions 2.1 and 2.1.1, Mercurial changed the behavior 822 - // of "hg pull" to return 1 in case of a successful pull with no changes. 823 - // This behavior has been reverted, but users who updated between Feb 1, 824 - // 2012 and Mar 1, 2012 will have the erroring version. Do a dumb test 825 - // against stdout to check for this possibility. 826 - // See: https://github.com/facebook/phabricator/issues/101/ 827 - 828 - // NOTE: Mercurial has translated versions, which translate this error 829 - // string. In a translated version, the string will be something else, 830 - // like "aucun changement trouve". There didn't seem to be an easy way 831 - // to handle this (there are hard ways but this is not a common problem 832 - // and only creates log spam, not application failures). Assume English. 833 - 834 - // TODO: Remove this once we're far enough in the future that deployment 835 - // of 2.1 is exceedingly rare? 836 - if ($err == 1 && preg_match('/no changes found/', $stdout)) { 837 - return; 838 - } else { 839 - throw $ex; 840 - } 841 - } 842 - } 843 628 844 629 private function executeHgDiscover(PhabricatorRepository $repository) { 845 630 // NOTE: "--debug" gives us 40-character hashes.
+267
src/applications/repository/engine/PhabricatorRepositoryPullEngine.php
··· 1 + <?php 2 + 3 + /** 4 + * Manages execution of `git pull` and `hg pull` commands for 5 + * @{class:PhabricatorRepository} objects. Used by 6 + * @{class:PhabricatorRepositoryPullLocalDaemon}. 7 + * 8 + * @task pull Pulling Working Copies 9 + * @task git Pulling Git Working Copies 10 + * @task hg Pulling Mercurial Working Copies 11 + * @task internal Internals 12 + */ 13 + final class PhabricatorRepositoryPullEngine { 14 + 15 + private $repository; 16 + 17 + 18 + /* -( Pulling Working Copies )--------------------------------------------- */ 19 + 20 + 21 + public function setRepository(PhabricatorRepository $repository) { 22 + $this->repository = $repository; 23 + return $this; 24 + } 25 + 26 + private function getRepository() { 27 + return $this->repository; 28 + } 29 + 30 + public function pullRepository() { 31 + $repository = $this->getRepository(); 32 + 33 + if (!$repository) { 34 + throw new Exception("Call setRepository() before pullRepository()!"); 35 + } 36 + 37 + $is_hg = false; 38 + $is_git = false; 39 + 40 + $vcs = $repository->getVersionControlSystem(); 41 + switch ($vcs) { 42 + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 43 + // We never pull a local copy of Subversion repositories. 44 + return; 45 + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 46 + $is_git = true; 47 + break; 48 + case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 49 + $is_hg = true; 50 + break; 51 + default: 52 + throw new Exception("Unsupported VCS '{$vcs}'!"); 53 + } 54 + 55 + $callsign = $repository->getCallsign(); 56 + $local_path = $repository->getLocalPath(); 57 + if ($local_path === null) { 58 + throw new Exception( 59 + "No local path is configured for repository '{$callsign}'."); 60 + } 61 + 62 + $dirname = dirname($local_path); 63 + if (!Filesystem::pathExists($dirname)) { 64 + Filesystem::createDirectory($dirname, 0755, $recursive = true); 65 + } 66 + 67 + if (!Filesystem::pathExists($local_path)) { 68 + if ($is_git) { 69 + $this->executeGitCreate(); 70 + } else { 71 + $this->executeMercurialCreate(); 72 + } 73 + } else { 74 + if ($is_git) { 75 + $this->executeGitUpdate(); 76 + } else { 77 + $this->executeMercurialUpdate(); 78 + } 79 + } 80 + 81 + return $this; 82 + } 83 + 84 + 85 + /* -( Pulling Git Working Copies )----------------------------------------- */ 86 + 87 + 88 + /** 89 + * @task git 90 + */ 91 + private function executeGitCreate() { 92 + $repository = $this->getRepository(); 93 + 94 + $repository->execxRemoteCommand( 95 + 'clone --origin origin %s %s', 96 + $repository->getRemoteURI(), 97 + rtrim($repository->getLocalPath(), '/')); 98 + } 99 + 100 + 101 + /** 102 + * @task git 103 + */ 104 + private function executeGitUpdate() { 105 + $repository = $this->getRepository(); 106 + 107 + list($err, $stdout) = $repository->execLocalCommand( 108 + 'rev-parse --show-toplevel'); 109 + 110 + $message = null; 111 + $path = $repository->getLocalPath(); 112 + if ($err) { 113 + // Try to raise a more tailored error message in the more common case 114 + // of the user creating an empty directory. (We could try to remove it, 115 + // but might not be able to, and it's much simpler to raise a good 116 + // message than try to navigate those waters.) 117 + if (is_dir($path)) { 118 + $files = Filesystem::listDirectory($path, $include_hidden = true); 119 + if (!$files) { 120 + $message = 121 + "Expected to find a git repository at '{$path}', but there ". 122 + "is an empty directory there. Remove the directory: the daemon ". 123 + "will run 'git clone' for you."; 124 + } else { 125 + $message = 126 + "Expected to find a git repository at '{$path}', but there is ". 127 + "a non-repository directory (with other stuff in it) there. Move ". 128 + "or remove this directory (or reconfigure the repository to use a ". 129 + "different directory), and then either clone a repository ". 130 + "yourself or let the daemon do it."; 131 + } 132 + } else if (is_file($path)) { 133 + $message = 134 + "Expected to find a git repository at '{$path}', but there is a ". 135 + "file there instead. Remove it and let the daemon clone a ". 136 + "repository for you."; 137 + } else { 138 + $message = 139 + "Expected to find a git repository at '{$path}', but did not."; 140 + } 141 + } else { 142 + $repo_path = rtrim($stdout, "\n"); 143 + 144 + if (empty($repo_path)) { 145 + $err = true; 146 + $msg = 147 + "Expected to find a git repository at '{$path}', but ". 148 + "there was no result from `git rev-parse --show-toplevel`. ". 149 + "Something is misconfigured or broken. The git repository ". 150 + "may be inside a '.git/' directory."; 151 + } else if (!Filesystem::pathsAreEquivalent($repo_path, $path)) { 152 + $err = true; 153 + $msg = 154 + "Expected to find repo at '{$path}', but the actual ". 155 + "git repository root for this directory is '{$repo_path}'. ". 156 + "Something is misconfigured. The repository's 'Local Path' should ". 157 + "be set to some place where the daemon can check out a working ". 158 + "copy, and should not be inside another git repository."; 159 + } 160 + } 161 + 162 + if ($err && $this->canDestroyWorkingCopy($path)) { 163 + phlog("Repository working copy at '{$path}' failed sanity check; ". 164 + "destroying and re-cloning. {$message}"); 165 + Filesystem::remove($path); 166 + $this->executeGitCreate(); 167 + } else if ($err) { 168 + throw new Exception($message); 169 + } 170 + 171 + $retry = false; 172 + do { 173 + // This is a local command, but needs credentials. 174 + $future = $repository->getRemoteCommandFuture('fetch --all --prune'); 175 + $future->setCWD($path); 176 + list($err, $stdout, $stderr) = $future->resolve(); 177 + 178 + if ($err && !$retry && $this->canDestroyWorkingCopy($path)) { 179 + $retry = true; 180 + // Fix remote origin url if it doesn't match our configuration 181 + $origin_url = $repository->execLocalCommand( 182 + 'config --get remote.origin.url'); 183 + $remote_uri = $repository->getDetail('remote-uri'); 184 + if ($origin_url != $remote_uri) { 185 + $repository->execLocalCommand( 186 + 'remote set-url origin %s', 187 + $remote_uri); 188 + } 189 + } else if ($err) { 190 + throw new Exception( 191 + "git fetch failed with error #{$err}:\n". 192 + "stdout:{$stdout}\n\n". 193 + "stderr:{$stderr}\n"); 194 + } else { 195 + $retry = false; 196 + } 197 + } while ($retry); 198 + } 199 + 200 + 201 + /* -( Pulling Mercurial Working Copies )----------------------------------- */ 202 + 203 + 204 + /** 205 + * @task hg 206 + */ 207 + private function executeMercurialCreate() { 208 + $repository = $this->getRepository(); 209 + 210 + $repository->execxRemoteCommand( 211 + 'clone %s %s', 212 + $repository->getRemoteURI(), 213 + rtrim($repository->getLocalPath(), '/')); 214 + } 215 + 216 + 217 + /** 218 + * @task hg 219 + */ 220 + private function executeMercurialUpdate() { 221 + $repository = $this->getRepository(); 222 + $path = $repository->getLocalPath(); 223 + 224 + // This is a local command, but needs credentials. 225 + $future = $repository->getRemoteCommandFuture('pull -u'); 226 + $future->setCWD($path); 227 + 228 + try { 229 + $future->resolvex(); 230 + } catch (CommandException $ex) { 231 + $err = $ex->getError(); 232 + $stdout = $ex->getStdOut(); 233 + 234 + // NOTE: Between versions 2.1 and 2.1.1, Mercurial changed the behavior 235 + // of "hg pull" to return 1 in case of a successful pull with no changes. 236 + // This behavior has been reverted, but users who updated between Feb 1, 237 + // 2012 and Mar 1, 2012 will have the erroring version. Do a dumb test 238 + // against stdout to check for this possibility. 239 + // See: https://github.com/facebook/phabricator/issues/101/ 240 + 241 + // NOTE: Mercurial has translated versions, which translate this error 242 + // string. In a translated version, the string will be something else, 243 + // like "aucun changement trouve". There didn't seem to be an easy way 244 + // to handle this (there are hard ways but this is not a common problem 245 + // and only creates log spam, not application failures). Assume English. 246 + 247 + // TODO: Remove this once we're far enough in the future that deployment 248 + // of 2.1 is exceedingly rare? 249 + if ($err == 1 && preg_match('/no changes found/', $stdout)) { 250 + return; 251 + } else { 252 + throw $ex; 253 + } 254 + } 255 + } 256 + 257 + 258 + /* -( Internals )---------------------------------------------------------- */ 259 + 260 + 261 + private function canDestroyWorkingCopy($path) { 262 + $default_path = PhabricatorEnv::getEnvConfig( 263 + 'repository.default-local-path'); 264 + return Filesystem::isDescendant($path, $default_path); 265 + } 266 + 267 + }
+32
src/applications/repository/engine/__tests__/PhabricatorWorkingCopyPullTestCase.php
··· 1 + <?php 2 + 3 + final class PhabricatorWorkingCopyPullTestCase 4 + extends PhabricatorWorkingCopyTestCase { 5 + 6 + public function testGitPullBasic() { 7 + $repo = $this->buildPulledRepository('GT'); 8 + 9 + $this->assertEqual( 10 + true, 11 + Filesystem::pathExists($repo->getLocalPath().'/.git')); 12 + } 13 + 14 + public function testHgPullBasic() { 15 + $repo = $this->buildPulledRepository('HT'); 16 + 17 + $this->assertEqual( 18 + true, 19 + Filesystem::pathExists($repo->getLocalPath().'/.hg')); 20 + } 21 + 22 + public function testSVNPullBasic() { 23 + $repo = $this->buildPulledRepository('ST'); 24 + 25 + // We don't pull local clones for SVN, so we don't expect there to be 26 + // a working copy. 27 + $this->assertEqual( 28 + false, 29 + Filesystem::pathExists($repo->getLocalPath())); 30 + } 31 + 32 + }
+95
src/applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorWorkingCopyTestCase extends PhabricatorTestCase { 4 + 5 + private $dirs = array(); 6 + private $repos = array(); 7 + private $pulled = array(); 8 + 9 + protected function getPhabricatorTestCaseConfiguration() { 10 + return array( 11 + self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true, 12 + ); 13 + } 14 + 15 + protected function buildBareRepository($callsign) { 16 + if (isset($this->repos[$callsign])) { 17 + return $this->repos[$callsign]; 18 + } 19 + 20 + $data_dir = dirname(__FILE__).'/data/'; 21 + 22 + $types = array( 23 + 'svn' => PhabricatorRepositoryType::REPOSITORY_TYPE_SVN, 24 + 'hg' => PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL, 25 + 'git' => PhabricatorRepositoryType::REPOSITORY_TYPE_GIT, 26 + ); 27 + 28 + $hits = array(); 29 + foreach ($types as $type => $const) { 30 + $path = $data_dir.$callsign.'.'.$type.'.tgz'; 31 + if (Filesystem::pathExists($path)) { 32 + $hits[$const] = $path; 33 + } 34 + } 35 + 36 + if (!$hits) { 37 + throw new Exception( 38 + "No test data for callsign '{$callsign}'. Expected an archive ". 39 + "like '{$callsign}.git.tgz' in '{$data_dir}'."); 40 + } 41 + 42 + if (count($hits) > 1) { 43 + throw new Exception( 44 + "Expected exactly one archive matching callsign '{$callsign}', ". 45 + "found too many: ".implode(', ', $hits)); 46 + } 47 + 48 + $path = head($hits); 49 + $vcs_type = head_key($hits); 50 + 51 + $dir = PhutilDirectoryFixture::newFromArchive($path); 52 + $local = new TempFile('.ignore'); 53 + 54 + $repo = id(new PhabricatorRepository()) 55 + ->setCallsign($callsign) 56 + ->setName(pht('Test Repo "%s"', $callsign)) 57 + ->setVersionControlSystem($vcs_type) 58 + ->setDetail('local-path', dirname($local).'/'.$callsign) 59 + ->setDetail('remote-uri', 'file://'.$dir->getPath().'/'); 60 + 61 + $this->didConstructRepository($repo); 62 + 63 + $repo->save(); 64 + $repo->makeEphemeral(); 65 + 66 + // Keep the disk resources around until we exit. 67 + $this->dirs[] = $dir; 68 + $this->dirs[] = $local; 69 + 70 + $this->repos[$callsign] = $repo; 71 + 72 + return $repo; 73 + } 74 + 75 + protected function didConstructRepository(PhabricatorRepository $repository) { 76 + return; 77 + } 78 + 79 + protected function buildPulledRepository($callsign) { 80 + $repository = $this->buildBareRepository($callsign); 81 + 82 + if (isset($this->pulled[$callsign])) { 83 + return $repository; 84 + } 85 + 86 + id(new PhabricatorRepositoryPullEngine()) 87 + ->setRepository($repository) 88 + ->pullRepository(); 89 + 90 + $this->pulled[$callsign] = true; 91 + 92 + return $repository; 93 + } 94 + 95 + }
src/applications/repository/engine/__tests__/data/GT.git.tgz

This is a binary file and will not be displayed.

src/applications/repository/engine/__tests__/data/HT.hg.tgz

This is a binary file and will not be displayed.

src/applications/repository/engine/__tests__/data/ST.svn.tgz

This is a binary file and will not be displayed.

+3 -7
src/applications/repository/management/PhabricatorRepositoryManagementPullWorkflow.php
··· 11 11 ->setArguments( 12 12 array( 13 13 array( 14 - 'name' => 'verbose', 15 - 'help' => 'Show additional debugging information.', 16 - ), 17 - array( 18 14 'name' => 'repos', 19 15 'wildcard' => true, 20 16 ), ··· 34 30 foreach ($repos as $repo) { 35 31 $console->writeOut("Pulling '%s'...\n", $repo->getCallsign()); 36 32 37 - $daemon = new PhabricatorRepositoryPullLocalDaemon(array()); 38 - $daemon->setVerbose($args->getArg('verbose')); 39 - $daemon->pullRepository($repo); 33 + id(new PhabricatorRepositoryPullEngine()) 34 + ->setRepository($repo) 35 + ->pullRepository(); 40 36 } 41 37 42 38 $console->writeOut("Done.\n");