• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eiriksm / cosy-composer / 16469015360

23 Jul 2025 11:10AM UTC coverage: 86.578% (-0.4%) from 86.939%
16469015360

push

github

web-flow
More accurate parsing of named prs  (#427)

82 of 102 new or added lines in 9 files covered. (80.39%)

1 existing line in 1 file now uncovered.

2019 of 2332 relevant lines covered (86.58%)

44.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.96
/src/Providers/Gitlab.php
1
<?php
2

3
namespace eiriksm\CosyComposer\Providers;
4

5
use eiriksm\CosyComposer\ProviderInterface;
6
use Gitlab\Client;
7
use Gitlab\ResultPager;
8
use Violinist\Slug\Slug;
9

10
class Gitlab implements ProviderInterface
11
{
12

13
    protected $client;
14

15
    private $cache;
16

17
    public function __construct(Client $client)
18
    {
19
        $this->client = $client;
18✔
20
    }
21

22
    public function authenticate($user, $token)
23
    {
24
        $this->client->authenticate($user, Client::AUTH_OAUTH_TOKEN);
4✔
25
    }
26

27
    public function authenticatePrivate($user, $token)
28
    {
29
        $this->client->authenticate($user, Client::AUTH_OAUTH_TOKEN);
2✔
30
    }
31

32
    public function authenticatePersonalAccessToken($user, $token)
33
    {
34
        $this->client->authenticate($user, Client::AUTH_OAUTH_TOKEN);
×
35
    }
36

37
    public function repoIsPrivate(Slug $slug)
38
    {
39
        // Consider all gitlab things private, since we have the API key to do so anyway.
40
        return true;
2✔
41
    }
42

43
    public function getDefaultBranch(Slug $slug)
44
    {
45
        $url = $slug->getUrl();
2✔
46
        if (!isset($this->cache['repo'])) {
2✔
47
            $this->cache['repo'] = $this->client->projects()->show(self::getProjectId($url));
2✔
48
        }
49
        return $this->cache['repo']['default_branch'];
2✔
50
    }
51

52
    protected function getBranches(Slug $slug)
53
    {
54
        if (!isset($this->cache['branches'])) {
2✔
55
            $pager = new ResultPager($this->client);
2✔
56
            $api = $this->client->repositories();
2✔
57
            $method = 'branches';
2✔
58
            $this->cache['branches'] = $pager->fetchAll($api, $method, [self::getProjectId($slug->getUrl())]);
2✔
59
        }
60
        return $this->cache['branches'];
2✔
61
    }
62

63
    public function getBranchesFlattened(Slug $slug)
64
    {
65
        $branches = $this->getBranches($slug);
2✔
66

67
        $branches_flattened = [];
2✔
68
        foreach ($branches as $branch) {
2✔
69
            $branches_flattened[] = $branch['name'];
2✔
70
        }
71
        return $branches_flattened;
2✔
72
    }
73

74
    public function getPrsNamed(Slug $slug) : NamedPrs
75
    {
76
        $pager = new ResultPager($this->client);
2✔
77
        $api = $this->client->mergeRequests();
2✔
78
        $method = 'all';
2✔
79
        $prs = $pager->fetchAll($api, $method, [self::getProjectId($slug->getUrl()), [
2✔
80
            'state' => 'opened',
2✔
81
        ]]);
2✔
82
        $prs_named = new NamedPrs();
2✔
83
        foreach ($prs as $pr) {
2✔
84
            if ($pr['state'] !== 'opened') {
2✔
85
                continue;
×
86
            }
87
            // Now get the last commits for this branch.
88
            $commits = $this->client->repositories()->commits(self::getProjectId($slug->getUrl()), [
2✔
89
                'ref_name' => $pr['source_branch'],
2✔
90
            ]);
2✔
91
            $data = [
2✔
92
                'title' => $pr['title'],
2✔
93
                'body' => !empty($pr['description']) ? $pr['description'] : '',
2✔
94
                'html_url' => !empty($pr['web_url']) ? $pr['web_url'] : '',
2✔
95
                'number' => $pr["iid"],
2✔
96
                'base' => [
2✔
97
                    'sha' => !empty($commits[1]["id"]) ? $commits[1]["id"] : $pr['sha'],
2✔
98
                    'ref' => $pr["target_branch"],
2✔
99
                ],
2✔
100
                'head' => [
2✔
101
                    'ref' => $pr['source_branch'],
2✔
102
                ],
2✔
103
            ];
2✔
104
            $prs_named->addFromPrData($data);
2✔
105
            if (!empty($commits[0]["id"]) && !empty($commits[0]["message"])) {
2✔
NEW
106
                $prs_named->addFromCommit($commits[0]["message"], $data);
×
107
            }
108
        }
109
        return $prs_named;
2✔
110
    }
111

112
    public function getDefaultBase(Slug $slug, $default_branch)
113
    {
114
        $branches = $this->getBranches($slug);
×
115
        $default_base = null;
×
116
        foreach ($branches as $branch) {
×
117
            if ($branch['name'] == $default_branch) {
×
118
                $default_base = $branch['commit']['id'];
×
119
            }
120
        }
121
        return $default_base;
×
122
    }
123

124
    public function createFork($user, $repo, $fork_user)
125
    {
126
        throw new \Exception('Gitlab integration only support creating PRs as the authenticated user.');
×
127
    }
128

129
    public function closePullRequestWithComment(Slug $slug, $pr_id, $comment)
130
    {
131
        $this->client->mergeRequests()->addNote(self::getProjectId($slug->getUrl()), $pr_id, $comment);
×
132
        $this->client->mergeRequests()->update(self::getProjectId($slug->getUrl()), $pr_id, [
×
133
            'state_event' => 'close',
×
134
        ]);
×
135
    }
136

137
    public function createPullRequest(Slug $slug, $params)
138
    {
139
        /** @var \Gitlab\Api\MergeRequests $mr */
140
        $mr = $this->client->mergeRequests();
2✔
141
        $data = $mr->create(self::getProjectId($slug->getUrl()), $params['head'], $params['base'], $params['title'], [
2✔
142
            'description' => $params['body'],
2✔
143
        ]);
2✔
144
        if (!empty($data['web_url'])) {
×
145
            $data['html_url'] = $data['web_url'];
×
146
        }
147
        if (!empty($data['iid'])) {
×
148
            $data['number'] = $data['iid'];
×
149
        }
150
        // Try to update with assignees.
151
        if (!empty($params['assignees'])) {
×
152
            $new_data = [
×
153
                'assignee_ids' => $params['assignees'],
×
154
            ];
×
155
            $mr->update(self::getProjectId($slug->getUrl()), $data["iid"], $new_data);
×
156
        }
157
        return $data;
×
158
    }
159

160
    public function updatePullRequest(Slug $slug, $id, $params)
161
    {
162

163
        $gitlab_params = [
2✔
164
            'source_branch' => $params['head'],
2✔
165
            'target_branch' => $params['base'],
2✔
166
            'title' => $params['title'],
2✔
167
            'assignee_ids' => $params['assignees'],
2✔
168
            'target_project_id' => null,
2✔
169
            'description' => $params['body'],
2✔
170
        ];
2✔
171
        return $this->client->mergeRequests()->update(self::getProjectId($slug->getUrl()), $id, $gitlab_params);
2✔
172
    }
173

174
    public static function getProjectId($url)
175
    {
176
        $url = parse_url($url);
10✔
177
        if (empty($url['path'])) {
10✔
NEW
178
            $url['path'] = '/';
×
179
        }
180
        return ltrim($url['path'], '/');
10✔
181
    }
182

183
    public function addLabels(array $pr_data, Slug $slug, array $labels): bool
184
    {
185
        $gitlab_params = [
×
186
            'labels' => implode(',', $labels),
×
187
        ];
×
188
        $id = $pr_data['number'];
×
189
        try {
190
            $data = $this->client->mergeRequests()->update(self::getProjectId($slug->getUrl()), $id, $gitlab_params);
×
191
            return true;
×
192
        } catch (\Throwable $e) {
×
193
            return false;
×
194
        }
195
    }
196

197
    public function enableAutomerge(array $pr_data, Slug $slug, $merge_method = self::MERGE_METHOD_MERGE) : bool
198
    {
199
        if (empty($pr_data['number']) && !empty($pr_data["iid"])) {
2✔
200
            $pr_data['number'] = $pr_data["iid"];
×
201
        }
202
        if ($merge_method === self::MERGE_METHOD_REBASE) {
2✔
203
            // Not supported on gitlab.
204
            return false;
×
205
        }
206
        $data = [
2✔
207
            'merge_when_pipeline_succeeds' => true,
2✔
208
        ];
2✔
209
        if ($merge_method === self::MERGE_METHOD_SQUASH) {
2✔
210
            $data['squash'] = true;
×
211
        }
212
        $project_id = self::getProjectId($slug->getUrl());
2✔
213
        $retries = 0;
2✔
214
        $mr = $this->client->mergeRequests();
2✔
215
        while (true) {
2✔
216
            try {
217
                $result = $mr->merge($project_id, $pr_data["number"], $data);
2✔
218
                if (!empty($result["merge_when_pipeline_succeeds"])) {
2✔
219
                    return true;
2✔
220
                }
221
            } catch (\Throwable $e) {
×
222
            }
223
            $retries++;
×
224
            if ($retries > 20) {
×
225
                return false;
×
226
            }
227
            // Sleep for 40 ms with a linear backoff. Max sleep time will be 800
228
            // ms. We want to keep it under 1 second because as it says in the
229
            // PHP documentation:
230
            // "Note: Values larger than 1000000 (i.e. sleeping for more than a
231
            // second) may not be supported by the operating system. Use sleep()
232
            // instead."
233
            usleep($retries * (1000 * 40));
×
234
        }
235
    }
236
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc