3 namespace Drush\Runtime;
5 use Consolidation\AnnotatedCommand\Hooks\InitializeHookInterface;
7 use Robo\Contract\ConfigAwareInterface;
8 use Symfony\Component\Console\Input\InputInterface;
9 use Consolidation\AnnotatedCommand\AnnotationData;
10 use Drush\Log\LogLevel;
11 use Robo\Common\ConfigAwareTrait;
14 * The RedispatchHook is installed as an init hook that runs before
15 * all commands. If the commandline contains an alias or a site specification
16 * that points at a remote machine, then we will stop execution of the
17 * current command and instead run the command remotely.
19 class RedispatchHook implements InitializeHookInterface, ConfigAwareInterface
24 * Check to see if it is necessary to redispatch to a remote site.
25 * We do not redispatch to local sites here; usually, local sites may
26 * simply be selected and require no redispatch. When a local redispatch
27 * is needed, it happens in the RedispatchToSiteLocal class.
29 * @param InputInterface $input
30 * @param AnnotationData $annotationData
32 public function initialize(InputInterface $input, AnnotationData $annotationData)
34 // See drush_preflight_command_dispatch; also needed are:
35 // - redispatch to a different site-local Drush on same system
36 // - site-list handling (REMOVED)
37 // These redispatches need to be done regardless of the presence
38 // of a @handle-remote-commands annotation.
40 // If the command has the @handle-remote-commands annotation, then
41 // short-circuit redispatches to remote hosts.
42 if ($annotationData->has('handle-remote-commands')) {
45 return $this->redispatchIfRemote($input);
49 * Check to see if the target of the command is remote. Call redispatch
52 * @param InputInterface $input
54 public function redispatchIfRemote(InputInterface $input)
56 // Determine if this is a remote command.
57 // n.b. 'hasOption' only means that the option definition exists, so don't use that here.
58 $root = $input->getOption('remote-host');
60 return $this->redispatch($input);
65 * Called from RemoteCommandProxy::execute() to run remote commands.
67 * @param InputInterface $input
69 public function redispatch(InputInterface $input)
71 $remote_host = $input->getOption('remote-host');
72 $remote_user = $input->getOption('remote-user');
74 // Get the command arguments, and shift off the Drush command.
75 $redispatchArgs = Drush::config()->get('runtime.argv');
76 $drush_path = array_shift($redispatchArgs);
77 $command_name = array_shift($redispatchArgs);
79 Drush::logger()->debug('Redispatch hook {command}', ['command' => $command_name]);
81 // Remove argument patterns that should not be propagated
82 $redispatchArgs = $this->alterArgsForRedispatch($redispatchArgs);
84 // The options the user provided on the commandline will be included
85 // in $redispatchArgs.
86 $redispatchOptions = [];
88 // n.b. Defining the 'backend' flag here causes failed execution in the
89 // non-interactive case, even if 'backend' is set to 'false'.
91 'drush-script' => $this->getConfig()->get('paths.drush-script', null),
92 'remote-host' => $remote_host,
93 'remote-user' => $remote_user,
94 'additional-global-options' => [],
95 'interactive' => true,
97 $backend_options['#tty'] = $this->getConfig()->get('ssh.tty', $input->isInteractive());
98 if ($input->isInteractive()) {
99 $backend_options['interactive'] = true;
104 'command' => $command_name,
105 'args' => $redispatchArgs,
108 $common_backend_options = [];
109 $default_command = null;
111 'remote-host' => $remote_host,
112 'remote-user' => $remote_user,
113 'root' => $input->getOption('root'),
114 'uri' => $input->getOption('uri'),
118 $values = drush_backend_invoke_concurrent(
127 return $this->exitEarly($values);
131 * Remove anything that is not necessary for the remote side.
132 * At the moment this is limited to configuration options
135 * @param array $redispatchArgs
137 protected function alterArgsForRedispatch($redispatchArgs)
139 return array_filter($redispatchArgs, function ($item) {
140 return strpos($item, '-D') !== 0;
145 * Abort the current execution without causing distress to our
148 * @param array $values The results from backend invoke.
150 protected function exitEarly($values)
152 Drush::logger()->log(LogLevel::DEBUG, 'Redispatch hook exit early');
154 // TODO: This is how Drush exits from redispatch commands today;
155 // perhaps this could be somewhat improved, though.
156 // Note that RemoteCommandProxy::execute() is expecting that
157 // the redispatch() method will not return, so that will need
158 // to be altered if this behavior is changed.
159 drush_set_context('DRUSH_EXECUTION_COMPLETED', true);
160 exit($values['error_status']);