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 ( 67f1c0...e9d99c )
by Anton
03:05
created

Deployer::__construct()   B

Complexity

Conditions 3
Paths 1

Size

Total Lines 112
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 3.0067

Importance

Changes 0
Metric Value
cc 3
eloc 60
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 112
rs 8.8727
ccs 40
cts 44
cp 0.9091
crap 3.0067

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/* (c) Anton Medvedev <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Deployer;
12
13
use Deployer\Collection\Collection;
14
use Deployer\Command\BlackjackCommand;
15
use Deployer\Command\ConfigCommand;
16
use Deployer\Command\InitCommand;
17
use Deployer\Command\MainCommand;
18
use Deployer\Command\RunCommand;
19
use Deployer\Command\SshCommand;
20
use Deployer\Command\TreeCommand;
21
use Deployer\Command\WorkerCommand;
22
use Deployer\Component\PharUpdate\Console\Command as PharUpdateCommand;
23
use Deployer\Component\PharUpdate\Console\Helper as PharUpdateHelper;
24
use Deployer\Component\Pimple\Container;
25
use Deployer\Component\ProcessRunner\Printer;
26
use Deployer\Component\ProcessRunner\ProcessRunner;
27
use Deployer\Component\Ssh\Client;
28
use Deployer\Configuration\Configuration;
29
use Deployer\Executor\Master;
30
use Deployer\Executor\Messenger;
31
use Deployer\Host\Host;
32
use Deployer\Host\HostCollection;
33
use Deployer\Host\Localhost;
34
use Deployer\Importer\Importer;
35
use Deployer\Logger\Handler\FileHandler;
36
use Deployer\Logger\Handler\NullHandler;
37
use Deployer\Logger\Logger;
38
use Deployer\Selector\Selector;
39
use Deployer\Task\ScriptManager;
40
use Deployer\Task\TaskCollection;
41
use Deployer\Utility\Httpie;
42
use Deployer\Utility\Rsync;
43
use Symfony\Component\Console;
44
use Symfony\Component\Console\Application;
45
use Symfony\Component\Console\Input\ArgvInput;
46
use Symfony\Component\Console\Input\InputDefinition;
47
use Symfony\Component\Console\Input\InputInterface;
48
use Symfony\Component\Console\Input\InputOption;
49
use Symfony\Component\Console\Output\ConsoleOutput;
50
use Symfony\Component\Console\Output\OutputInterface;
51
use Throwable;
52
53
/**
54
 * Deployer class represents DI container for configuring
55
 *
56
 * @property Application $console
57
 * @property InputInterface $input
58
 * @property OutputInterface $output
59
 * @property Task\TaskCollection|Task\Task[] $tasks
60
 * @property HostCollection|Host[] $hosts
61
 * @property Configuration $config
62
 * @property Rsync $rsync
63
 * @property Client $sshClient
64
 * @property ProcessRunner $processRunner
65
 * @property Task\ScriptManager $scriptManager
66
 * @property Selector $selector
67
 * @property Master $master
68
 * @property Messenger $messenger
69
 * @property Messenger $logger
70
 * @property Printer $pop
71
 * @property Collection $fail
72
 * @property InputDefinition $inputDefinition
73
 * @property Importer $importer
74
 */
