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 ( c538d5...8bac07 )
by Anton
02:32
created

src/Executor/ParallelExecutor.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\Collection\PersistentCollection;
11
use Deployer\Configuration\Configuration;
12
use Deployer\Console\Application;
13
use Deployer\Deployer;
14
use Deployer\Exception\Exception;
15
use Deployer\Exception\GracefulShutdownException;
16
use Deployer\Host\Host;
17
use Deployer\Host\Localhost;
18
use Deployer\Host\Storage;
19
use Deployer\Component\Ssh\Client;
20
use Deployer\Task\Context;
21
use Deployer\Task\Task;
22
use Symfony\Component\Console\Input\InputInterface;
23
use Symfony\Component\Console\Output\OutputInterface;
24
use Symfony\Component\Process\Process;
25
26
const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
27
28
function spinner($message = '')
29
{
30
    $frame = FRAMES[(int)(microtime(true) * 10) % count(FRAMES)];
31
    return "  $frame $message\r";
32
}
33
34
class ParallelExecutor
35
{
36
    private $input;
37
    private $output;
38
    private $messenger;
39
    private $console;
40
    private $client;
41
    private $config;
42
43
    public function __construct(
44
        InputInterface $input,
45
        OutputInterface $output,
46
        Messenger $messenger,
47
        Application $console,
48
        Client $client,
49
        Configuration $config
50
    )
51
    {
52
        $this->input = $input;
53
        $this->output = $output;
54
        $this->messenger = $messenger;
55
        $this->console = $console;
56
        $this->client = $client;
57
        $this->config = $config;
58
    }
59
60
    /**
61
     * @param Host[] $hosts
62
     */
63
    private function connect(array $hosts)
64
    {
65 View Code Duplication
        $callback = function (string $output) {
66
            $output = preg_replace('/\n$/', '', $output);
67
            if (strlen($output) !== 0) {
68
                $this->output->writeln($output);
69
            }
70
        };
71
72
        // Connect to each host sequentially, to prevent getting locked.
73
        foreach ($hosts as $host) {
74
            if ($host instanceof Localhost) {
75
                continue;
76
            }
77
            $process = $this->getProcess($host, new Task('connect'));
78
            $process->start();
79
80
            while ($process->isRunning()) {
81
                $this->gatherOutput([$process], $callback);
82
                $this->output->write(spinner(str_pad("connect {$host->tag()}", intval(getenv('COLUMNS')) - 1)));
83
                usleep(1000);
84
            }
85
        }
86
87
        // Clear spinner.
88
        $this->output->write(str_repeat(' ', intval(getenv('COLUMNS')) - 1) . "\r");
89
    }
90
91
    /**
92
     * @param Task[] $tasks
93
     * @param Host[] $hosts
94
     */
95
    public function run(array $tasks, array $hosts): int
96
    {
97
        $this->persistHosts($hosts);
98
        $this->connect($hosts);
99
100
        $localhost = new Localhost();
101
        $limit = (int)$this->input->getOption('limit') ?: count($hosts);
102
103
        foreach ($tasks as $task) {
104
            $this->messenger->startTask($task);
105
106
            if ($limit === 1 || count($hosts) === 1) {
107
                foreach ($hosts as $host) {
108
                    try {
109
                        Exception::setFilepath($task->getFilepath());
110
                        $host->getConfig()->getCollection()->load();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class Deployer\Collection\Collection as the method load() does only exist in the following sub-classes of Deployer\Collection\Collection: Deployer\Collection\PersistentCollection. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
111
112
                        $task->run(new Context($host, $this->input, $this->output));
113
114
                        $host->getConfig()->getCollection()->flush();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class Deployer\Collection\Collection as the method flush() does only exist in the following sub-classes of Deployer\Collection\Collection: Deployer\Collection\PersistentCollection. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
115
                    } catch (GracefulShutdownException $exception) {
116
                        $this->messenger->renderException($exception, $host);
117
                        return GracefulShutdownException::EXIT_CODE;
118
                    }
119
                }
120
            } else {
121
                foreach (array_chunk($hosts, $limit) as $chunk) {
122
                    $exitCode = $this->runTask($chunk, $task);
123
                    if ($exitCode !== 0) {
124
                        return $exitCode;
125
                    }
126
                }
127
            }
128
129
            $this->messenger->endTask($task);
130
        }
131
132
        return 0;
133
    }
134
135
    private function runTask(array $hosts, Task $task): int
136
    {
137
        $processes = [];
138
        foreach ($hosts as $host) {
139
            if ($task->shouldBePerformed($host)) {
140
                $processes[] = $this->getProcess($host, $task);
141
                if ($task->isOnce()) {
142
                    $task->setHasRun();
143
                }
144
            }
145
        }
146
147 View Code Duplication
        $callback = function (string $output) {
148
            $output = preg_replace('/\n$/', '', $output);
149
            if (strlen($output) !== 0) {
150
                $this->output->writeln($output);
151
            }
152
        };
153
154
        $this->startProcesses($processes);
155
156
        while ($this->areRunning($processes)) {
157
            $this->gatherOutput($processes, $callback);
158
            $this->output->write(spinner());
159
            usleep(1000);
160
        }
161
162
        // Clear spinner.
163
        $this->output->write("    \r");
164
165
        $this->gatherOutput($processes, $callback);
166
167
        return $this->gatherExitCodes($processes);
168
    }
169
170
    protected function getProcess(Host $host, Task $task): Process
171
    {
172
        $dep = PHP_BINARY . ' ' . DEPLOYER_BIN;
173
        $configFile = $host->get('worker-config');
174
        $decorated = $this->output->isDecorated() ? '--decorated' : '';
175
        $command = "$dep worker $task {$host->alias()} $configFile {$this->input} $decorated";
176
177
        if ($this->output->isDebug()) {
178
            $this->output->writeln("[{$host->tag()}] $command");
179
        }
180
181
        return Process::fromShellCommandline($command);
182
    }
183
184
    /**
185
     * Start all of the processes.
186
     *
187
     * @param Process[] $processes
188
     * @return void
189
     */
190
    protected function startProcesses(array $processes)
191
    {
192
        foreach ($processes as $process) {
193
            $process->start();
194
        }
195
    }
196
197
    /**
198
     * Determine if any of the processes are running.
199
     *
200
     * @param Process[] $processes
201
     * @return bool
202
     */
203
    protected function areRunning(array $processes): bool
204
    {
205
        foreach ($processes as $process) {
206
            if ($process->isRunning()) {
207
                return true;
208
            }
209
        }
210
211
        return false;
212
    }
213
214
    /**
215
     * Gather the output from all of the processes.
216
     *
217
     * @param Process[] $processes
218
     * @param callable $callback
219
     * @return void
220
     */
221
    protected function gatherOutput(array $processes, callable $callback)
222
    {
223
        foreach ($processes as $process) {
224
            $output = $process->getIncrementalOutput();
225
            if (strlen($output) !== 0) {
226
                $callback($output);
227
            }
228
229
            $errorOutput = $process->getIncrementalErrorOutput();
230
            if (strlen($errorOutput) !== 0) {
231
                $callback($errorOutput);
232
            }
233
        }
234
    }
235
236
    /**
237
     * Gather the cumulative exit code for the processes.
238
     */
239
    protected function gatherExitCodes(array $processes): int
240
    {
241
        foreach ($processes as $process) {
242
            if ($process->getExitCode() > 0) {
243
                return $process->getExitCode();
244
            }
245
        }
246
247
        return 0;
248
    }
249
250
    /**
251
     * @param Host[] $hosts
252
     */
253
    private function persistHosts(array $hosts)
254
    {
255
        foreach ($hosts as $host) {
256
            Context::push(new Context($host, $this->input, $this->output));
257
258
            $values = $host->getConfig()->persist();
259
            $workerConfig = sys_get_temp_dir() . '/' . uniqid('deployer-') . '-' . $host->alias() . '.dep';
260
            $values['worker-config'] = $workerConfig;
261
262
            $persistentCollection = new PersistentCollection($workerConfig, $values);
263
            $persistentCollection->flush();
264
            $host->getConfig()->setCollection($persistentCollection);
265
266
            Context::pop();
267
        }
268
    }
269
}
270