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.
Completed
Push — master ( e2875b...35ced9 )
by Anton
03:41
created

Master::stringifyVerbosity()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 10.5

Importance

Changes 0
Metric Value
cc 6
nc 6
nop 1
dl 0
loc 17
ccs 4
cts 8
cp 0.5
crap 10.5
rs 9.0777
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\Component\Ssh\Client;
11
use Deployer\Configuration\Configuration;
12
use Deployer\Deployer;
13
use Deployer\Exception\Exception;
14
use Deployer\Host\Host;
15
use Deployer\Host\Localhost;
16
use Deployer\Selector\Selector;
17
use Deployer\Task\Task;
18
use Symfony\Component\Console\Input\InputInterface;
19
use Symfony\Component\Console\Output\OutputInterface;
20
use Symfony\Component\Process\Process;
21
22 1
const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
23
24
function spinner($message = '')
25
{
26 4
    $frame = FRAMES[(int)(microtime(true) * 10) % count(FRAMES)];
27 4
    return "  $frame $message\r";
28
}
29
30
class Master
31
{
32
    private $input;
33
    private $output;
34
    private $server;
35
    private $messenger;
36
    private $client;
37
    private $config;
38
39 12
    public function __construct(
40
        InputInterface $input,
41
        OutputInterface $output,
42
        Server $server,
43
        Messenger $messenger,
44
        Client $client,
45
        Configuration $config
46
    )
47
    {
48 12
        $this->input = $input;
49 12
        $this->output = $output;
50 12
        $this->server = $server;
51 12
        $this->messenger = $messenger;
52 12
        $this->client = $client;
53 12
        $this->config = $config;
54 12
    }
55
56
    /**
57
     * @param Task[] $tasks
58
     * @param Host[] $hosts
59
     * @param Planner|null $plan
60
     * @return int
61
     */
62 12
    public function run(array $tasks, array $hosts, $plan = null): int
63
    {
64 12
        $plan || $this->server->start();
65 12
        $plan || $this->connect($hosts);
66
67 12
        $globalLimit = (int)$this->input->getOption('limit') ?: count($hosts);
68
69 12
        foreach ($tasks as $task) {
70 12
            $plan || $this->messenger->startTask($task);
71
72 12
            $plannedHosts = $hosts;
73
74 12
            $limit = min($globalLimit, $task->getLimit() ?? $globalLimit);
75
76 12
            if ($task->isOnce()) {
77 3
                $plannedHosts = [];
78 3
                foreach ($hosts as $currentHost) {
79 3
                    if (Selector::apply($task->getSelector(), $currentHost)) {
80 3
                        $plannedHosts[] = $currentHost;
81 3
                        break;
82
                    }
83
                }
84
            }
85
86 12
            if ($task->isLocal()) {
87
                $plannedHosts = [new Localhost('localhost')];
88
            }
89
90 12
            if ($limit === 1 || count($plannedHosts) === 1) {
91 9
                foreach ($plannedHosts as $currentHost) {
92 9
                    if (!Selector::apply($task->getSelector(), $currentHost)) {
93
                        if ($plan) {
94
                            $plan->commit([], $task);
95
                        }
96
                        continue;
97
                    }
98
99 9
                    if ($plan) {
100
                        $plan->commit([$currentHost], $task);
101
                        continue;
102
                    }
103
104 9
                    $exitCode = $this->runTask($task, [$currentHost]);
105 9
                    if ($exitCode !== 0) {
106 2
                        return $exitCode;
107
                    }
108
                }
109
            } else {
110 4
                foreach (array_chunk($hosts, $limit) as $chunk) {
111 4
                    $selector = $task->getSelector();
112 4
                    $selectedHosts = [];
113 4
                    foreach ($chunk as $currentHost) {
114 4
                        if ($selector === null || Selector::apply($selector, $currentHost)) {
115 4
                            $selectedHosts[] = $currentHost;
116
                        }
117
                    }
118
119 4
                    if ($plan) {
120
                        $plan->commit($selectedHosts, $task);
121
                        continue;
122
                    }
123
124 4
                    $exitCode = $this->runTask($task, $selectedHosts);
125 4
                    if ($exitCode !== 0) {
126
                        return $exitCode;
127
                    }
128
                }
129
            }
130
131 12
            if (!$plan) {
132 12
                $this->messenger->endTask($task);
133
            }
134
        }
135
136 12
        return 0;
137
    }
138
139
    /**
140
     * @param Host[] $hosts
141
     */
142 12
    private function connect(array $hosts)
143
    {
144 View Code Duplication
        $callback = function (string $output) {
145
            $output = preg_replace('/\n$/', '', $output);
146
            if (strlen($output) !== 0) {
147
                $this->output->writeln($output);
148
            }
149 12
        };
150
151
        // Connect to each host sequentially, to prevent getting locked.
152 12
        foreach ($hosts as $host) {
153 12
            if ($host instanceof Localhost) {
154 12
                continue;
155
            }
156
            $process = $this->createConnectProcess($host);
157
            $process->start();
158
159
            while ($process->isRunning()) {
160
                $this->gatherOutput([$process], $callback);
161
                if ($this->output->isDecorated()) {
162
                    $this->output->write(spinner(str_pad("connect {$host->getTag()}", intval(getenv('COLUMNS')) - 1)));
163
                }
164
                usleep(1000);
165
            }
166
        }
167
168
        // Clear spinner.
169 12
        $this->output->write(str_repeat(' ', intval(getenv('COLUMNS')) - 1) . "\r");
170 12
    }
171
172
    /**
173
     * @param Task $task
174
     * @param Host[] $hosts
175
     * @return int
176
     */
177 12
    private function runTask(Task $task, array $hosts): int
178
    {
179 12
        if (getenv('DEPLOYER_LOCAL_WORKER') === 'true') {
180
            // This allows to code coverage all recipe,
181
            // as well as speedup tests by not spawning
182
            // lots of processes. Also there is a few tests
183
            // what runs with workers for tests subprocess
184
            // communications.
185 8
            foreach ($hosts as $host) {
186 8
                $worker = new Worker(Deployer::get());
187 8
                $exitCode = $worker->execute($task, $host);
188 8
                if ($exitCode !== 0) {
189 2
                    return $exitCode;
190
                }
191
            }
192 8
            return 0;
193
        }
194
195 View Code Duplication
        $callback = function (string $output) {
196 3
            $output = preg_replace('/\n$/', '', $output);
197 3
            if (strlen($output) !== 0) {
198 3
                $this->output->writeln($output);
199
            }
200 4
        };
201
202
        /** @var Process[] $processes */
203 4
        $processes = [];
204
205
        $this->server->addTimer(0, function () use(&$processes, $hosts, $task) {
206 4
            foreach ($hosts as $host) {
207 4
                $processes[] = $this->createProcess($host, $task);
208
            }
209
210 4
            foreach ($processes as $process) {
211 4
                $process->start();
212
            }
213 4
        });
214
215
        $this->server->addPeriodicTimer(0.03, function ($timer) use (&$processes, $callback) {
216 4
            $this->gatherOutput($processes, $callback);
217 4
            $this->output->write(spinner());
218 4
            if ($this->allFinished($processes)) {
219 4
                $this->server->stop();
220 4
                $this->server->cancelTimer($timer);
221
            }
222 4
        });
223
224 4
        $this->server->run();
225
226 4
        $this->output->write("    \r"); // clear spinner
227 4
        $this->gatherOutput($processes, $callback);
228
229 4
        return $this->cumulativeExitCode($processes);
230
    }
231
232 4
    protected function createProcess(Host $host, Task $task): Process
233
    {
234 4
        $dep = PHP_BINARY . ' ' . DEPLOYER_BIN;
235 4
        $decorated = $this->output->isDecorated() ? '--decorated' : '';
236 4
        $verbosity = self::stringifyVerbosity($this->output->getVerbosity());
237 4
        $command = "$dep worker $task {$host->getAlias()} {$this->server->getPort()} {$this->input} $decorated $verbosity";
238
239 4
        if ($this->output->isDebug()) {
240 1
            $this->output->writeln("[{$host->getTag()}] $command");
241
        }
242
243 4
        return Process::fromShellCommandline($command);
244
    }
245
246
    protected function createConnectProcess(Host $host): Process
247
    {
248
        $dep = PHP_BINARY . ' ' . DEPLOYER_BIN;
249
        $decorated = $this->output->isDecorated() ? '--decorated' : '';
250
        $verbosity = self::stringifyVerbosity($this->output->getVerbosity());
251
        $command = "$dep connect {$host->getAlias()} $decorated $verbosity";
252
253
        if ($this->output->isDebug()) {
254
            $this->output->writeln("[{$host->getTag()}] $command");
255
        }
256
257
        return Process::fromShellCommandline($command);
258
    }
259
260
    /**
261
     * @param Process[] $processes
262
     * @return bool
263
     */
264 4
    protected function allFinished(array $processes): bool
265
    {
266 4
        foreach ($processes as $process) {
267 4
            if (!$process->isTerminated()) {
268 4
                return false;
269
            }
270
        }
271 4
        return true;
272
    }
273
274
    /**
275
     * @param Process[] $processes
276
     * @param callable $callback
277
     */
278 4
    protected function gatherOutput(array $processes, callable $callback)
279
    {
280 4
        foreach ($processes as $process) {
281 4
            $output = $process->getIncrementalOutput();
282 4
            if (strlen($output) !== 0) {
283 3
                $callback($output);
284
            }
285
286 4
            $errorOutput = $process->getIncrementalErrorOutput();
287 4
            if (strlen($errorOutput) !== 0) {
288
                $callback($errorOutput);
289
            }
290
        }
291 4
    }
292
293
    /**
294
     * @param Process[] $processes
295
     * @return int
296
     */
297 4
    protected function cumulativeExitCode(array $processes): int
298
    {
299 4
        foreach ($processes as $process) {
300 4
            if ($process->getExitCode() > 0) {
301
                return $process->getExitCode();
302
            }
303
        }
304 4
        return 0;
305
    }
306
307 4
    private static function stringifyVerbosity(int $verbosity): string
308
    {
309 4
        switch ($verbosity) {
310
            case OutputInterface::VERBOSITY_QUIET:
311
                return '-q';
312
            case OutputInterface::VERBOSITY_NORMAL:
313 3
                return '';
314
            case OutputInterface::VERBOSITY_VERBOSE:
315
                return '-v';
316
            case OutputInterface::VERBOSITY_VERY_VERBOSE:
317
                return '-vv';
318
            case OutputInterface::VERBOSITY_DEBUG:
319 1
                return '-vvv';
320
            default:
321
                throw new Exception('Unknown verbosity level: ' . $verbosity);
322
        }
323
    }
324
}
325