These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /* (c) Anton Medvedev <[email protected]> |
||
3 | * |
||
4 | * For the full copyright and license information, please view the LICENSE |
||
5 | * file that was distributed with this source code. |
||
6 | */ |
||
7 | |||
8 | namespace Deployer; |
||
9 | |||
10 | use Deployer\Exception\Exception; |
||
11 | use Deployer\Exception\GracefulShutdownException; |
||
12 | use Deployer\Exception\RunException; |
||
13 | use Deployer\Host\FileLoader; |
||
14 | use Deployer\Host\Host; |
||
15 | use Deployer\Host\Localhost; |
||
16 | use Deployer\Host\Range; |
||
17 | use Deployer\Support\Proxy; |
||
18 | use Deployer\Task\Context; |
||
19 | use Deployer\Task\GroupTask; |
||
20 | use Deployer\Task\Task as T; |
||
21 | use Symfony\Component\Console\Exception\MissingInputException; |
||
22 | use Symfony\Component\Console\Helper\QuestionHelper; |
||
23 | use Symfony\Component\Console\Input\InputInterface; |
||
24 | use Symfony\Component\Console\Input\InputOption; |
||
25 | use Symfony\Component\Console\Output\OutputInterface; |
||
26 | use Symfony\Component\Console\Question\ChoiceQuestion; |
||
27 | use Symfony\Component\Console\Question\ConfirmationQuestion; |
||
28 | use Symfony\Component\Console\Question\Question; |
||
29 | use function Deployer\Support\array_merge_alternate; |
||
30 | use function Deployer\Support\array_to_string; |
||
31 | use function Deployer\Support\str_contains; |
||
32 | |||
33 | /** |
||
34 | * @param string ...$hostname |
||
35 | * @return Host|Host[]|Proxy |
||
36 | */ |
||
37 | function host(...$hostname) |
||
38 | { |
||
39 | 1 | $deployer = Deployer::get(); |
|
40 | 1 | $aliases = Range::expand($hostname); |
|
41 | |||
42 | 1 | foreach ($aliases as $alias) { |
|
43 | 1 | if ($deployer->hosts->has($alias)) { |
|
44 | $host = $deployer->hosts->get($alias); |
||
45 | throw new \InvalidArgumentException( |
||
46 | "Host \"{$host->getTag()}\" already exists.\n" . |
||
47 | "If you want to override configuration options, get host with <fg=yellow>getHost</> function.\n" . |
||
48 | "\n" . |
||
49 | " <fg=yellow>getHost</>(<fg=green>'{$alias}'</>);" . |
||
50 | "\n" |
||
51 | ); |
||
52 | } |
||
53 | } |
||
54 | |||
55 | 1 | if (count($aliases) === 1) { |
|
56 | 1 | $host = new Host($aliases[0]); |
|
57 | 1 | $deployer->hosts->set($aliases[0], $host); |
|
58 | 1 | return $host; |
|
59 | } else { |
||
60 | $hosts = array_map(function ($hostname) use ($deployer) { |
||
61 | 1 | $host = new Host($hostname); |
|
62 | 1 | $deployer->hosts->set($hostname, $host); |
|
63 | 1 | return $host; |
|
64 | 1 | }, $aliases); |
|
65 | 1 | return new Proxy($hosts); |
|
66 | } |
||
67 | } |
||
68 | |||
69 | /** |
||
70 | * @param string ...$hostnames |
||
71 | * @return Localhost|Localhost[]|Proxy |
||
72 | */ |
||
73 | function localhost(...$hostnames) |
||
74 | { |
||
75 | 12 | $deployer = Deployer::get(); |
|
76 | 12 | $hostnames = Range::expand($hostnames); |
|
77 | |||
78 | 12 | if (count($hostnames) <= 1) { |
|
79 | 12 | $host = count($hostnames) === 1 ? new Localhost($hostnames[0]) : new Localhost(); |
|
80 | 12 | $deployer->hosts->set($host->getAlias(), $host); |
|
81 | 12 | return $host; |
|
82 | } else { |
||
83 | $hosts = array_map(function ($hostname) use ($deployer) { |
||
84 | $host = new Localhost($hostname); |
||
85 | $deployer->hosts->set($host->getAlias(), $host); |
||
86 | return $host; |
||
87 | }, $hostnames); |
||
88 | return new Proxy($hosts); |
||
89 | } |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * Get host by host alias. |
||
94 | * |
||
95 | * @param string $alias |
||
96 | * @return Host |
||
97 | */ |
||
98 | function getHost(string $alias) |
||
99 | { |
||
100 | 1 | return Deployer::get()->hosts->get($alias); |
|
101 | } |
||
102 | |||
103 | /** |
||
104 | * Get current host. |
||
105 | * |
||
106 | * @return Host |
||
107 | */ |
||
108 | function currentHost() |
||
109 | { |
||
110 | 8 | return Context::get()->getHost(); |
|
111 | } |
||
112 | |||
113 | |||
114 | /** |
||
115 | * Load list of hosts from file |
||
116 | * |
||
117 | * @param string $file |
||
118 | * @return Proxy |
||
119 | */ |
||
120 | function inventory($file) |
||
121 | { |
||
122 | $deployer = Deployer::get(); |
||
123 | $fileLoader = new FileLoader(); |
||
124 | $fileLoader->load($file); |
||
125 | |||
126 | $hosts = $fileLoader->getHosts(); |
||
127 | foreach ($hosts as $host) { |
||
128 | $deployer->hosts->set($host->getAlias(), $host); |
||
129 | } |
||
130 | |||
131 | return new Proxy($hosts); |
||
132 | } |
||
133 | |||
134 | /** |
||
135 | * Set task description. |
||
136 | * |
||
137 | * @param string $title |
||
138 | * @return string |
||
139 | */ |
||
140 | function desc($title = null) |
||
141 | { |
||
142 | 19 | static $store = null; |
|
143 | |||
144 | 19 | if ($title === null) { |
|
145 | 19 | return $store; |
|
146 | } else { |
||
147 | 11 | return $store = $title; |
|
148 | } |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Define a new task and save to tasks list. |
||
153 | * |
||
154 | * Alternatively get a defined task. |
||
155 | * |
||
156 | * @param string $name Name of current task. |
||
157 | * @param callable|array|string|null $body Callable task, array of other tasks names or nothing to get a defined tasks |
||
158 | * @return Task\Task |
||
159 | */ |
||
160 | function task($name, $body = null) |
||
161 | { |
||
162 | 19 | $deployer = Deployer::get(); |
|
163 | |||
164 | 19 | if (empty($body)) { |
|
165 | 14 | return $deployer->tasks->get($name); |
|
166 | } |
||
167 | |||
168 | 19 | if (is_callable($body)) { |
|
169 | 19 | $task = new T($name, $body); |
|
170 | 12 | } elseif (is_array($body)) { |
|
171 | 12 | $task = new GroupTask($name, $body); |
|
172 | } else { |
||
173 | throw new \InvalidArgumentException('Task should be a closure or array of other tasks.'); |
||
174 | } |
||
175 | |||
176 | 19 | $task->saveSourceLocation(); |
|
177 | 19 | $deployer->tasks->set($name, $task); |
|
178 | |||
179 | 19 | if (!empty(desc())) { |
|
180 | 11 | $task->desc(desc()); |
|
181 | 11 | desc(''); // Clear title. |
|
182 | } |
||
183 | |||
184 | 19 | return $task; |
|
185 | } |
||
186 | |||
187 | /** |
||
188 | * Call that task before specified task runs. |
||
189 | * |
||
190 | * @param string $task The task before $that should be run. |
||
191 | * @param string|callable $do The task to be run. |
||
192 | * @return T|void |
||
193 | */ |
||
194 | function before($task, $do) |
||
195 | { |
||
196 | 1 | if (is_callable($do)) { |
|
197 | 1 | $newTask = task("before:$task", $do); |
|
198 | 1 | before($task, "before:$task"); |
|
199 | 1 | return $newTask; |
|
200 | } |
||
201 | 1 | task($task)->addBefore($do); |
|
202 | 1 | } |
|
203 | |||
204 | /** |
||
205 | * Call that task after specified task runs. |
||
206 | * |
||
207 | * @param string $task The task after $that should be run. |
||
208 | * @param string|callable $do The task to be run. |
||
209 | * @return T|void |
||
210 | */ |
||
211 | function after($task, $do) |
||
212 | { |
||
213 | 12 | View Code Duplication | if (is_callable($do)) { |
214 | 1 | $newTask = task("after:$task", $do); |
|
215 | 1 | after($task, "after:$task"); |
|
216 | 1 | return $newTask; |
|
217 | } |
||
218 | 12 | task($task)->addAfter($do); |
|
219 | 12 | } |
|
220 | |||
221 | /** |
||
222 | * Setup which task run on failure of first. |
||
223 | * |
||
224 | * @param string $task The task which need to fail so $that should be run. |
||
225 | * @param string $do The task to be run. |
||
226 | * @return T|void |
||
227 | */ |
||
228 | function fail($task, $do) |
||
229 | { |
||
230 | 11 | View Code Duplication | if (is_callable($do)) { |
231 | $newTask = task("fail:$task", $do); |
||
232 | fail($task, "fail:$task"); |
||
233 | return $newTask; |
||
234 | } |
||
235 | 11 | $deployer = Deployer::get(); |
|
236 | 11 | $deployer->fail->set($task, $do); |
|
237 | 11 | } |
|
238 | |||
239 | /** |
||
240 | * Add users options. |
||
241 | * |
||
242 | * @param string $name The option name |
||
243 | * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts |
||
244 | * @param int|null $mode The option mode: One of the VALUE_* constants |
||
245 | * @param string $description A description text |
||
246 | * @param string|string[]|int|bool|null $default The default value (must be null for self::VALUE_NONE) |
||
247 | */ |
||
248 | function option($name, $shortcut = null, $mode = null, $description = '', $default = null) |
||
249 | { |
||
250 | 11 | Deployer::get()->inputDefinition->addOption( |
|
251 | 11 | new InputOption($name, $shortcut, $mode, $description, $default) |
|
252 | ); |
||
253 | 11 | } |
|
254 | |||
255 | /** |
||
256 | * Change the current working directory. |
||
257 | * |
||
258 | * @param string $path |
||
259 | */ |
||
260 | function cd($path) |
||
261 | { |
||
262 | 6 | set('working_path', parse($path)); |
|
263 | 6 | } |
|
264 | |||
265 | /** |
||
266 | * Execute a callback within a specific directory and revert back to the initial working directory. |
||
267 | * |
||
268 | * @param string $path |
||
269 | * @param callable $callback |
||
270 | */ |
||
271 | function within($path, $callback) |
||
272 | { |
||
273 | $lastWorkingPath = get('working_path', ''); |
||
274 | try { |
||
275 | set('working_path', parse($path)); |
||
276 | $callback(); |
||
277 | } finally { |
||
278 | set('working_path', $lastWorkingPath); |
||
279 | } |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * Run command. |
||
284 | * |
||
285 | * @param string $command |
||
286 | * @param array $options |
||
287 | * @return string |
||
288 | */ |
||
289 | function run($command, $options = []) |
||
290 | { |
||
291 | $run = function ($command, $options) { |
||
292 | 9 | $host = Context::get()->getHost(); |
|
293 | |||
294 | 9 | $command = parse($command); |
|
295 | 9 | $workingPath = get('working_path', ''); |
|
296 | |||
297 | 9 | if (!empty($workingPath)) { |
|
298 | 6 | $command = "cd $workingPath && ($command)"; |
|
299 | } |
||
300 | |||
301 | 9 | $env = array_merge_alternate(get('env', []), $options['env'] ?? []); |
|
302 | 9 | if (!empty($env)) { |
|
303 | $env = array_to_string($env); |
||
304 | $command = "export $env; $command"; |
||
305 | } |
||
306 | |||
307 | 9 | if ($host instanceof Localhost) { |
|
308 | 9 | $process = Deployer::get()->processRunner; |
|
309 | 9 | $output = $process->run($host, $command, $options); |
|
310 | } else { |
||
311 | $client = Deployer::get()->sshClient; |
||
312 | $output = $client->run($host, $command, $options); |
||
313 | } |
||
314 | |||
315 | 9 | return rtrim($output); |
|
316 | 9 | }; |
|
317 | |||
318 | 9 | if (preg_match('/^sudo\b/', $command)) { |
|
319 | try { |
||
320 | return $run($command, $options); |
||
321 | } catch (RunException $exception) { |
||
322 | $askpass = get('sudo_askpass', '/tmp/dep_sudo_pass'); |
||
323 | $password = get('sudo_pass', false); |
||
324 | if ($password === false) { |
||
325 | writeln("<fg=green;options=bold>run</> $command"); |
||
326 | $password = askHiddenResponse('Password:'); |
||
327 | } |
||
328 | $run("echo -e '#!/bin/sh\necho \"%secret%\"' > $askpass", array_merge($options, ['secret' => $password])); |
||
329 | $run("chmod a+x $askpass", $options); |
||
330 | $run(sprintf('export SUDO_ASKPASS=%s; %s', $askpass, preg_replace('/^sudo\b/', 'sudo -A', $command)), $options); |
||
331 | $run("rm $askpass", $options); |
||
332 | } |
||
333 | } else { |
||
334 | 9 | return $run($command, $options); |
|
335 | } |
||
336 | } |
||
337 | |||
338 | |||
339 | /** |
||
340 | * Execute commands on local machine |
||
341 | * |
||
342 | * @param string $command Command to run locally. |
||
343 | * @param array $options |
||
344 | * @return string Output of command. |
||
345 | */ |
||
346 | function runLocally($command, $options = []) |
||
347 | { |
||
348 | 6 | $process = Deployer::get()->processRunner; |
|
349 | 6 | $command = parse($command); |
|
350 | |||
351 | 6 | $env = array_merge_alternate(get('env', []), $options['env'] ?? []); |
|
352 | 6 | if (!empty($env)) { |
|
353 | 1 | $env = array_to_string($env); |
|
354 | 1 | $command = "export $env; $command"; |
|
355 | } |
||
356 | |||
357 | 6 | $output = $process->run(new Localhost(), $command, $options); |
|
358 | |||
359 | 6 | return rtrim($output); |
|
360 | } |
||
361 | |||
362 | /** |
||
363 | * Run test command. |
||
364 | * Example: |
||
365 | * |
||
366 | * test('[ -d {{release_path}} ]') |
||
367 | * |
||
368 | * @param string $command |
||
369 | * @return bool |
||
370 | */ |
||
371 | function test($command) |
||
372 | { |
||
373 | 8 | return run("if $command; then echo 'true'; fi") === 'true'; |
|
374 | } |
||
375 | |||
376 | /** |
||
377 | * Run test command locally. |
||
378 | * Example: |
||
379 | * |
||
380 | * testLocally('[ -d {{local_release_path}} ]') |
||
381 | * |
||
382 | * @param string $command |
||
383 | * @return bool |
||
384 | */ |
||
385 | function testLocally($command) |
||
386 | { |
||
387 | return runLocally("if $command; then echo 'true'; fi") === 'true'; |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * Iterate other hosts, allowing to call run func in callback. |
||
392 | * |
||
393 | * @experimental |
||
394 | * @param Host|Host[] $hosts |
||
395 | * @param callable $callback |
||
396 | */ |
||
397 | function on($hosts, callable $callback) |
||
398 | { |
||
399 | $deployer = Deployer::get(); |
||
400 | |||
401 | if (!is_array($hosts) && !($hosts instanceof \Traversable)) { |
||
402 | $hosts = [$hosts]; |
||
403 | } |
||
404 | |||
405 | foreach ($hosts as $host) { |
||
406 | if ($host instanceof Host) { |
||
407 | $host->getConfig()->load(); |
||
408 | Context::push(new Context($host, input(), output())); |
||
409 | try { |
||
410 | $callback($host); |
||
411 | $host->getConfig()->save(); |
||
412 | } catch (GracefulShutdownException $e) { |
||
413 | $deployer->messenger->renderException($e, $host); |
||
414 | } finally { |
||
415 | Context::pop(); |
||
416 | } |
||
417 | } else { |
||
418 | throw new \InvalidArgumentException("Function on can iterate only on Host instances."); |
||
419 | } |
||
420 | } |
||
421 | } |
||
422 | |||
423 | /** |
||
424 | * Run task |
||
425 | * |
||
426 | * @experimental |
||
427 | * @param string $task |
||
428 | */ |
||
429 | function invoke($task) |
||
430 | { |
||
431 | $hosts = [Context::get()->getHost()]; |
||
432 | $tasks = Deployer::get()->scriptManager->getTasks($task, $hosts); |
||
0 ignored issues
–
show
|
|||
433 | |||
434 | $master = Deployer::get()->master; |
||
435 | $master->run($tasks, $hosts); |
||
436 | } |
||
437 | |||
438 | /* |
||
439 | * Upload file or directory to host. |
||
440 | */ |
||
441 | View Code Duplication | function upload(string $source, string $destination, $config = []) |
|
442 | { |
||
443 | $rsync = Deployer::get()->rsync; |
||
444 | $host = currentHost(); |
||
445 | $source = parse($source); |
||
446 | $destination = parse($destination); |
||
447 | |||
448 | if ($host instanceof Localhost) { |
||
449 | $rsync->call($host, $source, $destination, $config); |
||
450 | } else { |
||
451 | $rsync->call($host, $source, "{$host->getConnectionString()}:$destination", $config); |
||
452 | } |
||
453 | } |
||
454 | |||
455 | /* |
||
456 | * Download file or directory from host |
||
457 | */ |
||
458 | View Code Duplication | function download(string $source, string $destination, $config = []) |
|
459 | { |
||
460 | $rsync = Deployer::get()->rsync; |
||
461 | $host = currentHost(); |
||
462 | $source = parse($source); |
||
463 | $destination = parse($destination); |
||
464 | |||
465 | if ($host instanceof Localhost) { |
||
466 | $rsync->call($host, $source, $destination, $config); |
||
467 | } else { |
||
468 | $rsync->call($host, "{$host->getConnectionString()}:$source", $destination, $config); |
||
469 | } |
||
470 | } |
||
471 | |||
472 | /** |
||
473 | * Writes an info message. |
||
474 | * @param string $message |
||
475 | */ |
||
476 | function info($message) |
||
477 | { |
||
478 | 5 | writeln("<fg=green;options=bold>info</> " . parse($message)); |
|
479 | 5 | } |
|
480 | |||
481 | /** |
||
482 | * Writes an warning message. |
||
483 | * @param string $message |
||
484 | */ |
||
485 | function warning($message) |
||
486 | { |
||
487 | writeln("<fg=yellow;options=bold>warning</> <comment>" . parse($message) . "</comment>"); |
||
488 | } |
||
489 | |||
490 | /** |
||
491 | * Writes a message to the output and adds a newline at the end. |
||
492 | * @param string|array $message |
||
493 | * @param int $options |
||
494 | */ |
||
495 | function writeln($message, $options = 0) |
||
496 | { |
||
497 | 8 | $host = currentHost(); |
|
498 | 8 | output()->writeln("[{$host->getTag()}] " . parse($message), $options); |
|
499 | 8 | } |
|
500 | |||
501 | /** |
||
502 | * Writes a message to the output. |
||
503 | * @param string $message |
||
504 | * @param int $options |
||
505 | */ |
||
506 | function write($message, $options = 0) |
||
507 | { |
||
508 | output()->write(parse($message), false, $options); |
||
509 | } |
||
510 | |||
511 | /** |
||
512 | * Parse set values. |
||
513 | * |
||
514 | * @param string $value |
||
515 | * @return string |
||
516 | */ |
||
517 | function parse($value) |
||
518 | { |
||
519 | 12 | return Context::get()->getConfig()->parse($value); |
|
520 | } |
||
521 | |||
522 | /** |
||
523 | * Setup configuration option. |
||
524 | * |
||
525 | * @param string $name |
||
526 | * @param mixed $value |
||
527 | */ |
||
528 | View Code Duplication | function set($name, $value) |
|
529 | { |
||
530 | 11 | if (!Context::has()) { |
|
531 | 11 | Deployer::get()->config->set($name, $value); |
|
532 | } else { |
||
533 | 6 | Context::get()->getConfig()->set($name, $value); |
|
534 | } |
||
535 | 11 | } |
|
536 | |||
537 | /** |
||
538 | * Merge new config params to existing config array. |
||
539 | * |
||
540 | * @param string $name |
||
541 | * @param array $array |
||
542 | */ |
||
543 | View Code Duplication | function add($name, $array) |
|
544 | { |
||
545 | if (!Context::has()) { |
||
546 | Deployer::get()->config->add($name, $array); |
||
547 | } else { |
||
548 | Context::get()->getConfig()->add($name, $array); |
||
549 | } |
||
550 | } |
||
551 | |||
552 | /** |
||
553 | * Get configuration value. |
||
554 | * |
||
555 | * @param string $name |
||
556 | * @param mixed|null $default |
||
557 | * @return mixed |
||
558 | */ |
||
559 | View Code Duplication | function get($name, $default = null) |
|
560 | { |
||
561 | 11 | if (!Context::has()) { |
|
562 | return Deployer::get()->config->get($name, $default); |
||
563 | } else { |
||
564 | 11 | return Context::get()->getConfig()->get($name, $default); |
|
565 | } |
||
566 | } |
||
567 | |||
568 | /** |
||
569 | * Check if there is such configuration option. |
||
570 | * |
||
571 | * @param string $name |
||
572 | * @return boolean |
||
573 | */ |
||
574 | View Code Duplication | function has($name) |
|
575 | { |
||
576 | 4 | if (!Context::has()) { |
|
577 | return Deployer::get()->config->has($name); |
||
578 | } else { |
||
579 | 4 | return Context::get()->getConfig()->has($name); |
|
580 | } |
||
581 | } |
||
582 | |||
583 | /** |
||
584 | * @param string $message |
||
585 | * @param string|null $default |
||
586 | * @param string[]|null $autocomplete |
||
587 | * @return string |
||
588 | */ |
||
589 | function ask($message, $default = null, $autocomplete = null) |
||
590 | { |
||
591 | Context::required(__FUNCTION__); |
||
592 | |||
593 | if (output()->isQuiet()) { |
||
594 | return $default; |
||
595 | } |
||
596 | |||
597 | /** @var QuestionHelper $helper */ |
||
598 | $helper = Deployer::get()->getHelper('question'); |
||
599 | |||
600 | $tag = currentHost()->getTag(); |
||
601 | $message = "[$tag] <question>$message</question> " . (($default === null) ? "" : "(default: $default) "); |
||
602 | |||
603 | $question = new Question($message, $default); |
||
604 | if (!empty($autocomplete)) { |
||
605 | $question->setAutocompleterValues($autocomplete); |
||
606 | } |
||
607 | |||
608 | // Master process will stop spinner when this env variable is true. |
||
609 | |||
610 | try { |
||
611 | return $helper->ask(input(), output(), $question); |
||
612 | } catch (MissingInputException $exception) { |
||
613 | throw new Exception("Failed to read input from stdin.\nMake sure what you are asking for input not from parallel task.", $exception->getCode(), $exception); |
||
614 | } finally { |
||
615 | } |
||
616 | } |
||
617 | |||
618 | /** |
||
619 | * @param string $message |
||
620 | * @param string[] $availableChoices |
||
621 | * @param string|null $default |
||
622 | * @param bool|false $multiselect |
||
623 | * @return string|string[] |
||
624 | */ |
||
625 | function askChoice($message, array $availableChoices, $default = null, $multiselect = false) |
||
626 | { |
||
627 | Context::required(__FUNCTION__); |
||
628 | |||
629 | if (empty($availableChoices)) { |
||
630 | throw new \InvalidArgumentException('Available choices should not be empty'); |
||
631 | } |
||
632 | |||
633 | if ($default !== null && !array_key_exists($default, $availableChoices)) { |
||
634 | throw new \InvalidArgumentException('Default choice is not available'); |
||
635 | } |
||
636 | |||
637 | if (output()->isQuiet()) { |
||
638 | if ($default === null) { |
||
639 | $default = key($availableChoices); |
||
640 | } |
||
641 | return [$default => $availableChoices[$default]]; |
||
642 | } |
||
643 | |||
644 | $helper = Deployer::get()->getHelper('question'); |
||
645 | |||
646 | $tag = currentHost()->getTag(); |
||
647 | $message = "[$tag] <question>$message</question> " . (($default === null) ? "" : "(default: $default) "); |
||
648 | |||
649 | $question = new ChoiceQuestion($message, $availableChoices, $default); |
||
650 | $question->setMultiselect($multiselect); |
||
651 | |||
652 | return $helper->ask(input(), output(), $question); |
||
653 | } |
||
654 | |||
655 | /** |
||
656 | * @param string $message |
||
657 | * @param bool $default |
||
658 | * @return bool |
||
659 | */ |
||
660 | function askConfirmation($message, $default = false) |
||
661 | { |
||
662 | Context::required(__FUNCTION__); |
||
663 | |||
664 | if (output()->isQuiet()) { |
||
665 | return $default; |
||
666 | } |
||
667 | |||
668 | $helper = Deployer::get()->getHelper('question'); |
||
669 | |||
670 | $yesOrNo = $default ? 'Y/n' : 'y/N'; |
||
671 | $tag = currentHost()->getTag(); |
||
672 | $message = "[$tag] <question>$message</question> [$yesOrNo] "; |
||
673 | |||
674 | $question = new ConfirmationQuestion($message, $default); |
||
675 | |||
676 | return $helper->ask(input(), output(), $question); |
||
677 | } |
||
678 | |||
679 | /** |
||
680 | * @param string $message |
||
681 | * @return string |
||
682 | */ |
||
683 | function askHiddenResponse($message) |
||
684 | { |
||
685 | Context::required(__FUNCTION__); |
||
686 | |||
687 | if (output()->isQuiet()) { |
||
688 | return ''; |
||
689 | } |
||
690 | |||
691 | $helper = Deployer::get()->getHelper('question'); |
||
692 | |||
693 | $tag = currentHost()->getTag(); |
||
694 | $message = "[$tag] <question>$message</question> "; |
||
695 | |||
696 | $question = new Question($message); |
||
697 | $question->setHidden(true); |
||
698 | $question->setHiddenFallback(false); |
||
699 | |||
700 | return $helper->ask(input(), output(), $question); |
||
701 | } |
||
702 | |||
703 | /** |
||
704 | * @return InputInterface |
||
705 | */ |
||
706 | function input() |
||
707 | { |
||
708 | 4 | return Context::get()->getInput(); |
|
709 | } |
||
710 | |||
711 | |||
712 | /** |
||
713 | * @return OutputInterface |
||
714 | */ |
||
715 | function output() |
||
716 | { |
||
717 | 8 | return Context::get()->getOutput(); |
|
718 | } |
||
719 | |||
720 | /** |
||
721 | * Check if command exists |
||
722 | * |
||
723 | * @param string $command |
||
724 | * @return bool |
||
725 | */ |
||
726 | function commandExist($command) |
||
727 | { |
||
728 | 3 | return test("hash $command 2>/dev/null"); |
|
729 | } |
||
730 | |||
731 | function commandSupportsOption($command, $option) |
||
732 | { |
||
733 | 5 | $man = run("(man $command 2>&1 || $command -h 2>&1 || $command --help 2>&1) | grep -- $option || true"); |
|
734 | 5 | if (empty($man)) { |
|
735 | return false; |
||
736 | } |
||
737 | 5 | return str_contains($man, $option); |
|
738 | } |
||
739 | |||
740 | function locateBinaryPath($name) |
||
741 | { |
||
742 | 4 | $nameEscaped = escapeshellarg($name); |
|
743 | |||
744 | // Try `command`, should cover all Bourne-like shells |
||
745 | // Try `which`, should cover most other cases |
||
746 | // Fallback to `type` command, if the rest fails |
||
747 | 4 | $path = run("command -v $nameEscaped || which $nameEscaped || type -p $nameEscaped"); |
|
748 | 4 | if (empty($path)) { |
|
749 | throw new \RuntimeException("Can't locate [$nameEscaped] - neither of [command|which|type] commands are available"); |
||
750 | } |
||
751 | |||
752 | // Deal with issue when `type -p` outputs something like `type -ap` in some implementations |
||
753 | 4 | return trim(str_replace("$name is", "", $path)); |
|
754 | |||
755 | } |
||
756 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.