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 ( 8d0fcf...6c7311 )
by Anton
05:08 queued 02:10
created

functions.php ➔ runLocally()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 2
dl 0
loc 16
ccs 9
cts 9
cp 1
crap 2
rs 9.4285
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 10
    $deployer = Deployer::get();
74 10
    $hostnames = Range::expand($hostnames);
75
76 10
    if (count($hostnames) <= 1) {
77 9
        $host = count($hostnames) === 1 ? new Localhost($hostnames[0]) : new Localhost();
78 9
        $deployer->hosts->set($host->getHostname(), $host);
79 9
        return $host;
80
    } else {
81
        $hosts = array_map(function ($hostname) use ($deployer) {
82 1
            $host = new Localhost($hostname);
83 1
            $deployer->hosts->set($host->getHostname(), $host);
84 1
            return $host;
85 1
        }, $hostnames);
86 1
        return new Proxy($hosts);
87
    }
88
}
89
90
/**
91
 * Load list of hosts from file
92
 *
93
 * @param string $file
94
 */
95
function inventory($file)
96
{
97 1
    $deployer = Deployer::get();
98 1
    $fileLoader = new FileLoader();
99 1
    $fileLoader->load($file);
100
101 1
    foreach ($fileLoader->getHosts() as $host) {
102 1
        $deployer->hosts->set($host->getHostname(), $host);
103
    }
104 1
}
105
106
/**
107
 * Set task description.
108
 *
109
 * @param string $title
110
 * @return string
111
 */
112
function desc($title = null)
113
{
114 12
    static $store = null;
115
116 12
    if ($title === null) {
117 12
        return $store;
118
    } else {
119 8
        return $store = $title;
120
    }
121
}
122
123
/**
124
 * Define a new task and save to tasks list.
125
 *
126
 * Alternatively get a defined task.
127
 *
128
 * @param string $name Name of current task.
129
 * @param callable|array|string|null $body Callable task, array of other tasks names or nothing to get a defined tasks
130
 * @return Task\Task
131
 * @throws \InvalidArgumentException
132
 */
133
function task($name, $body = null)
134
{
135 12
    $deployer = Deployer::get();
136
137 12
    if (empty($body)) {
138 1
        $task = $deployer->tasks->get($name);
139 1
        return $task;
140
    }
141
142 12
    if (is_callable($body)) {
143 10
        $task = new T($name, $body);
144 12
    } elseif (is_array($body)) {
145 10
        $task = new GroupTask($name, $body);
146 11
    } elseif (is_string($body)) {
147
        $task = new T($name, function () use ($body) {
148 1
            cd('{{release_path}}');
149 1
            run($body);
150 11
        });
151
    } else {
152
        throw new \InvalidArgumentException('Task should be an closure or array of other tasks.');
153
    }
154
155 12
    $deployer->tasks->set($name, $task);
156
157 12
    if (!empty(desc())) {
158 8
        $task->desc(desc());
159 8
        desc(''); // Clear title.
160
    }
161
162 12
    return $task;
163
}
164
165
/**
166
 * Call that task before specified task runs.
167
 *
168
 * @param string $it
169
 * @param string $that
170
 */
171
function before($it, $that)
172
{
173 1
    $deployer = Deployer::get();
174 1
    $beforeTask = $deployer->tasks->get($it);
175
176 1
    $beforeTask->addBefore($that);
177 1
}
178
179
/**
180
 * Call that task after specified task runs.
181
 *
182
 * @param string $it
183
 * @param string $that
184
 */
185
function after($it, $that)
186
{
187 1
    $deployer = Deployer::get();
188 1
    $afterTask = $deployer->tasks->get($it);
189
190 1
    $afterTask->addAfter($that);
191 1
}
192
193
/**
194
 * Setup which task run on failure of first.
195
 *
196
 * @param string $it
197
 * @param string $that
198
 */
199
function fail($it, $that)
200
{
201 8
    $deployer = Deployer::get();
202 8
    $deployer['fail']->set($it, $that);
203 8
}
204
205
/**
206
 * Add users arguments.
207
 *
208
 * Note what Deployer already has one argument: "stage".
209
 *
210
 * @param string $name
211
 * @param int $mode
212
 * @param string $description
213
 * @param mixed $default
214
 */
215
function argument($name, $mode = null, $description = '', $default = null)
216
{
217
    Deployer::get()->getConsole()->getUserDefinition()->addArgument(
218
        new InputArgument($name, $mode, $description, $default)
219
    );
220
}
221
222
/**
223
 * Add users options.
224
 *
225
 * @param string $name
226
 * @param string $shortcut
227
 * @param int $mode
228
 * @param string $description
229
 * @param mixed $default
230
 */
