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
Pull Request — master (#2180)
by
unknown
03:55
created

unescape()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

196
    task($task)->addBefore(/** @scrutinizer ignore-type */ $do);
Loading history...
197 1
}
198 1
199 1
/**
200 1
 * Call that task after specified task runs.
201
 *
202 1
 * @param string $task The task after $that should be run.
203 1
 * @param string|callable $do The task to be run.
204
 * @return Task|void
205
 */
206
function after($task, $do)
207
{
208
    if (is_callable($do)) {
209
        $newTask = task("after:$task", $do);
210
        after($task, "after:$task");
211
        return $newTask;
212
    }
213
    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

213
    task($task)->addAfter(/** @scrutinizer ignore-type */ $do);
Loading history...
214 13
}
215 5
216 5
/**
217 5
 * Setup which task run on failure of first.
218
 *
219 13
 * @param string $task The task which need to fail so $that should be run.
220 13
 * @param string $do The task to be run.
221
 * @return Task|void
222
 */
223
function fail($task, $do)
224
{
225
    if (is_callable($do)) {
226
        $newTask = task("fail:$task", $do);
227
        fail($task, "fail:$task");
228
        return $newTask;
229
    }
230
    $deployer = Deployer::get();
231 8
    $deployer->fail->set($task, $do);
232
}
233
234
/**
235
 * Add users options.
236 8
 *
237 8
 * @param string $name The option name
238 8
 * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
239
 * @param int|null $mode The option mode: One of the VALUE_* constants
240
 * @param string $description A description text
241
 * @param string|string[]|int|bool|null $default The default value (must be null for self::VALUE_NONE)
242
 */
243
function option($name, $shortcut = null, $mode = null, $description = '', $default = null)
244
{
245
    Deployer::get()->inputDefinition->addOption(
246
        new InputOption($name, $shortcut, $mode, $description, $default)
247
    );
248
}
249
250
/**
251 8
 * Change the current working directory.
252 8
 *
253
 * @param string $path
254 8
 */
255
function cd($path)
256
{
257
    set('working_path', parse($path));
258
}
259
260
/**
261
 * Execute a callback within a specific directory and revert back to the initial working directory.
262
 *
263 6
 * @param string $path
264 6
 * @param callable $callback
265
 * @return mixed|void Return value of the $callback function or void if callback doesn't return anything
266
 */
267
function within($path, $callback)
268
{
269
    $lastWorkingPath = get('working_path', '');
270
    try {
271
        set('working_path', parse($path));
272
        return $callback();
273
    } finally {
274
        set('working_path', $lastWorkingPath);
275
    }
276
}
277
278
/**
279
 * Executes given command on remote host.
280
 *
281
 * Options:
282
 * - `timeout` - Sets the process timeout (max. runtime). The timeout in seconds (default: 300 sec).
283
 * - `secret` - Placeholder `%secret%` can be used in command. Placeholder will be replaced with this value and will not appear in any logs.
284
 *
285
 * Examples:
286
 *
287
 * ```php
288
 * run('echo hello world');
289
 * run('cd {{deploy_path}} && git status');
290
 * run('password %secret%', ['secret' => getenv('CI_SECRET')]);
291
 * ```
292
 *
293 8
 * ```php
294
 * $path = run('readlink {{deploy_path}}/current');
295 8
 * run("echo $path");
296 8
 * ```
297
 *
298 8
 * @param string $command
299 6
 * @param array $options
300
 * @return string
301
 */
302 8
function run($command, $options = [])
303 8
{
304
    $run = function ($command, $options) {
305
        $host = Context::get()->getHost();
306
307
        $command = parse($command);
308 8
        $workingPath = get('working_path', '');
309 8
310 8
        if (!empty($workingPath)) {
311
            $command = "cd $workingPath && ($command)";
312
        }
313
314
        $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

314
        $env = array_merge_alternate(/** @scrutinizer ignore-type */ get('env', []), $options['env'] ?? []);
Loading history...
315
        if (!empty($env)) {
316 8
            $env = array_to_string($env);
317 8
            $command = "export $env; $command";
318
        }
319 8
320
        if ($host instanceof Localhost) {
321
            $process = Deployer::get()->processRunner;
322
            $output = $process->run($host, $command, $options);
323
        } else {
324
            $client = Deployer::get()->sshClient;
325
            $output = $client->run($host, $command, $options);
326
        }
327
328
        return rtrim($output);
329
    };
330
331
    if (preg_match('/^sudo\b/', $command)) {
332
        try {
333
            return $run($command, $options);
334
        } catch (RunException $exception) {
335 8
            $askpass = get('sudo_askpass', '/tmp/dep_sudo_pass');
336
            $password = get('sudo_pass', false);
337
            if ($password === false) {
338
                writeln("<fg=green;options=bold>run</> $command");
339
                $password = askHiddenResponse('Password:');
340
            }
341
            $run("echo -e '#!/bin/sh\necho \"%sudo_pass%\"' > $askpass", array_merge($options, ['sudo_pass' => $password]));
342
            $run("chmod a+x $askpass", $options);
343
            $run(sprintf('export SUDO_ASKPASS=%s; %s', $askpass, preg_replace('/^sudo\b/', 'sudo -A', $command)), $options);
344
            $run("rm $askpass", $options);
345
        }
346
    } else {
347
        return $run($command, $options);
348
    }
349 6
}
350 6
351
352 6
/**
353 6
 * Execute commands on local machine
354 1
 *
355 1
 * @param string $command Command to run locally.
356
 * @param array $options
357
 * @return string Output of command.
358 6
 */
