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 ( 97c63e...5f3221 )
by Anton
02:14
created

Client::createProcess()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 8
ccs 0
cts 8
cp 0
crap 6
rs 10
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\Ssh;
9
10
use Deployer\Deployer;
11
use Deployer\Exception\RuntimeException;
12
use Deployer\Host\Host;
13
use Deployer\Utility\ProcessOutputPrinter;
14
use Symfony\Component\Console\Output\OutputInterface;
15
use Symfony\Component\Process\Process;
16
17
class Client
18
{
19
    /**
20
     * @var OutputInterface
21
     */
22
    private $output;
23
24
    /**
25
     * @var ProcessOutputPrinter
26
     */
27
    private $pop;
28
29
    /**
30
     * @var bool
31
     */
32
    private $multiplexing;
33
34
    public function __construct(OutputInterface $output, ProcessOutputPrinter $pop, bool $multiplexing)
35
    {
36
        $this->output = $output;
37
        $this->pop = $pop;
38
        $this->multiplexing = $multiplexing;
39
    }
40
41
    /**
42
     * @param Host $host
43
     * @param string $command
44
     * @param array $config
45
     * @return string
46
     * @throws RuntimeException
47
     */
48
    public function run(Host $host, string $command, array $config = [])
49
    {
50
        $hostname = $host->getHostname();
51
        $defaults = [
52
            'timeout' => Deployer::getDefault('default_timeout', 300),
53
            'tty' => false,
54
        ];
55
        $config = array_merge($defaults, $config);
56
57
        $this->pop->command($hostname, $command);
58
59
        $sshArguments = $host->getSshArguments();
60
61
        $become = $host->has('become') ? 'sudo -H -u ' . $host->get('become') : '';
62
63
        // When tty need to be allocated, don't use multiplexing,
64
        // and pass command without bash allocation on remote host.
65
        if ($config['tty']) {
66
            $this->output->write(''); // Notify OutputWatcher
67
            $sshArguments = $sshArguments->withFlag('-tt');
68
            $command = escapeshellarg($command);
69
70
            $ssh = "ssh $sshArguments $host $command";
71
            $process = $this->createProcess($ssh);
72
            $process
73
                ->setTimeout($config['timeout'])
74
                ->setTty(true)
75
                ->mustRun();
76
77
            return $process->getOutput();
78
        }
79
80
        if ($host->isMultiplexing() === null ? $this->multiplexing : $host->isMultiplexing()) {
81
            $sshArguments = $this->initMultiplexing($host);
82
        }
83
84
        $shellCommand = $host->getShellCommand();
85
86
        if (strtolower(substr(PHP_OS, 0, 3)) === 'win') {
87
            $ssh = "ssh $sshArguments $host $become \"$shellCommand; printf '[exit_code:%s]' $?;\"";
88
        } else {
89
            $ssh = "ssh $sshArguments $host $become '$shellCommand; printf \"[exit_code:%s]\" $?;'";
90
        }
91
92
        $process = $this->createProcess($ssh);
93
        $process
94
            ->setInput($command)
95
            ->setTimeout($config['timeout']);
96
97
        $process->run($this->pop->callback($hostname));
98
99
        $output = $this->pop->filterOutput($process->getOutput());
100
        $exitCode = $this->parseExitStatus($process);
101
102
        if ($exitCode !== 0) {
103
            throw new RuntimeException(
104
                $hostname,
105
                $command,
106
                $exitCode,
107
                $output,
108
                $process->getErrorOutput()
109
            );
110
        }
111
112
        return $output;
113
    }
114
115
    private function parseExitStatus(Process $process)
116
    {
117
        $output = $process->getOutput();
118
        preg_match('/\[exit_code:(.*?)\]/', $output, $match);
119
120
        if (!isset($match[1])) {
121
            return -1;
122
        }
123
124
        $exitCode = (int)$match[1];
125
        return $exitCode;
126
    }
127
128
    private function initMultiplexing(Host $host)
129
    {
130
        $sshArguments = $host->getSshArguments()->withMultiplexing($host);
131
132
        if (!$this->isMultiplexingInitialized($host, $sshArguments)) {
133
            if ($this->output->isVeryVerbose()) {
134
                $this->pop->writeln(Process::OUT, $host->getHostname(), 'ssh multiplexing initialization');
135
            }
136
137
            $output = $this->exec("ssh -N $sshArguments $host");
138
139
            if ($this->output->isVeryVerbose()) {
140
                $this->pop->writeln(Process::OUT, $host->getHostname(), $output);
141
            }
142
        }
143
144
        return $sshArguments;
145
    }
146
147
    private function isMultiplexingInitialized(Host $host, Arguments $sshArguments)
148
    {
149
        $process = $this->createProcess("ssh -O check $sshArguments $host 2>&1");
150
        $process->run();
151
        return (bool)preg_match('/Master running/', $process->getOutput());
152
    }
153
154
    private function exec($command, &$exitCode = null)
155
    {
156
        $descriptors = [
157
            ['pipe', 'r'],
158
            ['pipe', 'w'],
159
            ['pipe', 'w'],
160
        ];
161
162
        // Don't read from stderr, there is a bug in OpenSSH_7.2p2 (stderr doesn't closed with ControlMaster)
163
164
        $process = proc_open($command, $descriptors, $pipes);
165
        if (is_resource($process)) {
166
            fclose($pipes[0]);
167
            $output = stream_get_contents($pipes[1]);
168
            fclose($pipes[1]);
169
            fclose($pipes[2]);
170
            $exitCode = proc_close($process);
171
        } else {
172
            $output = 'proc_open failure';
173
            $exitCode = 1;
174
        }
175
        return $output;
176
    }
177
178
    private function createProcess($command)
179
    {
180
        if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
181
            return Process::fromShellCommandline($command);
182
        } else {
183
            return new Process($command);
184
        }
185
    }
186
}
187