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.
Test Failed
Push — master ( 09a43c...4f79d5 )
by Anton
03:00
created

Deployer   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 34

Test Coverage

Coverage 64.42%

Importance

Changes 0
Metric Value
dl 0
loc 303
ccs 67
cts 104
cp 0.6442
rs 9.92
c 0
b 0
f 0
wmc 31
lcom 1
cbo 34

13 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 98 2
A get() 0 4 1
A init() 0 17 2
A addTaskCommands() 0 9 2
A __get() 0 8 2
A __set() 0 4 1
A getConsole() 0 4 1
A getHelper() 0 4 1
A run() 0 21 2
A load() 0 20 4
A printException() 0 15 1
C collectAnonymousStats() 0 35 11
A isPharArchive() 0 4 1
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\Collection\Collection;
11
use Deployer\Component\PharUpdate\Console\Command as PharUpdateCommand;
12
use Deployer\Component\PharUpdate\Console\Helper as PharUpdateHelper;
13
use Deployer\Component\ProcessRunner\Printer;
14
use Deployer\Component\ProcessRunner\ProcessRunner;
15
use Deployer\Component\Ssh\Client;
16
use Deployer\Configuration\Configuration;
17
use Deployer\Console\CommandEvent;
18
use Deployer\Console\DiceCommand;
19
use Deployer\Console\InitCommand;
20
use Deployer\Console\MainCommand;
21
use Deployer\Console\RunCommand;
22
use Deployer\Console\SshCommand;
23
use Deployer\Console\TreeCommand;
24
use Deployer\Console\WorkerCommand;
25
use Deployer\Executor\Messenger;
26
use Deployer\Executor\ParallelExecutor;
27
use Deployer\Logger\Handler\FileHandler;
28
use Deployer\Logger\Handler\NullHandler;
29
use Deployer\Logger\Logger;
30
use Deployer\Selector\Selector;
31
use Deployer\Task;
32
use Deployer\Utility\Reporter;
33
use Deployer\Utility\Rsync;
34
use Pimple\Container;
35
use Symfony\Component\Console;
36
use Symfony\Component\Console\Application;
37
use Symfony\Component\Console\Input\ArgvInput;
38
use Symfony\Component\Console\Input\InputDefinition;
39
use Symfony\Component\Console\Input\InputInterface;
40
use Symfony\Component\Console\Input\InputOption;
41
use Symfony\Component\Console\Output\ConsoleOutput;
42
use Symfony\Component\Console\Output\OutputInterface;
43
use Throwable;
44
45
/**
46
 * Deployer class represents DI container for configuring
47
 *
48
 * @property Application $console
49
 * @property InputInterface $input
50
 * @property OutputInterface $output
51
 * @property Task\TaskCollection|Task\Task[] $tasks
52
 * @property Host\HostCollection|Collection|Host\Host[] $hosts
53
 * @property Configuration $config
54
 * @property Rsync $rsync
55
 * @property Client $sshClient
56
 * @property ProcessRunner $processRunner
57
 * @property Task\ScriptManager $scriptManager
58
 * @property Selector $selector
59
 * @property ParallelExecutor $executor
60
 * @property Messenger $messenger
61
 * @property Messenger $logger
62
 * @property Printer $pop
63
 * @property Collection $fail
64
 * @property InputDefinition $inputDefinition
65
 */
