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 ( 075b5b...370756 )
by Anton
02:18
created

ParallelExecutor::generateOptions()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 9.2312

Importance

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