3 abstract class DiffusionCommandEngine
extends Phobject
{
7 private $credentialPHID;
10 private $connectAsDevice;
11 private $sudoAsDaemon;
14 public static function newCommandEngine(PhabricatorRepository
$repository) {
15 $engines = self
::newCommandEngines();
17 foreach ($engines as $engine) {
18 if ($engine->canBuildForRepository($repository)) {
19 return id(clone $engine)
20 ->setRepository($repository);
26 'No registered command engine can build commands for this '.
28 $repository->getDisplayName()));
31 private static function newCommandEngines() {
32 return id(new PhutilClassMapQuery())
33 ->setAncestorClass(__CLASS__
)
37 abstract protected function canBuildForRepository(
38 PhabricatorRepository
$repository);
40 abstract protected function newFormattedCommand($pattern, array $argv);
41 abstract protected function newCustomEnvironment();
43 public function setRepository(PhabricatorRepository
$repository) {
44 $this->repository
= $repository;
48 public function getRepository() {
49 return $this->repository
;
52 public function setURI(PhutilURI
$uri) {
54 $this->setProtocol($uri->getProtocol());
58 public function getURI() {
62 public function setProtocol($protocol) {
63 $this->protocol
= $protocol;
67 public function getProtocol() {
68 return $this->protocol
;
71 public function getDisplayProtocol() {
72 return $this->getProtocol().'://';
75 public function setCredentialPHID($credential_phid) {
76 $this->credentialPHID
= $credential_phid;
80 public function getCredentialPHID() {
81 return $this->credentialPHID
;
84 public function setArgv(array $argv) {
89 public function getArgv() {
93 public function setPassthru($passthru) {
94 $this->passthru
= $passthru;
98 public function getPassthru() {
99 return $this->passthru
;
102 public function setConnectAsDevice($connect_as_device) {
103 $this->connectAsDevice
= $connect_as_device;
107 public function getConnectAsDevice() {
108 return $this->connectAsDevice
;
111 public function setSudoAsDaemon($sudo_as_daemon) {
112 $this->sudoAsDaemon
= $sudo_as_daemon;
116 public function getSudoAsDaemon() {
117 return $this->sudoAsDaemon
;
120 protected function shouldAlwaysSudo() {
124 public function newFuture() {
125 $argv = $this->newCommandArgv();
126 $env = $this->newCommandEnvironment();
127 $is_passthru = $this->getPassthru();
129 if ($this->getSudoAsDaemon() ||
$this->shouldAlwaysSudo()) {
130 $command = call_user_func_array('csprintf', $argv);
131 $command = PhabricatorDaemon
::sudoCommandAsDaemonUser($command);
132 $argv = array('%C', $command);
136 $future = newv('PhutilExecPassthru', $argv);
138 $future = newv('ExecFuture', $argv);
141 $future->setEnv($env);
143 // See T13108. By default, don't let any cluster command run indefinitely
144 // to try to avoid cases where `git fetch` hangs for some reason and we're
145 // left sitting with a held lock forever.
146 $repository = $this->getRepository();
148 $future->setTimeout($repository->getEffectiveCopyTimeLimit());
154 private function newCommandArgv() {
157 $argv = array_slice($argv, 1);
159 list($pattern, $argv) = $this->newFormattedCommand($pattern, $argv);
161 return array_merge(array($pattern), $argv);
164 private function newCommandEnvironment() {
165 $env = $this->newCommonEnvironment() +
$this->newCustomEnvironment();
166 foreach ($env as $key => $value) {
167 if ($value === null) {
174 private function newCommonEnvironment() {
175 $repository = $this->getRepository();
178 // NOTE: Force the language to "en_US.UTF-8", which overrides locale
179 // settings. This makes stuff print in English instead of, e.g., French,
180 // so we can parse the output of some commands, error messages, etc.
181 $env['LANG'] = 'en_US.UTF-8';
183 // Propagate PHABRICATOR_ENV explicitly. For discussion, see T4155.
184 $env['PHABRICATOR_ENV'] = PhabricatorEnv
::getSelectedEnvironmentName();
186 $as_device = $this->getConnectAsDevice();
187 $credential_phid = $this->getCredentialPHID();
190 $device = AlmanacKeys
::getLiveDevice();
194 'Attempting to build a repository command (for repository "%s") '.
195 'as device, but this host ("%s") is not configured as a cluster '.
197 $repository->getDisplayName(),
201 if ($credential_phid) {
204 'Attempting to build a repository command (for repository "%s"), '.
205 'but the CommandEngine is configured to connect as both the '.
206 'current cluster device ("%s") and with a specific credential '.
207 '("%s"). These options are mutually exclusive. Connections must '.
208 'authenticate as one or the other, not both.',
209 $repository->getDisplayName(),
216 if ($this->isAnySSHProtocol()) {
217 if ($credential_phid) {
218 $env['PHABRICATOR_CREDENTIAL'] = $credential_phid;
221 $env['PHABRICATOR_AS_DEVICE'] = 1;
225 $env +
= $repository->getPassthroughEnvironmentalVariables();
230 public function isSSHProtocol() {
231 return ($this->getProtocol() == 'ssh');
234 public function isSVNProtocol() {
235 return ($this->getProtocol() == 'svn');
238 public function isSVNSSHProtocol() {
239 return ($this->getProtocol() == 'svn+ssh');
242 public function isHTTPProtocol() {
243 return ($this->getProtocol() == 'http');
246 public function isHTTPSProtocol() {
247 return ($this->getProtocol() == 'https');
250 public function isAnyHTTPProtocol() {
251 return ($this->isHTTPProtocol() ||
$this->isHTTPSProtocol());
254 public function isAnySSHProtocol() {
255 return ($this->isSSHProtocol() ||
$this->isSVNSSHProtocol());
258 public function isCredentialSupported() {
259 return ($this->getPassphraseProvidesCredentialType() !== null);
262 public function isCredentialOptional() {
263 if ($this->isAnySSHProtocol()) {
270 public function getPassphraseCredentialLabel() {
271 if ($this->isAnySSHProtocol()) {
272 return pht('SSH Key');
275 if ($this->isAnyHTTPProtocol() ||
$this->isSVNProtocol()) {
276 return pht('Password');
282 public function getPassphraseDefaultCredentialType() {
283 if ($this->isAnySSHProtocol()) {
284 return PassphraseSSHPrivateKeyTextCredentialType
::CREDENTIAL_TYPE
;
287 if ($this->isAnyHTTPProtocol() ||
$this->isSVNProtocol()) {
288 return PassphrasePasswordCredentialType
::CREDENTIAL_TYPE
;
294 public function getPassphraseProvidesCredentialType() {
295 if ($this->isAnySSHProtocol()) {
296 return PassphraseSSHPrivateKeyCredentialType
::PROVIDES_TYPE
;
299 if ($this->isAnyHTTPProtocol() ||
$this->isSVNProtocol()) {
300 return PassphrasePasswordCredentialType
::PROVIDES_TYPE
;
306 protected function getSSHWrapper() {
307 $root = dirname(phutil_get_library_root('phabricator'));
308 return $root.'/bin/ssh-connect';