GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 35ced9...058dc2 )
by Anton
07:38
created

functions.php ➔ host()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4.686

Importance

Changes 0
Metric Value
cc 4
nc 5
nop 1
dl 0
loc 31
ccs 13
cts 20
cp 0.65
crap 4.686
rs 9.424
c 0
b 0
f 0
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\ObjectProxy;
18
use Deployer\Task\Context;
19
use Deployer\Task\GroupTask;
20
use Deployer\Task\Task as T;
21
use Deployer\Utility\Httpie;
22
use Symfony\Component\Console\Exception\MissingInputException;
23
use Symfony\Component\Console\Helper\QuestionHelper;
24
use Symfony\Component\Console\Input\InputInterface;
25
use Symfony\Component\Console\Input\InputOption;
26
use Symfony\Component\Console\Output\OutputInterface;
27
use Symfony\Component\Console\Question\ChoiceQuestion;
28
use Symfony\Component\Console\Question\ConfirmationQuestion;
29
use Symfony\Component\Console\Question\Question;
30
use function Deployer\Support\array_merge_alternate;
31
use function Deployer\Support\array_to_string;
32
use function Deployer\Support\str_contains;
33
34
/**
35
 * @param string ...$hostname
36
 * @return Host|Host[]|ObjectProxy
37
 */
38
function host(...$hostname)
39
{
40 1
    $deployer = Deployer::get();
41 1
    $aliases = Range::expand($hostname);
42
43 1
    foreach ($aliases as $alias) {
44 1
        if ($deployer->hosts->has($alias)) {
45
            $host = $deployer->hosts->get($alias);
46
            throw new \InvalidArgumentException(
47
                "Host \"{$host->getTag()}\" already exists.\n" .
48
                "If you want to override configuration options, get host with <fg=yellow>getHost</> function.\n" .
49
                "\n" .
50
                "    <fg=yellow>getHost</>(<fg=green>'{$alias}'</>);" .
51
                "\n"
52
            );
53
        }
54
    }
55
56 1
    if (count($aliases) === 1) {
57 1
        $host = new Host($aliases[0]);
58 1
        $deployer->hosts->set($aliases[0], $host);
59 1
        return $host;
60
    } else {
61
        $hosts = array_map(function ($hostname) use ($deployer) {
62 1
            $host = new Host($hostname);
63 1
            $deployer->hosts->set($hostname, $host);
64 1
            return $host;
65 1
        }, $aliases);
66 1
        return new ObjectProxy($hosts);
67
    }
68
}
69
70
/**
71
 * @param string ...$hostnames
72
 * @return Localhost|Localhost[]|ObjectProxy
73
 */
74
function localhost(...$hostnames)
75
{
76 13
    $deployer = Deployer::get();
77 13
    $hostnames = Range::expand($hostnames);
78
79 13
    if (count($hostnames) <= 1) {
80 13
        $host = count($hostnames) === 1 ? new Localhost($hostnames[0]) : new Localhost();
81 13
        $deployer->hosts->set($host->getAlias(), $host);
82 13
        return $host;
83
    } else {
84
        $hosts = array_map(function ($hostname) use ($deployer) {
85
            $host = new Localhost($hostname);
86
            $deployer->hosts->set($host->getAlias(), $host);
87
            return $host;
88
        }, $hostnames);
89
        return new ObjectProxy($hosts);
90
    }
91
}
92
93
/**
94
 * Get host by host alias.
95
 *
96
 * @param string $alias
97
 * @return Host
98
 */
99
function getHost(string $alias)
100
{
101 5
    return Deployer::get()->hosts->get($alias);
102
}
103
104
/**
105
 * Get current host.
106
 *
107
 * @return Host
108
 */
109
function currentHost()
110
{
111 8
    return Context::get()->getHost();
112
}
113
114
115
/**
116
 * Load list of hosts from file
117
 *
118
 * @param string $file
119
 * @return ObjectProxy
120
 */
