Completed
Pull Request — master (#208)
by
unknown
01:14
created

GitWrapper::setPrivateKey()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 8.8497
c 0
b 0
f 0
cc 6
nc 10
nop 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GitWrapper;
6
7
use GitWrapper\Event\GitOutputEvent;
8
use GitWrapper\EventSubscriber\AbstractOutputEventSubscriber;
9
use GitWrapper\EventSubscriber\GitLoggerEventSubscriber;
10
use GitWrapper\EventSubscriber\StreamOutputEventSubscriber;
11
use GitWrapper\Exception\GitException;
12
use GitWrapper\Process\GitProcess;
13
use GitWrapper\Strings\GitStrings;
14
use Symfony\Component\EventDispatcher\EventDispatcher;
15
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
16
use Symfony\Component\Process\ExecutableFinder;
17
18
/**
19
 * A wrapper class around the Git binary.
20
 *
21
 * A GitWrapper object contains the necessary context to run Git commands such
22
 * as the path to the Git binary and environment variables. It also provides
23
 * helper methods to run Git commands as set up the connection to the GIT_SSH
24
 * wrapper script.
25
 */
26
final class GitWrapper
27
{
28
    /**
29
     * Path to the Git binary.
30
     *
31
     * @var string
32
     */
33
    private $gitBinary;
34
35
    /**
36
     * The timeout of the Git command in seconds.
37
     *
38
     * @var int
39
     */
40
    private $timeout = 60;
41
42
    /**
43
     * Environment variables defined in the scope of the Git command.
44
     *
45
     * @var string[]
46
     */
47
    private $env = [];
48
49
    /**
50
     * @var AbstractOutputEventSubscriber
51
     */
52
    private $outputEventSubscriber;
53
54
    /**
55
     * @var EventDispatcherInterface
56
     */
57
    private $eventDispatcher;
58
59
    public function __construct(?string $gitBinary = null)
60
    {
61
        if ($gitBinary === null) {
62
            $finder = new ExecutableFinder();
63
            $gitBinary = $finder->find('git');
64
            if (! $gitBinary) {
65
                throw new GitException('Unable to find the Git executable.');
66
            }
67
        }
68
69
        $this->setGitBinary($gitBinary);
70
71
        $this->eventDispatcher = new EventDispatcher();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Symfony\Component\E...tcher\EventDispatcher() of type object<Symfony\Component...atcher\EventDispatcher> is incompatible with the declared type object<Symfony\Component...entDispatcherInterface> of property $eventDispatcher.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
72
    }
73
74
    public function getDispatcher(): EventDispatcherInterface
75
    {
76
        return $this->eventDispatcher;
77
    }
78
79
    public function setDispatcher(EventDispatcherInterface $eventDispatcher): void
80
    {
81
        $this->eventDispatcher = $eventDispatcher;
82
    }
83
84
    public function setGitBinary(string $gitBinary): void
85
    {
86
        $this->gitBinary = $gitBinary;
87
    }
88
89
    public function getGitBinary(): string
90
    {
91
        return $this->gitBinary;
92
    }
93
94
    public function setEnvVar(string $var, $value): void
95
    {
96
        $this->env[$var] = $value;
97
    }
98
99
    public function unsetEnvVar(string $var): void
100
    {
101
        unset($this->env[$var]);
102
    }
103
104
    /**
105
     * Returns an environment variable that is defined only in the scope of the
106
     * Git command.
107
     *
108
     * @param string $var The name of the environment variable, e.g. "HOME", "GIT_SSH".
109
     * @param mixed $default The value returned if the environment variable is not set, defaults to
110
     *   null.
111
     */
112
    public function getEnvVar(string $var, $default = null)
113
    {
114
        return $this->env[$var] ?? $default;
115
    }
116
117
    /**
118
     * @return string[]
119
     */
120
    public function getEnvVars(): array
121
    {
122
        return $this->env;
123
    }
124
125
    public function setTimeout(int $timeout): void
126
    {
127
        $this->timeout = $timeout;
128
    }
129
130
    public function getTimeout(): int
131
    {
132
        return $this->timeout;
133
    }
134
135
    /**
136
     * Set an alternate private key used to connect to the repository.
137
     *
138
     * This method sets the GIT_SSH environment variable to use the wrapper
139
     * script included with this library. It also sets the custom GIT_SSH_KEY
140
     * and GIT_SSH_PORT environment variables that are used by the script.
141
     *
142
     * @param string|null $wrapper Path the the GIT_SSH wrapper script, defaults to null which uses the
143
     *   script included with this library.
144
     * @param string|null $UserKnownHostsFile PATH to the known_hosts file for ssh authentication, defaults
145
     *   to null which doesn't inject the option in the SSH command
146
     */
147
    public function setPrivateKey(string $privateKey, int $port = 22, ?string $wrapper = null, ?string $UserKnownHostsFile = null): void
148
    {
149
        if ($wrapper === null) {
150
            $wrapper = __DIR__ . '/../bin/git-ssh-wrapper.sh';
151
        }
152
153
        if (! $wrapperPath = realpath($wrapper)) {
154
            throw new GitException('Path to GIT_SSH wrapper script could not be resolved: ' . $wrapper);
155
        }
156
157
        if (! $privateKeyPath = realpath($privateKey)) {
158
            throw new GitException('Path private key could not be resolved: ' . $privateKey);
159
        }
160
161
        if ($UserKnownHostsFile === null) {
162
            $optUserKnownHostsFile = '';
163
        } else {
164
            if (! $UserKnownHostsFilePath = realpath($UserKnownHostsFile)) {
165
                throw new GitException('Path to UserKnownHostsFile  could not be resolved: ' . $UserKnownHostsFile);
166
            }
167
            $optUserKnownHostsFile = '-o UserKnownHostsFile=' . $UserKnownHostsFile;
168
        }
169
170
        $this->setEnvVar('GIT_SSH', $wrapperPath);
171
        $this->setEnvVar('GIT_SSH_KEY', $privateKeyPath);
172
        $this->setEnvVar('GIT_SSH_PORT', $port);
173
        $this->setEnvVar('GIT_SSH_OPT_UserKnownHostsFile', $optUserKnownHostsFile);
174
    }
175
176
    /**
177
     * Unsets the private key by removing the appropriate environment variables.
178
     */
179
    public function unsetPrivateKey(): void
180
    {
181
        $this->unsetEnvVar('GIT_SSH');
182
        $this->unsetEnvVar('GIT_SSH_KEY');
183
        $this->unsetEnvVar('GIT_SSH_PORT');
184
        $this->unsetEnvVar('GIT_SSH_OPT_UserKnownHostsFile');
185
    }
186
187
    public function addOutputEventSubscriber(AbstractOutputEventSubscriber $gitOutputEventSubscriber): void
188
    {
189
        $this->getDispatcher()->addSubscriber($gitOutputEventSubscriber);
190
    }
191
192
    public function addLoggerEventSubscriber(GitLoggerEventSubscriber $gitLoggerEventSubscriber): void
193
    {
194
        $this->getDispatcher()->addSubscriber($gitLoggerEventSubscriber);
195
    }
196
197
    public function removeOutputEventSubscriber(AbstractOutputEventSubscriber $gitOutputEventSubscriber): void
198
    {
199
        $this->getDispatcher()->removeSubscriber($gitOutputEventSubscriber);
200
    }
201
202
    /**
203
     * Set whether or not to stream real-time output to STDOUT and STDERR.
204
     */
205
    public function streamOutput(bool $streamOutput = true): void
206
    {
207
        if ($streamOutput && ! isset($this->outputEventSubscriber)) {
208
            $this->outputEventSubscriber = new StreamOutputEventSubscriber();
209
            $this->addOutputEventSubscriber($this->outputEventSubscriber);
210
        }
211
212
        if (! $streamOutput && isset($this->outputEventSubscriber)) {
213
            $this->removeOutputEventSubscriber($this->outputEventSubscriber);
214
            unset($this->outputEventSubscriber);
215
        }
216
    }
217
218
    /**
219
     * Returns an object that interacts with a working copy.
220
     *
221
     * @param string $directory Path to the directory containing the working copy.
222
     */
223
    public function workingCopy(string $directory): GitWorkingCopy
224
    {
225
        return new GitWorkingCopy($this, $directory);
226
    }
227
228
    /**
229
     * Returns the version of the installed Git client.
230
     */
231
    public function version(): string
232
    {
233
        return $this->git('--version');
234
    }
235
236
    /**
237
     * Executes a `git init` command.
238
     *
239
     * Create an empty git repository or reinitialize an existing one.
240
     *
241
     * @param mixed[] $options An associative array of command line options.
242
     */
243
    public function init(string $directory, array $options = []): GitWorkingCopy
244
    {
245
        $git = $this->workingCopy($directory);
246
        $git->init($options);
247
        $git->setCloned(true);
248
249
        return $git;
250
    }
251
252
    /**
253
     * Executes a `git clone` command and returns a working copy object.
254
     *
255
     * Clone a repository into a new directory. Use @see GitWorkingCopy::cloneRepository()
256
     * instead for more readable code.
257
     *
258
     * @param string $directory The directory that the repository will be cloned into. If null is
259
     *   passed, the directory will be generated from the URL with @see GitStrings::parseRepositoryName().
260
     * @param mixed[] $options
261
     */
262
    public function cloneRepository(string $repository, ?string $directory = null, array $options = []): GitWorkingCopy
263
    {
264
        if ($directory === null) {
265
            $directory = GitStrings::parseRepositoryName($repository);
266
        }
267
268
        $git = $this->workingCopy($directory);
269
        $git->cloneRepository($repository, $options);
270
        $git->setCloned(true);
271
        return $git;
272
    }
273
274
    /**
275
     * The command is simply a raw command line entry for everything after the Git binary.
276
     * For example, a `git config -l` command would be passed as `config -l` via the first argument of this method.
277
     *
278
     * @return string The STDOUT returned by the Git command.
279
     */
280
    public function git(string $commandLine, ?string $cwd = null): string
281
    {
282
        $command = new GitCommand($commandLine);
283
        $command->executeRaw(is_string($commandLine));
284
        $command->setDirectory($cwd);
285
        return $this->run($command);
286
    }
287
288
    /**
289
     * @return string The STDOUT returned by the Git command.
290
     */
291
    public function run(GitCommand $gitCommand, ?string $cwd = null): string
292
    {
293
        $process = new GitProcess($this, $gitCommand, $cwd);
294
        $process->run(function ($type, $buffer) use ($process, $gitCommand): void {
295
            $event = new GitOutputEvent($this, $process, $gitCommand, $type, $buffer);
296
            $this->getDispatcher()->dispatch($event);
297
        });
298
299
        return $gitCommand->notBypassed() ? $process->getOutput() : '';
300
    }
301
}
302