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

GitWrapper::setPrivateKey()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 8.8337
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
     */
145
    public function setPrivateKey(string $privateKey, int $port = 22, ?string $wrapper = null, ?string $UserKnownHostsFile = null): void
146
    {
147
        if ($wrapper === null) {
148
            $wrapper = __DIR__ . '/../bin/git-ssh-wrapper.sh';
149
        }
150
151
        if (! $wrapperPath = realpath($wrapper)) {
152
            throw new GitException('Path to GIT_SSH wrapper script could not be resolved: ' . $wrapper);
153
        }
154
155
        if (! $privateKeyPath = realpath($privateKey)) {
156
            throw new GitException('Path private key could not be resolved: ' . $privateKey);
157
        }
158
159
        if ($UserKnownHostsFile === null ) {
160
            $optUserKnownHostsFile = '';
161
        }
162
        else {
163
            if (! $UserKnownHostsFilePath = realpath($UserKnownHostsFile)) {
164
                throw new GitException('Path to UserKnownHostsFile  could not be resolved: ' . $UserKnownHostsFile);
165
            }
166
            $optUserKnownHostsFile = '-o UserKnownHostsFile=' . $UserKnownHostsFile;
167
        }
168
169
        $this->setEnvVar('GIT_SSH', $wrapperPath);
170
        $this->setEnvVar('GIT_SSH_KEY', $privateKeyPath);
171
        $this->setEnvVar('GIT_SSH_PORT', $port);
172
        $this->setEnvVar('GIT_SSH_OPT_UserKnownHostsFile',  $optUserKnownHostsFile);
173
    }
174
175
    /**
176
     * Unsets the private key by removing the appropriate environment variables.
177
     */
178
    public function unsetPrivateKey(): void
179
    {
180
        $this->unsetEnvVar('GIT_SSH');
181
        $this->unsetEnvVar('GIT_SSH_KEY');
182
        $this->unsetEnvVar('GIT_SSH_PORT');
183
        $this->unsetEnvVar('GIT_SSH_OPT_UserKnownHostsFile');
184
    }
185
186
    public function addOutputEventSubscriber(AbstractOutputEventSubscriber $gitOutputEventSubscriber): void
187
    {
188
        $this->getDispatcher()->addSubscriber($gitOutputEventSubscriber);
189
    }
190
191
    public function addLoggerEventSubscriber(GitLoggerEventSubscriber $gitLoggerEventSubscriber): void
192
    {
193
        $this->getDispatcher()->addSubscriber($gitLoggerEventSubscriber);
194
    }
195
196
    public function removeOutputEventSubscriber(AbstractOutputEventSubscriber $gitOutputEventSubscriber): void
197
    {
198
        $this->getDispatcher()->removeSubscriber($gitOutputEventSubscriber);
199
    }
200
201
    /**
202
     * Set whether or not to stream real-time output to STDOUT and STDERR.
203
     */
204
    public function streamOutput(bool $streamOutput = true): void
205
    {
206
        if ($streamOutput && ! isset($this->outputEventSubscriber)) {
207
            $this->outputEventSubscriber = new StreamOutputEventSubscriber();
208
            $this->addOutputEventSubscriber($this->outputEventSubscriber);
209
        }
210
211
        if (! $streamOutput && isset($this->outputEventSubscriber)) {
212
            $this->removeOutputEventSubscriber($this->outputEventSubscriber);
213
            unset($this->outputEventSubscriber);
214
        }
215
    }
216
217
    /**
218
     * Returns an object that interacts with a working copy.
219
     *
220
     * @param string $directory Path to the directory containing the working copy.
221
     */
222
    public function workingCopy(string $directory): GitWorkingCopy
223
    {
224
        return new GitWorkingCopy($this, $directory);
225
    }
226
227
    /**
228
     * Returns the version of the installed Git client.
229
     */
230
    public function version(): string
231
    {
232
        return $this->git('--version');
233
    }
234
235
    /**
236
     * Executes a `git init` command.
237
     *
238
     * Create an empty git repository or reinitialize an existing one.
239
     *
240
     * @param mixed[] $options An associative array of command line options.
241
     */
242
    public function init(string $directory, array $options = []): GitWorkingCopy
243
    {
244
        $git = $this->workingCopy($directory);
245
        $git->init($options);
246
        $git->setCloned(true);
247
248
        return $git;
249
    }
250
251
    /**
252
     * Executes a `git clone` command and returns a working copy object.
253
     *
254
     * Clone a repository into a new directory. Use @see GitWorkingCopy::cloneRepository()
255
     * instead for more readable code.
256
     *
257
     * @param string $directory The directory that the repository will be cloned into. If null is
258
     *   passed, the directory will be generated from the URL with @see GitStrings::parseRepositoryName().
259
     * @param mixed[] $options
260
     */
261
    public function cloneRepository(string $repository, ?string $directory = null, array $options = []): GitWorkingCopy
262
    {
263
        if ($directory === null) {
264
            $directory = GitStrings::parseRepositoryName($repository);
265
        }
266
267
        $git = $this->workingCopy($directory);
268
        $git->cloneRepository($repository, $options);
269
        $git->setCloned(true);
270
        return $git;
271
    }
272
273
    /**
274
     * The command is simply a raw command line entry for everything after the Git binary.
275
     * For example, a `git config -l` command would be passed as `config -l` via the first argument of this method.
276
     *
277
     * @return string The STDOUT returned by the Git command.
278
     */
279
    public function git(string $commandLine, ?string $cwd = null): string
280
    {
281
        $command = new GitCommand($commandLine);
282
        $command->executeRaw(is_string($commandLine));
283
        $command->setDirectory($cwd);
284
        return $this->run($command);
285
    }
286
287
    /**
288
     * @return string The STDOUT returned by the Git command.
289
     */
290
    public function run(GitCommand $gitCommand, ?string $cwd = null): string
291
    {
292
        $process = new GitProcess($this, $gitCommand, $cwd);
293
        $process->run(function ($type, $buffer) use ($process, $gitCommand): void {
294
            $event = new GitOutputEvent($this, $process, $gitCommand, $type, $buffer);
295
            $this->getDispatcher()->dispatch($event);
296
        });
297
298
        return $gitCommand->notBypassed() ? $process->getOutput() : '';
299
    }
300
}
301