121
function inventory($file)
122
{
123
    $deployer = Deployer::get();
124
    $fileLoader = new FileLoader();
125
    $fileLoader->load($file);
126
127
    $hosts = $fileLoader->getHosts();
128
    foreach ($hosts as $host) {
129
        $deployer->hosts->set($host->getAlias(), $host);
130
    }
131
132
    return new ObjectProxy($hosts);
133
}
134
135
/**
136
 * Set task description.
137
 *
138
 * @param string $title
139
 * @return string
140
 */
141
function desc($title = null)
142
{
143 15
    static $store = null;
144
145 15
    if ($title === null) {
146 15
        return $store;
147
    } else {
148 8
        return $store = $title;
149
    }
150
}
151
152
/**
153
 * Define a new task and save to tasks list.
154
 *
155
 * Alternatively get a defined task.
156
 *
157
 * @param string $name Name of current task.
158
 * @param callable|array|string|null $body Callable task, array of other tasks names or nothing to get a defined tasks
159
 * @return Task\Task
160
 */
161
function task($name, $body = null)
162
{
163 15
    $deployer = Deployer::get();
164
165 15
    if (empty($body)) {
166 15
        return $deployer->tasks->get($name);
167
    }
168
169 15
    if (is_callable($body)) {
170 15
        $task = new T($name, $body);
171 9
    } elseif (is_array($body)) {
172 9
        $task = new GroupTask($name, $body);
173
    } else {
174
        throw new \InvalidArgumentException('Task should be a closure or array of other tasks.');
175
    }
176
177 15
    $task->saveSourceLocation();
178 15
    $deployer->tasks->set($name, $task);
179
180 15
    if (!empty(desc())) {
181 8
        $task->desc(desc());
182 8
        desc(''); // Clear title.
183
    }
184
185 15
    return $task;
186
}
187
188
/**
189
 * Call that task before specified task runs.
190
 *
191
 * @param string $task The task before $that should be run.
192
 * @param string|callable $do The task to be run.
193
 * @return T|void
194
 */
195
function before($task, $do)
196
{
197 1
    if (is_callable($do)) {
198 1
        $newTask = task("before:$task", $do);
199 1
        before($task, "before:$task");
200 1
        return $newTask;
201
    }
202 1
    task($task)->addBefore($do);
0 ignored issues
show
Bug introduced by
It seems like $do can also be of type callable; however, parameter $task of Deployer\Task\Task::addBefore() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

202
    task($task)->addBefore(/** @scrutinizer ignore-type */ $do);
Loading history...
203 1
}
204
205
/**
206
 * Call that task after specified task runs.
207
 *
208
 * @param string $task The task after $that should be run.
209
 * @param string|callable $do The task to be run.
210
 * @return T|void
211
 */
212
function after($task, $do)
213
{
214 13
    if (is_callable($do)) {
215 5
        $newTask = task("after:$task", $do);
216 5
        after($task, "after:$task");
217 5
        return $newTask;
218
    }
219 13
    task($task)->addAfter($do);
0 ignored issues
show
Bug introduced by
It seems like $do can also be of type callable; however, parameter $task of Deployer\Task\Task::addAfter() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

219
    task($task)->addAfter(/** @scrutinizer ignore-type */ $do);
Loading history...
220 13
}
221
222
/**
223
 * Setup which task run on failure of first.
224
 *
225
 * @param string $task The task which need to fail so $that should be run.
226
 * @param string $do The task to be run.
227
 * @return T|void
228
 */
229
function fail($task, $do)
230
{
231 8
    if (is_callable($do)) {
232
        $newTask = task("fail:$task", $do);
233
        fail($task, "fail:$task");
234
        return $newTask;
235
    }
236 8
    $deployer = Deployer::get();
237 8
    $deployer->fail->set($task, $do);
238 8
}
239
240
/**
241
 * Add users options.
242
 *
243
 * @param string $name The option name
244
 * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
245
 * @param int|null $mode The option mode: One of the VALUE_* constants
246
 * @param string $description A description text
247
 * @param string|string[]|int|bool|null $default The default value (must be null for self::VALUE_NONE)
248
 */
249
function option($name, $shortcut = null, $mode = null, $description = '', $default = null)
250
{
251 8
    Deployer::get()->inputDefinition->addOption(
252 8
        new InputOption($name, $shortcut, $mode, $description, $default)
253
    );
254 8
}
255
256
/**
257
 * Change the current working directory.
258
 *
259
 * @param string $path
260
 */