231
function option($name, $shortcut = null, $mode = null, $description = '', $default = null)
232
{
233 8
    Deployer::get()->getConsole()->getUserDefinition()->addOption(
234 8
        new InputOption($name, $shortcut, $mode, $description, $default)
235
    );
236 8
}
237
238
/**
239
 * Change the current working directory.
240
 *
241
 * @param string $path
242
 */
243
function cd($path)
244
{
245 5
    set('working_path', parse($path));
246 5
}
247
248
/**
249
 * Execute a callback within a specific directory and revert back to the initial working directory.
250
 *
251
 * @param string $path
252
 * @param callable $callback
253
 */
254
function within($path, $callback)
255
{
256
    $lastWorkingPath = get('working_path', '');
257
    set('working_path', parse($path));
258
    $callback();
259
    set('working_path', $lastWorkingPath);
260
}
261
262
/**
263
 * Return the current working path.
264
 *
265
 * @deprecated Will be removed in 6.x
266
 * @return string
267
 */
268
function workingPath()
269
{
270
    return get('working_path', false);
271
}
272
273
/**
274
 * Run command.
275
 *
276
 * @param string $command
277
 * @param array $options
278
 * @return string
279
 */
280
function run($command, $options = [])
281
{
282 7
    $client = Deployer::get()->sshClient;
283 7
    $process = Deployer::get()->processRunner;
284 7
    $host = Context::get()->getHost();
285 7
    $hostname = $host->getHostname();
286
287 7
    $command = parse($command);
288 7
    $workingPath = get('working_path', '');
289
290 7
    if (!empty($workingPath)) {
291 5
        $command = "cd $workingPath && ($command)";
292
    }
293
294 7
    $env = get('env', []) + ($options['env'] ?? []);
295 7
    if (!empty($env)) {
296 1
        $env = array_to_string($env);
297 1
        $command = "export $env; $command";
298
    }
299
300 7
    if ($host instanceof Localhost) {
301 7
        $output = $process->run($hostname, $command, $options);
302
    } else {
303
        $output = $client->run($host, $command, $options);
304
    }
305
306 7
    return rtrim($output);
307
}
308
309
/**
310
 * Execute commands on local machine
311
 *
312
 * @param string $command Command to run locally.
313
 * @param array $options
314
 * @return string Output of command.
315
 */
316
function runLocally($command, $options = [])
317
{
318 5
    $process = Deployer::get()->processRunner;
319 5
    $hostname = 'localhost';
320 5
    $command = parse($command);
321
322 5
    $env = get('env', []) + ($options['env'] ?? []);
323 5
    if (!empty($env)) {
324 1
        $env = array_to_string($env);
325 1
        $command = "export $env; $command";
326
    }
327
328 5
    $output = $process->run($hostname, $command, $options);
329
330 5
    return rtrim($output);
331
}
332
333
/**
334
 * Run test command.
335
 * Example:
336
 *
337
 *     test('[ -d {{release_path}} ]')
338
 *
339
 * @param string $command
340
 * @return bool
341
 */
342
function test($command)
343
{
344 5
    return run("if $command; then echo 'true'; fi") === 'true';
345
}
346
347
/**
348
 * Run test command locally.
349
 * Example:
350
 *
351
 *     testLocally('[ -d {{local_release_path}} ]')
352
 *
353
 * @param string $command
354
 * @return bool
355
 */
356
function testLocally($command)
357
{
358
    return runLocally("if $command; then echo 'true'; fi") === 'true';
359
}
360
361
/**
362
 * Iterate other hosts, allowing to call run func in callback.
363
 *
364
 * @experimental
365
 * @param Host|Host[] $hosts
366
 * @param callable $callback
367
 */
368
function on($hosts, callable $callback)
369
{
370 3
    $input = Context::has() ? input() : null;
371 3
    $output = Context::has() ? output() : null;
372
373 3
    if (!is_array($hosts) && !($hosts instanceof \Traversable)) {
374
        $hosts = [$hosts];
375
    }
376
377 3
    foreach ($hosts as $host) {
378 3
        if ($host instanceof Host) {
379 3
            Context::push(new Context($host, $input, $output));
380 3
            $callback($host);
381 3
            Context::pop();
382
        } else {
383 3
            throw new \InvalidArgumentException("Function on can iterate only on Host instances.");
384
        }
385
    }
386 3
}
387
388
/**
389
 * Return hosts based on roles.
390
 *
391
 * @experimental
392
 * @param string[] $roles
393
 * @return Host[]
394
 */
