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

klinge / sl-webapp / 18972011891

31 Oct 2025 12:06PM UTC coverage: 74.73% (+11.1%) from 63.602%
18972011891

push

github

klinge
Fixed phpcs errors

1662 of 2224 relevant lines covered (74.73%)

3.82 hits per line

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

73.26
/App/Controllers/WebhookController.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\Controllers;
6

7
use App\Services\UrlGeneratorService;
8
use App\Services\Github\GitHubService;
9
use App\Services\Github\GitRepositoryService;
10
use App\Services\Github\DeploymentService;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Psr\Http\Message\ResponseInterface;
13
use Monolog\Logger;
14
use League\Container\Container;
15

16
class WebhookController extends BaseController
17
{
18
    private string $remoteIp;
19

20
    public function __construct(
21
        UrlGeneratorService $urlGenerator,
22
        ServerRequestInterface $request,
23
        Logger $logger,
24
        Container $container,
25
        private GitHubService $githubService,
26
        private GitRepositoryService $gitRepositoryService,
27
        private DeploymentService $deploymentService
28
    ) {
29
        parent::__construct($urlGenerator, $request, $logger, $container);
12✔
30
        $this->remoteIp = $this->request->getServerParams()['REMOTE_ADDR'];
12✔
31
    }
32

33
    public function handle(): ResponseInterface
34
    {
35
        $this->logger->info(
4✔
36
            'Starting to process webhook call from: ' . $this->remoteIp,
4✔
37
            ['class' => __CLASS__, 'function' => __FUNCTION__]
4✔
38
        );
4✔
39
        $this->logger->debug(
4✔
40
            'Headers: ' . json_encode($this->request->getHeaders()),
4✔
41
            ['class' => __CLASS__, 'function' => __FUNCTION__]
4✔
42
        );
4✔
43
        $this->logger->debug(
4✔
44
            'Payload: ' . json_encode($this->request->getParsedBody()),
4✔
45
            ['class' => __CLASS__, 'function' => __FUNCTION__]
4✔
46
        );
4✔
47

48
        $verificationResult = $this->verifyRequest();
4✔
49

50
        // Handle verification failures with appropriate responses
51
        if (!$verificationResult) {
4✔
52
            if (!$this->request->hasHeader('X-GitHub-Event')) {
×
53
                return $this->jsonResponse(['status' => 'error', 'message' => 'Missing event header'], 400);
×
54
            }
55

56
            $event = $this->request->getHeader('X-GitHub-Event')[0] ?? '';
×
57

58
            if ($event !== 'push' && $event !== 'ping') {
×
59
                return $this->jsonResponse(['status' => 'error', 'message' => 'Event not supported'], 400);
×
60
            }
61

62
            if (!$this->request->hasHeader('X-Hub-Signature-256')) {
×
63
                return $this->jsonResponse(['status' => 'error', 'message' => 'Missing signature header'], 400);
×
64
            }
65

66
            $signature = $this->request->getHeader('X-Hub-Signature-256')[0] ?? '';
×
67
            $signature_parts = explode('=', $signature);
×
68

69
            if (count($signature_parts) != 2 || $signature_parts[0] != 'sha256') {
×
70
                return $this->jsonResponse(['status' => 'error', 'message' => 'Bad header format'], 400);
×
71
            }
72

73
            $rawBody = (string) $this->request->getBody();
×
74

75
            if (!$this->githubService->validateSignature($rawBody, $signature_parts[1])) {
×
76
                return $this->jsonResponse(['status' => 'error', 'message' => 'Invalid signature'], 401);
×
77
            }
78

79
            $payload = json_decode($rawBody, true);
×
80

81
            if (!$this->githubService->isValidRepository($payload)) {
×
82
                return $this->jsonResponse(
×
83
                    ['status' => 'ignored', 'message' => "Not handling requests for this repo, {$payload['repository']['full_name']}"],
×
84
                    200
×
85
                );
×
86
            }
87

88
            if ($event === 'ping') {
×
89
                return $this->jsonResponse(['status' => 'ok', 'message' => 'Pong'], 200);
×
90
            }
91

92
            return $this->jsonResponse(['status' => 'ok', 'message' => 'Request processed']);
×
93
        }
94

95
        $payload = $verificationResult;
4✔
96
        $branch = $this->githubService->extractBranchName($payload['ref']);
4✔
97

98
        // Check if it's a release branch
99
        if (!$this->githubService->isReleaseBranch($branch)) {
4✔
100
            $this->logger->debug('Not a push to the release branch', ['class' => __CLASS__, 'function' => __FUNCTION__]);
1✔
101
            return $this->jsonResponse(['status' => 'ignored', 'message' => 'Not a push to the release branch']);
1✔
102
        }
103

104
        $this->logger->info('Received a push on the release branch. Continuing to deploy.. ');
3✔
105

106
        $repoUrl = $payload['repository']['clone_url'];
3✔
107
        $result = $this->gitRepositoryService->updateRepository($branch, $repoUrl);
3✔
108
        if ($result['status'] !== 'success') {
3✔
109
            $this->logger->error(
1✔
110
                "Error occurred while handling repository operations. Message: {$result['message']}",
1✔
111
                ['class' => __CLASS__, 'function' => __FUNCTION__]
1✔
112
            );
1✔
113
            return $this->jsonResponse(['status' => 'error', 'message' => $result['message']], 500);
1✔
114
        }
115

116
        //Finally schedule the deploy of the repository to the web server
117
        $deploymentScheduled = $this->deploymentService->scheduleDeployment();
2✔
118

119
        if (!$deploymentScheduled) {
2✔
120
            return $this->jsonResponse(['status' => 'error', 'message' => 'Failed to schedule deployment'], 500);
1✔
121
        }
122

123
        return $this->jsonResponse(['status' => 'success', 'message' => 'Successfully received push and scheduled deployment']);
1✔
124
    }
125

126
    /**
127
     * Verifies and validates the incoming GitHub webhook request.
128
     *
129
     * This method performs several checks on the incoming request to ensure its authenticity and integrity.
130
     *
131
     * If it's a 'ping' event, it responds with a 'pong'.
132
     * For 'push' events, it processes the payload for further action.
133
     *
134
     * @return array The validated payload for 'push' events, or an empty array for other cases
135
     */
136
    public function verifyRequest(): array
137
    {
138
        $payload = [];
8✔
139
        $event = '';
8✔
140

141
        // Verify that it's a github webhook request
142
        if (!$this->request->hasHeader('X-GitHub-Event')) {
8✔
143
            $this->logger->warning("Missing X-GitHub-Event header. Headers was: " . json_encode($this->request->getHeaders()));
1✔
144
            return $payload;
1✔
145
        } else {
146
            $event = $this->request->getHeader('X-GitHub-Event')[0];
7✔
147
        }
148
        // Verify that it's ping or a push
149
        if (!empty($event) && $event !== 'push' && $event !== 'ping') {
7✔
150
            $this->logger->warning("Github event was not push or ping");
1✔
151
            return $payload;
1✔
152
        }
153
        // Verify that the signature header is there
154
        if (!$this->request->hasHeader('X-Hub-Signature-256')) {
6✔
155
            $this->logger->warning("Github signature header missing");
1✔
156
            return $payload;
1✔
157
        }
158
        // Verify signature header format
159
        $signature = $this->request->getHeader('X-Hub-Signature-256')[0];
5✔
160
        $signature_parts = explode('=', $signature);
5✔
161

162
        if (count($signature_parts) != 2 || $signature_parts[0] != 'sha256') {
5✔
163
            $this->logger->warning("Bad format for the github signature header");
1✔
164
            return $payload;
1✔
165
        }
166
        //All is well so far - get the request body and validate the signature
167
        $rawBody = (string) $this->request->getBody();
4✔
168

169
        if (!$this->githubService->validateSignature($rawBody, $signature_parts[1])) {
4✔
170
            $this->logger->warning("Github signature did not verify");
1✔
171
            return [];
1✔
172
        }
173

174
        $payload = json_decode($rawBody, true);
3✔
175

176
        //Lastly check that the request was for the correct repository
177
        if (!$this->githubService->isValidRepository($payload)) {
3✔
178
            $this->logger->warning("Repository Id in the request was not correct");
1✔
179
            return [];
1✔
180
        }
181

182
        //Handle the ping event here, just send a pong back
183
        if ($event === 'ping') {
2✔
184
            $this->logger->debug("Got a ping event, sent a pong back");
1✔
185
            return [];
1✔
186
        }
187

188
        //Return paylod, empty array if there were any errors
189
        return $payload;
1✔
190
    }
191
}
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