359
function runLocally($command, $options = [])
360 6
{
361
    $process = Deployer::get()->processRunner;
362
    $command = parse($command);
363
364
    $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

364
    $env = array_merge_alternate(/** @scrutinizer ignore-type */ get('env', []), $options['env'] ?? []);
Loading history...
365
    if (!empty($env)) {
366
        $env = array_to_string($env);
367
        $command = "export $env; $command";
368
    }
369
370
    $output = $process->run(new Localhost(), $command, $options);
371
372
    return rtrim($output);
373
}
374 8
375
/**
376
 * Run test command.
377
 * Example:
378
 *
379
 * ```php
380
 * if (test('[ -d {{release_path}} ]')) {
381
 * ...
382
 * }
383
 * ```
384
 *
385
 * @param string $command
386
 * @return bool
387
 */
388
function test($command)
389
{
390
    return run("if $command; then echo 'true'; fi") === 'true';
391
}
392
393
/**
394
 * Run test command locally.
395
 * Example:
396
 *
397
 *     testLocally('[ -d {{local_release_path}} ]')
398
 *
399
 * @param string $command
400
 * @return bool
401
 */
402
function testLocally($command)
403
{
404
    return runLocally("if $command; then echo 'true'; fi") === 'true';
405
}
406
407
/**
408
 * Iterate other hosts, allowing to call run func in callback.
409
 *
410
 * @experimental
411
 * @param Host|Host[] $hosts
412
 * @param callable $callback
413
 */
414
function on($hosts, callable $callback)
415
{
416
    $deployer = Deployer::get();
417
418
    if (!is_array($hosts) && !($hosts instanceof \Traversable)) {
419
        $hosts = [$hosts];
420
    }
421
422
    foreach ($hosts as $host) {
423
        if ($host instanceof Host) {
424
            $host->config()->load();
425
            Context::push(new Context($host, input(), output()));
426
            try {
427
                $callback($host);
428
                $host->config()->save();
429
            } catch (GracefulShutdownException $e) {
430
                $deployer->messenger->renderException($e, $host);
431
            } finally {
432
                Context::pop();
433
            }
434
        } else {
435
            throw new \InvalidArgumentException("Function on can iterate only on Host instances.");
436
        }
437
    }
438
}
439
440
/**
441
 * Run task
442
 *
443
 * @experimental
444
 * @param string $task
445
 */
446
function invoke($task)
447
{
448
    $hosts = [Context::get()->getHost()];
449
    $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

449
    /** @scrutinizer ignore-call */ 
450
    $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...
450
451
    $master = Deployer::get()->master;
452
    $master->run($tasks, $hosts);
453
}
454
455
/**
456
 * Upload file or directory to host.
457
 *
458
 * > You may have noticed that there is a trailing slash (/) at the end of the first argument in the above command, this is necessary to mean “the contents of build“.
459
 * >
460
 * > The alternative, without the trailing slash, would place build, including the directory, within public. This would create a hierarchy that looks like: {{release_path}}/public/build
461
 *
462
 * @param string $source
463
 * @param string $destination
464
 * @param array $config
465
 * @throws RunException
466
 */
467
function upload(string $source, string $destination, $config = [])
468
{
469
    $rsync = Deployer::get()->rsync;
470
    $host = currentHost();
471
    $source = parse($source);
472
    $destination = parse($destination);
473
474
    if ($host instanceof Localhost) {
475
        $rsync->call($host, $source, $destination, $config);
476
    } else {
477
        $rsync->call($host, $source, "{$host->getConnectionString()}:$destination", $config);
478
    }
479 4
}
480 4
481
/**
482
 * Download file or directory from host
483
 *
484
 * @param string $source
485
 * @param string $destination
486
 * @param array $config
487
 * @throws RunException
488
 */
