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 ( e51949...763526 )
by Anton
01:57
created

functions.php ➔ ask()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

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