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.
Test Failed
Push — master ( 44f5f8...8d9d8e )
by Anton
02:15
created

src/functions.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

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
The call to ScriptManager::getTasks() has too many arguments starting with $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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
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