Completed
Push — master ( 850ec9...77efa3 )
by Matthias
02:37
created

All   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 204
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 9
Bugs 0 Features 2
Metric Value
wmc 20
c 9
b 0
f 2
lcom 1
cbo 5
dl 0
loc 204
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 1
A __destruct() 0 7 2
A setBranch() 0 4 1
A getCommits() 0 6 1
B execute() 0 40 4
A getImportedCommits() 0 19 3
A getDefaultBranch() 0 7 2
A getEnvironment() 0 17 1
B flatten() 0 21 5
1
<?php
2
3
namespace Cauditor\Runners;
4
5
use Cauditor\Analyzers\AnalyzerInterface;
6
use Cauditor\Api;
7
use Cauditor\Exception;
8
use MatthiasMullie\CI\Factory as CIFactory;
9
10
/**
11
 * @author Matthias Mullie <[email protected]>
12
 * @copyright Copyright (c) 2016, Matthias Mullie. All rights reserved.
13
 * @license LICENSE MIT
14
 */
15
class All implements RunnerInterface
16
{
17
    /**
18
     * @var Api
19
     */
20
    protected $api;
21
22
    /**
23
     * @var AnalyzerInterface
24
     */
25
    protected $analyzer;
26
27
    /**
28
     * @var bool
29
     */
30
    protected $stashed;
31
32
    /**
33
     * @var string
34
     */
35
    protected $branch;
36
37
    /**
38
     * @param Api               $api
39
     * @param AnalyzerInterface $analyzer
40
     */
41
    public function __construct(Api $api, AnalyzerInterface $analyzer)
42
    {
43
        $this->api = $api;
44
        $this->analyzer = $analyzer;
45
46
        // lets make sure the code we're testing is this specific commit,
47
        // without lingering uncommitted bits
48
        $output = exec('git stash');
49
        $this->stashed = $output !== 'No local changes to save';
50
51
        // store current branch
52
        $environment = $this->getEnvironment();
53
        $this->branch = $environment['branch'];
54
55
        // move to default branch, which is likely the one we want to analyze
56
        $this->setBranch($this->getDefaultBranch());
57
    }
58
59
    /**
60
     * Restores the original environment after analyzing.
61
     */
62
    public function __destruct()
63
    {
64
        exec("git checkout $this->branch");
65
        if ($this->stashed) {
66
            exec('git stash pop');
67
        }
68
    }
69
70
    /**
71
     * @param string $branch
72
     */
73
    public function setBranch($branch)
74
    {
75
        exec("git checkout $branch && git reset --hard && git pull");
76
    }
77
78
    /**
79
     * @return string[]
80
     */
81
    protected function getCommits()
82
    {
83
        exec("git log --pretty=format:'%H'", $commits);
84
85
        return $commits;
86
    }
87
88
    /**
89
     * @throws Exception
90
     */
91
    public function execute()
92
    {
93
        // exclude commits that have already been imported
94
        $commits = $this->getCommits();
95
        $imported = $this->getImportedCommits();
96
        $missing = array_diff($commits, $imported);
97
        foreach ($missing as $commit) {
98
            exec("git reset $commit --hard");
99
100
            $metrics = $this->analyzer->execute();
101
            $avg = $min = $max = array();
102
            $flat = $this->flatten($metrics);
103
            foreach ($flat as $metric => $values) {
104
                $avg[$metric] = (float) number_format(array_sum($values) / count($values), 2, '.', '');
105
                $min[$metric] = min($values);
106
                $max[$metric] = max($values);
107
            }
108
109
            $data = array(
110
                'default-branch' => $this->getDefaultBranch(),
111
                'metrics' => $metrics,
112
                'avg' => $avg,
113
                'min' => $min,
114
                'max' => $max,
115
            ) + $this->getEnvironment();
116
117
            // submit to cauditor (note that branch can be empty for PRs)
118
            $uri = "/api/v1/{$data['slug']}/{$data['branch']}/{$data['commit']}";
119
            $uri = preg_replace('/(?<!:)\/+/', '/', $uri);
120
            $result = $this->api->put($uri, $data);
121
122
            if ($result !== false) {
123
                echo "Submitted metrics for {$data['commit']} @ {$data['slug']} {$data['branch']}\n";
124
            } else {
125
                echo "Failed to submit metrics for {$data['commit']} @ {$data['slug']} {$data['branch']}\n";
126
            }
127
        }
128
129
        echo "Done!\n";
130
    }
131
132
    /**
133
     * Returns array of commit hashes that have already been imported.
134
     *
135
     * @return string[]
136
     *
137
     * @throws Exception
138
     */
139
    protected function getImportedCommits()
140
    {
141
        $environment = $this->getEnvironment();
142
        $slug = $environment['slug'];
143
        $branch = $environment['branch'];
144
145
        $imported = $this->api->get("/api/v1/$slug/$branch");
146
        if ($imported === false) {
147
            throw new Exception('Failed to reach API.');
148
        }
149
150
        $commits = json_decode($imported, true);
151
        $hashes = array();
152
        foreach ($commits as $commit) {
153
            $hashes[] = $commit['hash'];
154
        }
155
156
        return $hashes;
157
    }
158
159
    /**
160
     * @return string
161
     */
162
    protected function getDefaultBranch()
163
    {
164
        $config = shell_exec('cat .git/config');
165
        preg_match('/\[branch "(.+)"\]/', $config, $match);
166
167
        return isset($match[1]) ? $match[1] : 'master';
168
    }
169
170
    /**
171
     * @return array
172
     */
173
    protected function getEnvironment()
174
    {
175
        // get build data from CI
176
        $factory = new CIFactory();
177
        $environment = $factory->getCurrent();
178
179
        return array(
180
            'repo' => $environment->getRepo(),
181
            'slug' => $environment->getSlug(),
182
            'branch' => $environment->getBranch(),
183
            'pull-request' => $environment->getPullRequest(),
184
            'commit' => $environment->getCommit(),
185
            'previous-commit' => $environment->getPreviousCommit(),
186
            'author-email' => $environment->getAuthorEmail(),
187
            'timestamp' => $environment->getTimestamp(),
188
        );
189
    }
190
191
    /**
192
     * Flatten metrics into [metric => [value1, value1, value3]] array
193
     *
194
     * @param array $metrics
195
     * @return float[][]
196
     */
197
    protected function flatten(array $metrics)
198
    {
199
        $flat = array();
200
        $metrics = (array) $metrics;
201
202
        foreach ($metrics as $metric => $value) {
203
            // name & children are meta data, not metrics
204
            if (!in_array($metric, array('name', 'children'))) {
205
                $flat[$metric][] = $value;
206
            }
207
        }
208
209
        if (isset($metrics['children'])) {
210
            foreach ($metrics['children'] as $child) {
211
                $childTotals = $this->flatten($child);
212
                $flat = array_merge_recursive($flat, $childTotals);
213
            }
214
        }
215
216
        return $flat;
217
    }
218
}
219