1
|
|
|
<?php |
2
|
|
|
namespace TheCodingMachine\WashingMachine\Gitlab; |
3
|
|
|
use Gitlab\Client; |
4
|
|
|
use GuzzleHttp\Psr7\Stream; |
5
|
|
|
use GuzzleHttp\Psr7\StreamWrapper; |
6
|
|
|
use Symfony\Component\Filesystem\Filesystem; |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* Class to access different data in Gitlab from the build reference |
10
|
|
|
*/ |
11
|
|
|
class BuildService |
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* @var Client |
15
|
|
|
*/ |
16
|
|
|
private $client; |
17
|
|
|
|
18
|
|
|
public function __construct(Client $client) |
19
|
|
|
{ |
20
|
|
|
|
21
|
|
|
$this->client = $client; |
22
|
|
|
} |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @param string $projectName |
26
|
|
|
* @param string $commitSha |
27
|
|
|
* @return array The merge request object |
28
|
|
|
* @throws MergeRequestNotFoundException |
29
|
|
|
*/ |
30
|
|
|
public function findMergeRequestByCommitSha(string $projectName, string $commitSha) : array |
31
|
|
|
{ |
32
|
|
|
// Find in the merge requests (since our build was triggered recently, it should definitely be there) |
33
|
|
|
$mergeRequests = $this->client->merge_requests->all($projectName, [ |
34
|
|
|
'order_by' => 'updated_at', |
35
|
|
|
'sort' => 'desc' |
36
|
|
|
]); |
37
|
|
|
|
38
|
|
|
foreach ($mergeRequests as $mergeRequest) { |
39
|
|
|
// Let's only return this PR if the returned commit is the FIRST one (otherwise, the commit ID is on an outdated version of the PR) |
40
|
|
|
|
41
|
|
|
if ($mergeRequest['sha'] === $commitSha) { |
42
|
|
|
return $mergeRequest; |
43
|
|
|
} |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
throw new MergeRequestNotFoundException('Could not find a PR whose last commit/buildRef ID is '.$commitSha); |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
public function getLatestCommitIdFromBranch(string $projectName, string $branchName) : string |
50
|
|
|
{ |
51
|
|
|
$branch = $this->client->repositories->branch($projectName, $branchName); |
52
|
|
|
return $branch['commit']['id']; |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
private $pipelines = []; |
56
|
|
|
|
57
|
|
|
private function getPipelines(string $projectName) : array |
58
|
|
|
{ |
59
|
|
|
if (!isset($this->pipelines[$projectName])) { |
60
|
|
|
$this->pipelines[$projectName] = $this->client->projects->pipelines($projectName); |
61
|
|
|
} |
62
|
|
|
return $this->pipelines[$projectName]; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
public function findPipelineByCommit(string $projectName, string $commitId) : ?array |
66
|
|
|
{ |
67
|
|
|
$pipelines = $this->getPipelines($projectName); |
68
|
|
|
|
69
|
|
|
foreach ($pipelines as $pipeline) { |
70
|
|
|
if ($pipeline['sha'] === $commitId) { |
71
|
|
|
return $pipeline; |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
return null; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Recursive function that attempts to find a build in the previous commits. |
80
|
|
|
* |
81
|
|
|
* @param string $projectName |
82
|
|
|
* @param string $commitId |
83
|
|
|
* @param int $numIter |
84
|
|
|
* @return array |
85
|
|
|
* @throws BuildNotFoundException |
86
|
|
|
*/ |
87
|
|
|
public function getLatestPipelineFromCommitId(string $projectName, string $commitId, int $numIter = 0) : array |
88
|
|
|
{ |
89
|
|
|
|
90
|
|
|
$pipeline = $this->findPipelineByCommit($projectName, $commitId); |
91
|
|
|
|
92
|
|
|
if ($pipeline !== null) { |
93
|
|
|
return $pipeline; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
$numIter++; |
97
|
|
|
// Let's find a build in the last 10 commits. |
98
|
|
|
if ($numIter > 10) { |
99
|
|
|
throw new BuildNotFoundException('Could not find a build for commit '.$projectName.':'.$commitId); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
// Let's get the commit info |
103
|
|
|
$commit = $this->client->repositories->commit($projectName, $commitId); |
104
|
|
|
$parentIds = $commit['parent_ids']; |
105
|
|
|
|
106
|
|
|
if (count($parentIds) !== 1) { |
107
|
|
|
throw new BuildNotFoundException('Could not find a build for commit '.$projectName.':'.$commitId); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
// Not found? Let's recurse. |
111
|
|
|
return $this->getLatestPipelineFromCommitId($projectName, $parentIds[0], $numIter); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* @param string $projectName |
116
|
|
|
* @param string $branchName |
117
|
|
|
* @return array |
118
|
|
|
* @throws BuildNotFoundException |
119
|
|
|
*/ |
120
|
|
|
public function getLatestPipelineFromBranch(string $projectName, string $branchName) : array |
121
|
|
|
{ |
122
|
|
|
$commitId = $this->getLatestCommitIdFromBranch($projectName, $branchName); |
123
|
|
|
|
124
|
|
|
try { |
125
|
|
|
return $this->getLatestPipelineFromCommitId($projectName, $commitId); |
126
|
|
|
} catch (BuildNotFoundException $e) { |
127
|
|
|
throw new BuildNotFoundException('Could not find a build for branch '.$projectName.':'.$branchName, 0, $e); |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
public function dumpArtifact(string $projectName, string $pipelineId, string $buildName, string $jobStage, string $file) |
132
|
|
|
{ |
133
|
|
|
// Call seems broken |
134
|
|
|
//$artifactContent = $this->client->jobs->artifactsByRefName($projectName, $buildRef, $jobName); |
|
|
|
|
135
|
|
|
|
136
|
|
|
$jobs = $this->client->jobs->pipelineJobs($projectName, $pipelineId); |
137
|
|
|
$job = null; |
138
|
|
|
foreach ($jobs as $jobItem) { |
139
|
|
|
if ($jobItem['name'] === $buildName && $jobItem['stage'] === $jobStage && (in_array($jobItem['status'], ['failed', 'success']))) { |
140
|
|
|
$job = $jobItem; |
141
|
|
|
break; |
142
|
|
|
} |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
if ($job === null) { |
146
|
|
|
throw new \RuntimeException('Could not find finished job with build name "'.$buildName.'" and stage "'.$jobStage.'" in pipeline "'.$pipelineId.'"'); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
$artifactContent = $this->client->jobs->artifacts($projectName, $job['id']); |
150
|
|
|
|
151
|
|
|
$stream = StreamWrapper::getResource($artifactContent); |
152
|
|
|
|
153
|
|
|
$filesystem = new Filesystem(); |
154
|
|
|
$filesystem->dumpFile($file, $stream); |
|
|
|
|
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
public function dumpArtifactFromBranch(string $projectName, string $branchName, string $buildName, string $jobStage, string $file) |
158
|
|
|
{ |
159
|
|
|
$pipeline = $this->getLatestPipelineFromBranch($projectName, $branchName); |
160
|
|
|
$this->dumpArtifact($projectName, $pipeline['id'], $buildName, $jobStage, $file); |
161
|
|
|
} |
162
|
|
|
} |
163
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.