261
function cd($path)
262
{
263 6
    set('working_path', parse($path));
264 6
}
265
266
/**
267
 * Execute a callback within a specific directory and revert back to the initial working directory.
268
 *
269
 * @param string $path
270
 * @param callable $callback
271
 */
272
function within($path, $callback)
273
{
274
    $lastWorkingPath = get('working_path', '');
275
    try {
276
        set('working_path', parse($path));
277
        $callback();
278
    } finally {
279
        set('working_path', $lastWorkingPath);
280
    }
281
}
282
283
/**
284
 * Run command.
285
 *
286
 * @param string $command
287
 * @param array $options
288
 * @return string
289
 */
290
function run($command, $options = [])
291
{
292
    $run = function ($command, $options) {
293 8
        $host = Context::get()->getHost();
294
295 8
        $command = parse($command);
296 8
        $workingPath = get('working_path', '');
297
298 8
        if (!empty($workingPath)) {
299 6
            $command = "cd $workingPath && ($command)";
300
        }
301
302 8
        $env = array_merge_alternate(get('env', []), $options['env'] ?? []);
0 ignored issues
show
Bug introduced by
It seems like get('env', array()) can also be of type string; however, parameter $original of Deployer\Support\array_merge_alternate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

302
        $env = array_merge_alternate(/** @scrutinizer ignore-type */ get('env', []), $options['env'] ?? []);
Loading history...
303 8
        if (!empty($env)) {
304
            $env = array_to_string($env);
305
            $command = "export $env; $command";
306
        }
307
308 8
        if ($host instanceof Localhost) {
309 8
            $process = Deployer::get()->processRunner;
310 8
            $output = $process->run($host, $command, $options);
311
        } else {
312
            $client = Deployer::get()->sshClient;
313
            $output = $client->run($host, $command, $options);
314
        }
315
316 8
        return rtrim($output);
317 8
    };
318
319 8
    if (preg_match('/^sudo\b/', $command)) {
320
        try {
321
            return $run($command, $options);
322
        } catch (RunException $exception) {
323
            $askpass = get('sudo_askpass', '/tmp/dep_sudo_pass');
324
            $password = get('sudo_pass', false);
325
            if ($password === false) {
326
                writeln("<fg=green;options=bold>run</> $command");
327
                $password = askHiddenResponse('Password:');
328
            }
329
            $run("echo -e '#!/bin/sh\necho \"%sudo_pass%\"' > $askpass", array_merge($options, ['sudo_pass' => $password]));
330
            $run("chmod a+x $askpass", $options);
331
            $run(sprintf('export SUDO_ASKPASS=%s; %s', $askpass, preg_replace('/^sudo\b/', 'sudo -A', $command)), $options);
332
            $run("rm $askpass", $options);
333
        }
334
    } else {
335 8
        return $run($command, $options);
336
    }
337
}
338
339
340
/**
341
 * Execute commands on local machine
342
 *
343
 * @param string $command Command to run locally.
344
 * @param array $options
345
 * @return string Output of command.
346
 */
347
function runLocally($command, $options = [])
348
{
349 6
    $process = Deployer::get()->processRunner;
350 6
    $command = parse($command);
351
352 6
    $env = array_merge_alternate(get('env', []), $options['env'] ?? []);
0 ignored issues
show
Bug introduced by
It seems like get('env', array()) can also be of type string; however, parameter $original of Deployer\Support\array_merge_alternate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

352
    $env = array_merge_alternate(/** @scrutinizer ignore-type */ get('env', []), $options['env'] ?? []);
Loading history...
353 6
    if (!empty($env)) {
354 1
        $env = array_to_string($env);
355 1
        $command = "export $env; $command";
356
    }
357
358 6
    $output = $process->run(new Localhost(), $command, $options);
359
360 6
    return rtrim($output);
361
}
362
363
/**
364
 * Run test command.
365
 * Example:
366
 *
367
 *     test('[ -d {{release_path}} ]')
368
 *
369
 * @param string $command
370
 * @return bool
371
 */
