diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -570,6 +570,8 @@ 'DiffusionCachedResolveRefsQuery' => 'applications/diffusion/query/DiffusionCachedResolveRefsQuery.php', 'DiffusionChangeController' => 'applications/diffusion/controller/DiffusionChangeController.php', 'DiffusionChangeHeraldFieldGroup' => 'applications/diffusion/herald/DiffusionChangeHeraldFieldGroup.php', + 'DiffusionCommandEngine' => 'applications/diffusion/protocol/DiffusionCommandEngine.php', + 'DiffusionCommandEngineTestCase' => 'applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php', 'DiffusionCommitAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionCommitAffectedFilesHeraldField.php', 'DiffusionCommitAuthorHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorHeraldField.php', 'DiffusionCommitAutocloseHeraldField' => 'applications/diffusion/herald/DiffusionCommitAutocloseHeraldField.php', @@ -634,6 +636,7 @@ 'DiffusionGitBlameQuery' => 'applications/diffusion/query/blame/DiffusionGitBlameQuery.php', 'DiffusionGitBranch' => 'applications/diffusion/data/DiffusionGitBranch.php', 'DiffusionGitBranchTestCase' => 'applications/diffusion/data/__tests__/DiffusionGitBranchTestCase.php', + 'DiffusionGitCommandEngine' => 'applications/diffusion/protocol/DiffusionGitCommandEngine.php', 'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionGitFileContentQuery.php', 'DiffusionGitLFSAuthenticateWorkflow' => 'applications/diffusion/gitlfs/DiffusionGitLFSAuthenticateWorkflow.php', 'DiffusionGitLFSResponse' => 'applications/diffusion/response/DiffusionGitLFSResponse.php', @@ -667,6 +670,7 @@ 'DiffusionLowLevelQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelQuery.php', 'DiffusionLowLevelResolveRefsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php', 'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php', + 'DiffusionMercurialCommandEngine' => 'applications/diffusion/protocol/DiffusionMercurialCommandEngine.php', 'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php', 'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php', 'DiffusionMercurialRequest' => 'applications/diffusion/request/DiffusionMercurialRequest.php', @@ -788,6 +792,7 @@ 'DiffusionServeController' => 'applications/diffusion/controller/DiffusionServeController.php', 'DiffusionSetPasswordSettingsPanel' => 'applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php', 'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php', + 'DiffusionSubversionCommandEngine' => 'applications/diffusion/protocol/DiffusionSubversionCommandEngine.php', 'DiffusionSubversionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionSSHWorkflow.php', 'DiffusionSubversionServeSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php', 'DiffusionSubversionWireProtocol' => 'applications/diffusion/protocol/DiffusionSubversionWireProtocol.php', @@ -4771,6 +4776,8 @@ 'DiffusionCachedResolveRefsQuery' => 'DiffusionLowLevelQuery', 'DiffusionChangeController' => 'DiffusionController', 'DiffusionChangeHeraldFieldGroup' => 'HeraldFieldGroup', + 'DiffusionCommandEngine' => 'Phobject', + 'DiffusionCommandEngineTestCase' => 'PhabricatorTestCase', 'DiffusionCommitAffectedFilesHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitAuthorHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField', @@ -4835,6 +4842,7 @@ 'DiffusionGitBlameQuery' => 'DiffusionBlameQuery', 'DiffusionGitBranch' => 'Phobject', 'DiffusionGitBranchTestCase' => 'PhabricatorTestCase', + 'DiffusionGitCommandEngine' => 'DiffusionCommandEngine', 'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery', 'DiffusionGitLFSAuthenticateWorkflow' => 'DiffusionGitSSHWorkflow', 'DiffusionGitLFSResponse' => 'AphrontResponse', @@ -4868,6 +4876,7 @@ 'DiffusionLowLevelQuery' => 'Phobject', 'DiffusionLowLevelResolveRefsQuery' => 'DiffusionLowLevelQuery', 'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery', + 'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine', 'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery', 'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery', 'DiffusionMercurialRequest' => 'DiffusionRequest', @@ -4989,6 +4998,7 @@ 'DiffusionServeController' => 'DiffusionController', 'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel', 'DiffusionSetupException' => 'Exception', + 'DiffusionSubversionCommandEngine' => 'DiffusionCommandEngine', 'DiffusionSubversionSSHWorkflow' => 'DiffusionSSHWorkflow', 'DiffusionSubversionServeSSHWorkflow' => 'DiffusionSubversionSSHWorkflow', 'DiffusionSubversionWireProtocol' => 'Phobject', diff --git a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php --- a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php @@ -131,7 +131,8 @@ hgsprintf('reverse(ancestors(%s))', $commit_hash), $path_arg); - $stdout = PhabricatorRepository::filterMercurialDebugOutput($stdout); + $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( + $stdout); $lines = explode("\n", trim($stdout)); $lines = array_slice($lines, $offset); diff --git a/src/applications/diffusion/protocol/DiffusionCommandEngine.php b/src/applications/diffusion/protocol/DiffusionCommandEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/diffusion/protocol/DiffusionCommandEngine.php @@ -0,0 +1,173 @@ +canBuildForRepository($repository)) { + return id(clone $engine) + ->setRepository($repository); + } + } + + throw new Exception( + pht( + 'No registered command engine can build commands for this '. + 'repository ("%s").', + $repository->getDisplayName())); + } + + private static function newCommandEngines() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->execute(); + } + + abstract protected function canBuildForRepository( + PhabricatorRepository $repository); + + abstract protected function newFormattedCommand($pattern, array $argv); + abstract protected function newCustomEnvironment(); + + public function setRepository(PhabricatorRepository $repository) { + $this->repository = $repository; + return $this; + } + + public function getRepository() { + return $this->repository; + } + + public function setProtocol($protocol) { + $this->protocol = $protocol; + return $this; + } + + public function getProtocol() { + return $this->protocol; + } + + public function setCredentialPHID($credential_phid) { + $this->credentialPHID = $credential_phid; + return $this; + } + + public function getCredentialPHID() { + return $this->credentialPHID; + } + + public function setArgv(array $argv) { + $this->argv = $argv; + return $this; + } + + public function getArgv() { + return $this->argv; + } + + public function setPassthru($passthru) { + $this->passthru = $passthru; + return $this; + } + + public function getPassthru() { + return $this->passthru; + } + + public function newFuture() { + $argv = $this->newCommandArgv(); + $env = $this->newCommandEnvironment(); + + if ($this->getPassthru()) { + $future = newv('PhutilExecPassthru', $argv); + } else { + $future = newv('ExecFuture', $argv); + } + + $future->setEnv($env); + + return $future; + } + + private function newCommandArgv() { + $argv = $this->argv; + $pattern = $argv[0]; + $argv = array_slice($argv, 1); + + list($pattern, $argv) = $this->newFormattedCommand($pattern, $argv); + + return array_merge(array($pattern), $argv); + } + + private function newCommandEnvironment() { + $env = $this->newCommonEnvironment() + $this->newCustomEnvironment(); + foreach ($env as $key => $value) { + if ($value === null) { + unset($env[$key]); + } + } + return $env; + } + + private function newCommonEnvironment() { + $env = array(); + // NOTE: Force the language to "en_US.UTF-8", which overrides locale + // settings. This makes stuff print in English instead of, e.g., French, + // so we can parse the output of some commands, error messages, etc. + $env['LANG'] = 'en_US.UTF-8'; + + // Propagate PHABRICATOR_ENV explicitly. For discussion, see T4155. + $env['PHABRICATOR_ENV'] = PhabricatorEnv::getSelectedEnvironmentName(); + + if ($this->isAnySSHProtocol()) { + $credential_phid = $this->getCredentialPHID(); + if ($credential_phid) { + $env['PHABRICATOR_CREDENTIAL'] = $credential_phid; + } + } + + return $env; + } + + protected function isSSHProtocol() { + return ($this->getProtocol() == 'ssh'); + } + + protected function isSVNProtocol() { + return ($this->getProtocol() == 'svn'); + } + + protected function isSVNSSHProtocol() { + return ($this->getProtocol() == 'svn+ssh'); + } + + protected function isHTTPProtocol() { + return ($this->getProtocol() == 'http'); + } + + protected function isHTTPSProtocol() { + return ($this->getProtocol() == 'https'); + } + + protected function isAnyHTTPProtocol() { + return ($this->isHTTPProtocol() || $this->isHTTPSProtocol()); + } + + protected function isAnySSHProtocol() { + return ($this->isSSHProtocol() || $this->isSVNSSHProtocol()); + } + + protected function getSSHWrapper() { + $root = dirname(phutil_get_library_root('phabricator')); + return $root.'/bin/ssh-connect'; + } + +} diff --git a/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php b/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php @@ -0,0 +1,37 @@ +isGit(); + } + + protected function newFormattedCommand($pattern, array $argv) { + $pattern = "git {$pattern}"; + return array($pattern, $argv); + } + + protected function newCustomEnvironment() { + $env = array(); + + // NOTE: See T2965. Some time after Git 1.7.5.4, Git started fataling if + // it can not read $HOME. For many users, $HOME points at /root (this + // seems to be a default result of Apache setup). Instead, explicitly + // point $HOME at a readable, empty directory so that Git looks for the + // config file it's after, fails to locate it, and moves on. This is + // really silly, but seems like the least damaging approach to + // mitigating the issue. + + $root = dirname(phutil_get_library_root('phabricator')); + $env['HOME'] = $root.'/support/empty/'; + + if ($this->isAnySSHProtocol()) { + $env['GIT_SSH'] = $this->getSSHWrapper(); + } + + return $env; + } + +} diff --git a/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php b/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php @@ -0,0 +1,70 @@ +isHg(); + } + + protected function newFormattedCommand($pattern, array $argv) { + $args = array(); + + if ($this->isAnySSHProtocol()) { + $pattern = "hg --config ui.ssh=%s {$pattern}"; + $args[] = $this->getSSHWrapper(); + } else { + $pattern = "hg {$pattern}"; + } + + return array($pattern, array_merge($args, $argv)); + } + + protected function newCustomEnvironment() { + $env = array(); + + // NOTE: This overrides certain configuration, extensions, and settings + // which make Mercurial commands do random unusual things. + $env['HGPLAIN'] = 1; + + return $env; + } + + /** + * Sanitize output of an `hg` command invoked with the `--debug` flag to make + * it usable. + * + * @param string Output from `hg --debug ...` + * @return string Usable output. + */ + public static function filterMercurialDebugOutput($stdout) { + // When hg commands are run with `--debug` and some config file isn't + // trusted, Mercurial prints out a warning to stdout, twice, after Feb 2011. + // + // http://selenic.com/pipermail/mercurial-devel/2011-February/028541.html + // + // After Jan 2015, it may also fail to write to a revision branch cache. + + $ignore = array( + 'ignoring untrusted configuration option', + "couldn't write revision branch cache:", + ); + + foreach ($ignore as $key => $pattern) { + $ignore[$key] = preg_quote($pattern, '/'); + } + + $ignore = '('.implode('|', $ignore).')'; + + $lines = preg_split('/(?<=\n)/', $stdout); + $regex = '/'.$ignore.'.*\n$/'; + + foreach ($lines as $key => $line) { + $lines[$key] = preg_replace($regex, '', $line); + } + + return implode('', $lines); + } + +} diff --git a/src/applications/diffusion/protocol/DiffusionSubversionCommandEngine.php b/src/applications/diffusion/protocol/DiffusionSubversionCommandEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/diffusion/protocol/DiffusionSubversionCommandEngine.php @@ -0,0 +1,54 @@ +isSVN(); + } + + protected function newFormattedCommand($pattern, array $argv) { + $flags = array(); + $args = array(); + + $flags[] = '--non-interactive'; + + if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) { + $flags[] = '--no-auth-cache'; + + if ($this->isAnyHTTPProtocol()) { + $flags[] = '--trust-server-cert'; + } + + $credential_phid = $this->getCredentialPHID(); + if ($credential_phid) { + $key = PassphrasePasswordKey::loadFromPHID( + $credential_phid, + PhabricatorUser::getOmnipotentUser()); + + $flags[] = '--username %P'; + $args[] = $key->getUsernameEnvelope(); + + $flags[] = '--password %P'; + $args[] = $key->getPasswordEnvelope(); + } + } + + $flags = implode(' ', $flags); + $pattern = "svn {$flags} {$pattern}"; + + return array($pattern, array_merge($args, $argv)); + } + + protected function newCustomEnvironment() { + $env = array(); + + if ($this->isAnySSHProtocol()) { + $env['SVN_SSH'] = $this->getSSHWrapper(); + } + + return $env; + } + +} diff --git a/src/applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php b/src/applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php new file mode 100644 --- /dev/null +++ b/src/applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php @@ -0,0 +1,155 @@ +assertCommandEngineFormat( + 'git xyz', + array( + 'LANG' => 'en_US.UTF-8', + 'HOME' => $home, + ), + array( + 'vcs' => $type_git, + 'argv' => 'xyz', + )); + + $this->assertCommandEngineFormat( + 'hg xyz', + array( + 'LANG' => 'en_US.UTF-8', + 'HGPLAIN' => '1', + ), + array( + 'vcs' => $type_hg, + 'argv' => 'xyz', + )); + + $this->assertCommandEngineFormat( + 'svn --non-interactive xyz', + array( + 'LANG' => 'en_US.UTF-8', + ), + array( + 'vcs' => $type_svn, + 'argv' => 'xyz', + )); + + + // Commands with SSH. + + $this->assertCommandEngineFormat( + 'git xyz', + array( + 'LANG' => 'en_US.UTF-8', + 'HOME' => $home, + 'GIT_SSH' => $ssh_wrapper, + ), + array( + 'vcs' => $type_git, + 'argv' => 'xyz', + 'protocol' => 'ssh', + )); + + $this->assertCommandEngineFormat( + (string)csprintf('hg --config ui.ssh=%s xyz', $ssh_wrapper), + array( + 'LANG' => 'en_US.UTF-8', + 'HGPLAIN' => '1', + ), + array( + 'vcs' => $type_hg, + 'argv' => 'xyz', + 'protocol' => 'ssh', + )); + + $this->assertCommandEngineFormat( + 'svn --non-interactive xyz', + array( + 'LANG' => 'en_US.UTF-8', + 'SVN_SSH' => $ssh_wrapper, + ), + array( + 'vcs' => $type_svn, + 'argv' => 'xyz', + 'protocol' => 'ssh', + )); + + + // Commands with HTTP. + + $this->assertCommandEngineFormat( + 'git xyz', + array( + 'LANG' => 'en_US.UTF-8', + 'HOME' => $home, + ), + array( + 'vcs' => $type_git, + 'argv' => 'xyz', + 'protocol' => 'https', + )); + + $this->assertCommandEngineFormat( + 'hg xyz', + array( + 'LANG' => 'en_US.UTF-8', + 'HGPLAIN' => '1', + ), + array( + 'vcs' => $type_hg, + 'argv' => 'xyz', + 'protocol' => 'https', + )); + + $this->assertCommandEngineFormat( + 'svn --non-interactive --no-auth-cache --trust-server-cert xyz', + array( + 'LANG' => 'en_US.UTF-8', + ), + array( + 'vcs' => $type_svn, + 'argv' => 'xyz', + 'protocol' => 'https', + )); + } + + private function assertCommandEngineFormat( + $command, + array $env, + array $inputs) { + + $repository = id(new PhabricatorRepository()) + ->setVersionControlSystem($inputs['vcs']); + + $future = DiffusionCommandEngine::newCommandEngine($repository) + ->setArgv((array)$inputs['argv']) + ->setProtocol(idx($inputs, 'protocol')) + ->newFuture(); + + $command_string = $future->getCommand(); + + $actual_command = $command_string->getUnmaskedString(); + $this->assertEqual($command, $actual_command); + + $actual_environment = $future->getEnv(); + + $compare_environment = array_select_keys( + $actual_environment, + array_keys($env)); + + $this->assertEqual($env, $compare_environment); + } + +} diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php --- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php @@ -50,7 +50,9 @@ list($stdout) = $repository->execxLocalCommand( 'log --debug --limit 1 --template={parents} --rev %s', $this->identifier); - $stdout = PhabricatorRepository::filterMercurialDebugOutput($stdout); + + $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( + $stdout); $hashes = preg_split('/\s+/', trim($stdout)); foreach ($hashes as $key => $value) { diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -487,19 +487,22 @@ } private function newRemoteCommandFuture(array $argv) { - $argv = $this->formatRemoteCommand($argv); - $future = newv('ExecFuture', $argv); - $future->setEnv($this->getRemoteCommandEnvironment()); - return $future; + return $this->newRemoteCommandEngine($argv) + ->newFuture(); } private function newRemoteCommandPassthru(array $argv) { - $argv = $this->formatRemoteCommand($argv); - $passthru = newv('PhutilExecPassthru', $argv); - $passthru->setEnv($this->getRemoteCommandEnvironment()); - return $passthru; + return $this->newRemoteCommandEngine($argv) + ->setPassthru(true) + ->newFuture(); } + private function newRemoteCommandEngine(array $argv) { + return DiffusionCommandEngine::newCommandEngine($this) + ->setArgv($argv) + ->setCredentialPHID($this->getCredentialPHID()) + ->setProtocol($this->getRemoteProtocol()); + } /* -( Local Command Execution )-------------------------------------------- */ @@ -527,9 +530,9 @@ private function newLocalCommandFuture(array $argv) { $this->assertLocalExists(); - $argv = $this->formatLocalCommand($argv); - $future = newv('ExecFuture', $argv); - $future->setEnv($this->getLocalCommandEnvironment()); + $future = DiffusionCommandEngine::newCommandEngine($this) + ->setArgv($argv) + ->newFuture(); if ($this->usesLocalWorkingCopy()) { $future->setCWD($this->getLocalPath()); @@ -541,9 +544,10 @@ private function newLocalCommandPassthru(array $argv) { $this->assertLocalExists(); - $argv = $this->formatLocalCommand($argv); - $future = newv('PhutilExecPassthru', $argv); - $future->setEnv($this->getLocalCommandEnvironment()); + $future = DiffusionCommandEngine::newCommandEngine($this) + ->setArgv($argv) + ->setPassthru(true) + ->newFuture(); if ($this->usesLocalWorkingCopy()) { $future->setCWD($this->getLocalPath()); @@ -552,199 +556,6 @@ return $future; } - -/* -( Command Infrastructure )--------------------------------------------- */ - - - private function getSSHWrapper() { - $root = dirname(phutil_get_library_root('phabricator')); - return $root.'/bin/ssh-connect'; - } - - private function getCommonCommandEnvironment() { - $env = array( - // NOTE: Force the language to "en_US.UTF-8", which overrides locale - // settings. This makes stuff print in English instead of, e.g., French, - // so we can parse the output of some commands, error messages, etc. - 'LANG' => 'en_US.UTF-8', - - // Propagate PHABRICATOR_ENV explicitly. For discussion, see T4155. - 'PHABRICATOR_ENV' => PhabricatorEnv::getSelectedEnvironmentName(), - ); - - switch ($this->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - break; - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - // NOTE: See T2965. Some time after Git 1.7.5.4, Git started fataling if - // it can not read $HOME. For many users, $HOME points at /root (this - // seems to be a default result of Apache setup). Instead, explicitly - // point $HOME at a readable, empty directory so that Git looks for the - // config file it's after, fails to locate it, and moves on. This is - // really silly, but seems like the least damaging approach to - // mitigating the issue. - - $root = dirname(phutil_get_library_root('phabricator')); - $env['HOME'] = $root.'/support/empty/'; - break; - case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: - // NOTE: This overrides certain configuration, extensions, and settings - // which make Mercurial commands do random unusual things. - $env['HGPLAIN'] = 1; - break; - default: - throw new Exception(pht('Unrecognized version control system.')); - } - - return $env; - } - - private function getLocalCommandEnvironment() { - return $this->getCommonCommandEnvironment(); - } - - private function getRemoteCommandEnvironment() { - $env = $this->getCommonCommandEnvironment(); - - if ($this->shouldUseSSH()) { - // NOTE: This is read by `bin/ssh-connect`, and tells it which credentials - // to use. - $env['PHABRICATOR_CREDENTIAL'] = $this->getCredentialPHID(); - switch ($this->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - // Force SVN to use `bin/ssh-connect`. - $env['SVN_SSH'] = $this->getSSHWrapper(); - break; - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - // Force Git to use `bin/ssh-connect`. - $env['GIT_SSH'] = $this->getSSHWrapper(); - break; - case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: - // We force Mercurial through `bin/ssh-connect` too, but it uses a - // command-line flag instead of an environmental variable. - break; - default: - throw new Exception(pht('Unrecognized version control system.')); - } - } - - return $env; - } - - private function formatRemoteCommand(array $args) { - $pattern = $args[0]; - $args = array_slice($args, 1); - - switch ($this->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - if ($this->shouldUseHTTP() || $this->shouldUseSVNProtocol()) { - $flags = array(); - $flag_args = array(); - $flags[] = '--non-interactive'; - $flags[] = '--no-auth-cache'; - if ($this->shouldUseHTTP()) { - $flags[] = '--trust-server-cert'; - } - - $credential_phid = $this->getCredentialPHID(); - if ($credential_phid) { - $key = PassphrasePasswordKey::loadFromPHID( - $credential_phid, - PhabricatorUser::getOmnipotentUser()); - $flags[] = '--username %P'; - $flags[] = '--password %P'; - $flag_args[] = $key->getUsernameEnvelope(); - $flag_args[] = $key->getPasswordEnvelope(); - } - - $flags = implode(' ', $flags); - $pattern = "svn {$flags} {$pattern}"; - $args = array_mergev(array($flag_args, $args)); - } else { - $pattern = "svn --non-interactive {$pattern}"; - } - break; - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - $pattern = "git {$pattern}"; - break; - case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: - if ($this->shouldUseSSH()) { - $pattern = "hg --config ui.ssh=%s {$pattern}"; - array_unshift( - $args, - $this->getSSHWrapper()); - } else { - $pattern = "hg {$pattern}"; - } - break; - default: - throw new Exception(pht('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 = "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(pht('Unrecognized version control system.')); - } - - array_unshift($args, $pattern); - - return $args; - } - - /** - * Sanitize output of an `hg` command invoked with the `--debug` flag to make - * it usable. - * - * @param string Output from `hg --debug ...` - * @return string Usable output. - */ - public static function filterMercurialDebugOutput($stdout) { - // When hg commands are run with `--debug` and some config file isn't - // trusted, Mercurial prints out a warning to stdout, twice, after Feb 2011. - // - // http://selenic.com/pipermail/mercurial-devel/2011-February/028541.html - // - // After Jan 2015, it may also fail to write to a revision branch cache. - - $ignore = array( - 'ignoring untrusted configuration option', - "couldn't write revision branch cache:", - ); - - foreach ($ignore as $key => $pattern) { - $ignore[$key] = preg_quote($pattern, '/'); - } - - $ignore = '('.implode('|', $ignore).')'; - - $lines = preg_split('/(?<=\n)/', $stdout); - $regex = '/'.$ignore.'.*\n$/'; - - foreach ($lines as $key => $line) { - $lines[$key] = preg_replace($regex, '', $line); - } - - return implode('', $lines); - } - public function getURI() { $callsign = $this->getCallsign(); if (strlen($callsign)) { diff --git a/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php b/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php --- a/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php +++ b/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php @@ -147,7 +147,8 @@ ); foreach ($map as $input => $expect) { - $actual = PhabricatorRepository::filterMercurialDebugOutput($input); + $actual = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( + $input); $this->assertEqual($expect, $actual, $input); } }