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.
Passed
Pull Request — master (#2180)
by
unknown
01:59
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
 */
266
function within($path, $callback)
267
{
268
    $lastWorkingPath = get('working_path', '');
269
    try {
270
        set('working_path', parse($path));
271
        $callback();
272
    } finally {
273
        set('working_path', $lastWorkingPath);
274
    }
275
}
276
277
/**
278
 * Executes given command on remote host.
279
 *
280
 * Options:
281
 * - `timeout` - Sets the process timeout (max. runtime). The timeout in seconds (default: 300 sec).
282
 * - `secret` - Placeholder `%secret%` can be used in command. Placeholder will be replaced with this value and will not appear in any logs.
283
 *
284
 * Examples:
285
 *
286
 * ```php
287
 * run('echo hello world');
288
 * run('cd {{deploy_path}} && git status');
289
 * run('password %secret%', ['secret' => getenv('CI_SECRET')]);
290
 * ```
291
 *
292
 * ```php
293 8
 * $path = run('readlink {{deploy_path}}/current');
294
 * run("echo $path");
295 8
 * ```
296 8
 *
297
 * @param string $command
298 8
 * @param array $options
299 6
 * @return string
300
 */
301
function run($command, $options = [])
302 8
{
303 8
    $run = function ($command, $options) {
304
        $host = Context::get()->getHost();
305
306
        $command = parse($command);
307
        $workingPath = get('working_path', '');
308 8
309 8
        if (!empty($workingPath)) {
310 8
            $command = "cd $workingPath && ($command)";
311
        }
312
313
        $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

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

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

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

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

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