395
function roles(...$roles)
396
{
397
    return Deployer::get()->hostSelector->getByRoles($roles);
398
}
399
400
/**
401
 * Run task
402
 *
403
 * @experimental
404
 * @param string $task
405
 */
406
function invoke($task)
407
{
408
    $informer = Deployer::get()->informer;
409
    $task = Deployer::get()->tasks->get($task);
410
    $input = Context::get()->getInput();
411
    $output = Context::get()->getOutput();
412
    $host = Context::get()->getHost();
413
414
    $informer->startTask($task->getName());
415
    $task->run(new Context($host, $input, $output));
416
    $informer->endTask();
0 ignored issues
show
Bug introduced by
The call to endTask() misses a required argument $task.

This check looks for function calls that miss required arguments.

Loading history...
417
}
418
419
/**
420
 * Upload file or directory to host
421
 *
422
 * @param string $source
423
 * @param string $destination
424
 * @param array $config
425
 */
426 View Code Duplication
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 View Code Duplication
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
        $sshArguments = $host->getSshArguments()->getCliArguments();
465
        if (empty($sshArguments) === false) {
466
            if (!isset($config['options']) || !is_array($config['options'])) {
467
                $config['options'] = [];
468
            }
469
            $config['options'][] = "-e 'ssh $sshArguments'";
470
        }
471
        $rsync->call($host->getHostname(), "$host:$source", $destination, $config);
472
    }
473
}
474
475
/**
476
 * Writes a message to the output and adds a newline at the end.
477
 * @param string|array $message
478
 * @param int $options
479
 */
480
function writeln($message, $options = 0)
481
{
482 5
    output()->writeln(parse($message), $options);
483 5
}
484
485
/**
486
 * Writes a message to the output.
487
 * @param string $message
488
 * @param int $options
489
 */
490
function write($message, $options = 0)
491
{
492
    output()->write(parse($message), $options);
493
}
494
495
/**
496
 * Setup configuration option.
497
 *
498
 * @param string $name
499
 * @param mixed $value
500
 */
501
function set($name, $value)
502
{
503 9
    if (!Context::has()) {
504 9
        Deployer::setDefault($name, $value);
505
    } else {
506 5
        Context::get()->getConfig()->set($name, $value);
507
    }
508 9
}
509
510
/**
511
 * Merge new config params to existing config array.
512
 *
513
 * @param string $name
514
 * @param array $array
515
 */
516
function add($name, $array)
517
{
518 1
    if (!Context::has()) {
519
        Deployer::addDefault($name, $array);
520
    } else {
521 1
        Context::get()->getConfig()->add($name, $array);
522
    }
523 1
}
524
525
/**
526
 * Get configuration value.
527
 *
528
 * @param string $name
529
 * @param mixed|null $default
530
 * @return mixed
531
 */
532
function get($name, $default = null)
533
{
534 8
    if (!Context::has()) {
535
        return Deployer::getDefault($name, $default);
536
    } else {
537 8
        return Context::get()->getConfig()->get($name, $default);
538
    }
539
}
540
541
/**
542
 * Check if there is such configuration option.
543
 *
544
 * @param string $name
545
 * @return boolean
546
 */
547
function has($name)
548
{
549
    if (!Context::has()) {
550
        return Deployer::hasDefault($name);
551
    } else {
552
        return Context::get()->getConfig()->has($name);
553
    }
554
}
555
556
/**
557
 * @param string $message
558
 * @param string|null $default
559
 * @param string[]|null $suggestedChoices
560
 * @return string
561
 * @codeCoverageIgnore
562
 */
563
function ask($message, $default = null, $suggestedChoices = null)
564
{
565
    Context::required(__FUNCTION__);
566
567
    if (($suggestedChoices !== null) && (empty($suggestedChoices))) {
568
        throw new \InvalidArgumentException('Suggested choices should not be empty');
569
    }
570
571
    if (isQuiet()) {
572
        return $default;
573
    }
574
575
    $helper = Deployer::get()->getHelper('question');
576
577
    $message = "<question>$message" . (($default === null) ? "" : " [$default]") . "</question> ";
578
579
    $question = new Question($message, $default);
580
581
    if (empty($suggestedChoices) === false) {
582
        $question->setAutocompleterValues($suggestedChoices);
583
    }
584
585
    return $helper->ask(input(), output(), $question);
586
}
587
588
/**
589
 * @param string $message
590
 * @param string[] $availableChoices
591
 * @param string|null $default
592
 * @param bool|false $multiselect
593
 * @return array
594
 * @codeCoverageIgnore
595
 */
