From e8c14263356b835ee1740e89951716a159b483d7 Mon Sep 17 00:00:00 2001
From: eldadfux <eldad.fux@gmail.com>
Date: Sun, 22 Feb 2026 20:49:03 +0100
Subject: [PATCH 1/5] fix: Ensure installation ID is set correctly in GitHub
 payload handling

---
 src/VCS/Adapter/Git/GitHub.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/VCS/Adapter/Git/GitHub.php b/src/VCS/Adapter/Git/GitHub.php
index fdc49299..76564044 100644
--- a/src/VCS/Adapter/Git/GitHub.php
+++ b/src/VCS/Adapter/Git/GitHub.php
@@ -631,7 +631,7 @@ public function getEvent(string $event, string $payload): array
             throw new Exception("Invalid payload.");
         }
 
-        $installationId = strval($payload['installation']['id']);
+        $installationId = strval(isset($payload['installation']['id']) ? $payload['installation']['id'] : '');
 
         switch ($event) {
             case 'push':

From 4b6a37dd34512ea639840103a4cc16cbab16b0f6 Mon Sep 17 00:00:00 2001
From: eldadfux <eldad.fux@gmail.com>
Date: Sun, 22 Feb 2026 21:03:27 +0100
Subject: [PATCH 2/5] Missing a lot of null safety checks

---
 src/VCS/Adapter/Git/GitHub.php | 48 ++++++++++++++++++----------------
 src/VCS/Adapter/Git/Gitea.php  |  4 +--
 2 files changed, 27 insertions(+), 25 deletions(-)

diff --git a/src/VCS/Adapter/Git/GitHub.php b/src/VCS/Adapter/Git/GitHub.php
index 76564044..6678cb3e 100644
--- a/src/VCS/Adapter/Git/GitHub.php
+++ b/src/VCS/Adapter/Git/GitHub.php
@@ -638,19 +638,19 @@ public function getEvent(string $event, string $payload): array
                 $branchCreated = isset($payload['created']) ? $payload['created'] : false;
                 $branchDeleted = isset($payload['deleted']) ? $payload['deleted'] : false;
                 $ref = $payload['ref'] ?? '';
-                $repositoryId = strval($payload['repository']['id'] ?? '');
-                $repositoryName = $payload['repository']['name'] ?? '';
+                $repositoryId = strval(isset($payload['repository']['id']) ? $payload['repository']['id'] : '');
+                $repositoryName = isset($payload['repository']['name']) ? $payload['repository']['name'] : '';
                 $branch = str_replace('refs/heads/', '', $ref);
-                $branchUrl = $payload['repository']['html_url'] . "/tree/" . $branch;
-                $repositoryUrl = $payload['repository']['html_url'];
+                $repositoryUrl = isset($payload['repository']['html_url']) ? $payload['repository']['html_url'] : '';
+                $branchUrl = $repositoryUrl ? $repositoryUrl . "/tree/" . $branch : '';
                 $commitHash = $payload['after'] ?? '';
-                $owner = $payload['repository']['owner']['name'] ?? '';
-                $authorUrl = $payload['sender']['html_url'];
-                $authorAvatarUrl = $payload['sender']['avatar_url'] ?? '';
-                $headCommitAuthorName = $payload['head_commit']['author']['name'] ?? '';
-                $headCommitAuthorEmail = $payload['head_commit']['author']['email'] ?? '';
-                $headCommitMessage = $payload['head_commit']['message'] ?? '';
-                $headCommitUrl = $payload['head_commit']['url'] ?? '';
+                $owner = isset($payload['repository']['owner']['name']) ? $payload['repository']['owner']['name'] : '';
+                $authorUrl = isset($payload['sender']['html_url']) ? $payload['sender']['html_url'] : '';
+                $authorAvatarUrl = isset($payload['sender']['avatar_url']) ? $payload['sender']['avatar_url'] : '';
+                $headCommitAuthorName = isset($payload['head_commit']['author']['name']) ? $payload['head_commit']['author']['name'] : '';
+                $headCommitAuthorEmail = isset($payload['head_commit']['author']['email']) ? $payload['head_commit']['author']['email'] : '';
+                $headCommitMessage = isset($payload['head_commit']['message']) ? $payload['head_commit']['message'] : '';
+                $headCommitUrl = isset($payload['head_commit']['url']) ? $payload['head_commit']['url'] : '';
 
                 $affectedFiles = [];
                 foreach (($payload['commits'] ?? []) as $commit) {
@@ -690,19 +690,21 @@ public function getEvent(string $event, string $payload): array
                     'affectedFiles' => \array_keys($affectedFiles),
                 ];
             case 'pull_request':
-                $repositoryId = strval($payload['repository']['id'] ?? '');
-                $branch = $payload['pull_request']['head']['ref'] ?? '';
-                $repositoryName = $payload['repository']['name'] ?? '';
-                $repositoryUrl = $payload['repository']['html_url'] ?? '';
-                $branchUrl = "$repositoryUrl/tree/$branch";
+                $repositoryId = strval(isset($payload['repository']['id']) ? $payload['repository']['id'] : '');
+                $branch = isset($payload['pull_request']['head']['ref']) ? $payload['pull_request']['head']['ref'] : '';
+                $repositoryName = isset($payload['repository']['name']) ? $payload['repository']['name'] : '';
+                $repositoryUrl = isset($payload['repository']['html_url']) ? $payload['repository']['html_url'] : '';
+                $branchUrl = $repositoryUrl ? "$repositoryUrl/tree/$branch" : '';
                 $pullRequestNumber = $payload['number'] ?? '';
                 $action = $payload['action'] ?? '';
-                $owner = $payload['repository']['owner']['login'] ?? '';
-                $authorUrl = $payload['sender']['html_url'];
-                $authorAvatarUrl = $payload['pull_request']['user']['avatar_url'] ?? '';
-                $commitHash = $payload['pull_request']['head']['sha'] ?? '';
-                $headCommitUrl = $repositoryUrl . "/commits/" . $commitHash;
-                $external = $payload['pull_request']['head']['user']['login'] !== $payload['pull_request']['base']['user']['login'];
+                $owner = isset($payload['repository']['owner']['login']) ? $payload['repository']['owner']['login'] : '';
+                $authorUrl = isset($payload['sender']['html_url']) ? $payload['sender']['html_url'] : '';
+                $authorAvatarUrl = isset($payload['pull_request']['user']['avatar_url']) ? $payload['pull_request']['user']['avatar_url'] : '';
+                $commitHash = isset($payload['pull_request']['head']['sha']) ? $payload['pull_request']['head']['sha'] : '';
+                $headCommitUrl = $repositoryUrl ? $repositoryUrl . "/commits/" . $commitHash : '';
+                $headLogin = isset($payload['pull_request']['head']['user']['login']) ? $payload['pull_request']['head']['user']['login'] : '';
+                $baseLogin = isset($payload['pull_request']['base']['user']['login']) ? $payload['pull_request']['base']['user']['login'] : '';
+                $external = $headLogin !== $baseLogin;
 
                 return [
                     'branch' => $branch,
@@ -723,7 +725,7 @@ public function getEvent(string $event, string $payload): array
             case 'installation':
             case 'installation_repositories':
                 $action = $payload['action'] ?? '';
-                $userName = $payload['installation']['account']['login'] ?? '';
+                $userName = isset($payload['installation']['account']['login']) ? $payload['installation']['account']['login'] : '';
 
                 return [
                     'action' => $action,
diff --git a/src/VCS/Adapter/Git/Gitea.php b/src/VCS/Adapter/Git/Gitea.php
index 944b87bd..eea3524e 100644
--- a/src/VCS/Adapter/Git/Gitea.php
+++ b/src/VCS/Adapter/Git/Gitea.php
@@ -124,7 +124,7 @@ public function getRepository(string $owner, string $repositoryName): array
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]);
 
-        $statusCode = $response['headers']['status-code'] ?? 0;
+        $statusCode = isset($response['headers']['status-code']) ? $response['headers']['status-code'] : 0;
         if ($statusCode >= 400) {
             throw new RepositoryNotFound("Repository not found");
         }
@@ -171,7 +171,7 @@ public function deleteRepository(string $owner, string $repositoryName): bool
 
         $response = $this->call(self::METHOD_DELETE, $url, ['Authorization' => "token $this->accessToken"]);
 
-        $statusCode = $response['headers']['status-code'];
+        $statusCode = isset($response['headers']['status-code']) ? $response['headers']['status-code'] : 0;
 
         if ($statusCode >= 400) {
             throw new Exception("Deleting repository {$repositoryName} failed with status code {$statusCode}");

From 9f65942a3813aa6f37dcf61e1e34473881ec8e59 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matej=20Ba=C4=8Do?= <matejbaco2000@gmail.com>
Date: Thu, 5 Mar 2026 10:20:25 +0100
Subject: [PATCH 3/5] Improve null check quality

---
 src/VCS/Adapter/Git/GitHub.php | 224 ++++++++++++++++++++-------------
 src/VCS/Adapter/Git/Gitea.php  |  25 ++--
 2 files changed, 156 insertions(+), 93 deletions(-)

diff --git a/src/VCS/Adapter/Git/GitHub.php b/src/VCS/Adapter/Git/GitHub.php
index 6678cb3e..2282b7e1 100644
--- a/src/VCS/Adapter/Git/GitHub.php
+++ b/src/VCS/Adapter/Git/GitHub.php
@@ -115,13 +115,15 @@ public function searchRepositories(string $owner, int $page, int $per_page, stri
             'sort' => 'updated'
         ]);
 
-        if (!isset($response['body']['items'])) {
+        $responseBody = $response['body'];
+
+        if (!array_key_exists('items', $responseBody)) {
             throw new Exception("Repositories list missing in the response.");
         }
 
         return [
-            'items' => $response['body']['items'],
-            'total' => $response['body']['total_count'],
+            'items' => $responseBody['items'] ?? [],
+            'total' => $responseBody['total_count'] ?? 0,
         ];
     }
 
@@ -152,11 +154,13 @@ public function getRepositoryName(string $repositoryId): string
         $url = "/repositories/$repositoryId";
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
 
-        if (!isset($response['body']['name'])) {
+        $responseBody = $response['body'] ?? [];
+
+        if (!array_key_exists('name', $responseBody)) {
             throw new RepositoryNotFound("Repository not found");
         }
 
-        return $response['body']['name'];
+        return $responseBody['name'] ?? '';
     }
 
     /**
@@ -174,11 +178,15 @@ public function getRepositoryTree(string $owner, string $repositoryName, string
         $url = "/repos/$owner/$repositoryName/git/trees/$branch" . ($recursive ? '?recursive=1' : '');
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
 
-        if ($response['headers']['status-code'] == 404) {
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode == 404) {
             return [];
         }
 
-        return array_column($response['body']['tree'], 'path');
+        $responseBody = $response['body'] ?? [];
+
+        return array_column($responseBody['tree'] ?? [], 'path');
     }
 
     /**
@@ -219,22 +227,26 @@ public function getRepositoryContent(string $owner, string $repositoryName, stri
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
 
-        if ($response['headers']['status-code'] !== 200) {
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode !== 200) {
             throw new FileNotFound();
         }
 
-        $encoding = $response['body']['encoding'];
+        $responseBody = $response['body'];
+
+        $encoding = $responseBody['encoding'] ?? '';
 
         $content = '';
         if ($encoding === 'base64') {
-            $content = base64_decode($response['body']['content']);
+            $content = base64_decode($responseBody['content'] ?? '');
         } else {
             throw new FileNotFound();
         }
 
         $output = [
-           'sha' => $response['body']['sha'],
-           'size' => $response['body']['size'],
+           'sha' => $responseBody['sha'] ?? '',
+           'size' => $responseBody['size'] ?? 0,
            'content' => $content
         ];
 
@@ -262,16 +274,20 @@ public function listRepositoryContents(string $owner, string $repositoryName, st
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
 
-        if (($response['headers']['status-code'] == 404)) {
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode == 404) {
             return [];
         }
 
+        $responseBody = $response['body'] ?? [];
+
         $items = [];
 
-        if (!empty($response['body'][0])) {
-            $items = $response['body'];
-        } elseif (!empty($response['body'])) {
-            $items = [$response['body']];
+        if (!empty($responseBody[0] ?? [])) {
+            $items = $responseBody;
+        } elseif (!empty($responseBody)) {
+            $items = [$responseBody];
         }
 
         $contents = [];
@@ -293,10 +309,10 @@ public function deleteRepository(string $owner, string $repositoryName): bool
 
         $response = $this->call(self::METHOD_DELETE, $url, ['Authorization' => "Bearer $this->accessToken"]);
 
-        $statusCode = $response['headers']['status-code'];
-
-        if ($statusCode >= 400) {
-            throw new Exception("Deleting repository $repositoryName failed with status code $statusCode");
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode >= 400) {
+            throw new Exception("Deleting repository $repositoryName failed with status code $responseHeadersStatusCode");
         }
         return true;
     }
@@ -314,11 +330,13 @@ public function createComment(string $owner, string $repositoryName, int $pullRe
 
         $response = $this->call(self::METHOD_POST, $url, ['Authorization' => "Bearer $this->accessToken"], ['body' => $comment]);
 
-        if (!isset($response['body']['id'])) {
+        $responseBody = $response['body'] ?? [];
+
+        if (!array_key_exists('id', $responseBody)) {
             throw new Exception("Comment creation response is missing comment ID.");
         }
 
-        $commentId = $response['body']['id'];
+        $commentId = $responseBody['id'] ?? '';
 
         return $commentId;
     }
@@ -338,7 +356,10 @@ public function getComment(string $owner, string $repositoryName, string $commen
         $url = '/repos/' . $owner . '/' . $repositoryName . '/issues/comments/' . $commentId;
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
-        $comment = $response['body']['body'] ?? '';
+
+        $responseBody = $response['body'] ?? [];
+
+        $comment = $responseBody['body'] ?? '';
 
         return $comment;
     }
@@ -360,11 +381,13 @@ public function updateComment(string $owner, string $repositoryName, int $commen
 
         $response = $this->call(self::METHOD_PATCH, $url, ['Authorization' => "Bearer $this->accessToken"], ['body' => $comment]);
 
-        if (!isset($response['body']['id'])) {
+        $responseBody = $response['body'] ?? [];
+
+        if (!array_key_exists('id', $responseBody)) {
             throw new Exception("Comment update response is missing comment ID.");
         }
 
-        $commentId = $response['body']['id'];
+        $commentId = $responseBody['id'] ?? '';
 
         return $commentId;
     }
@@ -393,11 +416,12 @@ protected function generateAccessToken(string $privateKey, ?string $appId): void
         $jwt = new JWT($privateKeyObj, 'RS256');
         $token = $jwt->encode($payload);
         $this->jwtToken = $token;
-        $res = $this->call(self::METHOD_POST, '/app/installations/' . $this->installationId . '/access_tokens', ['Authorization' => 'Bearer ' . $token]);
-        if (!isset($res['body']['token'])) {
+        $response = $this->call(self::METHOD_POST, '/app/installations/' . $this->installationId . '/access_tokens', ['Authorization' => 'Bearer ' . $token]);
+        $responseBody = $response['body'] ?? [];
+        if (!array_key_exists('token', $responseBody)) {
             throw new Exception('Failed to retrieve access token from GitHub API.');
         }
-        $this->accessToken = $res['body']['token'];
+        $this->accessToken = $responseBody['token'] ?? '';
     }
 
     /**
@@ -424,11 +448,14 @@ public function getOwnerName(string $installationId): string
         $url = '/app/installations/' . $installationId;
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->jwtToken"]);
 
-        if (!isset($response['body']['account']['login'])) {
+        $responseBody = $response['body'] ?? [];
+        $responseBodyAccount = $responseBody['account'] ?? [];
+
+        if (!array_key_exists('login', $responseBodyAccount)) {
             throw new Exception("Owner name retrieval response is missing account login.");
         }
 
-        return $response['body']['account']['login'];
+        return $responseBodyAccount['login'] ?? '';
     }
 
     /**
@@ -456,7 +483,9 @@ public function getPullRequestFromBranch(string $owner, string $repositoryName,
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
 
-        return $response['body'][0] ?? [];
+        $responseBody = $response['body'] ?? [];
+
+        return $responseBody[0] ?? [];
     }
 
     /**
@@ -472,9 +501,11 @@ public function listBranches(string $owner, string $repositoryName): array
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
 
+        $responseBody = $response['body'] ?? [];
+
         $names = [];
-        foreach ($response['body'] as $subarray) {
-            $names[] = $subarray['name'];
+        foreach ($responseBody as $subarray) {
+            $names[] = $subarray['name'] ?? '';
         }
 
         return $names;
@@ -494,18 +525,18 @@ public function getCommit(string $owner, string $repositoryName, string $commitH
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
 
-        $body = $response['body'] ?? [];
-        $author = $body['author'] ?? [];
-        $commit = $body['commit'] ?? [];
-        $commitAuthor = $commit['author'] ?? [];
+        $responseBody = $response['body'] ?? [];
+        $responseBodyAuthor = $responseBody['author'] ?? [];
+        $responseBodyCommit = $responseBody['commit'] ?? [];
+        $responseBodyCommitAuthor = $responseBodyCommit['author'] ?? [];
 
         return [
-            'commitAuthor' => $commitAuthor['name'] ?? 'Unknown',
-            'commitMessage' => $commit['message'] ?? 'No message',
-            'commitAuthorAvatar' => $author['avatar_url'] ?? '',
-            'commitAuthorUrl' => $author['html_url'] ?? '',
-            'commitHash' => $body['sha'] ?? '',
-            'commitUrl' => $body['html_url'] ?? '',
+            'commitAuthor' => $responseBodyCommitAuthor['name'] ?? 'Unknown',
+            'commitMessage' => $responseBodyCommit['message'] ?? 'No message',
+            'commitAuthorAvatar' => $responseBodyAuthor['avatar_url'] ?? '',
+            'commitAuthorUrl' => $responseBodyAuthor['html_url'] ?? '',
+            'commitHash' => $responseBody['sha'] ?? '',
+            'commitUrl' => $responseBody['html_url'] ?? '',
         ];
     }
 
@@ -523,24 +554,29 @@ public function getLatestCommit(string $owner, string $repositoryName, string $b
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
 
+        $responseBody = $response['body'] ?? [];
+        $responseBodyCommit = $responseBody['commit'] ?? [];
+        $responseBodyCommitAuthor = $responseBodyCommit['author'] ?? [];
+        $responseBodyAuthor = $responseBody['author'] ?? [];
+
         if (
-            !isset($response['body']['commit']['author']['name']) ||
-            !isset($response['body']['commit']['message']) ||
-            !isset($response['body']['sha']) ||
-            !isset($response['body']['html_url']) ||
-            !isset($response['body']['author']['avatar_url']) ||
-            !isset($response['body']['author']['html_url'])
+            !array_key_exists('name', $responseBodyCommitAuthor) ||
+            !array_key_exists('message', $responseBodyCommit) ||
+            !array_key_exists('sha', $responseBody) ||
+            !array_key_exists('html_url', $responseBody) ||
+            !array_key_exists('avatar_url', $responseBodyAuthor) ||
+            !array_key_exists('html_url', $responseBodyAuthor)
         ) {
             throw new Exception("Latest commit response is missing required information.");
         }
 
         return [
-            'commitAuthor' => $response['body']['commit']['author']['name'],
-            'commitMessage' => $response['body']['commit']['message'],
-            'commitHash' => $response['body']['sha'],
-            'commitUrl' => $response['body']['html_url'],
-            'commitAuthorAvatar' => $response['body']['author']['avatar_url'],
-            'commitAuthorUrl' => $response['body']['author']['html_url'],
+            'commitAuthor' => $responseBodyCommitAuthor['name'] ?? '',
+            'commitMessage' => $responseBodyCommit['message'] ?? '',
+            'commitHash' => $responseBody['sha'] ?? '',
+            'commitUrl' => $responseBody['html_url'] ?? '',
+            'commitAuthorAvatar' => $responseBodyAuthor['avatar_url'] ?? '',
+            'commitAuthorUrl' => $responseBodyAuthor['html_url'] ?? '',
         ];
     }
 
@@ -631,26 +667,33 @@ public function getEvent(string $event, string $payload): array
             throw new Exception("Invalid payload.");
         }
 
-        $installationId = strval(isset($payload['installation']['id']) ? $payload['installation']['id'] : '');
+        $payloadInstallation = $payload['installation'] ?? [];
+
+        $installationId = strval($payloadInstallation['id'] ?? '');
 
         switch ($event) {
             case 'push':
-                $branchCreated = isset($payload['created']) ? $payload['created'] : false;
-                $branchDeleted = isset($payload['deleted']) ? $payload['deleted'] : false;
-                $ref = $payload['ref'] ?? '';
-                $repositoryId = strval(isset($payload['repository']['id']) ? $payload['repository']['id'] : '');
-                $repositoryName = isset($payload['repository']['name']) ? $payload['repository']['name'] : '';
-                $branch = str_replace('refs/heads/', '', $ref);
-                $repositoryUrl = isset($payload['repository']['html_url']) ? $payload['repository']['html_url'] : '';
-                $branchUrl = $repositoryUrl ? $repositoryUrl . "/tree/" . $branch : '';
+                $payloadRepository = $payload['repository'] ?? [];
+                $payloadRepositoryOwner = $payloadRepository['owner'] ?? [];
+                $payloadSender = $payload['sender'] ?? [];
+                $payloadHeadCommit = $payload['head_commit'] ?? [];
+                $payloadHeadCommitAuthor = $payloadHeadCommit['author'] ?? [];
+
+                $branchCreated = $payload['created'] ?? false;
+                $branchDeleted = $payload['deleted'] ?? false;
+                $repositoryId = strval($payloadRepository['id'] ?? '');
+                $repositoryName = $payloadRepository['name'] ?? '';
+                $branch = str_replace('refs/heads/', '', $payload['ref'] ?? '');
+                $repositoryUrl = $payloadRepository['html_url'] ?? '';
+                $branchUrl = !empty($repositoryUrl) && !empty($branch) ? $repositoryUrl . "/tree/" . $branch : '';
                 $commitHash = $payload['after'] ?? '';
-                $owner = isset($payload['repository']['owner']['name']) ? $payload['repository']['owner']['name'] : '';
-                $authorUrl = isset($payload['sender']['html_url']) ? $payload['sender']['html_url'] : '';
-                $authorAvatarUrl = isset($payload['sender']['avatar_url']) ? $payload['sender']['avatar_url'] : '';
-                $headCommitAuthorName = isset($payload['head_commit']['author']['name']) ? $payload['head_commit']['author']['name'] : '';
-                $headCommitAuthorEmail = isset($payload['head_commit']['author']['email']) ? $payload['head_commit']['author']['email'] : '';
-                $headCommitMessage = isset($payload['head_commit']['message']) ? $payload['head_commit']['message'] : '';
-                $headCommitUrl = isset($payload['head_commit']['url']) ? $payload['head_commit']['url'] : '';
+                $owner = $payloadRepositoryOwner['name'] ?? '';
+                $authorUrl = $payloadSender['html_url'] ?? '';
+                $authorAvatarUrl = $payloadSender['avatar_url'] ?? '';
+                $headCommitAuthorName = $payloadHeadCommitAuthor['name'] ?? '';
+                $headCommitAuthorEmail = $payloadHeadCommitAuthor['email'] ?? '';
+                $headCommitMessage = $payloadHeadCommit['message'] ?? '';
+                $headCommitUrl = $payloadHeadCommit['url'] ?? '';
 
                 $affectedFiles = [];
                 foreach (($payload['commits'] ?? []) as $commit) {
@@ -690,20 +733,30 @@ public function getEvent(string $event, string $payload): array
                     'affectedFiles' => \array_keys($affectedFiles),
                 ];
             case 'pull_request':
-                $repositoryId = strval(isset($payload['repository']['id']) ? $payload['repository']['id'] : '');
-                $branch = isset($payload['pull_request']['head']['ref']) ? $payload['pull_request']['head']['ref'] : '';
-                $repositoryName = isset($payload['repository']['name']) ? $payload['repository']['name'] : '';
-                $repositoryUrl = isset($payload['repository']['html_url']) ? $payload['repository']['html_url'] : '';
-                $branchUrl = $repositoryUrl ? "$repositoryUrl/tree/$branch" : '';
+                $payloadRepository = $payload['repository'] ?? [];
+                $payloadRepositoryOwner = $payloadRepository['owner'] ?? [];
+                $payloadSender = $payload['sender'] ?? [];
+                $payloadPullRequest = $payload['pull_request'] ?? [];
+                $payloadPullRequestHead = $payloadPullRequest['head'] ?? [];
+                $payloadPullRequestHeadUser = $payloadPullRequestHead['user'] ?? [];
+                $payloadPullRequestUser = $payloadPullRequest['user'] ?? [];
+                $payloadPullRequestBase = $payloadPullRequest['base'] ?? [];
+                $payloadPullRequestBaseUser = $payloadPullRequestBase['user'] ?? [];
+
+                $repositoryId = strval($payloadRepository['id'] ?? '');
+                $branch = $payloadPullRequestHead['ref'] ?? '';
+                $repositoryName = $payloadRepository['name'] ?? '';
+                $repositoryUrl = $payloadRepository['html_url'] ?? '';
+                $branchUrl = !empty($repositoryUrl) && !empty($branch) ? $repositoryUrl . "/tree/" . $branch : '';
                 $pullRequestNumber = $payload['number'] ?? '';
                 $action = $payload['action'] ?? '';
-                $owner = isset($payload['repository']['owner']['login']) ? $payload['repository']['owner']['login'] : '';
-                $authorUrl = isset($payload['sender']['html_url']) ? $payload['sender']['html_url'] : '';
-                $authorAvatarUrl = isset($payload['pull_request']['user']['avatar_url']) ? $payload['pull_request']['user']['avatar_url'] : '';
-                $commitHash = isset($payload['pull_request']['head']['sha']) ? $payload['pull_request']['head']['sha'] : '';
+                $owner = $payloadRepositoryOwner['login'] ?? '';
+                $authorUrl = $payloadSender['html_url'] ?? '';
+                $authorAvatarUrl = $payloadPullRequestUser['avatar_url'] ?? '';
+                $commitHash = $payloadPullRequestHead['sha'] ?? '';
                 $headCommitUrl = $repositoryUrl ? $repositoryUrl . "/commits/" . $commitHash : '';
-                $headLogin = isset($payload['pull_request']['head']['user']['login']) ? $payload['pull_request']['head']['user']['login'] : '';
-                $baseLogin = isset($payload['pull_request']['base']['user']['login']) ? $payload['pull_request']['base']['user']['login'] : '';
+                $headLogin = $payloadPullRequestHeadUser['login'] ?? '';
+                $baseLogin = $payloadPullRequestBaseUser['login'] ?? '';
                 $external = $headLogin !== $baseLogin;
 
                 return [
@@ -724,8 +777,11 @@ public function getEvent(string $event, string $payload): array
                 ];
             case 'installation':
             case 'installation_repositories':
+                $payloadInstallation = $payload['installation'] ?? [];
+                $payloadInstallationAccount = $payloadInstallation['account'] ?? [];
+
                 $action = $payload['action'] ?? '';
-                $userName = isset($payload['installation']['account']['login']) ? $payload['installation']['account']['login'] : '';
+                $userName = $payloadInstallationAccount['login'] ?? '';
 
                 return [
                     'action' => $action,
diff --git a/src/VCS/Adapter/Git/Gitea.php b/src/VCS/Adapter/Git/Gitea.php
index eea3524e..75e15203 100644
--- a/src/VCS/Adapter/Git/Gitea.php
+++ b/src/VCS/Adapter/Git/Gitea.php
@@ -107,7 +107,9 @@ public function createOrganization(string $orgName): string
             'visibility' => 'public',
         ]);
 
-        return $response['body']['name'] ?? '';
+        $responseBody = $response['body'] ?? [];
+
+        return $responseBody['name'] ?? '';
     }
 
     // Stub methods to satisfy abstract class requirements
@@ -124,12 +126,14 @@ public function getRepository(string $owner, string $repositoryName): array
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]);
 
-        $statusCode = isset($response['headers']['status-code']) ? $response['headers']['status-code'] : 0;
-        if ($statusCode >= 400) {
+
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode >= 400) {
             throw new RepositoryNotFound("Repository not found");
         }
 
-        return $response['body'];
+        return $response['body'] ?? [];
     }
 
     public function getRepositoryName(string $repositoryId): string
@@ -138,11 +142,13 @@ public function getRepositoryName(string $repositoryId): string
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]);
 
-        if (!isset($response['body']['name'])) {
+        $responseBody = $response['body'] ?? [];
+
+        if (!array_key_exists('name', $responseBody)) {
             throw new RepositoryNotFound("Repository not found");
         }
 
-        return $response['body']['name'];
+        return $responseBody['name'] ?? '';
     }
 
     public function getRepositoryTree(string $owner, string $repositoryName, string $branch, bool $recursive = false): array
@@ -171,10 +177,11 @@ public function deleteRepository(string $owner, string $repositoryName): bool
 
         $response = $this->call(self::METHOD_DELETE, $url, ['Authorization' => "token $this->accessToken"]);
 
-        $statusCode = isset($response['headers']['status-code']) ? $response['headers']['status-code'] : 0;
 
-        if ($statusCode >= 400) {
-            throw new Exception("Deleting repository {$repositoryName} failed with status code {$statusCode}");
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode >= 400) {
+            throw new Exception("Deleting repository {$repositoryName} failed with status code {$responseHeadersStatusCode}");
         }
 
         return true;

From 4ee38f2bced7e30300108273c9c7378773bd7955 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matej=20Ba=C4=8Do?= <matejbaco2000@gmail.com>
Date: Thu, 5 Mar 2026 10:25:31 +0100
Subject: [PATCH 4/5] Fix leftover

---
 src/VCS/Adapter/Git/GitHub.php | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/VCS/Adapter/Git/GitHub.php b/src/VCS/Adapter/Git/GitHub.php
index 2282b7e1..3de0be09 100644
--- a/src/VCS/Adapter/Git/GitHub.php
+++ b/src/VCS/Adapter/Git/GitHub.php
@@ -115,7 +115,7 @@ public function searchRepositories(string $owner, int $page, int $per_page, stri
             'sort' => 'updated'
         ]);
 
-        $responseBody = $response['body'];
+        $responseBody = $response['body'] ?? [];
 
         if (!array_key_exists('items', $responseBody)) {
             throw new Exception("Repositories list missing in the response.");
@@ -202,8 +202,10 @@ public function listRepositoryLanguages(string $owner, string $repositoryName):
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
 
-        if (isset($response['body'])) {
-            return array_keys($response['body']);
+        $responseBody = $response['body'] ?? [];
+
+        if (!empty($responseBody)) {
+            return array_keys($responseBody);
         }
 
         return [];
@@ -233,7 +235,7 @@ public function getRepositoryContent(string $owner, string $repositoryName, stri
             throw new FileNotFound();
         }
 
-        $responseBody = $response['body'];
+        $responseBody = $response['body'] ?? [];
 
         $encoding = $responseBody['encoding'] ?? '';
 

From a45c690ea7c3ca5e1916eacedc708a8ea097a3ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matej=20Ba=C4=8Do?= <matejbaco2000@gmail.com>
Date: Thu, 5 Mar 2026 10:36:40 +0100
Subject: [PATCH 5/5] More null fixes after merge

---
 src/VCS/Adapter/Git/GitHub.php | 12 ++++----
 src/VCS/Adapter/Git/Gitea.php  | 56 ++++++++++++++++++++++------------
 2 files changed, 42 insertions(+), 26 deletions(-)

diff --git a/src/VCS/Adapter/Git/GitHub.php b/src/VCS/Adapter/Git/GitHub.php
index cc5a0b45..a7394ea4 100644
--- a/src/VCS/Adapter/Git/GitHub.php
+++ b/src/VCS/Adapter/Git/GitHub.php
@@ -174,9 +174,9 @@ public function searchRepositories(string $installationId, string $owner, int $p
                 'page' => $page,
                 'per_page' => $per_page,
             ]);
-            
-            $responseBody = $response['body'];
-            
+
+            $responseBody = $response['body'] ?? [];
+
             if (!array_key_exists('repositories', $responseBody)) {
                 throw new Exception("Repositories list missing in the response.");
             }
@@ -196,7 +196,7 @@ public function searchRepositories(string $installationId, string $owner, int $p
             ]);
 
             $responseBody = $response['body'] ?? [];
-            
+
             if (!array_key_exists('repositories', $responseBody)) {
                 throw new Exception("Repositories list missing in the response.");
             }
@@ -208,7 +208,7 @@ public function searchRepositories(string $installationId, string $owner, int $p
             $repositories = array_merge($repositories, $filteredRepositories);
 
             // If less than 100 repositories are returned, we have fetched all repositories.
-            if (\count($responseBody['repositories'] ??[]) < 100) {
+            if (\count($responseBody['repositories'] ?? []) < 100) {
                 break;
             }
 
@@ -239,7 +239,7 @@ public function getInstallationRepository(string $repositoryName): array
             ]);
 
             $responseBody = $response['body'] ?? [];
-            
+
             if (!array_key_exists('repositories', $responseBody)) {
                 throw new Exception("Repositories list missing in the response.");
             }
diff --git a/src/VCS/Adapter/Git/Gitea.php b/src/VCS/Adapter/Git/Gitea.php
index 7f373b07..303f97aa 100644
--- a/src/VCS/Adapter/Git/Gitea.php
+++ b/src/VCS/Adapter/Git/Gitea.php
@@ -167,11 +167,15 @@ public function getRepositoryTree(string $owner, string $repositoryName, string
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]);
 
-        if (($response['headers']['status-code'] ?? 0) === 404) {
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode === 404) {
             return [];
         }
 
-        return array_column($response['body']['tree'] ?? [], 'path');
+        $responseBody = $response['body'] ?? [];
+
+        return array_column($responseBody['tree'] ?? [], 'path');
     }
 
     /**
@@ -198,9 +202,10 @@ public function createFile(string $owner, string $repositoryName, string $filepa
             ]
         );
 
-        $statusCode = $response['headers']['status-code'] ?? 0;
-        if ($statusCode >= 400) {
-            throw new Exception("Failed to create file {$filepath}: HTTP {$statusCode}");
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode >= 400) {
+            throw new Exception("Failed to create file {$filepath}: HTTP {$responseHeadersStatusCode}");
         }
 
         return $response['body'] ?? [];
@@ -229,9 +234,10 @@ public function createBranch(string $owner, string $repositoryName, string $newB
             ]
         );
 
-        $statusCode = $response['headers']['status-code'] ?? 0;
-        if ($statusCode >= 400) {
-            throw new Exception("Failed to create branch {$newBranchName}: HTTP {$statusCode}");
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode >= 400) {
+            throw new Exception("Failed to create branch {$newBranchName}: HTTP {$responseHeadersStatusCode}");
         }
 
         return $response['body'] ?? [];
@@ -243,8 +249,10 @@ public function listRepositoryLanguages(string $owner, string $repositoryName):
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]);
 
-        if (isset($response['body'])) {
-            return array_keys($response['body']);
+        $responseBody = $response['body'] ?? [];
+
+        if (!empty($responseBody)) {
+            return array_keys($responseBody);
         }
 
         return [];
@@ -259,22 +267,26 @@ public function getRepositoryContent(string $owner, string $repositoryName, stri
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]);
 
-        if (($response['headers']['status-code'] ?? 0) !== 200) {
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode !== 200) {
             throw new FileNotFound();
         }
 
-        $encoding = $response['body']['encoding'] ?? '';
+        $responseBody = $response['body'] ?? [];
+
+        $encoding = $responseBody['encoding'] ?? '';
         $content = '';
 
         if ($encoding === 'base64') {
-            $content = base64_decode($response['body']['content'] ?? '');
+            $content = base64_decode($responseBody['content'] ?? '');
         } else {
             throw new FileNotFound();
         }
 
         return [
-            'sha' => $response['body']['sha'] ?? '',
-            'size' => $response['body']['size'] ?? 0,
+            'sha' => $responseBody['sha'] ?? '',
+            'size' => $responseBody['size'] ?? 0,
             'content' => $content
         ];
     }
@@ -291,15 +303,19 @@ public function listRepositoryContents(string $owner, string $repositoryName, st
 
         $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]);
 
-        if (($response['headers']['status-code'] ?? 0) === 404) {
+        $responseHeaders = $response['headers'] ?? [];
+        $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0;
+        if ($responseHeadersStatusCode === 404) {
             return [];
         }
 
+        $responseBody = $response['body'] ?? [];
+
         $items = [];
-        if (!empty($response['body'][0])) {
-            $items = $response['body'];
-        } elseif (!empty($response['body'])) {
-            $items = [$response['body']];
+        if (!empty($responseBody[0] ?? [])) {
+            $items = $responseBody;
+        } elseif (!empty($responseBody)) {
+            $items = [$responseBody];
         }
 
         $contents = [];
