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 ( c538d5...8bac07 )
by Anton
02:32
created

functions.php ➔ task()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.246

Importance

Changes 0
Metric Value
cc 5
nc 6
nop 2
dl 0
loc 26
ccs 11
cts 14
cp 0.7856
crap 5.246
rs 9.1928
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\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
31
/**
32
 * @param string|array ...$hostname
33
 * @return Host|Host[]|Proxy
34
 */
35
function host(...$hostname)
36
{
37 1
    $deployer = Deployer::get();
38 1
    $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
            throw new \InvalidArgumentException(
44
                "Host \"{$host->tag()}\" 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 1
    if (count($aliases) === 1) {
54 1
        $host = new Host($aliases[0]);
55 1
        $deployer->hosts->set($aliases[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
        }, $aliases);
63 1
        return new Proxy($hosts);
64
    }
65
}
66
67
/**
68
 * Get host by host alias.
69
 *
70
 * @param string $alias
71
 * @return Host
72
 */
73
function getHost(string $alias) {
74 1
    return Deployer::get()->hosts->get($alias);
75
}
76
77
/**
78
 * Get current host.
79
 *
80
 * @return Host
81
 */
82
function currentHost()
83
{
84
    return Context::get()->getHost();
85
}
86
87
/**
88
 * @param array ...$hostnames
89
 * @return Localhost|Localhost[]|Proxy
90
 */
91
function localhost(...$hostnames)
92
{
93 2
    $deployer = Deployer::get();
94 2
    $hostnames = Range::expand($hostnames);
95
96 2
    if (count($hostnames) <= 1) {
97 2
        $host = count($hostnames) === 1 ? new Localhost($hostnames[0]) : new Localhost();
98 2
        $deployer->hosts->set($host->alias(), $host);
99 2
        return $host;
100
    } else {
101
        $hosts = array_map(function ($hostname) use ($deployer) {
102
            $host = new Localhost($hostname);
103
            $deployer->hosts->set($host->alias(), $host);
104
            return $host;
105
        }, $hostnames);
106
        return new Proxy($hosts);
107
    }
108
}
109
110
/**
111
 * Load list of hosts from file
112
 *
113
 * @param string $file
114
 * @return Proxy
115
 */
116
function inventory($file)
117
{
118
    $deployer = Deployer::get();
119
    $fileLoader = new FileLoader();
120
    $fileLoader->load($file);
121
122
    $hosts = $fileLoader->getHosts();
123
    foreach ($hosts as $host) {
124
        $deployer->hosts->set($host->alias(), $host);
125
    }
126
127
    return new Proxy($hosts);
128
}
129
130
/**
131
 * Set task description.
132
 *
133
 * @param string $title
134
 * @return string
135
 */
136
function desc($title = null)
137
{
138 10
    static $store = null;
139
140 10
    if ($title === null) {
141 10
        return $store;
142
    } else {
143
        return $store = $title;
144
    }
145
}
146
147
/**
148
 * Define a new task and save to tasks list.
149
 *
150
 * Alternatively get a defined task.
151
 *
152
 * @param string $name Name of current task.
153
 * @param callable|array|string|null $body Callable task, array of other tasks names or nothing to get a defined tasks
154
 * @return Task\Task
155
 */
156
function task($name, $body = null)
157
{
158 10
    $deployer = Deployer::get();
159
160 10
    if (empty($body)) {
161 1
        return $deployer->tasks->get($name);
162
    }
163
164 10
    if (is_callable($body)) {
165 10
        $task = new T($name, $body);
166 1
    } elseif (is_array($body)) {
167 1
        $task = new GroupTask($name, $body);
168
    } else {
169
        throw new \InvalidArgumentException('Task should be a closure or array of other tasks.');
170
    }
171
172 10
    $task->setFilepath(Exception::await());
173 10
    $deployer->tasks->set($name, $task);
174
175 10
    if (!empty(desc())) {
176
        $task->desc(desc());
177
        desc(''); // Clear title.
178
    }
179
180 10
    return $task;
181
}
182
183
/**
184
 * Call that task before specified task runs.
185
 *
186
 * @param string $it The task before $that should be run.
187
 * @param string $that The task to be run.
188
 */
189
function before($it, $that)
190
{
191 1
    $deployer = Deployer::get();
192 1
    $beforeTask = $deployer->tasks->get($it);
193
194 1
    $beforeTask->addBefore($that);
195 1
}
196
197
/**
198
 * Call that task after specified task runs.
199
 *
200
 * @param string $it The task after $that should be run.
201
 * @param string $that The task to be run.
202
 */
203
function after($it, $that)
204
{
205 1
    $deployer = Deployer::get();
206 1
    $afterTask = $deployer->tasks->get($it);
207
208 1
    $afterTask->addAfter($that);
209 1
}
210
211
/**
212
 * Setup which task run on failure of first.
213
 *
214
 * @param string $it The task which need to fail so $that should be run.
215
 * @param string $that The task to be run.
216
 */
217
function fail($it, $that)
218
{
219
    $deployer = Deployer::get();
220
    $deployer->fail->set($it, $that);
221
}
222
223
/**
224
 * Add users options.
225
 *
226
 * @param string $name The option name
227
 * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
228
 * @param int|null $mode The option mode: One of the VALUE_* constants
229
 * @param string $description A description text
230
 * @param string|string[]|int|bool|null $default The default value (must be null for self::VALUE_NONE)
231
 */
232
function option($name, $shortcut = null, $mode = null, $description = '', $default = null)
233
{
234
    Deployer::get()->getConsole()->getDefinition()->addOption(
235
        new InputOption($name, $shortcut, $mode, $description, $default)
236
    );
237
//    Deployer::get()->getConsole()->getUserDefinition()->addOption(
238
//        new InputOption($name, $shortcut, $mode, $description, $default)
239
//    );
240
}
241
242
/**
243
 * Change the current working directory.
244
 *
245
 * @param string $path
246
 */
247
function cd($path)
248
{
249
    set('working_path', parse($path));
250
}
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
    $run = function ($command, $options) {
279
        $host = Context::get()->getHost();
280
281
        $command = parse($command);
282
        $workingPath = get('working_path', '');
283
284
        if (!empty($workingPath)) {
285
            $command = "cd $workingPath && ($command)";
286
        }
287
288
        $env = get('env', []) + ($options['env'] ?? []);
289
        if (!empty($env)) {
290
            $env = array_to_string($env);
291
            $command = "export $env; $command";
292
        }
293
294
        if ($host instanceof Localhost) {
295
            $process = Deployer::get()->processRunner;
296
            $output = $process->run($host, $command, $options);
297
        } else {
298
            $client = Deployer::get()->sshClient;
299
            $output = $client->run($host, $command, $options);
300
        }
301
302
        return rtrim($output);
303
    };
304
305
    if (preg_match('/^sudo\b/', $command)) {
306
        try {
307
            return $run($command, $options);
308
        } catch (RunException $exception) {
309
            $askpass = get('sudo_askpass', '/tmp/dep_sudo_pass');
310
            $password = get('sudo_pass', false);
311
            if ($password === false) {
312
                writeln("<fg=green;options=bold>run</> $command");
313
                $password = askHiddenResponse('Password:');
314
            }
315
            $run("echo -e '#!/bin/sh\necho \"%secret%\"' > $askpass", array_merge($options, ['secret' => $password]));
316
            $run("chmod a+x $askpass", $options);
317
            $run(sprintf('export SUDO_ASKPASS=%s; %s', $askpass, preg_replace('/^sudo\b/', 'sudo -A', $command)), $options);
318
            $run("rm $askpass", $options);
319
        }
320
    } else {
321
        return $run($command, $options);
322
    }
323
}
324
325
326
/**
327
 * Execute commands on local machine
328
 *
329
 * @param string $command Command to run locally.
330
 * @param array $options
331
 * @return string Output of command.
332
 */
