mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 00:42:41 +01:00
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
This commit is contained in:
parent
a3bb35e9d2
commit
575c01373e
10 changed files with 524 additions and 210 deletions
|
@ -570,6 +570,8 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'DiffusionCachedResolveRefsQuery' => 'DiffusionLowLevelQuery',
|
||||
'DiffusionChangeController' => 'DiffusionController',
|
||||
'DiffusionChangeHeraldFieldGroup' => 'HeraldFieldGroup',
|
||||
'DiffusionCommandEngine' => 'Phobject',
|
||||
'DiffusionCommandEngineTestCase' => 'PhabricatorTestCase',
|
||||
'DiffusionCommitAffectedFilesHeraldField' => 'DiffusionCommitHeraldField',
|
||||
'DiffusionCommitAuthorHeraldField' => 'DiffusionCommitHeraldField',
|
||||
'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField',
|
||||
|
@ -4835,6 +4842,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionGitBlameQuery' => 'DiffusionBlameQuery',
|
||||
'DiffusionGitBranch' => 'Phobject',
|
||||
'DiffusionGitBranchTestCase' => 'PhabricatorTestCase',
|
||||
'DiffusionGitCommandEngine' => 'DiffusionCommandEngine',
|
||||
'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery',
|
||||
'DiffusionGitLFSAuthenticateWorkflow' => 'DiffusionGitSSHWorkflow',
|
||||
'DiffusionGitLFSResponse' => 'AphrontResponse',
|
||||
|
@ -4868,6 +4876,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionLowLevelQuery' => 'Phobject',
|
||||
'DiffusionLowLevelResolveRefsQuery' => 'DiffusionLowLevelQuery',
|
||||
'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery',
|
||||
'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine',
|
||||
'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery',
|
||||
'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery',
|
||||
'DiffusionMercurialRequest' => 'DiffusionRequest',
|
||||
|
@ -4989,6 +4998,7 @@ phutil_register_library_map(array(
|
|||
'DiffusionServeController' => 'DiffusionController',
|
||||
'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'DiffusionSetupException' => 'Exception',
|
||||
'DiffusionSubversionCommandEngine' => 'DiffusionCommandEngine',
|
||||
'DiffusionSubversionSSHWorkflow' => 'DiffusionSSHWorkflow',
|
||||
'DiffusionSubversionServeSSHWorkflow' => 'DiffusionSubversionSSHWorkflow',
|
||||
'DiffusionSubversionWireProtocol' => 'Phobject',
|
||||
|
|
|
@ -131,7 +131,8 @@ final class DiffusionHistoryQueryConduitAPIMethod
|
|||
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);
|
||||
|
||||
|
|
173
src/applications/diffusion/protocol/DiffusionCommandEngine.php
Normal file
173
src/applications/diffusion/protocol/DiffusionCommandEngine.php
Normal file
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
abstract class DiffusionCommandEngine extends Phobject {
|
||||
|
||||
private $repository;
|
||||
private $protocol;
|
||||
private $credentialPHID;
|
||||
private $argv;
|
||||
private $passthru;
|
||||
|
||||
public static function newCommandEngine(PhabricatorRepository $repository) {
|
||||
$engines = self::newCommandEngines();
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
if ($engine->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';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionGitCommandEngine
|
||||
extends DiffusionCommandEngine {
|
||||
|
||||
protected function canBuildForRepository(
|
||||
PhabricatorRepository $repository) {
|
||||
return $repository->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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionMercurialCommandEngine
|
||||
extends DiffusionCommandEngine {
|
||||
|
||||
protected function canBuildForRepository(
|
||||
PhabricatorRepository $repository) {
|
||||
return $repository->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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionSubversionCommandEngine
|
||||
extends DiffusionCommandEngine {
|
||||
|
||||
protected function canBuildForRepository(
|
||||
PhabricatorRepository $repository) {
|
||||
return $repository->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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
final class DiffusionCommandEngineTestCase extends PhabricatorTestCase {
|
||||
|
||||
public function testCommandEngine() {
|
||||
$type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
|
||||
$type_hg = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL;
|
||||
$type_svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
|
||||
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
$ssh_wrapper = $root.'/bin/ssh-connect';
|
||||
$home = $root.'/support/empty/';
|
||||
|
||||
|
||||
// Plain commands.
|
||||
|
||||
$this->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);
|
||||
}
|
||||
|
||||
}
|
|
@ -50,7 +50,9 @@ final class DiffusionLowLevelParentsQuery
|
|||
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) {
|
||||
|
|
|
@ -487,19 +487,22 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
}
|
||||
|
||||
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 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
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 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
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 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
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)) {
|
||||
|
|
|
@ -147,7 +147,8 @@ final class PhabricatorRepositoryTestCase
|
|||
);
|
||||
|
||||
foreach ($map as $input => $expect) {
|
||||
$actual = PhabricatorRepository::filterMercurialDebugOutput($input);
|
||||
$actual = DiffusionMercurialCommandEngine::filterMercurialDebugOutput(
|
||||
$input);
|
||||
$this->assertEqual($expect, $actual, $input);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue