Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ services:
- TESTS_GITHUB_APP_IDENTIFIER
- TESTS_GITHUB_INSTALLATION_ID
- TESTS_GITEA_URL=http://gitea:3000
- TESTS_GITEA_REQUEST_CATCHER_URL=http://request-catcher:5000
depends_on:
gitea:
condition: service_healthy
gitea-bootstrap:
condition: service_completed_successfully
request-catcher:
condition: service_started

gitea:
image: gitea/gitea:1.21.5
Expand All @@ -25,6 +28,9 @@ services:
- USER_GID=1000
- GITEA__database__DB_TYPE=sqlite3
- GITEA__security__INSTALL_LOCK=true
- GITEA__webhook__ALLOWED_HOST_LIST=*
- GITEA__webhook__SKIP_TLS_VERIFY=true
- GITEA__webhook__DELIVER_TIMEOUT=10
ports:
- "3000:3000"
volumes:
Expand Down Expand Up @@ -54,6 +60,9 @@ services:
TOKEN=$$(su git -c \"gitea admin user generate-access-token --username $$GITEA_ADMIN_USERNAME --token-name $$GITEA_ADMIN_USERNAME-token --scopes all --raw\") &&
echo $$TOKEN > /data/gitea/token.txt
"

request-catcher:
image: appwrite/requestcatcher:1.1.0
ports:
- "5000:5000"
volumes:
gitea-data:
133 changes: 131 additions & 2 deletions src/VCS/Adapter/Git/Gitea.php
Original file line number Diff line number Diff line change
Expand Up @@ -739,13 +739,142 @@ public function generateCloneCommand(string $owner, string $repositoryName, stri
throw new Exception("Not implemented yet");
}

/**
* Parses webhook event payload
*
* @param string $event Type of event: push, pull_request, etc
* @param string $payload The webhook payload received from Gitea
* @return array<mixed> Parsed payload as an array
*/
public function getEvent(string $event, string $payload): array
{
throw new Exception("Not implemented yet");
$payload = json_decode($payload, true);

if ($payload === null || !is_array($payload)) {
throw new Exception("Invalid payload.");
}

switch ($event) {
case 'push':
$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 . "/src/branch/" . $branch : '';
$commitHash = $payload['after'] ?? '';
$owner = $payloadRepositoryOwner['login'] ?? '';
$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) {
foreach (($commit['added'] ?? []) as $added) {
$affectedFiles[$added] = true;
}

foreach (($commit['removed'] ?? []) as $removed) {
$affectedFiles[$removed] = true;
}

foreach (($commit['modified'] ?? []) as $modified) {
$affectedFiles[$modified] = true;
}
}

return [
'branchCreated' => $branchCreated,
'branchDeleted' => $branchDeleted,
'branch' => $branch,
'branchUrl' => $branchUrl,
'repositoryId' => $repositoryId,
'repositoryName' => $repositoryName,
'repositoryUrl' => $repositoryUrl,
'installationId' => '', // Gitea doesn't have installations
'commitHash' => $commitHash,
'owner' => $owner,
'authorUrl' => $authorUrl,
'authorAvatarUrl' => $authorAvatarUrl,
'headCommitAuthorName' => $headCommitAuthorName,
'headCommitAuthorEmail' => $headCommitAuthorEmail,
'headCommitMessage' => $headCommitMessage,
'headCommitUrl' => $headCommitUrl,
'external' => false,
'pullRequestNumber' => '',
'action' => '',
'affectedFiles' => \array_keys($affectedFiles),
];

case 'pull_request':
$payloadRepository = $payload['repository'] ?? [];
$payloadRepositoryOwner = $payloadRepository['owner'] ?? [];
$payloadSender = $payload['sender'] ?? [];
$payloadPullRequest = $payload['pull_request'] ?? [];
$payloadPullRequestHead = $payloadPullRequest['head'] ?? [];
$payloadPullRequestHeadRepo = $payloadPullRequestHead['repo'] ?? [];
$payloadPullRequestUser = $payloadPullRequest['user'] ?? [];
$payloadPullRequestBase = $payloadPullRequest['base'] ?? [];

$repositoryId = strval($payloadRepository['id'] ?? '');
$branch = $payloadPullRequestHead['ref'] ?? '';
$repositoryName = $payloadRepository['name'] ?? '';
$repositoryUrl = $payloadRepository['html_url'] ?? '';
$branchUrl = !empty($repositoryUrl) && !empty($branch) ? $repositoryUrl . "/src/branch/" . $branch : '';
$pullRequestNumber = $payload['number'] ?? '';
$action = $payload['action'] ?? '';
$owner = $payloadRepositoryOwner['login'] ?? '';
$authorUrl = $payloadSender['html_url'] ?? '';
$authorAvatarUrl = $payloadPullRequestUser['avatar_url'] ?? '';
$commitHash = $payloadPullRequestHead['sha'] ?? '';
$headCommitUrl = $repositoryUrl ? $repositoryUrl . "/commit/" . $commitHash : '';

// Check if PR is from a fork (external)
$headRepoFullName = $payloadPullRequestHeadRepo['full_name'] ?? '';
$baseRepoFullName = $payloadRepository['full_name'] ?? '';
$external = !empty($headRepoFullName) && !empty($baseRepoFullName) && $headRepoFullName !== $baseRepoFullName;

return [
'branch' => $branch,
'branchUrl' => $branchUrl,
'repositoryId' => $repositoryId,
'repositoryName' => $repositoryName,
'repositoryUrl' => $repositoryUrl,
'installationId' => '', // Gitea doesn't have installations
'commitHash' => $commitHash,
'owner' => $owner,
'authorUrl' => $authorUrl,
'authorAvatarUrl' => $authorAvatarUrl,
'headCommitUrl' => $headCommitUrl,
'external' => $external,
'pullRequestNumber' => $pullRequestNumber,
'action' => $action,
];
}

return [];
}