333
function runLocally($command, $options = [])
334
{
335 1
    $process = Deployer::get()->processRunner;
336 1
    $command = parse($command);
337
338 1
    $env = get('env', []) + ($options['env'] ?? []);
339 1
    if (!empty($env)) {
340
        $env = array_to_string($env);
341
        $command = "export $env; $command";
342
    }
343
344 1
    $output = $process->run(localhost(), $command, $options);
345
346 1
    return rtrim($output);
347
}
348
349
/**
350
 * Run test command.
351
 * Example:
352
 *
353
 *     test('[ -d {{release_path}} ]')
354
 *
355
 * @param string $command
356
 * @return bool
357
 */
358
function test($command)
359
{
360
    return run("if $command; then echo 'true'; fi") === 'true';
361
}
362
363
/**
364
 * Run test command locally.
365
 * Example:
366
 *
367
 *     testLocally('[ -d {{local_release_path}} ]')
368
 *
369
 * @param string $command
370
 * @return bool
371
 */
372
function testLocally($command)
373
{
374
    return runLocally("if $command; then echo 'true'; fi") === 'true';
375
}
376
377
/**
378
 * Iterate other hosts, allowing to call run func in callback.
379
 *
380
 * @experimental
381
 * @param Host|Host[] $hosts
382
 * @param callable $callback
383
 */