66
class Deployer extends Container
67
{
68
    /**
69
     * Global instance of deployer. It's can be accessed only after constructor call.
70
     * @var Deployer
71
     */
72
    private static $instance;
73
74 16
    public function __construct(Application $console)
75
    {
76 16
        parent::__construct();
77
78
        /******************************
79
         *           Console          *
80
         ******************************/
81
82 16
        $console->getDefinition()->addOption(
83 16
            new InputOption('--file', '-f', InputOption::VALUE_OPTIONAL, 'Specify Deployer file')
84
        );
85
86
        $this['console'] = function () use ($console) {
87 6
            return $console;
88
        };
89
        $this['input'] = function () {
90
            throw new \RuntimeException('Uninitialized "input" in Deployer container.');
91
        };
92
        $this['output'] = function () {
93
            throw new \RuntimeException('Uninitialized "output" in Deployer container.');
94
        };
95
        $this['inputDefinition'] = function () {
96 6
            return new InputDefinition();
97
        };
98
99
        /******************************
100
         *           Config           *
101
         ******************************/
102
103
        $this['config'] = function () {
104 16
            return new Configuration();
105
        };
106 16
        $this->config['ssh_multiplexing'] = true;
107 16
        $this->config['default_stage'] = null;
108
109
        /******************************
110
         *            Core            *
111
         ******************************/
112
113
        $this['pop'] = function ($c) {
114 6
            return new Printer($c['output']);
115
        };
116
        $this['sshClient'] = function ($c) {
117 5
            return new Client($c['output'], $c['pop'], $c['logger']);
118
        };
119
        $this['rsync'] = function ($c) {
120
            return new Rsync($c['pop'], $c['output']);
121
        };
122
        $this['processRunner'] = function ($c) {
123 5
            return new ProcessRunner($c['pop'], $c['logger']);
124
        };
125
        $this['tasks'] = function () {
126 16
            return new Task\TaskCollection();
127
        };
128
        $this['hosts'] = function () {
129 9
            return new Host\HostCollection();
130
        };
131
        $this['scriptManager'] = function ($c) {
132 7
            return new Task\ScriptManager($c['tasks']);
133
        };
134
        $this['selector'] = function ($c) {
135 4
            return new Selector($c['hosts']);
136
        };
137
        $this['fail'] = function () {
138 6
            return new Collection();
139
        };
140
        $this['messenger'] = function ($c) {
141 6
            return new Messenger($c['input'], $c['output']);
142
        };
143
        $this['executor'] = function ($c) {
144 5
            return new ParallelExecutor(
145 5
                $c['input'],
146 5
                $c['output'],
147 5
                $c['messenger'],
148 5
                $c['sshClient'],
149 5
                $c['config']
150
            );
151
        };
152
153
        /******************************
154
         *           Logger           *
155
         ******************************/
156
157
        $this['log_handler'] = function () {
158 6
            return !empty($this->config['log_file'])
159
                ? new FileHandler($this->config['log_file'])
160 6
                : new NullHandler();
161
        };
162
        $this['logger'] = function () {
163 6
            return new Logger($this['log_handler']);
164
        };
165
166 16
        self::$instance = $this;
167
168
        task('connect', function () {
169
            $this['sshClient']->connect(currentHost());
170 16
        })->desc('Connect to remote server');
171 16
    }
172
173
    /**
174
     * @return Deployer
175
     */
176 25
    public static function get()
177
    {
178 25
        return self::$instance;
179
    }
180
181
    /**
182
     * Init console application
183
     */
184 6
    public function init()
185
    {
186 6
        $this->addTaskCommands();
187 6
        $this->getConsole()->add(new WorkerCommand($this));
188 6
        $this->getConsole()->add(new DiceCommand());
189 6
        $this->getConsole()->add(new InitCommand());
190 6
        $this->getConsole()->add(new TreeCommand($this));
191 6
        $this->getConsole()->add(new SshCommand($this));
192 6
        $this->getConsole()->add(new RunCommand($this));
193 6
        if (self::isPharArchive()) {
194
            $selfUpdate = new PharUpdateCommand('self-update');
195
            $selfUpdate->setDescription('Updates deployer.phar to the latest version');
196
            $selfUpdate->setManifestUri('https://deployer.org/manifest.json');
197
            $this->getConsole()->add($selfUpdate);
198
            $this->getConsole()->getHelperSet()->set(new PharUpdateHelper());
199
        }
200 6
    }
201
202
    /**
203
     * Transform tasks to console commands.
204
     */
205 6
    public function addTaskCommands()
206
    {
207 6
        foreach ($this->tasks as $name => $task) {
208 6
            $command = new MainCommand($name, $task->getDescription(), $this);
209 6
            $command->setHidden($task->isHidden());
210
211 6
            $this->getConsole()->add($command);
212
        }
213 6
    }
214
215
    /**
216
     * @param string $name
217
     * @return mixed
218
     * @throws \InvalidArgumentException
219
     */
220 23
    public function __get($name)
221
    {
222 23
        if (isset($this[$name])) {
223 23
            return $this[$name];
224
        } else {
225 1
            throw new \InvalidArgumentException("Property \"$name\" does not exist.");
226
        }
227
    }
228
229
    /**
230
     * @param string $name
231
     * @param mixed $value
232
     */
233 6
    public function __set($name, $value)
234
    {
235 6
        $this[$name] = $value;
236 6
    }
237
238
    /**
239
     * @return Application
240
     */
241 6
    public function getConsole()
242
    {
243 6
        return $this['console'];
244
    }
245
246
    /**
247
     * @param string $name
248
     * @return Console\Helper\HelperInterface
249
     */
250
    public function getHelper($name)
251
    {
252
        return $this->getConsole()->getHelperSet()->get($name);
253
    }
254
255
    /**
256
     * Run Deployer
257
     *
258
     * @param string $version
259
     * @param string $deployFile
260
     */
261
    public static function run($version, $deployFile)
262
    {
263
        $input = new ArgvInput();
264
        $output = new ConsoleOutput();
265
266
        try {
267
            // Init Deployer
268
            $console = new Application('Deployer', $version);
269
            $deployer = new self($console);
270
271
            // Require deploy.php file
272
            self::load($deployFile);
273
274
            // Run Deployer
275
            $deployer->init();
276
            $console->run($input, $output);
277
278
        } catch (Throwable $exception) {
279
            self::printException($output, $exception);
280
        }
281
    }
282
283 6
    public static function load(string $deployFile)
284
    {
285 6
        if (is_readable($deployFile)) {
286
            // Prevent variable leak into deploy.php file
287
            call_user_func(function () use ($deployFile) {
288
                // Reorder autoload stack
289 6
                $originStack = spl_autoload_functions();
290
291 6
                require $deployFile;
292
293 6
                $newStack = spl_autoload_functions();
294 6
                if ($originStack[0] !== $newStack[0]) {
295
                    foreach (array_reverse($originStack) as $loader) {
296
                        spl_autoload_unregister($loader);
297
                        spl_autoload_register($loader, true, true);
298
                    }
299
                }
300 6
            });
301
        }
302 6
    }
303
304
    private static function printException(OutputInterface $output, Throwable $exception)
305
    {
306
        $class = get_class($exception);
307
        $file = basename($exception->getFile());
308
        $output->writeln([
309
            "<fg=white;bg=red> {$class} </> <comment>in {$file} on line {$exception->getLine()}:</>",
310
            "",
311
            implode("\n", array_map(function ($line) {
312
                return "  " . $line;
313
            }, explode("\n", $exception->getMessage()))),
314
            "",
315
        ]);
316
        $output->writeln($exception->getTraceAsString());
317
        return;
318
    }
319
320
    /**
321
     * Collect anonymous stats about Deployer usage for improving developer experience.
322
     * If you are not comfortable with this, you will always be able to disable this
323
     * by setting `allow_anonymous_stats` to false in your deploy.php file.
324
     *
325
     * @param CommandEvent $commandEvent
326
     * @codeCoverageIgnore
327
     */
328
    public function collectAnonymousStats(CommandEvent $commandEvent)
329
    {
330
        if ($this->config->has('allow_anonymous_stats') && $this->config['allow_anonymous_stats'] === false) {
331
            return;
332
        }
333
334
        $stats = [
335
            'status' => 'success',
336
            'command_name' => $commandEvent->getCommand()->getName(),
337
            'project_hash' => empty($this->config['repository']) ? null : sha1($this->config['repository']),
338
            'hosts_count' => $this->hosts->count(),
339
            'deployer_version' => $this->getConsole()->getVersion(),
340
            'deployer_phar' => self::isPharArchive(),
341
            'php_version' => phpversion(),
342
            'extension_pcntl' => extension_loaded('pcntl'),
343
            'extension_curl' => extension_loaded('curl'),
344
            'os' => defined('PHP_OS_FAMILY') ? PHP_OS_FAMILY : (stristr(PHP_OS, 'DAR') ? 'OSX' : (stristr(PHP_OS, 'WIN') ? 'WIN' : (stristr(PHP_OS, 'LINUX') ? 'LINUX' : PHP_OS))),
345
            'exception' => null,
346
        ];
347
348
        if ($commandEvent->getException() !== null) {
349
            $stats['status'] = 'error';
350
            $stats['exception'] = get_class($commandEvent->getException());
351
        }
352
353
        if ($stats['command_name'] === 'init') {
354
            $stats['allow_anonymous_stats'] = $GLOBALS['allow_anonymous_stats'] ?? false;
355
        }
356
357
        if (in_array($stats['command_name'], ['worker', 'list', 'help'], true)) {
358
            return;
359
        }
360
361
        Reporter::report($stats);
362
    }
363
364 6
    public static function isPharArchive()
365
    {
366 6
        return 'phar:' === substr(__FILE__, 0, 5);
367
    }
368
}
369