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
Push — master ( 695db5...fba8ee )
by Anton
02:16
created

src/functions.php (1 issue)

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