596
function askChoice($message, array $availableChoices, $default = null, $multiselect = false)
597
{
598
    Context::required(__FUNCTION__);
599
600
    if (empty($availableChoices)) {
601
        throw new \InvalidArgumentException('Available choices should not be empty');
602
    }
603
604
    if ($default !== null && !array_key_exists($default, $availableChoices)) {
605
        throw new \InvalidArgumentException('Default choice is not available');
606
    }
607
608
    if (isQuiet()) {
609
        if ($default === null) {
610
            $default = key($availableChoices);
611
        }
612
        return [$default => $availableChoices[$default]];
613
    }
614
615
    $helper = Deployer::get()->getHelper('question');
616
617
    $message = "<question>$message" . (($default === null) ? "" : " [$default]") . "</question> ";
618
619
    $question = new ChoiceQuestion($message, $availableChoices, $default);
620
    $question->setMultiselect($multiselect);
621
622
    return $helper->ask(input(), output(), $question);
623
}
624
625
/**
626
 * @param string $message
627
 * @param bool $default
628
 * @return bool
629
 * @codeCoverageIgnore
630
 */
631
function askConfirmation($message, $default = false)
632
{
633
    Context::required(__FUNCTION__);
634
635
    if (isQuiet()) {
636
        return $default;
637
    }
638
639
    $helper = Deployer::get()->getHelper('question');
640
641
    $yesOrNo = $default ? 'Y/n' : 'y/N';
642
    $message = "<question>$message [$yesOrNo]</question> ";
643
644
    $question = new ConfirmationQuestion($message, $default);
645
646
    return $helper->ask(input(), output(), $question);
647
}
648
649
/**
650
 * @param string $message
651
 * @return string
652
 * @codeCoverageIgnore
653
 */
654
function askHiddenResponse($message)
655
{
656
    Context::required(__FUNCTION__);
657
658
    if (isQuiet()) {
659
        return '';
660
    }
661
662
    $helper = Deployer::get()->getHelper('question');
663
664
    $message = "<question>$message</question> ";
665
666
    $question = new Question($message);
667
    $question->setHidden(true);
668
    $question->setHiddenFallback(false);
669
670
    return $helper->ask(input(), output(), $question);
671
}
672
673
/**
674
 * @return InputInterface
675
 */
676
function input()
677
{
678 6
    return Context::get()->getInput();
679
}
680
681
682
/**
683
 * @return OutputInterface
684
 */
685
function output()
686
{
687 7
    return Context::get()->getOutput();
688
}
689
690
/**
691
 * @return bool
692
 */
693
function isQuiet()
694
{
695
    return OutputInterface::VERBOSITY_QUIET === output()->getVerbosity();
696
}
697
698
699
/**
700
 * @return bool
701
 */
702
function isVerbose()
703
{
704 1
    return OutputInterface::VERBOSITY_VERBOSE <= output()->getVerbosity();
705
}
706
707
708
/**
709
 * @return bool
710
 */
711
function isVeryVerbose()
712
{
713
    return OutputInterface::VERBOSITY_VERY_VERBOSE <= output()->getVerbosity();
714
}
715
716
717
/**
718
 * @return bool
719
 */
720
function isDebug()
721
{
722
    return OutputInterface::VERBOSITY_DEBUG <= output()->getVerbosity();
723
}
724
725
/**
726
 * Check if command exists
727
 *
728
 * @param string $command
729
 * @return bool
730
 */
731
function commandExist($command)
732
{
733 3
    return test("hash $command 2>/dev/null");
734
}
735
736
function commandSupportsOption($command, $option)
737
{
738 4
    return test("[[ $(man $command 2>&1 || $command -h 2>&1 || $command --help 2>&1) =~ '$option' ]]");
739
}
740
741
/**
742
 * Parse set values.
743
 *
744
 * @param string $value
745
 * @return string
746
 */
747
function parse($value)
748
{
749 9
    return Context::get()->getConfig()->parse($value);
750
}
751
752
function locateBinaryPath($name)
753
{
754 3
    $nameEscaped = escapeshellarg($name);
755
756
    // Try `command`, should cover all Bourne-like shells
757 3
    if (commandExist("command")) {
758 3
        return run("command -v $nameEscaped");
759
    }
760
761
    // Try `which`, should cover most other cases
762
    if (commandExist("which")) {
763
        return run("which $nameEscaped");
764
    }
765
766
    // Fallback to `type` command, if the rest fails
767
    if (commandExist("type")) {
768
        $result = run("type -p $nameEscaped");
769
770
        if ($result) {
771
            // Deal with issue when `type -p` outputs something like `type -ap` in some implementations
772
            return trim(str_replace("$name is", "", $result));
773
        }
774
    }
775
776
    throw new \RuntimeException("Can't locate [$nameEscaped] - neither of [command|which|type] commands are available");
777
}
778