372
function test($command)
373
{
374 8
    return run("if $command; then echo 'true'; fi") === 'true';
375
}
376
377
/**
378
 * Run test command locally.
379
 * Example:
380
 *
381
 *     testLocally('[ -d {{local_release_path}} ]')
382
 *
383
 * @param string $command
384
 * @return bool
385
 */
386
function testLocally($command)
387
{
388
    return runLocally("if $command; then echo 'true'; fi") === 'true';
389
}
390
391
/**
392
 * Iterate other hosts, allowing to call run func in callback.
393
 *
394
 * @experimental
395
 * @param Host|Host[] $hosts
396
 * @param callable $callback
397
 */
398
function on($hosts, callable $callback)
399
{
400
    $deployer = Deployer::get();
401
402
    if (!is_array($hosts) && !($hosts instanceof \Traversable)) {
403
        $hosts = [$hosts];
404
    }
405
406
    foreach ($hosts as $host) {
407
        if ($host instanceof Host) {
408
            $host->config()->load();
409
            Context::push(new Context($host, input(), output()));
410
            try {
411
                $callback($host);
412
                $host->config()->save();
413
            } catch (GracefulShutdownException $e) {
414
                $deployer->messenger->renderException($e, $host);
415
            } finally {
416
                Context::pop();
417
            }
418
        } else {
419
            throw new \InvalidArgumentException("Function on can iterate only on Host instances.");
420
        }
421
    }
422
}
423
424
/**
425
 * Run task
426
 *
427
 * @experimental
428
 * @param string $task
429
 */
430
function invoke($task)
431
{
432
    $hosts = [Context::get()->getHost()];
433
    $tasks = Deployer::get()->scriptManager->getTasks($task, $hosts);
0 ignored issues
show
Unused Code introduced by
The call to Deployer\Task\ScriptManager::getTasks() has too many arguments starting with $hosts. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

433
    /** @scrutinizer ignore-call */ 
434
    $tasks = Deployer::get()->scriptManager->getTasks($task, $hosts);

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. Please note the @ignore annotation hint above.

Loading history...
434
435
    $master = Deployer::get()->master;
436
    $master->run($tasks, $hosts);
437
}
438
439
/*
440
 * Upload file or directory to host.
441
 */
442
function upload(string $source, string $destination, $config = [])
443
{
444
    $rsync = Deployer::get()->rsync;
445
    $host = currentHost();
446
    $source = parse($source);
447
    $destination = parse($destination);
448
449
    if ($host instanceof Localhost) {
450
        $rsync->call($host, $source, $destination, $config);
451
    } else {
452
        $rsync->call($host, $source, "{$host->getConnectionString()}:$destination", $config);
453
    }
454
}
455
456
/*
457
 * Download file or directory from host
458
 */
459
function download(string $source, string $destination, $config = [])
460
{
461
    $rsync = Deployer::get()->rsync;
462
    $host = currentHost();
463
    $source = parse($source);
464
    $destination = parse($destination);
465
466
    if ($host instanceof Localhost) {
467
        $rsync->call($host, $source, $destination, $config);
468
    } else {
469
        $rsync->call($host, "{$host->getConnectionString()}:$source", $destination, $config);
470
    }
471
}
472
473
/**
474
 * Writes an info message.
475
 * @param string $message
476
 */
477
function info($message)
478
{
479 4
    writeln("<fg=green;options=bold>info</> " . parse($message));
480 4
}
481
482
/**
483
 * Writes an warning message.
484
 * @param string $message
485
 */
486
function warning($message)
487
{
488
    writeln("<fg=yellow;options=bold>warning</> <comment>" . parse($message) . "</comment>");
489
}
490
491
/**
492
 * Writes a message to the output and adds a newline at the end.
493
 * @param string|array $message
494
 * @param int $options
495
 */