384
function on($hosts, callable $callback)
385
{
386
    $deployer = Deployer::get();
387
388
    if (!is_array($hosts) && !($hosts instanceof \Traversable)) {
389
        $hosts = [$hosts];
390
    }
391
392
    foreach ($hosts as $host) {
393
        if ($host instanceof Host) {
394
            $host->getConfig()->load();
395
            Context::push(new Context($host, input(), output()));
396
            try {
397
                $callback($host);
398
                $host->getConfig()->save();
399
            } catch (GracefulShutdownException $e) {
400
                $deployer->messenger->renderException($e, $host);
401
            } finally {
402
                Context::pop();
403
            }
404
        } else {
405
            throw new \InvalidArgumentException("Function on can iterate only on Host instances.");
406
        }
407
    }
408
}
409
410
/**
411
 * Return hosts based on roles.
412
 *
413
 * @experimental
414
 * @param string[] $roles
415
 * @return Host[]
416
 */
417
function roles(...$roles)
418
{
419
    return Deployer::get()->hostSelector->getByRoles($roles);
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);
432
433
    $executor = Deployer::get()->seriesExecutor;
0 ignored issues
show
Bug introduced by Tseho
The property seriesExecutor does not seem to exist. Did you mean executor?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
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 = [])
0 ignored issues
show
Duplication introduced by Anton Medvedev
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 = [])
0 ignored issues
show
Duplication introduced by Anton Medvedev
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
    output()->writeln("<fg=green;options=bold>info</> " . parse($message));
478
}
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
    $host = currentHost();
497
    output()->writeln("[{$host->tag()}] " . parse($message), $options);
498
}
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
 * Setup configuration option.
512
 *
513
 * @param string $name
514
 * @param mixed $value
515
 */
516 View Code Duplication
function set($name, $value)
0 ignored issues
show
Duplication introduced by Anton Medvedev
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
517
{
518
    if (!Context::has()) {
519
        Deployer::get()->config->set($name, $value);
520
    } else {
521
        Context::get()->getConfig()->set($name, $value);
522
    }
523
}
524
525
/**
526
 * Merge new config params to existing config array.
527
 *
528
 * @param string $name
529
 * @param array $array
530
 */
531 View Code Duplication
function add($name, $array)
0 ignored issues
show
Duplication introduced by Anton Medvedev
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
532
{
533
    if (!Context::has()) {
534
        Deployer::get()->config->add($name, $array);
535
    } else {
536
        Context::get()->getConfig()->add($name, $array);
537
    }
538
}
539
540
/**
541
 * Get configuration value.
542
 *
543
 * @param string $name
544
 * @param mixed|null $default
545
 * @return mixed
546
 */
547 View Code Duplication
function get($name, $default = null)
0 ignored issues
show
Duplication introduced by Anton Medvedev
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
548
{
549 1
    if (!Context::has()) {
550
        return Deployer::get()->config->get($name, $default);
551
    } else {
552 1
        return Context::get()->getConfig()->get($name, $default);
553
    }
554
}
555
556
/**
557
 * Check if there is such configuration option.
558
 *
559
 * @param string $name
560
 * @return boolean
561
 */
562 View Code Duplication
function has($name)
0 ignored issues
show
Duplication introduced by Anton Medvedev
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
563
{
564
    if (!Context::has()) {
565
        return Deployer::get()->config->has($name);
566
    } else {
567
        return Context::get()->getConfig()->has($name);
568
    }
569
}
570
571
/**
572
 * @param string $message
573
 * @param string|null $default
574
 * @param string[]|null $autocomplete
575
 * @return string
576
 */
