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.
Completed
Push — master ( 0b2179...cf8a23 )
by Anton
07:33 queued 05:04
created

functions.php ➔ download()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

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