Completed
Push — master ( c299ad...293d26 )
by Dave
15s queued 11s
created

GitCliWrapper::isClean()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 6
rs 10
1
<?php
2
3
/**
4
 * Static Analysis Results Baseliner (sarb).
5
 *
6
 * (c) Dave Liddament
7
 *
8
 * For the full copyright and licence information please view the LICENSE file distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace DaveLiddament\StaticAnalysisResultsBaseliner\Plugins\GitDiffHistoryAnalyser\internal;
14
15
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\ProjectRoot;
16
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\HistoryAnalyser\InvalidHistoryMarkerException;
17
use DaveLiddament\StaticAnalysisResultsBaseliner\Plugins\GitDiffHistoryAnalyser\GitCommit;
18
use DaveLiddament\StaticAnalysisResultsBaseliner\Plugins\GitDiffHistoryAnalyser\GitException;
19
use LogicException;
20
use Symfony\Component\Process\Exception\RuntimeException;
21
use Symfony\Component\Process\Process;
22
23
class GitCliWrapper implements GitWrapper
24
{
25
    public function getCurrentSha(ProjectRoot $projectRoot): GitCommit
26
    {
27
        try {
28
            $gitCommand = $this->getGitCommand(['rev-parse', 'HEAD'], $projectRoot);
29
            $rawOutput = $this->runCommand($gitCommand, 'Failed to get SHA');
30
            $processOutput = trim($rawOutput);
31
32
            return new GitCommit($processOutput);
33
        } catch (CommandFailedException $e) {
34
            throw GitException::failedToGetSha($e);
35
        } catch (InvalidHistoryMarkerException $e) { // @codeCoverageIgnore
36
            // This should never happen as git SHA got from running git command will always return valid SHA.
37
            throw new LogicException('Invalid git SHA '.$e->getMessage()); // @codeCoverageIgnore
38
        }
39
    }
40
41
    public function getGitDiff(ProjectRoot $projectRoot, GitCommit $originalCommit): string
42
    {
43
        $arguments = [
44
            'diff',
45
            '-w',
46
            '-M',
47
            $originalCommit->asString(),
48
        ];
49
        $command = $this->getGitCommand($arguments, $projectRoot);
50
51
        try {
52
            return $this->runCommand($command, 'Failed to get git-diff');
53
        } catch (CommandFailedException $e) {
54
            throw GitException::failedDiff($e);
55
        }
56
    }
57
58
    /**
59
     * @param string[] $gitCommand
60
     *
61
     * @throws CommandFailedException
62
     */
63
    private function runCommand(array $gitCommand, string $context): string
64
    {
65
        try {
66
            $process = new Process($gitCommand);
67
            $process->run();
68
69
            if ($process->isSuccessful()) {
70
                return $process->getOutput();
71
            }
72
73
            $exitCode = $process->getExitCode();
74
            throw CommandFailedException::newInstance($context, $exitCode, $process->getErrorOutput());
75
        } catch (RuntimeException $e) { // @codeCoverageIgnore
76
            // Impossible to simulate this happening
77
            throw CommandFailedException::newInstance($context, null, $e->getMessage()); // @codeCoverageIgnore
78
        }
79
    }
80
81
    /**
82
     * @param string[] $arguments
83
     *
84
     * @return string[]
85
     */
86
    private function getGitCommand(array $arguments, ProjectRoot $projectRoot): array
87
    {
88
        $gitCommand = [
89
            'git',
90
            '--git-dir='.$projectRoot.\DIRECTORY_SEPARATOR.'.git',
91
            "--work-tree={$projectRoot}",
92
        ];
93
94
        return array_merge($gitCommand, $arguments);
95
    }
96
97
    /**
98
     * Only used for testing.
99
     *
100
     * @throws CommandFailedException
101
     */
102
    public function init(ProjectRoot $projectRoot): void
103
    {
104
        $command = [
105
            'git',
106
            'init',
107
            (string) $projectRoot,
108
        ];
109
        $this->runCommand($command, "git init {$projectRoot}");
110
    }
111
112
    /**
113
     * Only used for testing.
114
     *
115
     * @throws CommandFailedException
116
     */
117
    public function addAndCommit(string $message, ProjectRoot $projectRoot): void
118
    {
119
        $this->addAll($projectRoot);
120
121
        $commitCommand = $this->getGitCommand([
122
            '-c',
123
            'user.name=Anon',
124
            '-c',
125
            '[email protected]',
126
            'commit',
127
            '-m',
128
            "$message",
129
        ], $projectRoot);
130
        $this->runCommand($commitCommand, 'git commit');
131
    }
132
133
    /**
134
     * Only used for testing.
135
     *
136
     * @throws CommandFailedException
137
     */
138
    public function addAll(ProjectRoot $projectRoot): void
139
    {
140
        $addCommand = $this->getGitCommand(['add', '.'], $projectRoot);
141
        $this->runCommand($addCommand, 'Git add .');
142
    }
143
144
    /**
145
     * Only used for testing.
146
     *
147
     * @throws CommandFailedException
148
     */
149
    public function isClean(ProjectRoot $projectRoot): bool
150
    {
151
        $addCommand = $this->getGitCommand(['status', '--porcelain'], $projectRoot);
152
        $changes = $this->runCommand($addCommand, 'Git status --porcelain');
153
154
        return '' === trim($changes);
155
    }
156
}
157