Completed
Push — 2.0 ( dff28c...eae3da )
by David
05:00
created

BuildService   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 9
dl 0
loc 167
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getCommitId() 0 5 1
A findMergeRequestByCommitSha() 0 18 3
A getLatestCommitIdFromBranch() 0 5 1
A getPipelines() 0 7 2
A findPipelineByCommit() 0 12 3
B getLatestPipelineFromCommitId() 0 26 4
A getLatestPipelineFromBranch() 0 10 2
B dumpArtifact() 0 27 5
A dumpArtifactFromBranch() 0 5 1
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
     * Returns a commit ID from a project name and build ref.
26
     *
27
     * @param string $projectName
28
     * @param string $buildRef
29
     * @return string
30
     */
31
    public function getCommitId(string $projectName, string $buildRef) : string
32
    {
33
        $build = $this->client->projects->build($projectName, $buildRef);
0 ignored issues
show
Bug introduced by
The method build() does not seem to exist on object<Gitlab\Api\Projects>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
34
        return $build['commit']['id'];
35
    }
36
37
    /**
38
     * @param string $projectName
39
     * @param string $commitSha
40
     * @return array The merge request object
41
     * @throws MergeRequestNotFoundException
42
     */
43
    public function findMergeRequestByCommitSha(string $projectName, string $commitSha) : array
44
    {
45
        // Find in the merge requests (since our build was triggered recently, it should definitely be there)
46
        $mergeRequests = $this->client->merge_requests->all($projectName, [
47
            'order_by' => 'updated_at',
48
            'sort' => 'desc'
49
        ]);
50
51
        foreach ($mergeRequests as $mergeRequest) {
52
            // 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)
53
54
            if ($mergeRequest['sha'] === $commitSha) {
55
                return $mergeRequest;
56
            }
57
        }
58
59
        throw new MergeRequestNotFoundException('Could not find a PR whose last commit/buildRef ID is '.$commitSha);
60
    }
61
62
    public function getLatestCommitIdFromBranch(string $projectName, string $branchName) : string
63
    {
64
        $branch = $this->client->repositories->branch($projectName, $branchName);
65
        return $branch['commit']['id'];
66
    }
67
68
    private $pipelines = [];
69
70
    private function getPipelines(string $projectName) : array
71
    {
72
        if (!isset($this->pipelines[$projectName])) {
73
            $this->pipelines[$projectName] = $this->client->projects->pipelines($projectName);
74
        }
75
        return $this->pipelines[$projectName];
76
    }
77
78
    public function findPipelineByCommit(string $projectName, string $commitId) : ?array
79
    {
80
        $pipelines = $this->getPipelines($projectName);
81
82
        foreach ($pipelines as $pipeline) {
83
            if ($pipeline['sha'] === $commitId) {
84
                return $pipeline;
85
            }
86
        }
87
88
        return null;
89
    }
90
91
    /**
92
     * Recursive function that attempts to find a build in the previous commits.
93
     *
94
     * @param string $projectName
95
     * @param string $commitId
96
     * @param int $numIter
97
     * @return array
98
     * @throws BuildNotFoundException
99
     */
100
    public function getLatestPipelineFromCommitId(string $projectName, string $commitId, int $numIter = 0) : array
101
    {
102
103
        $pipeline = $this->findPipelineByCommit($projectName, $commitId);
104
105
        if ($pipeline !== null) {
106
            return $pipeline;
107
        }
108
109
        $numIter++;
110
        // Let's find a build in the last 10 commits.
111
        if ($numIter > 10) {
112
            throw new BuildNotFoundException('Could not find a build for commit '.$projectName.':'.$commitId);
113
        }
114
115
        // Let's get the commit info
116
        $commit = $this->client->repositories->commit($projectName, $commitId);
117
        $parentIds = $commit['parent_ids'];
118
119
        if (count($parentIds) !== 1) {
120
            throw new BuildNotFoundException('Could not find a build for commit '.$projectName.':'.$commitId);
121
        }
122
123
        // Not found? Let's recurse.
124
        return $this->getLatestPipelineFromCommitId($projectName, $parentIds[0], $numIter);
125
    }
126
127
    /**
128
     * @param string $projectName
129
     * @param string $branchName
130
     * @return array
131
     * @throws BuildNotFoundException
132
     */
133
    public function getLatestPipelineFromBranch(string $projectName, string $branchName) : array
134
    {
135
        $commitId = $this->getLatestCommitIdFromBranch($projectName, $branchName);
136
137
        try {
138
            return $this->getLatestPipelineFromCommitId($projectName, $commitId);
139
        } catch (BuildNotFoundException $e) {
140
            throw new BuildNotFoundException('Could not find a build for branch '.$projectName.':'.$branchName, 0, $e);
141
        }
142
    }
143
144
    public function dumpArtifact(string $projectName, string $pipelineId, string $jobName, string $file)
145
    {
146
        var_dump($projectName);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($projectName); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
147
        var_dump($buildRef);
0 ignored issues
show
Bug introduced by
The variable $buildRef does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
148
        var_dump($jobName);
149
        // Call seems broken
150
        //$artifactContent = $this->client->jobs->artifactsByRefName($projectName, $buildRef, $jobName);
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
151
152
        $jobs = $this->client->jobs->pipelineJobs($projectName, $pipelineId);
153
        $job = null;
154
        foreach ($jobs as $job) {
155
            if ($job['name'] === $jobName && (in_array($job['status'], ['failed', 'success']))) {
156
                break;
157
            }
158
        }
159
160
        if ($job === null) {
161
            throw new \RuntimeException('Could not find finished job with name '.$job['name'].'.');
162
        }
163
164
        $artifactContent = $this->client->jobs->artifacts($projectName, $job['id']);
165
166
        $stream = StreamWrapper::getResource($artifactContent);
167
168
        $filesystem = new Filesystem();
169
        $filesystem->dumpFile($file, $stream);
0 ignored issues
show
Documentation introduced by
$stream is of type resource, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
170
    }
171
172
    public function dumpArtifactFromBranch(string $projectName, string $branchName, string $jobStage, string $file)
173
    {
174
        $pipeline = $this->getLatestPipelineFromBranch($projectName, $branchName);
175
        $this->dumpArtifact($projectName, $pipeline['id'], $jobStage, $file);
176
    }
177
}
178