489
function download(string $source, string $destination, $config = [])
490
{
491
    $rsync = Deployer::get()->rsync;
492
    $host = currentHost();
493
    $source = parse($source);
494
    $destination = parse($destination);
495
496
    if ($host instanceof Localhost) {
497
        $rsync->call($host, $source, $destination, $config);
498 7
    } else {
499 7
        $rsync->call($host, "{$host->getConnectionString()}:$source", $destination, $config);
500 7
    }
501
}
502
503
/**
504
 * Writes an info message.
505
 * @param string $message
506
 */
507
function info($message)
508
{
509
    writeln("<fg=green;options=bold>info</> " . parse($message));
510
}
511
512
/**
513
 * Writes an warning message.
514
 * @param string $message
515
 */
516
function warning($message)
517
{
518
    writeln("<fg=yellow;options=bold>warning</> <comment>" . parse($message) . "</comment>");
519
}
520 10
521
/**
522
 * Writes a message to the output and adds a newline at the end.
523
 * @param string|array $message
524
 * @param int $options
525
 */
526
function writeln($message, $options = 0)
527
{
528
    $host = currentHost();
529
    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

529
    output()->writeln("[{$host->getTag()}] " . parse(/** @scrutinizer ignore-type */ $message), $options);
Loading history...
530
}
531 12
532 12
/**
533
 * Writes a message to the output.
534 6
 * @param string $message
535
 * @param int $options
536 12
 */
537
function write($message, $options = 0)
538
{
539
    output()->write(parse($message), false, $options);
540
}
541
542
/**
543
 * Parse set values.
544
 *
545
 * @param string $value
546
 * @return string
547
 */
548
function parse($value)
549
{
550
    return Context::get()->getConfig()->parse($value);
551
}
552
553
/**
554
 * Escapes value so it won't be parsed by @see parse()
555
 *
556
 * @param string $value value to be escaped
557
 * @return string escaped value
558
 */
559
function escape($value)
560
{
561
    $output = str_replace(['{{', '}}'], ['\\{\\{', '\\}\\}'], $value);
562 10
    return $output;
563
}
564
565 10
/**
566
 * Removes escape characters from the string that was processed with @see escape()
567
 *
568
 * @param string $value that has been previous escaped
569
 * @return string value with escape characters removed
570
 */
571
function unescape($value)
572
{
573
    $output = str_replace(['\\{\\{', '\\}\\}'], ['{{', '}}'], $value);
574
    return $output;
575
}
576
577 4
/**
578
 * Setup configuration option.
579
 *
580 4
 * @param string $name
581
 * @param mixed $value
582
 */
583
function set($name, $value)
584
{
585
    if (!Context::has()) {
586
        Deployer::get()->config->set($name, $value);
587
    } else {
588
        Context::get()->getConfig()->set($name, $value);
589
    }
590
}
591
592 1
/**
593
 * Merge new config params to existing config array.
594 1
 *
595
 * @param string $name
596
 * @param array $array
597
 */
598 1
function add($name, $array)
599
{
600
    if (!Context::has()) {
601
        Deployer::get()->config->add($name, $array);
602
    } else {
603 1
        Context::get()->getConfig()->add($name, $array);
604
    }
605 1
}
606 1
607
/**
608 1
 * Get configuration value.
609 1
 *
610
 * @param string $name
611
 * @param mixed|null $default
612
 * @return mixed
613 1
 */
614
function get($name, $default = null)
615
{
616
    if (!Context::has()) {
617
        return Deployer::get()->config->get($name, $default);
618
    } else {
619
        return Context::get()->getConfig()->get($name, $default);
620
    }
621
}
622
623
/**
624
 * Check if there is such configuration option.
625
 *
626
 * @param string $name
627
 * @return boolean
628
 */
629
function has($name)
630
{
631
    if (!Context::has()) {
632
        return Deployer::get()->config->has($name);
633
    } else {
634
        return Context::get()->getConfig()->has($name);
635
    }
636
}
637
638
/**
639
 * @param string $message
640
 * @param string|null $default
641
 * @param string[]|null $autocomplete
642
 * @return string
643
 */
644
function ask($message, $default = null, $autocomplete = null)
645
{
646
    Context::required(__FUNCTION__);
647
648
    if (output()->isQuiet()) {
649
        return $default;
650
    }
651
652
    if (Deployer::isWorker()) {
653
        return Deployer::proxyCallToMaster(currentHost(), __FUNCTION__, ...func_get_args());
654
    }
655
656
    /** @var QuestionHelper $helper */
657
    $helper = Deployer::get()->getHelper('question');
658
659
    $tag = currentHost()->getTag();
660
    $message = "[$tag] <question>$message</question> " . (($default === null) ? "" : "(default: $default) ");
661
662
    $question = new Question($message, $default);
663
    if (!empty($autocomplete)) {
664
        $question->setAutocompleterValues($autocomplete);
665
    }
666
667
    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...
668
}
669
670
/**
671
 * @param string $message
672
 * @param string[] $availableChoices
673
 * @param string|null $default
674
 * @param bool|false $multiselect
675
 * @return string|string[]
676
 */