577
function ask($message, $default = null, $autocomplete = null)
578
{
579
    Context::required(__FUNCTION__);
580
581
    if (output()->isQuiet()) {
582
        return $default;
583
    }
584
585
    /** @var QuestionHelper $helper */
586
    $helper = Deployer::get()->getHelper('question');
587
588
    $tag = currentHost()->tag();
589
    $message = "[$tag] <question>$message</question> " . (($default === null) ? "" : "(default: $default) ");
590
591
    $question = new Question($message, $default);
592
    if (!empty($autocomplete)) {
593
        $question->setAutocompleterValues($autocomplete);
594
    }
595
596
    try {
597
        return $helper->ask(input(), output(), $question);
598
    } catch (MissingInputException $exception) {
599
        throw new Exception("Failed to read input from stdin.\nMake sure what you are asking for input not from parallel task.", $exception->getCode(), $exception);
600
    }
601
}
602
603
/**
604
 * @param string $message
605
 * @param string[] $availableChoices
606
 * @param string|null $default
607
 * @param bool|false $multiselect
608
 * @return string|string[]
609
 */
610
function askChoice($message, array $availableChoices, $default = null, $multiselect = false)
611
{
612
    Context::required(__FUNCTION__);
613
614
    if (empty($availableChoices)) {
615
        throw new \InvalidArgumentException('Available choices should not be empty');
616
    }
617
618
    if ($default !== null && !array_key_exists($default, $availableChoices)) {
619
        throw new \InvalidArgumentException('Default choice is not available');
620
    }
621
622
    if (output()->isQuiet()) {
623
        if ($default === null) {
624
            $default = key($availableChoices);
625
        }
626
        return [$default => $availableChoices[$default]];
627
    }
628
629
    $helper = Deployer::get()->getHelper('question');
630
631
    $tag = currentHost()->tag();
632
    $message = "[$tag] <question>$message</question> " . (($default === null) ? "" : "(default: $default) ");
633
634
    $question = new ChoiceQuestion($message, $availableChoices, $default);
635
    $question->setMultiselect($multiselect);
636
637
    return $helper->ask(input(), output(), $question);
638
}
639
640
/**
641
 * @param string $message
642
 * @param bool $default
643
 * @return bool
644
 */
645
function askConfirmation($message, $default = false)
646
{
647
    Context::required(__FUNCTION__);
648
649
    if (output()->isQuiet()) {
650
        return $default;
651
    }
652
653
    $helper = Deployer::get()->getHelper('question');
654
655
    $yesOrNo = $default ? 'Y/n' : 'y/N';
656
    $tag = currentHost()->tag();
657
    $message = "[$tag] <question>$message</question> [$yesOrNo] ";
658
659
    $question = new ConfirmationQuestion($message, $default);
660
661
    return $helper->ask(input(), output(), $question);
662
}
663
664
/**
665
 * @param string $message
666
 * @return string
667
 */
668
function askHiddenResponse($message)
669
{
670
    Context::required(__FUNCTION__);
671
672
    if (output()->isQuiet()) {
673
        return '';
674
    }
675
676
    $helper = Deployer::get()->getHelper('question');
677
678
    $tag = currentHost()->tag();
679
    $message = "[$tag] <question>$message</question> ";
680
681
    $question = new Question($message);
682
    $question->setHidden(true);
683
    $question->setHiddenFallback(false);
684
685
    return $helper->ask(input(), output(), $question);
686
}
687
688
/**
689
 * @return InputInterface
690
 */
691
function input()
692
{
693
    return Context::get()->getInput();
694
}
695
696
697
/**
698
 * @return OutputInterface
699
 */
700
function output()
701
{
702
    return Context::get()->getOutput();
703
}
704
705
/**
706
 * Check if command exists
707
 *
708
 * @param string $command
709
 * @return bool
710
 */
711
function commandExist($command)
712
{
713
    return test("hash $command 2>/dev/null");
714
}
715
716
function commandSupportsOption($command, $option)
717
{
718
    return test("[[ $(man $command 2>&1 || $command -h 2>&1 || $command --help 2>&1) =~ '$option' ]]");
719
}
720
721
/**
722
 * Parse set values.
723
 *
724
 * @param string $value
725
 * @return string
726
 */
727
function parse($value)
728
{
729 1
    return Context::get()->getConfig()->parse($value);
730
}
731
732
function locateBinaryPath($name)
733
{
734
    $nameEscaped = escapeshellarg($name);
735
736
    // Try `command`, should cover all Bourne-like shells
737
    // Try `which`, should cover most other cases
738
    // Fallback to `type` command, if the rest fails
739
    $path = run("command -v $nameEscaped || which $nameEscaped || type -p $nameEscaped");
740
    if ($path) {
741
        // Deal with issue when `type -p` outputs something like `type -ap` in some implementations
742
        return trim(str_replace("$name is", "", $path));
743
    }
744
745
    throw new \RuntimeException("Can't locate [$nameEscaped] - neither of [command|which|type] commands are available");
746
}
747