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
Pull Request — master (#1470)
by Markus
03:01 queued 51s
created

functions.php ➔ task()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 31
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 6.1666

Importance

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