677
function askChoice($message, array $availableChoices, $default = null, $multiselect = false)
678
{
679
    Context::required(__FUNCTION__);
680
681
    if (empty($availableChoices)) {
682
        throw new \InvalidArgumentException('Available choices should not be empty');
683
    }
684
685
    if ($default !== null && !array_key_exists($default, $availableChoices)) {
686
        throw new \InvalidArgumentException('Default choice is not available');
687
    }
688
689
    if (output()->isQuiet()) {
690
        if ($default === null) {
691
            $default = key($availableChoices);
692
        }
693
        return [$default => $availableChoices[$default]];
694
    }
695
696
    if (Deployer::isWorker()) {
697
        return Deployer::proxyCallToMaster(currentHost(), __FUNCTION__, ...func_get_args());
698
    }
699
700
    $helper = Deployer::get()->getHelper('question');
701
702
    $tag = currentHost()->getTag();
703
    $message = "[$tag] <question>$message</question> " . (($default === null) ? "" : "(default: $default) ");
704
705
    $question = new ChoiceQuestion($message, $availableChoices, $default);
706
    $question->setMultiselect($multiselect);
707
708
    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

708
    return $helper->/** @scrutinizer ignore-call */ ask(input(), output(), $question);
Loading history...
709
}
710
711
/**
712
 * @param string $message
713
 * @param bool $default
714
 * @return bool
715
 */
716
function askConfirmation($message, $default = false)
717
{
718 5
    Context::required(__FUNCTION__);
719
720
    if (output()->isQuiet()) {
721
        return $default;
722
    }
723
724
    if (Deployer::isWorker()) {
725
        return Deployer::proxyCallToMaster(currentHost(), __FUNCTION__, ...func_get_args());
726
    }
727 8
728
    $helper = Deployer::get()->getHelper('question');
729
730
    $yesOrNo = $default ? 'Y/n' : 'y/N';
731
    $tag = currentHost()->getTag();
732
    $message = "[$tag] <question>$message</question> [$yesOrNo] ";
733
734
    $question = new ConfirmationQuestion($message, $default);
735
736
    return $helper->ask(input(), output(), $question);
737
}
738 3
739
/**
740
 * @param string $message
741
 * @return string
742
 */
743 5
function askHiddenResponse(string $message)
744 5
{
745
    Context::required(__FUNCTION__);
746
747 5
    if (output()->isQuiet()) {
748
        return '';
749
    }
750
751
    if (Deployer::isWorker()) {
752 4
        return Deployer::proxyCallToMaster(currentHost(), __FUNCTION__, ...func_get_args());
753
    }
754
755
    $helper = Deployer::get()->getHelper('question');
756
757 4
    $tag = currentHost()->getTag();
758 4
    $message = "[$tag] <question>$message</question> ";
759
760
    $question = new Question($message);
761
    $question->setHidden(true);
762
    $question->setHiddenFallback(false);
763 4
764
    return $helper->ask(input(), output(), $question);
765
}
766
767
/**
768
 * @return InputInterface
769
 */
770
function input()
771
{
772
    return Context::get()->getInput();
773
}
774
775
776
/**
777
 * @return OutputInterface
778
 */
779
function output()
780
{
781
    return Context::get()->getOutput();
782
}
783
784
/**
785
 * Check if command exists
786
 *
787
 * @param string $command
788
 * @return bool
789
 */
790
function commandExist($command)
791
{
792
    return test("hash $command 2>/dev/null");
793
}
794
795
function commandSupportsOption($command, $option)
796
{
797
    $man = run("(man $command 2>&1 || $command -h 2>&1 || $command --help 2>&1) | grep -- $option || true");
798
    if (empty($man)) {
799
        return false;
800
    }
801
    return str_contains($man, $option);
802
}
803
804
function locateBinaryPath($name)
805
{
806
    $nameEscaped = escapeshellarg($name);
807
808
    // Try `command`, should cover all Bourne-like shells
809
    // Try `which`, should cover most other cases
810
    // Fallback to `type` command, if the rest fails
811
    $path = run("command -v $nameEscaped || which $nameEscaped || type -p $nameEscaped");
812
    if (empty($path)) {
813
        throw new \RuntimeException("Can't locate [$nameEscaped] - neither of [command|which|type] commands are available");
814
    }
815
816
    // Deal with issue when `type -p` outputs something like `type -ap` in some implementations
817
    return trim(str_replace("$name is", "", $path));
818
819
}
820