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 ( a071d7...870a84 )
by Anton
02:30
created

ParallelExecutor::generateOptions()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 7.3329

Importance

Changes 0
Metric Value
cc 6
eloc 15
nc 12
nop 0
dl 0
loc 29
ccs 10
cts 15
cp 0.6667
crap 7.3329
rs 8.439
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\Executor;
9
10
use Deployer\Console\Application;
11
use Deployer\Console\Output\Informer;
12
use Deployer\Console\Output\VerbosityString;
13
use Deployer\Exception\Exception;
14
use Deployer\Exception\GracefulShutdownException;
15
use Deployer\Host\Host;
16
use Deployer\Host\Localhost;
17
use Deployer\Host\Storage;
18
use Deployer\Task\Context;
19
use Deployer\Task\Task;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\Process\Process;
23
24
class ParallelExecutor implements ExecutorInterface
25
{
26
    /**
27
     * @var InputInterface
28
     */
29
    private $input;
30
31
    /**
32
     * @var OutputInterface
33
     */
34
    private $output;
35
36
    /**
37
     * @var Informer
38
     */
39
    private $informer;
40
41
    /**
42
     * @var Application
43
     */
44
    private $console;
45
46
    /**
47
     * @param InputInterface $input
48
     * @param OutputInterface $output
49
     * @param Informer $informer
50
     * @param Application $console
51
     */
52 4
    public function __construct(InputInterface $input, OutputInterface $output, Informer $informer, Application $console)
53
    {
54 4
        $this->input = $input;
55 4
        $this->output = $output;
56 4
        $this->informer = $informer;
57 4
        $this->console = $console;
58 4
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 4
    public function run($tasks, $hosts)
64
    {
65 4
        $localhost = new Localhost();
66 4
        $limit = (int)$this->input->getOption('limit') ?: count($hosts);
67
68
        // We need contexts here for usage inside `on` function. Pass input/output to callback of it.
69
        // This allows to use code like this in parallel mode:
70
        //
71
        //     host('prod')
72
        //         ->set('branch', function () {
73
        //             return input()->getOption('branch') ?: 'production';
74
        //     })
75
        //
76
        // Otherwise `input()` wont be accessible (i.e. null)
77
        //
78 4
        Context::push(new Context($localhost, $this->input, $this->output));
79
        {
80 4
            Storage::persist($hosts);
81
        }
82 4
        Context::pop();
83
84 4
        foreach ($tasks as $task) {
85 4
            $success = true;
86 4
            $this->informer->startTask($task);
87
88 4
            if ($task->isLocal()) {
89 2
                Storage::load($hosts);
90
                {
91 2
                    $task->run(new Context($localhost, $this->input, $this->output));
92
                }
93 2
                Storage::flush($hosts);
94
            } else {
95 4
                foreach (array_chunk($hosts, $limit) as $chunk) {
96 4
                    $exitCode = $this->runTask($chunk, $task);
97
98
                    switch ($exitCode) {
99 4
                        case 1:
100
                            throw new GracefulShutdownException();
101 4
                        case 2:
102
                            $success = false;
103
                            break;
104 4
                        case 255:
105 4
                            throw new Exception();
106
                    }
107
                }
108
            }
109
110 4
            if ($success) {
111 4
                $this->informer->endTask($task);
112
            } else {
113 4
                $this->informer->taskError();
114
            }
115
        }
116 4
    }
117
118
    /**
119
     * Run task on hosts.
120
     *
121
     * @param Host[] $hosts
122
     * @param Task $task
123
     * @return int
124
     */
125 4
    private function runTask(array $hosts, Task $task)
126
    {
127 4
        $processes = [];
128
129 4
        foreach ($hosts as $host) {
130 4
            if ($task->shouldBePerformed($host)) {
131 4
                $processes[$host->getHostname()] = $this->getProcess($host, $task);
132 4
                if ($task->isOnce()) {
133 4
                    break;
134
                }
135
            }
136
        }
137
138 4
        $callback = function ($type, $host, $output) {
139 4
            $output = rtrim($output);
140 4
            if (!empty($output)) {
141 4
                $this->output->writeln($output);
142
            }
143 4
        };
144
145 4
        $this->startProcesses($processes);
146
147 4
        while ($this->areRunning($processes)) {
148 4
            $this->gatherOutput($processes, $callback);
149
        }
150 4
        $this->gatherOutput($processes, $callback);
151
152 4
        return $this->gatherExitCodes($processes);
153
    }
154
155
    /**
156
     * Get process for task on host.
157
     *
158
     * @param Host $host
159
     * @param Task $task
160
     * @return Process
161
     */
162 4
    protected function getProcess($host, Task $task)
163
    {
164 4
        $dep = PHP_BINARY . ' ' . DEPLOYER_BIN;
165 4
        $options = $this->generateOptions();
166 4
        $arguments = $this->generateArguments();
167 4
        $hostname = $host->getHostname();
168 4
        $taskName = $task->getName();
169 4
        $configFile = $host->get('host_config_storage');
170 4
        $value = $this->input->getOption('file');
171 4
        $file = $value ? "--file='$value'" : '';
172
173 4
        if ($this->output->isDecorated()) {
174
            $options .= ' --ansi';
175
        }
176
177 4
        $command = "$dep $file worker $arguments $options --hostname $hostname --task $taskName --config-file $configFile";
178 4
        $process = new Process($command);
179
180 4
        if (!defined('DEPLOYER_PARALLEL_PTY')) {
181
            $process->setPty(true);
182
        }
183
184 4
        return $process;
185
    }
186
187
    /**
188
     * Start all of the processes.
189
     *
190
     * @param Process[] $processes
191
     * @return void
192
     */
193 4
    protected function startProcesses(array $processes)
194
    {
195 4
        foreach ($processes as $process) {
196 4
            $process->start();
197
        }
198 4
    }
199
200
    /**
201
     * Determine if any of the processes are running.
202
     *
203
     * @param Process[] $processes
204
     * @return bool
205
     */
206 4
    protected function areRunning(array $processes)
207
    {
208 4
        foreach ($processes as $process) {
209 4
            if ($process->isRunning()) {
210 4
                return true;
211
            }
212
        }
213 4
        return false;
214
    }
215
216
    /**
217
     * Gather the output from all of the processes.
218
     *
219
     * @param Process[] $processes
220
     * @param callable $callback
221
     */
222 4
    protected function gatherOutput(array $processes, callable $callback)
223
    {
224 4
        foreach ($processes as $host => $process) {
225
            $methods = [
226 4
                Process::OUT => 'getIncrementalOutput',
227
                Process::ERR => 'getIncrementalErrorOutput',
228
            ];
229 4
            foreach ($methods as $type => $method) {
230 4
                $output = $process->{$method}();
231 4
                if (!empty($output)) {
232 4
                    $callback($type, $host, $output);
233
                }
234
            }
235
        }
236 4
    }
237
238
    /**
239
     * Gather the cumulative exit code for the processes.
240
     *
241
     * @param Process[] $processes
242
     * @return int
243
     */
244 4
    protected function gatherExitCodes(array $processes)
245
    {
246 4
        $code = 0;
247 4
        foreach ($processes as $process) {
248 4
            if ($process->getExitCode() > 0) {
249 4
                $code = $process->getExitCode();
250
            }
251
        }
252 4
        return $code;
253
    }
254
255
    /**
256
     * Generate options and arguments string.
257
     * @return string
258
     */
259 4
    private function generateOptions()
260
    {
261 4
        $verbosity = new VerbosityString($this->output);
262 4
        $input = $verbosity;
263
264
        // Get user arguments
265 4
        foreach ($this->console->getUserDefinition()->getArguments() as $argument) {
266
            $value = $this->input->getArgument($argument->getName());
267
            if ($value) {
268
                $input .= " $value";
269
            }
270
        }
271
272
        // Get user options
273 4
        foreach ($this->console->getUserDefinition()->getOptions() as $option) {
274 3
            $name = $option->getName();
275 3
            $value = $this->input->getOption($name);
276
277 3
            if ($value) {
278
                $input .= " --{$name}";
279
280
                if ($option->acceptValue()) {
281 3
                    $input .= " {$value}";
282
                }
283
            }
284
        }
285
286 4
        return $input;
287
    }
288
289 4
    private function generateArguments(): string
290
    {
291 4
        $arguments = '';
292
293 4
        if ($this->input->hasArgument('stage')) {
294
            // Some people rely on stage argument, so pass it to worker too.
295 4
            $arguments .= $this->input->getArgument('stage');
296
        }
297
298 4
        return $arguments;
299
    }
300
}
301