@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.

Extract repository command construction from Repositories

Summary:
Ref T4292. Ref T10366. Depends on D15751. Today, generating repository commands is purely a function of the repository, so they use protocols and credentials based on the repository configuration.

For example, a repository with an SSH "remote URI" always generate SSH "remote commands".

This needs to change in the future:

- After T10366, repositories won't necessarily just have one type of remote URI. They can only have one at a time still, but the repository itself won't change based on which one is currently active.
- For T4292, I need to generate intracluster commands, regardless of repository configuration. These will have different protocols and credentials.

Prepare for these cases by separating out command construction, so they'll be able to generate commands in a more flexible way.

Test Plan:
- Added unit tests.
- Browsed diffusion.
- Ran `bin/phd debug pull` to pull a bunch of repos.
- Ran daemons.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4292, T10366

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

+524 -210
+10
src/__phutil_library_map__.php
··· 570 570 'DiffusionCachedResolveRefsQuery' => 'applications/diffusion/query/DiffusionCachedResolveRefsQuery.php', 571 571 'DiffusionChangeController' => 'applications/diffusion/controller/DiffusionChangeController.php', 572 572 'DiffusionChangeHeraldFieldGroup' => 'applications/diffusion/herald/DiffusionChangeHeraldFieldGroup.php', 573 + 'DiffusionCommandEngine' => 'applications/diffusion/protocol/DiffusionCommandEngine.php', 574 + 'DiffusionCommandEngineTestCase' => 'applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php', 573 575 'DiffusionCommitAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionCommitAffectedFilesHeraldField.php', 574 576 'DiffusionCommitAuthorHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorHeraldField.php', 575 577 'DiffusionCommitAutocloseHeraldField' => 'applications/diffusion/herald/DiffusionCommitAutocloseHeraldField.php', ··· 634 636 'DiffusionGitBlameQuery' => 'applications/diffusion/query/blame/DiffusionGitBlameQuery.php', 635 637 'DiffusionGitBranch' => 'applications/diffusion/data/DiffusionGitBranch.php', 636 638 'DiffusionGitBranchTestCase' => 'applications/diffusion/data/__tests__/DiffusionGitBranchTestCase.php', 639 + 'DiffusionGitCommandEngine' => 'applications/diffusion/protocol/DiffusionGitCommandEngine.php', 637 640 'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionGitFileContentQuery.php', 638 641 'DiffusionGitLFSAuthenticateWorkflow' => 'applications/diffusion/gitlfs/DiffusionGitLFSAuthenticateWorkflow.php', 639 642 'DiffusionGitLFSResponse' => 'applications/diffusion/response/DiffusionGitLFSResponse.php', ··· 667 670 'DiffusionLowLevelQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelQuery.php', 668 671 'DiffusionLowLevelResolveRefsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php', 669 672 'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php', 673 + 'DiffusionMercurialCommandEngine' => 'applications/diffusion/protocol/DiffusionMercurialCommandEngine.php', 670 674 'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php', 671 675 'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php', 672 676 'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php', ··· 788 792 'DiffusionServeController' => 'applications/diffusion/controller/DiffusionServeController.php', 789 793 'DiffusionSetPasswordSettingsPanel' => 'applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php', 790 794 'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php', 795 + 'DiffusionSubversionCommandEngine' => 'applications/diffusion/protocol/DiffusionSubversionCommandEngine.php', 791 796 'DiffusionSubversionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionSSHWorkflow.php', 792 797 'DiffusionSubversionServeSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php', 793 798 'DiffusionSubversionWireProtocol' => 'applications/diffusion/protocol/DiffusionSubversionWireProtocol.php', ··· 4771 4776 'DiffusionCachedResolveRefsQuery' => 'DiffusionLowLevelQuery', 4772 4777 'DiffusionChangeController' => 'DiffusionController', 4773 4778 'DiffusionChangeHeraldFieldGroup' => 'HeraldFieldGroup', 4779 + 'DiffusionCommandEngine' => 'Phobject', 4780 + 'DiffusionCommandEngineTestCase' => 'PhabricatorTestCase', 4774 4781 'DiffusionCommitAffectedFilesHeraldField' => 'DiffusionCommitHeraldField', 4775 4782 'DiffusionCommitAuthorHeraldField' => 'DiffusionCommitHeraldField', 4776 4783 'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField', ··· 4835 4842 'DiffusionGitBlameQuery' => 'DiffusionBlameQuery', 4836 4843 'DiffusionGitBranch' => 'Phobject', 4837 4844 'DiffusionGitBranchTestCase' => 'PhabricatorTestCase', 4845 + 'DiffusionGitCommandEngine' => 'DiffusionCommandEngine', 4838 4846 'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery', 4839 4847 'DiffusionGitLFSAuthenticateWorkflow' => 'DiffusionGitSSHWorkflow', 4840 4848 'DiffusionGitLFSResponse' => 'AphrontResponse', ··· 4868 4876 'DiffusionLowLevelQuery' => 'Phobject', 4869 4877 'DiffusionLowLevelResolveRefsQuery' => 'DiffusionLowLevelQuery', 4870 4878 'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery', 4879 + 'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine', 4871 4880 'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery', 4872 4881 'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery', 4873 4882 'DiffusionMercurialRequest' => 'DiffusionRequest', ··· 4989 4998 'DiffusionServeController' => 'DiffusionController', 4990 4999 'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel', 4991 5000 'DiffusionSetupException' => 'Exception', 5001 + 'DiffusionSubversionCommandEngine' => 'DiffusionCommandEngine', 4992 5002 'DiffusionSubversionSSHWorkflow' => 'DiffusionSSHWorkflow', 4993 5003 'DiffusionSubversionServeSSHWorkflow' => 'DiffusionSubversionSSHWorkflow', 4994 5004 'DiffusionSubversionWireProtocol' => 'Phobject',
+2 -1
src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php
··· 131 131 hgsprintf('reverse(ancestors(%s))', $commit_hash), 132 132 $path_arg); 133 133 134 - $stdout = PhabricatorRepository::filterMercurialDebugOutput($stdout); 134 + $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( 135 + $stdout); 135 136 $lines = explode("\n", trim($stdout)); 136 137 $lines = array_slice($lines, $offset); 137 138
+173
src/applications/diffusion/protocol/DiffusionCommandEngine.php
··· 1 + <?php 2 + 3 + abstract class DiffusionCommandEngine extends Phobject { 4 + 5 + private $repository; 6 + private $protocol; 7 + private $credentialPHID; 8 + private $argv; 9 + private $passthru; 10 + 11 + public static function newCommandEngine(PhabricatorRepository $repository) { 12 + $engines = self::newCommandEngines(); 13 + 14 + foreach ($engines as $engine) { 15 + if ($engine->canBuildForRepository($repository)) { 16 + return id(clone $engine) 17 + ->setRepository($repository); 18 + } 19 + } 20 + 21 + throw new Exception( 22 + pht( 23 + 'No registered command engine can build commands for this '. 24 + 'repository ("%s").', 25 + $repository->getDisplayName())); 26 + } 27 + 28 + private static function newCommandEngines() { 29 + return id(new PhutilClassMapQuery()) 30 + ->setAncestorClass(__CLASS__) 31 + ->execute(); 32 + } 33 + 34 + abstract protected function canBuildForRepository( 35 + PhabricatorRepository $repository); 36 + 37 + abstract protected function newFormattedCommand($pattern, array $argv); 38 + abstract protected function newCustomEnvironment(); 39 + 40 + public function setRepository(PhabricatorRepository $repository) { 41 + $this->repository = $repository; 42 + return $this; 43 + } 44 + 45 + public function getRepository() { 46 + return $this->repository; 47 + } 48 + 49 + public function setProtocol($protocol) { 50 + $this->protocol = $protocol; 51 + return $this; 52 + } 53 + 54 + public function getProtocol() { 55 + return $this->protocol; 56 + } 57 + 58 + public function setCredentialPHID($credential_phid) { 59 + $this->credentialPHID = $credential_phid; 60 + return $this; 61 + } 62 + 63 + public function getCredentialPHID() { 64 + return $this->credentialPHID; 65 + } 66 + 67 + public function setArgv(array $argv) { 68 + $this->argv = $argv; 69 + return $this; 70 + } 71 + 72 + public function getArgv() { 73 + return $this->argv; 74 + } 75 + 76 + public function setPassthru($passthru) { 77 + $this->passthru = $passthru; 78 + return $this; 79 + } 80 + 81 + public function getPassthru() { 82 + return $this->passthru; 83 + } 84 + 85 + public function newFuture() { 86 + $argv = $this->newCommandArgv(); 87 + $env = $this->newCommandEnvironment(); 88 + 89 + if ($this->getPassthru()) { 90 + $future = newv('PhutilExecPassthru', $argv); 91 + } else { 92 + $future = newv('ExecFuture', $argv); 93 + } 94 + 95 + $future->setEnv($env); 96 + 97 + return $future; 98 + } 99 + 100 + private function newCommandArgv() { 101 + $argv = $this->argv; 102 + $pattern = $argv[0]; 103 + $argv = array_slice($argv, 1); 104 + 105 + list($pattern, $argv) = $this->newFormattedCommand($pattern, $argv); 106 + 107 + return array_merge(array($pattern), $argv); 108 + } 109 + 110 + private function newCommandEnvironment() { 111 + $env = $this->newCommonEnvironment() + $this->newCustomEnvironment(); 112 + foreach ($env as $key => $value) { 113 + if ($value === null) { 114 + unset($env[$key]); 115 + } 116 + } 117 + return $env; 118 + } 119 + 120 + private function newCommonEnvironment() { 121 + $env = array(); 122 + // NOTE: Force the language to "en_US.UTF-8", which overrides locale 123 + // settings. This makes stuff print in English instead of, e.g., French, 124 + // so we can parse the output of some commands, error messages, etc. 125 + $env['LANG'] = 'en_US.UTF-8'; 126 + 127 + // Propagate PHABRICATOR_ENV explicitly. For discussion, see T4155. 128 + $env['PHABRICATOR_ENV'] = PhabricatorEnv::getSelectedEnvironmentName(); 129 + 130 + if ($this->isAnySSHProtocol()) { 131 + $credential_phid = $this->getCredentialPHID(); 132 + if ($credential_phid) { 133 + $env['PHABRICATOR_CREDENTIAL'] = $credential_phid; 134 + } 135 + } 136 + 137 + return $env; 138 + } 139 + 140 + protected function isSSHProtocol() { 141 + return ($this->getProtocol() == 'ssh'); 142 + } 143 + 144 + protected function isSVNProtocol() { 145 + return ($this->getProtocol() == 'svn'); 146 + } 147 + 148 + protected function isSVNSSHProtocol() { 149 + return ($this->getProtocol() == 'svn+ssh'); 150 + } 151 + 152 + protected function isHTTPProtocol() { 153 + return ($this->getProtocol() == 'http'); 154 + } 155 + 156 + protected function isHTTPSProtocol() { 157 + return ($this->getProtocol() == 'https'); 158 + } 159 + 160 + protected function isAnyHTTPProtocol() { 161 + return ($this->isHTTPProtocol() || $this->isHTTPSProtocol()); 162 + } 163 + 164 + protected function isAnySSHProtocol() { 165 + return ($this->isSSHProtocol() || $this->isSVNSSHProtocol()); 166 + } 167 + 168 + protected function getSSHWrapper() { 169 + $root = dirname(phutil_get_library_root('phabricator')); 170 + return $root.'/bin/ssh-connect'; 171 + } 172 + 173 + }
+37
src/applications/diffusion/protocol/DiffusionGitCommandEngine.php
··· 1 + <?php 2 + 3 + final class DiffusionGitCommandEngine 4 + extends DiffusionCommandEngine { 5 + 6 + protected function canBuildForRepository( 7 + PhabricatorRepository $repository) { 8 + return $repository->isGit(); 9 + } 10 + 11 + protected function newFormattedCommand($pattern, array $argv) { 12 + $pattern = "git {$pattern}"; 13 + return array($pattern, $argv); 14 + } 15 + 16 + protected function newCustomEnvironment() { 17 + $env = array(); 18 + 19 + // NOTE: See T2965. Some time after Git 1.7.5.4, Git started fataling if 20 + // it can not read $HOME. For many users, $HOME points at /root (this 21 + // seems to be a default result of Apache setup). Instead, explicitly 22 + // point $HOME at a readable, empty directory so that Git looks for the 23 + // config file it's after, fails to locate it, and moves on. This is 24 + // really silly, but seems like the least damaging approach to 25 + // mitigating the issue. 26 + 27 + $root = dirname(phutil_get_library_root('phabricator')); 28 + $env['HOME'] = $root.'/support/empty/'; 29 + 30 + if ($this->isAnySSHProtocol()) { 31 + $env['GIT_SSH'] = $this->getSSHWrapper(); 32 + } 33 + 34 + return $env; 35 + } 36 + 37 + }
+70
src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php
··· 1 + <?php 2 + 3 + final class DiffusionMercurialCommandEngine 4 + extends DiffusionCommandEngine { 5 + 6 + protected function canBuildForRepository( 7 + PhabricatorRepository $repository) { 8 + return $repository->isHg(); 9 + } 10 + 11 + protected function newFormattedCommand($pattern, array $argv) { 12 + $args = array(); 13 + 14 + if ($this->isAnySSHProtocol()) { 15 + $pattern = "hg --config ui.ssh=%s {$pattern}"; 16 + $args[] = $this->getSSHWrapper(); 17 + } else { 18 + $pattern = "hg {$pattern}"; 19 + } 20 + 21 + return array($pattern, array_merge($args, $argv)); 22 + } 23 + 24 + protected function newCustomEnvironment() { 25 + $env = array(); 26 + 27 + // NOTE: This overrides certain configuration, extensions, and settings 28 + // which make Mercurial commands do random unusual things. 29 + $env['HGPLAIN'] = 1; 30 + 31 + return $env; 32 + } 33 + 34 + /** 35 + * Sanitize output of an `hg` command invoked with the `--debug` flag to make 36 + * it usable. 37 + * 38 + * @param string Output from `hg --debug ...` 39 + * @return string Usable output. 40 + */ 41 + public static function filterMercurialDebugOutput($stdout) { 42 + // When hg commands are run with `--debug` and some config file isn't 43 + // trusted, Mercurial prints out a warning to stdout, twice, after Feb 2011. 44 + // 45 + // http://selenic.com/pipermail/mercurial-devel/2011-February/028541.html 46 + // 47 + // After Jan 2015, it may also fail to write to a revision branch cache. 48 + 49 + $ignore = array( 50 + 'ignoring untrusted configuration option', 51 + "couldn't write revision branch cache:", 52 + ); 53 + 54 + foreach ($ignore as $key => $pattern) { 55 + $ignore[$key] = preg_quote($pattern, '/'); 56 + } 57 + 58 + $ignore = '('.implode('|', $ignore).')'; 59 + 60 + $lines = preg_split('/(?<=\n)/', $stdout); 61 + $regex = '/'.$ignore.'.*\n$/'; 62 + 63 + foreach ($lines as $key => $line) { 64 + $lines[$key] = preg_replace($regex, '', $line); 65 + } 66 + 67 + return implode('', $lines); 68 + } 69 + 70 + }
+54
src/applications/diffusion/protocol/DiffusionSubversionCommandEngine.php
··· 1 + <?php 2 + 3 + final class DiffusionSubversionCommandEngine 4 + extends DiffusionCommandEngine { 5 + 6 + protected function canBuildForRepository( 7 + PhabricatorRepository $repository) { 8 + return $repository->isSVN(); 9 + } 10 + 11 + protected function newFormattedCommand($pattern, array $argv) { 12 + $flags = array(); 13 + $args = array(); 14 + 15 + $flags[] = '--non-interactive'; 16 + 17 + if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) { 18 + $flags[] = '--no-auth-cache'; 19 + 20 + if ($this->isAnyHTTPProtocol()) { 21 + $flags[] = '--trust-server-cert'; 22 + } 23 + 24 + $credential_phid = $this->getCredentialPHID(); 25 + if ($credential_phid) { 26 + $key = PassphrasePasswordKey::loadFromPHID( 27 + $credential_phid, 28 + PhabricatorUser::getOmnipotentUser()); 29 + 30 + $flags[] = '--username %P'; 31 + $args[] = $key->getUsernameEnvelope(); 32 + 33 + $flags[] = '--password %P'; 34 + $args[] = $key->getPasswordEnvelope(); 35 + } 36 + } 37 + 38 + $flags = implode(' ', $flags); 39 + $pattern = "svn {$flags} {$pattern}"; 40 + 41 + return array($pattern, array_merge($args, $argv)); 42 + } 43 + 44 + protected function newCustomEnvironment() { 45 + $env = array(); 46 + 47 + if ($this->isAnySSHProtocol()) { 48 + $env['SVN_SSH'] = $this->getSSHWrapper(); 49 + } 50 + 51 + return $env; 52 + } 53 + 54 + }
+155
src/applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php
··· 1 + <?php 2 + 3 + final class DiffusionCommandEngineTestCase extends PhabricatorTestCase { 4 + 5 + public function testCommandEngine() { 6 + $type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT; 7 + $type_hg = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL; 8 + $type_svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN; 9 + 10 + $root = dirname(phutil_get_library_root('phabricator')); 11 + $ssh_wrapper = $root.'/bin/ssh-connect'; 12 + $home = $root.'/support/empty/'; 13 + 14 + 15 + // Plain commands. 16 + 17 + $this->assertCommandEngineFormat( 18 + 'git xyz', 19 + array( 20 + 'LANG' => 'en_US.UTF-8', 21 + 'HOME' => $home, 22 + ), 23 + array( 24 + 'vcs' => $type_git, 25 + 'argv' => 'xyz', 26 + )); 27 + 28 + $this->assertCommandEngineFormat( 29 + 'hg xyz', 30 + array( 31 + 'LANG' => 'en_US.UTF-8', 32 + 'HGPLAIN' => '1', 33 + ), 34 + array( 35 + 'vcs' => $type_hg, 36 + 'argv' => 'xyz', 37 + )); 38 + 39 + $this->assertCommandEngineFormat( 40 + 'svn --non-interactive xyz', 41 + array( 42 + 'LANG' => 'en_US.UTF-8', 43 + ), 44 + array( 45 + 'vcs' => $type_svn, 46 + 'argv' => 'xyz', 47 + )); 48 + 49 + 50 + // Commands with SSH. 51 + 52 + $this->assertCommandEngineFormat( 53 + 'git xyz', 54 + array( 55 + 'LANG' => 'en_US.UTF-8', 56 + 'HOME' => $home, 57 + 'GIT_SSH' => $ssh_wrapper, 58 + ), 59 + array( 60 + 'vcs' => $type_git, 61 + 'argv' => 'xyz', 62 + 'protocol' => 'ssh', 63 + )); 64 + 65 + $this->assertCommandEngineFormat( 66 + (string)csprintf('hg --config ui.ssh=%s xyz', $ssh_wrapper), 67 + array( 68 + 'LANG' => 'en_US.UTF-8', 69 + 'HGPLAIN' => '1', 70 + ), 71 + array( 72 + 'vcs' => $type_hg, 73 + 'argv' => 'xyz', 74 + 'protocol' => 'ssh', 75 + )); 76 + 77 + $this->assertCommandEngineFormat( 78 + 'svn --non-interactive xyz', 79 + array( 80 + 'LANG' => 'en_US.UTF-8', 81 + 'SVN_SSH' => $ssh_wrapper, 82 + ), 83 + array( 84 + 'vcs' => $type_svn, 85 + 'argv' => 'xyz', 86 + 'protocol' => 'ssh', 87 + )); 88 + 89 + 90 + // Commands with HTTP. 91 + 92 + $this->assertCommandEngineFormat( 93 + 'git xyz', 94 + array( 95 + 'LANG' => 'en_US.UTF-8', 96 + 'HOME' => $home, 97 + ), 98 + array( 99 + 'vcs' => $type_git, 100 + 'argv' => 'xyz', 101 + 'protocol' => 'https', 102 + )); 103 + 104 + $this->assertCommandEngineFormat( 105 + 'hg xyz', 106 + array( 107 + 'LANG' => 'en_US.UTF-8', 108 + 'HGPLAIN' => '1', 109 + ), 110 + array( 111 + 'vcs' => $type_hg, 112 + 'argv' => 'xyz', 113 + 'protocol' => 'https', 114 + )); 115 + 116 + $this->assertCommandEngineFormat( 117 + 'svn --non-interactive --no-auth-cache --trust-server-cert xyz', 118 + array( 119 + 'LANG' => 'en_US.UTF-8', 120 + ), 121 + array( 122 + 'vcs' => $type_svn, 123 + 'argv' => 'xyz', 124 + 'protocol' => 'https', 125 + )); 126 + } 127 + 128 + private function assertCommandEngineFormat( 129 + $command, 130 + array $env, 131 + array $inputs) { 132 + 133 + $repository = id(new PhabricatorRepository()) 134 + ->setVersionControlSystem($inputs['vcs']); 135 + 136 + $future = DiffusionCommandEngine::newCommandEngine($repository) 137 + ->setArgv((array)$inputs['argv']) 138 + ->setProtocol(idx($inputs, 'protocol')) 139 + ->newFuture(); 140 + 141 + $command_string = $future->getCommand(); 142 + 143 + $actual_command = $command_string->getUnmaskedString(); 144 + $this->assertEqual($command, $actual_command); 145 + 146 + $actual_environment = $future->getEnv(); 147 + 148 + $compare_environment = array_select_keys( 149 + $actual_environment, 150 + array_keys($env)); 151 + 152 + $this->assertEqual($env, $compare_environment); 153 + } 154 + 155 + }
+3 -1
src/applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php
··· 50 50 list($stdout) = $repository->execxLocalCommand( 51 51 'log --debug --limit 1 --template={parents} --rev %s', 52 52 $this->identifier); 53 - $stdout = PhabricatorRepository::filterMercurialDebugOutput($stdout); 53 + 54 + $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( 55 + $stdout); 54 56 55 57 $hashes = preg_split('/\s+/', trim($stdout)); 56 58 foreach ($hashes as $key => $value) {
+18 -207
src/applications/repository/storage/PhabricatorRepository.php
··· 487 487 } 488 488 489 489 private function newRemoteCommandFuture(array $argv) { 490 - $argv = $this->formatRemoteCommand($argv); 491 - $future = newv('ExecFuture', $argv); 492 - $future->setEnv($this->getRemoteCommandEnvironment()); 493 - return $future; 490 + return $this->newRemoteCommandEngine($argv) 491 + ->newFuture(); 494 492 } 495 493 496 494 private function newRemoteCommandPassthru(array $argv) { 497 - $argv = $this->formatRemoteCommand($argv); 498 - $passthru = newv('PhutilExecPassthru', $argv); 499 - $passthru->setEnv($this->getRemoteCommandEnvironment()); 500 - return $passthru; 495 + return $this->newRemoteCommandEngine($argv) 496 + ->setPassthru(true) 497 + ->newFuture(); 501 498 } 502 499 500 + private function newRemoteCommandEngine(array $argv) { 501 + return DiffusionCommandEngine::newCommandEngine($this) 502 + ->setArgv($argv) 503 + ->setCredentialPHID($this->getCredentialPHID()) 504 + ->setProtocol($this->getRemoteProtocol()); 505 + } 503 506 504 507 /* -( Local Command Execution )-------------------------------------------- */ 505 508 ··· 527 530 private function newLocalCommandFuture(array $argv) { 528 531 $this->assertLocalExists(); 529 532 530 - $argv = $this->formatLocalCommand($argv); 531 - $future = newv('ExecFuture', $argv); 532 - $future->setEnv($this->getLocalCommandEnvironment()); 533 + $future = DiffusionCommandEngine::newCommandEngine($this) 534 + ->setArgv($argv) 535 + ->newFuture(); 533 536 534 537 if ($this->usesLocalWorkingCopy()) { 535 538 $future->setCWD($this->getLocalPath()); ··· 541 544 private function newLocalCommandPassthru(array $argv) { 542 545 $this->assertLocalExists(); 543 546 544 - $argv = $this->formatLocalCommand($argv); 545 - $future = newv('PhutilExecPassthru', $argv); 546 - $future->setEnv($this->getLocalCommandEnvironment()); 547 + $future = DiffusionCommandEngine::newCommandEngine($this) 548 + ->setArgv($argv) 549 + ->setPassthru(true) 550 + ->newFuture(); 547 551 548 552 if ($this->usesLocalWorkingCopy()) { 549 553 $future->setCWD($this->getLocalPath()); 550 554 } 551 555 552 556 return $future; 553 - } 554 - 555 - 556 - /* -( Command Infrastructure )--------------------------------------------- */ 557 - 558 - 559 - private function getSSHWrapper() { 560 - $root = dirname(phutil_get_library_root('phabricator')); 561 - return $root.'/bin/ssh-connect'; 562 - } 563 - 564 - private function getCommonCommandEnvironment() { 565 - $env = array( 566 - // NOTE: Force the language to "en_US.UTF-8", which overrides locale 567 - // settings. This makes stuff print in English instead of, e.g., French, 568 - // so we can parse the output of some commands, error messages, etc. 569 - 'LANG' => 'en_US.UTF-8', 570 - 571 - // Propagate PHABRICATOR_ENV explicitly. For discussion, see T4155. 572 - 'PHABRICATOR_ENV' => PhabricatorEnv::getSelectedEnvironmentName(), 573 - ); 574 - 575 - switch ($this->getVersionControlSystem()) { 576 - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 577 - break; 578 - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 579 - // NOTE: See T2965. Some time after Git 1.7.5.4, Git started fataling if 580 - // it can not read $HOME. For many users, $HOME points at /root (this 581 - // seems to be a default result of Apache setup). Instead, explicitly 582 - // point $HOME at a readable, empty directory so that Git looks for the 583 - // config file it's after, fails to locate it, and moves on. This is 584 - // really silly, but seems like the least damaging approach to 585 - // mitigating the issue. 586 - 587 - $root = dirname(phutil_get_library_root('phabricator')); 588 - $env['HOME'] = $root.'/support/empty/'; 589 - break; 590 - case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 591 - // NOTE: This overrides certain configuration, extensions, and settings 592 - // which make Mercurial commands do random unusual things. 593 - $env['HGPLAIN'] = 1; 594 - break; 595 - default: 596 - throw new Exception(pht('Unrecognized version control system.')); 597 - } 598 - 599 - return $env; 600 - } 601 - 602 - private function getLocalCommandEnvironment() { 603 - return $this->getCommonCommandEnvironment(); 604 - } 605 - 606 - private function getRemoteCommandEnvironment() { 607 - $env = $this->getCommonCommandEnvironment(); 608 - 609 - if ($this->shouldUseSSH()) { 610 - // NOTE: This is read by `bin/ssh-connect`, and tells it which credentials 611 - // to use. 612 - $env['PHABRICATOR_CREDENTIAL'] = $this->getCredentialPHID(); 613 - switch ($this->getVersionControlSystem()) { 614 - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 615 - // Force SVN to use `bin/ssh-connect`. 616 - $env['SVN_SSH'] = $this->getSSHWrapper(); 617 - break; 618 - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 619 - // Force Git to use `bin/ssh-connect`. 620 - $env['GIT_SSH'] = $this->getSSHWrapper(); 621 - break; 622 - case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 623 - // We force Mercurial through `bin/ssh-connect` too, but it uses a 624 - // command-line flag instead of an environmental variable. 625 - break; 626 - default: 627 - throw new Exception(pht('Unrecognized version control system.')); 628 - } 629 - } 630 - 631 - return $env; 632 - } 633 - 634 - private function formatRemoteCommand(array $args) { 635 - $pattern = $args[0]; 636 - $args = array_slice($args, 1); 637 - 638 - switch ($this->getVersionControlSystem()) { 639 - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 640 - if ($this->shouldUseHTTP() || $this->shouldUseSVNProtocol()) { 641 - $flags = array(); 642 - $flag_args = array(); 643 - $flags[] = '--non-interactive'; 644 - $flags[] = '--no-auth-cache'; 645 - if ($this->shouldUseHTTP()) { 646 - $flags[] = '--trust-server-cert'; 647 - } 648 - 649 - $credential_phid = $this->getCredentialPHID(); 650 - if ($credential_phid) { 651 - $key = PassphrasePasswordKey::loadFromPHID( 652 - $credential_phid, 653 - PhabricatorUser::getOmnipotentUser()); 654 - $flags[] = '--username %P'; 655 - $flags[] = '--password %P'; 656 - $flag_args[] = $key->getUsernameEnvelope(); 657 - $flag_args[] = $key->getPasswordEnvelope(); 658 - } 659 - 660 - $flags = implode(' ', $flags); 661 - $pattern = "svn {$flags} {$pattern}"; 662 - $args = array_mergev(array($flag_args, $args)); 663 - } else { 664 - $pattern = "svn --non-interactive {$pattern}"; 665 - } 666 - break; 667 - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 668 - $pattern = "git {$pattern}"; 669 - break; 670 - case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 671 - if ($this->shouldUseSSH()) { 672 - $pattern = "hg --config ui.ssh=%s {$pattern}"; 673 - array_unshift( 674 - $args, 675 - $this->getSSHWrapper()); 676 - } else { 677 - $pattern = "hg {$pattern}"; 678 - } 679 - break; 680 - default: 681 - throw new Exception(pht('Unrecognized version control system.')); 682 - } 683 - 684 - array_unshift($args, $pattern); 685 - 686 - return $args; 687 - } 688 - 689 - private function formatLocalCommand(array $args) { 690 - $pattern = $args[0]; 691 - $args = array_slice($args, 1); 692 - 693 - switch ($this->getVersionControlSystem()) { 694 - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 695 - $pattern = "svn --non-interactive {$pattern}"; 696 - break; 697 - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 698 - $pattern = "git {$pattern}"; 699 - break; 700 - case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 701 - $pattern = "hg {$pattern}"; 702 - break; 703 - default: 704 - throw new Exception(pht('Unrecognized version control system.')); 705 - } 706 - 707 - array_unshift($args, $pattern); 708 - 709 - return $args; 710 - } 711 - 712 - /** 713 - * Sanitize output of an `hg` command invoked with the `--debug` flag to make 714 - * it usable. 715 - * 716 - * @param string Output from `hg --debug ...` 717 - * @return string Usable output. 718 - */ 719 - public static function filterMercurialDebugOutput($stdout) { 720 - // When hg commands are run with `--debug` and some config file isn't 721 - // trusted, Mercurial prints out a warning to stdout, twice, after Feb 2011. 722 - // 723 - // http://selenic.com/pipermail/mercurial-devel/2011-February/028541.html 724 - // 725 - // After Jan 2015, it may also fail to write to a revision branch cache. 726 - 727 - $ignore = array( 728 - 'ignoring untrusted configuration option', 729 - "couldn't write revision branch cache:", 730 - ); 731 - 732 - foreach ($ignore as $key => $pattern) { 733 - $ignore[$key] = preg_quote($pattern, '/'); 734 - } 735 - 736 - $ignore = '('.implode('|', $ignore).')'; 737 - 738 - $lines = preg_split('/(?<=\n)/', $stdout); 739 - $regex = '/'.$ignore.'.*\n$/'; 740 - 741 - foreach ($lines as $key => $line) { 742 - $lines[$key] = preg_replace($regex, '', $line); 743 - } 744 - 745 - return implode('', $lines); 746 557 } 747 558 748 559 public function getURI() {
+2 -1
src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php
··· 147 147 ); 148 148 149 149 foreach ($map as $input => $expect) { 150 - $actual = PhabricatorRepository::filterMercurialDebugOutput($input); 150 + $actual = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( 151 + $input); 151 152 $this->assertEqual($expect, $actual, $input); 152 153 } 153 154 }