75
class Deployer extends Container
76
{
77
    /**
78
     * Global instance of deployer. It's can be accessed only after constructor call.
79
     */
80
    private static Deployer $instance;
81 20
82
    public function __construct(Application $console)
83 20
    {
84
        parent::__construct();
85
86
        /******************************
87
         *           Console          *
88
         ******************************/
89 20
90 20
        $console->getDefinition()->addOption(
91
            new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'Recipe file path'),
92
        );
93
94 12
        $this['console'] = function () use ($console) {
95
            return $console;
96
        };
97
        $this['input'] = function () {
98
            throw new \RuntimeException('Uninitialized "input" in Deployer container.');
99
        };
100
        $this['output'] = function () {
101
            throw new \RuntimeException('Uninitialized "output" in Deployer container.');
102
        };
103 12
        $this['inputDefinition'] = function () {
104
            return new InputDefinition();
105
        };
106 12
        $this['questionHelper'] = function () {
107
            return $this->getHelper('question');
108
        };
109
110
        /******************************
111
         *           Config           *
112
         ******************************/
113
114 20
        $this['config'] = function () {
115
            return new Configuration();
116 20
        };
117 20
        // -l  act as if it had been invoked as a login shell (i.e. source ~/.profile file)
118 20
        // -s  commands are read from the standard input (no arguments should remain after this option)
119 20
        $this->config['shell'] = function () {
120 20
            if (currentHost() instanceof Localhost) {
121 20
                return 'bash -s'; // Non-login shell for localhost.
122 20
            }
123
            return 'bash -ls';
124
        };
125
        $this->config['forward_agent'] = true;
126
        $this->config['ssh_multiplexing'] = true;
127
128
        /******************************
129 14
         *            Core            *
130
         ******************************/
131
132 12
        $this['pop'] = function ($c) {
133
            return new Printer($c['output']);
134
        };
135
        $this['sshClient'] = function ($c) {
136
            return new Client($c['output'], $c['pop'], $c['logger']);
137
        };
138 10
        $this['rsync'] = function ($c) {
139
            return new Rsync($c['pop'], $c['output']);
140
        };
141 15
        $this['processRunner'] = function ($c) {
142
            return new ProcessRunner($c['pop'], $c['logger']);
143
        };
144 14
        $this['tasks'] = function () {
145
            return new TaskCollection();
146
        };
147 14
        $this['hosts'] = function () {
148
            return new HostCollection();
149
        };
150 10
        $this['scriptManager'] = function ($c) {
151
            return new ScriptManager($c['tasks']);
152
        };
153 8
        $this['selector'] = function ($c) {
154
            return new Selector($c['hosts']);
155
        };
156 12
        $this['fail'] = function () {
157
            return new Collection();
158
        };
159 12
        $this['messenger'] = function ($c) {
160 12
            return new Messenger($c['input'], $c['output'], $c['logger']);
161 12
        };
162 12
        $this['master'] = function ($c) {
163
            return new Master(
164
                $c['hosts'],
165
                $c['input'],
166 12
                $c['output'],
167 12
                $c['messenger'],
168 12
            );
169 12
        };
170 12
        $this['importer'] = function () {
171 12
            return new Importer();
172 12
        };
173
174
        /******************************
175
         *           Logger           *
176
         ******************************/
177
178
        $this['log_handler'] = function () {
179
            return !empty($this['log'])
180
                ? new FileHandler($this['log'])
181 14
                : new NullHandler();
182
        };
183 14
        $this['logger'] = function () {
184
            return new Logger($this['log_handler']);
185
        };
186 14
187
        self::$instance = $this;
188
    }
189 20
190 20
    public static function get(): self
191
    {
192
        return self::$instance;
193
    }
194
195 29
    /**
196
     * Init console application
197 29
     */
198
    public function init()
199
    {
200
        $this->addTaskCommands();
201
        $this->getConsole()->add(new BlackjackCommand());
202
        $this->getConsole()->add(new ConfigCommand($this));
203 12
        $this->getConsole()->add(new WorkerCommand($this));
204
        $this->getConsole()->add(new InitCommand());
205 12
        $this->getConsole()->add(new TreeCommand($this));
206 12
        $this->getConsole()->add(new SshCommand($this));
207 12
        $this->getConsole()->add(new RunCommand($this));
208 12
        if (self::isPharArchive()) {
209 12
            $selfUpdate = new PharUpdateCommand('self-update');
210 12
            $selfUpdate->setDescription('Updates deployer.phar to the latest version');
211 12
            $selfUpdate->setManifestUri('https://deployer.org/manifest.json');
212 12
            $selfUpdate->setRunningFile(DEPLOYER_BIN);
213 12
            $this->getConsole()->add($selfUpdate);
214
            $this->getConsole()->getHelperSet()->set(new PharUpdateHelper());
215
        }
216
    }
217
218
    /**
219
     * Transform tasks to console commands.
220 12
     */
221
    public function addTaskCommands()
222
    {
223
        foreach ($this->tasks as $name => $task) {
224
            $command = new MainCommand($name, $task->getDescription(), $this);
225 12
            $command->setHidden($task->isHidden());
226
227 12
            $this->getConsole()->add($command);
228 12
        }
229 12
    }
230
231 12
    /**
232
     * @return mixed
233 12
     * @throws \InvalidArgumentException
234
     */
235
    public function __get(string $name)