496
function writeln($message, $options = 0)
497
{
498 7
    $host = currentHost();
499 7
    output()->writeln("[{$host->getTag()}] " . parse($message), $options);
0 ignored issues
show
Bug introduced by
It seems like $message can also be of type array; however, parameter $value of Deployer\parse() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

499
    output()->writeln("[{$host->getTag()}] " . parse(/** @scrutinizer ignore-type */ $message), $options);
Loading history...
500 7
}
501
502
/**
503
 * Writes a message to the output.
504
 * @param string $message
505
 * @param int $options
506
 */
507
function write($message, $options = 0)
508
{
509
    output()->write(parse($message), false, $options);
510
}
511
512
/**
513
 * Parse set values.
514
 *
515
 * @param string $value
516
 * @return string
517
 */
518
function parse($value)
519
{
520 10
    return Context::get()->getConfig()->parse($value);
521
}
522
523
/**
524
 * Setup configuration option.
525
 *
526
 * @param string $name
527
 * @param mixed $value
528
 */
529
function set($name, $value)
530
{
531 12
    if (!Context::has()) {
532 12
        Deployer::get()->config->set($name, $value);
533
    } else {
534 6
        Context::get()->getConfig()->set($name, $value);
535
    }
536 12
}
537
538
/**
539
 * Merge new config params to existing config array.
540
 *
541
 * @param string $name
542
 * @param array $array
543
 */
544
function add($name, $array)
545
{
546
    if (!Context::has()) {
547
        Deployer::get()->config->add($name, $array);
548
    } else {
549
        Context::get()->getConfig()->add($name, $array);
550
    }
551
}
552
553
/**
554
 * Get configuration value.
555
 *
556
 * @param string $name
557
 * @param mixed|null $default
558
 * @return mixed
559
 */
560
function get($name, $default = null)
561
{
562 10
    if (!Context::has()) {
563
        return Deployer::get()->config->get($name, $default);
564
    } else {
565 10
        return Context::get()->getConfig()->get($name, $default);
566
    }
567
}
568
569
/**
570
 * Check if there is such configuration option.
571
 *
572
 * @param string $name
573
 * @return boolean
574
 */
575
function has($name)
576
{
577 4
    if (!Context::has()) {
578
        return Deployer::get()->config->has($name);
579
    } else {
580 4
        return Context::get()->getConfig()->has($name);
581
    }
582
}
583
584
/**
585
 * @param string $message
586
 * @param string|null $default
587
 * @param string[]|null $autocomplete
588
 * @return string
589
 */
590
function ask($message, $default = null, $autocomplete = null)
591
{
592 1
    Context::required(__FUNCTION__);
593
594 1
    if (output()->isQuiet()) {
595
        return $default;
596
    }
597
598 1
    if (Deployer::isWorker()) {
599
        return Deployer::proxyCallToMaster(currentHost(), __FUNCTION__, ...func_get_args());
600
    }
601
602
    /** @var QuestionHelper $helper */
603 1
    $helper = Deployer::get()->getHelper('question');
604
605 1
    $tag = currentHost()->getTag();
606 1
    $message = "[$tag] <question>$message</question> " . (($default === null) ? "" : "(default: $default) ");
607
608 1
    $question = new Question($message, $default);
609 1
    if (!empty($autocomplete)) {
610
        $question->setAutocompleterValues($autocomplete);
611
    }
612
613 1
    return $helper->ask(input(), output(), $question);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $helper->ask(input(), output(), $question) also could return the type boolean|string[] which is incompatible with the documented return type string.
Loading history...
614
}
615
616
/**
617
 * @param string $message
618
 * @param string[] $availableChoices
619
 * @param string|null $default
620
 * @param bool|false $multiselect
621
 * @return string|string[]
622
 */
