From d54667d93be666f46fdd572555d8421e31fc51fd Mon Sep 17 00:00:00 2001 From: jayesh Date: Sun, 1 Mar 2026 09:42:06 +0530 Subject: [PATCH 1/5] feat: Add Gitea commit endpoints (getCommit, getLatestCommit) --- src/VCS/Adapter/Git/Gitea.php | 61 ++++++++++++++++++++++-- tests/VCS/Adapter/GiteaTest.php | 83 ++++++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 5 deletions(-) diff --git a/src/VCS/Adapter/Git/Gitea.php b/src/VCS/Adapter/Git/Gitea.php index 10fe1b9..de279cd 100644 --- a/src/VCS/Adapter/Git/Gitea.php +++ b/src/VCS/Adapter/Git/Gitea.php @@ -364,16 +364,71 @@ public function listBranches(string $owner, string $repositoryName): array throw new Exception("Not implemented yet"); } + /** + * Get details of a commit using commit hash + * + * @param string $owner Owner name of the repository + * @param string $repositoryName Name of the repository + * @param string $commitHash SHA of the commit + * @return array Details of the commit + */ public function getCommit(string $owner, string $repositoryName, string $commitHash): array { - throw new Exception("Not implemented yet"); + $url = "/repos/{$owner}/{$repositoryName}/git/commits/{$commitHash}"; + + $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]); + + $statusCode = $response['headers']['status-code'] ?? 0; + if ($statusCode >= 400) { + throw new Exception("Commit not found or inaccessible"); + } + + $body = $response['body'] ?? []; + $commit = $body['commit'] ?? []; + $author = $body['author'] ?? []; + + return [ + 'commitAuthor' => $commit['author']['name'] ?? 'Unknown', + 'commitMessage' => $commit['message'] ?? 'No message', + 'commitAuthorAvatar' => $author['avatar_url'] ?? '', + 'commitAuthorUrl' => $author['html_url'] ?? '', + 'commitHash' => $body['sha'] ?? '', + 'commitUrl' => $body['html_url'] ?? '', + ]; } + /** + * Get latest commit of a branch + * + * @param string $owner Owner name of the repository + * @param string $repositoryName Name of the repository + * @param string $branch Name of the branch + * @return array Details of the commit + */ public function getLatestCommit(string $owner, string $repositoryName, string $branch): array { - throw new Exception("Not implemented yet"); - } + $url = "/repos/{$owner}/{$repositoryName}/commits?sha={$branch}&limit=1"; + + $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]); + $statusCode = $response['headers']['status-code'] ?? 0; + if ($statusCode >= 400 || empty($response['body'][0])) { + throw new Exception("Latest commit response is missing required information."); + } + + $body = $response['body'][0]; + $commit = $body['commit'] ?? []; + $author = $body['author'] ?? []; + + return [ + 'commitAuthor' => $commit['author']['name'] ?? 'Unknown', + 'commitMessage' => $commit['message'] ?? 'No message', + 'commitHash' => $body['sha'] ?? '', + 'commitUrl' => $body['html_url'] ?? '', + 'commitAuthorAvatar' => $author['avatar_url'] ?? '', + 'commitAuthorUrl' => $author['html_url'] ?? '', + ]; + } public function updateCommitStatus(string $repositoryName, string $commitHash, string $owner, string $state, string $description = '', string $target_url = '', string $context = ''): void { throw new Exception("Not implemented yet"); diff --git a/tests/VCS/Adapter/GiteaTest.php b/tests/VCS/Adapter/GiteaTest.php index 18f30c3..474fa6f 100644 --- a/tests/VCS/Adapter/GiteaTest.php +++ b/tests/VCS/Adapter/GiteaTest.php @@ -365,12 +365,91 @@ public function testUpdateComment(): void public function testGetCommit(): void { - $this->markTestSkipped('Will be implemented in follow-up PR'); + $repositoryName = 'test-get-commit-' . \uniqid(); + $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); + + // Create a file to generate a commit + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test Commit'); + + // Get the latest commit to get its SHA + $latestCommit = $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'main'); + $commitHash = $latestCommit['commitHash']; + + // Now test getCommit with that SHA + $result = $this->vcsAdapter->getCommit(self::$owner, $repositoryName, $commitHash); + + $this->assertIsArray($result); + $this->assertArrayHasKey('commitHash', $result); + $this->assertArrayHasKey('commitMessage', $result); + $this->assertArrayHasKey('commitAuthor', $result); + $this->assertArrayHasKey('commitUrl', $result); + $this->assertArrayHasKey('commitAuthorAvatar', $result); + $this->assertArrayHasKey('commitAuthorUrl', $result); + + $this->assertSame($commitHash, $result['commitHash']); + $this->assertNotEmpty($result['commitMessage']); + + $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); } public function testGetLatestCommit(): void { - $this->markTestSkipped('Will be implemented in follow-up PR'); + $repositoryName = 'test-get-latest-commit-' . \uniqid(); + $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); + + // Create files to generate commits + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test'); + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'test.txt', 'test content'); + + $result = $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'main'); + + $this->assertIsArray($result); + $this->assertArrayHasKey('commitHash', $result); + $this->assertArrayHasKey('commitMessage', $result); + $this->assertArrayHasKey('commitAuthor', $result); + $this->assertArrayHasKey('commitUrl', $result); + $this->assertArrayHasKey('commitAuthorAvatar', $result); + $this->assertArrayHasKey('commitAuthorUrl', $result); + + $this->assertNotEmpty($result['commitHash']); + $this->assertNotEmpty($result['commitAuthor']); + + $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); + } + + public function testGetCommitWithInvalidSha(): void + { + $repositoryName = 'test-get-commit-invalid-' . \uniqid(); + $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test'); + + $this->expectException(\Exception::class); + $this->vcsAdapter->getCommit(self::$owner, $repositoryName, 'invalid-sha-12345'); + } + + public function testGetLatestCommitWithInvalidBranch(): void + { + $repositoryName = 'test-get-latest-commit-invalid-' . \uniqid(); + $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test'); + + $this->expectException(\Exception::class); + $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'non-existing-branch'); + } + + public function testGetCommitVerifyMessageContent(): void + { + $repositoryName = 'test-commit-message-' . \uniqid(); + $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); + + $customMessage = 'Custom commit message'; + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test', $customMessage); + + $latestCommit = $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'main'); + + $this->assertStringContainsString($customMessage, $latestCommit['commitMessage']); + + $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); } public function testGetEvent(): void From 698def76bcb2ac1e935adb2245093f80c518858d Mon Sep 17 00:00:00 2001 From: jayesh Date: Tue, 3 Mar 2026 17:19:49 +0530 Subject: [PATCH 2/5] fix: Use correct Gitea API endpoint for getCommit and add proper cleanup in tests --- src/VCS/Adapter/Git/Gitea.php | 19 +++++++++++++------ tests/VCS/Adapter/GiteaTest.php | 20 ++++++++++++-------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/VCS/Adapter/Git/Gitea.php b/src/VCS/Adapter/Git/Gitea.php index de279cd..ade8936 100644 --- a/src/VCS/Adapter/Git/Gitea.php +++ b/src/VCS/Adapter/Git/Gitea.php @@ -374,6 +374,7 @@ public function listBranches(string $owner, string $repositoryName): array */ public function getCommit(string $owner, string $repositoryName, string $commitHash): array { + // Use /git/commits endpoint which works with SHA $url = "/repos/{$owner}/{$repositoryName}/git/commits/{$commitHash}"; $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]); @@ -384,14 +385,16 @@ public function getCommit(string $owner, string $repositoryName, string $commitH } $body = $response['body'] ?? []; - $commit = $body['commit'] ?? []; + + // /git/commits has different structure - commit data is at top level $author = $body['author'] ?? []; + $committer = $body['committer'] ?? []; return [ - 'commitAuthor' => $commit['author']['name'] ?? 'Unknown', - 'commitMessage' => $commit['message'] ?? 'No message', - 'commitAuthorAvatar' => $author['avatar_url'] ?? '', - 'commitAuthorUrl' => $author['html_url'] ?? '', + 'commitAuthor' => $author['name'] ?? 'Unknown', + 'commitMessage' => $body['message'] ?? 'No message', + 'commitAuthorAvatar' => $committer['avatar_url'] ?? '', + 'commitAuthorUrl' => $committer['html_url'] ?? '', 'commitHash' => $body['sha'] ?? '', 'commitUrl' => $body['html_url'] ?? '', ]; @@ -407,7 +410,11 @@ public function getCommit(string $owner, string $repositoryName, string $commitH */ public function getLatestCommit(string $owner, string $repositoryName, string $branch): array { - $url = "/repos/{$owner}/{$repositoryName}/commits?sha={$branch}&limit=1"; + $query = http_build_query([ + 'sha' => $branch, + 'limit' => 1, + ]); + $url = "/repos/{$owner}/{$repositoryName}/commits?{$query}"; $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]); diff --git a/tests/VCS/Adapter/GiteaTest.php b/tests/VCS/Adapter/GiteaTest.php index 474fa6f..42eb548 100644 --- a/tests/VCS/Adapter/GiteaTest.php +++ b/tests/VCS/Adapter/GiteaTest.php @@ -368,14 +368,11 @@ public function testGetCommit(): void $repositoryName = 'test-get-commit-' . \uniqid(); $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); - // Create a file to generate a commit $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test Commit'); - // Get the latest commit to get its SHA $latestCommit = $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'main'); $commitHash = $latestCommit['commitHash']; - // Now test getCommit with that SHA $result = $this->vcsAdapter->getCommit(self::$owner, $repositoryName, $commitHash); $this->assertIsArray($result); @@ -397,7 +394,6 @@ public function testGetLatestCommit(): void $repositoryName = 'test-get-latest-commit-' . \uniqid(); $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); - // Create files to generate commits $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test'); $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'test.txt', 'test content'); @@ -423,8 +419,12 @@ public function testGetCommitWithInvalidSha(): void $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test'); - $this->expectException(\Exception::class); - $this->vcsAdapter->getCommit(self::$owner, $repositoryName, 'invalid-sha-12345'); + try { + $this->expectException(\Exception::class); + $this->vcsAdapter->getCommit(self::$owner, $repositoryName, 'invalid-sha-12345'); + } finally { + $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); + } } public function testGetLatestCommitWithInvalidBranch(): void @@ -433,8 +433,12 @@ public function testGetLatestCommitWithInvalidBranch(): void $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test'); - $this->expectException(\Exception::class); - $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'non-existing-branch'); + try { + $this->expectException(\Exception::class); + $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'non-existing-branch'); + } finally { + $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); + } } public function testGetCommitVerifyMessageContent(): void From 81e6419859fd405ccc6ad28f685053a69cb5a7e3 Mon Sep 17 00:00:00 2001 From: jayesh Date: Tue, 3 Mar 2026 22:55:21 +0530 Subject: [PATCH 3/5] updated with the fix --- src/VCS/Adapter/Git/Gitea.php | 22 ++++++++++----------- tests/VCS/Adapter/GiteaTest.php | 34 +++++++++++++++------------------ 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/VCS/Adapter/Git/Gitea.php b/src/VCS/Adapter/Git/Gitea.php index ade8936..1120aff 100644 --- a/src/VCS/Adapter/Git/Gitea.php +++ b/src/VCS/Adapter/Git/Gitea.php @@ -374,27 +374,27 @@ public function listBranches(string $owner, string $repositoryName): array */ public function getCommit(string $owner, string $repositoryName, string $commitHash): array { - // Use /git/commits endpoint which works with SHA $url = "/repos/{$owner}/{$repositoryName}/git/commits/{$commitHash}"; - + $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]); - + $statusCode = $response['headers']['status-code'] ?? 0; if ($statusCode >= 400) { throw new Exception("Commit not found or inaccessible"); } - + $body = $response['body'] ?? []; - - // /git/commits has different structure - commit data is at top level + + // Response structure: body['commit']['message'], body['author'], body['committer'] + $commit = $body['commit'] ?? []; $author = $body['author'] ?? []; $committer = $body['committer'] ?? []; - + return [ - 'commitAuthor' => $author['name'] ?? 'Unknown', - 'commitMessage' => $body['message'] ?? 'No message', - 'commitAuthorAvatar' => $committer['avatar_url'] ?? '', - 'commitAuthorUrl' => $committer['html_url'] ?? '', + 'commitAuthor' => $commit['author']['name'] ?? 'Unknown', + 'commitMessage' => $commit['message'] ?? 'No message', // ← THIS is correct! + 'commitAuthorAvatar' => $author['avatar_url'] ?? '', + 'commitAuthorUrl' => $author['html_url'] ?? '', 'commitHash' => $body['sha'] ?? '', 'commitUrl' => $body['html_url'] ?? '', ]; diff --git a/tests/VCS/Adapter/GiteaTest.php b/tests/VCS/Adapter/GiteaTest.php index 42eb548..4900c5f 100644 --- a/tests/VCS/Adapter/GiteaTest.php +++ b/tests/VCS/Adapter/GiteaTest.php @@ -368,7 +368,9 @@ public function testGetCommit(): void $repositoryName = 'test-get-commit-' . \uniqid(); $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); - $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test Commit'); + // Create file with custom commit message to verify message content + $customMessage = 'Test commit message'; + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test Commit', $customMessage); $latestCommit = $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'main'); $commitHash = $latestCommit['commitHash']; @@ -383,8 +385,11 @@ public function testGetCommit(): void $this->assertArrayHasKey('commitAuthorAvatar', $result); $this->assertArrayHasKey('commitAuthorUrl', $result); + // Strong assertions $this->assertSame($commitHash, $result['commitHash']); - $this->assertNotEmpty($result['commitMessage']); + $this->assertStringContainsString($customMessage, $result['commitMessage']); + $this->assertNotEmpty($result['commitAuthor']); + $this->assertNotEmpty($result['commitUrl']); $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); } @@ -394,8 +399,11 @@ public function testGetLatestCommit(): void $repositoryName = 'test-get-latest-commit-' . \uniqid(); $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); - $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test'); - $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'test.txt', 'test content'); + // Create files with custom messages to verify latest commit has correct message + $firstMessage = 'First commit'; + $secondMessage = 'Second commit'; + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test', $firstMessage); + $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'test.txt', 'test content', $secondMessage); $result = $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'main'); @@ -407,8 +415,11 @@ public function testGetLatestCommit(): void $this->assertArrayHasKey('commitAuthorAvatar', $result); $this->assertArrayHasKey('commitAuthorUrl', $result); + // Strong assertions - verify actual values $this->assertNotEmpty($result['commitHash']); + $this->assertStringContainsString($secondMessage, $result['commitMessage']); // Should be the latest (second) commit $this->assertNotEmpty($result['commitAuthor']); + $this->assertNotEmpty($result['commitUrl']); $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); } @@ -441,21 +452,6 @@ public function testGetLatestCommitWithInvalidBranch(): void } } - public function testGetCommitVerifyMessageContent(): void - { - $repositoryName = 'test-commit-message-' . \uniqid(); - $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); - - $customMessage = 'Custom commit message'; - $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test', $customMessage); - - $latestCommit = $this->vcsAdapter->getLatestCommit(self::$owner, $repositoryName, 'main'); - - $this->assertStringContainsString($customMessage, $latestCommit['commitMessage']); - - $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); - } - public function testGetEvent(): void { $this->markTestSkipped('Will be implemented in follow-up PR'); From a905862a1033790805d1d0120f25e6d726a1e671 Mon Sep 17 00:00:00 2001 From: jayesh Date: Tue, 3 Mar 2026 22:57:52 +0530 Subject: [PATCH 4/5] fixed linting --- src/VCS/Adapter/Git/Gitea.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/VCS/Adapter/Git/Gitea.php b/src/VCS/Adapter/Git/Gitea.php index 1120aff..8c7c889 100644 --- a/src/VCS/Adapter/Git/Gitea.php +++ b/src/VCS/Adapter/Git/Gitea.php @@ -375,21 +375,21 @@ public function listBranches(string $owner, string $repositoryName): array public function getCommit(string $owner, string $repositoryName, string $commitHash): array { $url = "/repos/{$owner}/{$repositoryName}/git/commits/{$commitHash}"; - + $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]); - + $statusCode = $response['headers']['status-code'] ?? 0; if ($statusCode >= 400) { throw new Exception("Commit not found or inaccessible"); } - + $body = $response['body'] ?? []; - + // Response structure: body['commit']['message'], body['author'], body['committer'] $commit = $body['commit'] ?? []; $author = $body['author'] ?? []; $committer = $body['committer'] ?? []; - + return [ 'commitAuthor' => $commit['author']['name'] ?? 'Unknown', 'commitMessage' => $commit['message'] ?? 'No message', // ← THIS is correct! From 0552bea1fa99a6a981b1eb68eb514c7a60970f89 Mon Sep 17 00:00:00 2001 From: jayesh Date: Tue, 3 Mar 2026 22:59:47 +0530 Subject: [PATCH 5/5] removed unwanted comments --- src/VCS/Adapter/Git/Gitea.php | 2 +- tests/VCS/Adapter/GiteaTest.php | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/VCS/Adapter/Git/Gitea.php b/src/VCS/Adapter/Git/Gitea.php index 8c7c889..ee3d7eb 100644 --- a/src/VCS/Adapter/Git/Gitea.php +++ b/src/VCS/Adapter/Git/Gitea.php @@ -392,7 +392,7 @@ public function getCommit(string $owner, string $repositoryName, string $commitH return [ 'commitAuthor' => $commit['author']['name'] ?? 'Unknown', - 'commitMessage' => $commit['message'] ?? 'No message', // ← THIS is correct! + 'commitMessage' => $commit['message'] ?? 'No message', 'commitAuthorAvatar' => $author['avatar_url'] ?? '', 'commitAuthorUrl' => $author['html_url'] ?? '', 'commitHash' => $body['sha'] ?? '', diff --git a/tests/VCS/Adapter/GiteaTest.php b/tests/VCS/Adapter/GiteaTest.php index 4900c5f..f21a4c9 100644 --- a/tests/VCS/Adapter/GiteaTest.php +++ b/tests/VCS/Adapter/GiteaTest.php @@ -368,7 +368,6 @@ public function testGetCommit(): void $repositoryName = 'test-get-commit-' . \uniqid(); $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); - // Create file with custom commit message to verify message content $customMessage = 'Test commit message'; $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test Commit', $customMessage); @@ -385,7 +384,6 @@ public function testGetCommit(): void $this->assertArrayHasKey('commitAuthorAvatar', $result); $this->assertArrayHasKey('commitAuthorUrl', $result); - // Strong assertions $this->assertSame($commitHash, $result['commitHash']); $this->assertStringContainsString($customMessage, $result['commitMessage']); $this->assertNotEmpty($result['commitAuthor']); @@ -399,7 +397,6 @@ public function testGetLatestCommit(): void $repositoryName = 'test-get-latest-commit-' . \uniqid(); $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); - // Create files with custom messages to verify latest commit has correct message $firstMessage = 'First commit'; $secondMessage = 'Second commit'; $this->vcsAdapter->createFile(self::$owner, $repositoryName, 'README.md', '# Test', $firstMessage); @@ -415,9 +412,8 @@ public function testGetLatestCommit(): void $this->assertArrayHasKey('commitAuthorAvatar', $result); $this->assertArrayHasKey('commitAuthorUrl', $result); - // Strong assertions - verify actual values $this->assertNotEmpty($result['commitHash']); - $this->assertStringContainsString($secondMessage, $result['commitMessage']); // Should be the latest (second) commit + $this->assertStringContainsString($secondMessage, $result['commitMessage']); $this->assertNotEmpty($result['commitAuthor']); $this->assertNotEmpty($result['commitUrl']);