/**
* Validate webhook event
*
* @param string $payload Raw body of HTTP request
* @param string $signature Signature provided by Gitea in X-Gitea-Signature header
* @param string $signatureKey Webhook secret configured on Gitea
* @return bool
*/
public function validateWebhookEvent(string $payload, string $signature, string $signatureKey): bool
{
throw new Exception("Not implemented yet");
return hash_equals($signature, hash_hmac('sha256', $payload, $signatureKey));
}
}
52 changes: 30 additions & 22 deletions tests/VCS/Adapter/GitHubTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ public function setUp(): void
$this->vcsAdapter->initializeVariables(installationId: $installationId, privateKey: $privateKey, appId: $appId, accessToken: '', refreshToken: '');
}

public function testGetEvent(): void
public function testGetEventPush(): void
{
$payload_push = '{
$payload = '{
"created": false,
"ref": "refs/heads/main",
"before": "1234",
Expand Down Expand Up @@ -91,7 +91,19 @@ public function testGetEvent(): void
}
}';

$payload_pull_request = '{
$result = $this->vcsAdapter->getEvent('push', $payload);

$this->assertSame('main', $result['branch']);
$this->assertSame('603754812', $result['repositoryId']);
$this->assertCount(3, $result['affectedFiles']);
$this->assertSame('src/lib.js', $result['affectedFiles'][0]);
$this->assertSame('README.md', $result['affectedFiles'][1]);
$this->assertSame('src/main.js', $result['affectedFiles'][2]);
}

public function testGetEventPullRequest(): void
{
$payload = '{
"action": "opened",
"number": 1,
"pull_request": {
Expand Down Expand Up @@ -133,32 +145,28 @@ public function testGetEvent(): void
}
}';

$payload_uninstall = '{
$result = $this->vcsAdapter->getEvent('pull_request', $payload);

$this->assertSame('opened', $result['action']);
$this->assertSame(1, $result['pullRequestNumber']);
}

public function testGetEventInstallation(): void
{
$payload = '{
"action": "deleted",
"installation": {
"id": 1234,
"account": {
"login": "vermakhushboo"
}
}
}
';

$pushResult = $this->vcsAdapter->getEvent('push', $payload_push);
$this->assertSame('main', $pushResult['branch']);
$this->assertSame('603754812', $pushResult['repositoryId']);
$this->assertCount(3, $pushResult['affectedFiles']);
$this->assertSame('src/lib.js', $pushResult['affectedFiles'][0]);
$this->assertSame('README.md', $pushResult['affectedFiles'][1]);
$this->assertSame('src/main.js', $pushResult['affectedFiles'][2]);

$pullRequestResult = $this->vcsAdapter->getEvent('pull_request', $payload_pull_request);
$this->assertSame('opened', $pullRequestResult['action']);
$this->assertSame(1, $pullRequestResult['pullRequestNumber']);

$uninstallResult = $this->vcsAdapter->getEvent('installation', $payload_uninstall);
$this->assertSame('deleted', $uninstallResult['action']);
$this->assertSame('1234', $uninstallResult['installationId']);
}';

$result = $this->vcsAdapter->getEvent('installation', $payload);

$this->assertSame('deleted', $result['action']);
$this->assertSame('1234', $result['installationId']);
}

public function testGetComment(): void
Expand Down
Loading