236
    {
237
        if (isset($this[$name])) {
238
            return $this[$name];
239
        } else {
240 27
            throw new \InvalidArgumentException("Property \"$name\" does not exist.");
241
        }
242 27
    }
243 27
244
    /**
245
     * @param mixed $value
246
     */
247
    public function __set(string $name, $value)
248
    {
249
        $this[$name] = $value;
250
    }
251
252
    public function getConsole(): Application
253 12
    {
254
        return $this['console'];
255 12
    }
256 12
257
    public function getHelper(string $name): Console\Helper\HelperInterface
258
    {
259
        return $this->getConsole()->getHelperSet()->get($name);
260
    }
261 12
262
    /**
263 12
     * Run Deployer
264
     */
265
    public static function run(string $version, ?string $deployFile)
266
    {
267
        if (str_contains($version, 'master')) {
268
            // Get version from composer.lock
269
            $lockFile = __DIR__ . '/../../../../composer.lock';
270 12
            if (file_exists($lockFile)) {
271
                $content = file_get_contents($lockFile);
272 12
                $json = json_decode($content);
273
                foreach ($json->packages as $package) {
274
                    if ($package->name === 'deployer/deployer') {
275
                        $version = $package->version;
276
                    }
277
                }
278
            }
279
        }
280
281
        // Version must be without "v" prefix.
282
        //    Incorrect: v7.0.0
283
        //    Correct: 7.0.0
284
        // But deployphp/deployer uses tags with "v", and it gets passed to
285
        // the composer.json file. Let's manually remove it from the version.
286
        if (preg_match("/^v/", $version)) {
287
            $version = substr($version, 1);
288
        }
289
290
        if (!defined('DEPLOYER_VERSION')) {
291
            define('DEPLOYER_VERSION', $version);
292
        }
293
294
        $input = new ArgvInput();
295
        $output = new ConsoleOutput();
296
297
        try {
298
            // Init Deployer
299
            $console = new Application('Deployer', $version);
300
            $deployer = new self($console);
301
302
            // Import recipe file
303 12
            if (is_readable($deployFile ?? '')) {
304
                $deployer->importer->import($deployFile);
305 12
            }
306
307
            // Run Deployer
308
            $deployer->init();
309 12
            $console->run($input, $output);
310
311 12
        } catch (Throwable $exception) {
312
            if (str_contains("$input", "-vvv")) {
313 12
                $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
314 12
            }
315
            self::printException($output, $exception);
316
317
            exit(1);
318
        }
319
    }
320 12
321
    public static function printException(OutputInterface $output, Throwable $exception)
322 12
    {
323
        $class = get_class($exception);
324
        $file = basename($exception->getFile());
325
        $output->writeln([
326
            "<fg=white;bg=red> {$class} </> <comment>in {$file} on line {$exception->getLine()}:</>",
327
            "",
328
            implode("\n", array_map(function ($line) {
329
                return "  " . $line;
330
            }, explode("\n", $exception->getMessage()))),
331
            "",
332
        ]);
333
        if ($output->isDebug()) {
334
            $output->writeln($exception->getTraceAsString());
335
        }
336
337
        if ($exception->getPrevious()) {
338
            self::printException($output, $exception->getPrevious());
339
        }
340
    }
341 1
342 1
    public static function isWorker(): bool
343
    {
344
        return defined('MASTER_ENDPOINT');
345
    }
346
347
    /**
348
     * @param mixed ...$arguments
349
     * @return array|bool|string
350
     * @throws \Exception
351
     */
352
    public static function masterCall(Host $host, string $func, ...$arguments)
353
    {
354
        // As request to master will stop master permanently, wait a little bit
355
        // in order for ticker gather worker outputs and print it to user.
356
        usleep(100_000); // Sleep 100ms.
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting ',' or ')' on line 356 at column 18
Loading history...
357
358
        return Httpie::get(MASTER_ENDPOINT . '/proxy')
359
            ->setopt(CURLOPT_CONNECTTIMEOUT, 0) // no timeout
360
            ->setopt(CURLOPT_TIMEOUT, 0) // no timeout
361
            ->jsonBody([
362
                'host' => $host->getAlias(),
363
                'func' => $func,
364
                'arguments' => $arguments,
365
            ])
366
            ->getJson();
367
    }
368
369
    public static function isPharArchive(): bool
370
    {
371
        return 'phar:' === substr(__FILE__, 0, 5);
372
    }
373
}
374