623
function askChoice($message, array $availableChoices, $default = null, $multiselect = false)
624
{
625
    Context::required(__FUNCTION__);
626
627
    if (empty($availableChoices)) {
628
        throw new \InvalidArgumentException('Available choices should not be empty');
629
    }
630
631
    if ($default !== null && !array_key_exists($default, $availableChoices)) {
632
        throw new \InvalidArgumentException('Default choice is not available');
633
    }
634
635
    if (output()->isQuiet()) {
636
        if ($default === null) {
637
            $default = key($availableChoices);
638
        }
639
        return [$default => $availableChoices[$default]];
640
    }
641
642
    if (Deployer::isWorker()) {
643
        return Deployer::proxyCallToMaster(currentHost(), __FUNCTION__, ...func_get_args());
644
    }
645
646
    $helper = Deployer::get()->getHelper('question');
647
648
    $tag = currentHost()->getTag();
649
    $message = "[$tag] <question>$message</question> " . (($default === null) ? "" : "(default: $default) ");
650
651
    $question = new ChoiceQuestion($message, $availableChoices, $default);
652
    $question->setMultiselect($multiselect);
653
654
    return $helper->ask(input(), output(), $question);
0 ignored issues
show
Bug introduced by
The method ask() does not exist on Symfony\Component\Console\Helper\Helper. It seems like you code against a sub-type of Symfony\Component\Console\Helper\Helper such as Symfony\Component\Console\Helper\QuestionHelper. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

654
    return $helper->/** @scrutinizer ignore-call */ ask(input(), output(), $question);
Loading history...
655
}
656
657
/**
658
 * @param string $message
659
 * @param bool $default
660
 * @return bool
661
 */
662
function askConfirmation($message, $default = false)
663
{
664
    Context::required(__FUNCTION__);
665
666
    if (output()->isQuiet()) {
667
        return $default;
668
    }
669
670
    if (Deployer::isWorker()) {
671
        return Deployer::proxyCallToMaster(currentHost(), __FUNCTION__, ...func_get_args());
672
    }
673
674
    $helper = Deployer::get()->getHelper('question');
675
676
    $yesOrNo = $default ? 'Y/n' : 'y/N';
677
    $tag = currentHost()->getTag();
678
    $message = "[$tag] <question>$message</question> [$yesOrNo] ";
679
680
    $question = new ConfirmationQuestion($message, $default);
681
682
    return $helper->ask(input(), output(), $question);
683
}
684
685
/**
686
 * @param string $message
687
 * @return string
688
 */
689
function askHiddenResponse(string $message)
690
{
691
    Context::required(__FUNCTION__);
692
693
    if (output()->isQuiet()) {
694
        return '';
695
    }
696
697
    if (Deployer::isWorker()) {
698
        return Deployer::proxyCallToMaster(currentHost(), __FUNCTION__, ...func_get_args());
699
    }
700
701
    $helper = Deployer::get()->getHelper('question');
702
703
    $tag = currentHost()->getTag();
704
    $message = "[$tag] <question>$message</question> ";
705
706
    $question = new Question($message);
707
    $question->setHidden(true);
708
    $question->setHiddenFallback(false);
709
710
    return $helper->ask(input(), output(), $question);
711
}
712
713
/**
714
 * @return InputInterface
715
 */
716
function input()
717
{
718 5
    return Context::get()->getInput();
719
}
720
721
722
/**
723
 * @return OutputInterface
724
 */
725
function output()
726
{
727 8
    return Context::get()->getOutput();
728
}
729
730
/**
731
 * Check if command exists
732
 *
733
 * @param string $command
734
 * @return bool
735
 */
736
function commandExist($command)
737
{
738 3
    return test("hash $command 2>/dev/null");
739
}
740
741
function commandSupportsOption($command, $option)
742
{
743 5
    $man = run("(man $command 2>&1 || $command -h 2>&1 || $command --help 2>&1) | grep -- $option || true");
744 5
    if (empty($man)) {
745
        return false;
746
    }
747 5
    return str_contains($man, $option);
748
}
749
750
function locateBinaryPath($name)
751
{
752 4
    $nameEscaped = escapeshellarg($name);
753
754
    // Try `command`, should cover all Bourne-like shells
755
    // Try `which`, should cover most other cases
756
    // Fallback to `type` command, if the rest fails
757 4
    $path = run("command -v $nameEscaped || which $nameEscaped || type -p $nameEscaped");
758 4
    if (empty($path)) {
759
        throw new \RuntimeException("Can't locate [$nameEscaped] - neither of [command|which|type] commands are available");
760
    }
761
762
    // Deal with issue when `type -p` outputs something like `type -ap` in some implementations
763 4
    return trim(str_replace("$name is", "", $path));
764
765
}
766