Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
44 changes: 44 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,51 @@ services:
- ./src:/usr/local/src/src
- ./tests:/usr/local/src/tests
- ./phpunit.xml:/usr/local/src/phpunit.xml
- gitea-data:/data:ro
environment:
- PRIVATE_KEY
- APP_IDENTIFIER
- INSTALLATION_ID
- GITEA_URL=http://gitea:3000
- GITEA_TEST_OWNER=jayesh
depends_on:
gitea:
condition: service_healthy
gitea-bootstrap:
condition: service_completed_successfully

gitea:
image: gitea/gitea:1.21.5
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=sqlite3
- GITEA__security__INSTALL_LOCK=true
ports:
- "3000:3000"
volumes:
- gitea-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthz"]
interval: 10s
timeout: 5s
retries: 10
start_period: 10s

gitea-bootstrap:
image: gitea/gitea:1.21.5
volumes:
- gitea-data:/data
depends_on:
gitea:
condition: service_healthy
entrypoint: /bin/sh
command: >
-c "
su git -c 'gitea admin user create --username jayesh --password jayesh123 --email jayesh@gmail.com --admin --must-change-password=false' || true &&
Comment thread
jaysomani marked this conversation as resolved.
Outdated
Comment thread
jaysomani marked this conversation as resolved.
Outdated
TOKEN=$$(su git -c 'gitea admin user generate-access-token --username jayesh --token-name jayesh-token --scopes all --raw') &&
echo $$TOKEN > /data/gitea/token.txt
"
Comment thread
jaysomani marked this conversation as resolved.

volumes:
gitea-data:
201 changes: 201 additions & 0 deletions src/VCS/Adapter/Git/Gitea.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<?php

namespace Utopia\VCS\Adapter\Git;

use Exception;
use Utopia\Cache\Cache;
use Utopia\VCS\Adapter\Git;

class Gitea extends Git
{
protected string $endpoint = 'http://gitea:3000/api/v1';

protected string $accessToken;

protected string $refreshToken;

protected string $giteaUrl;

protected Cache $cache;

/**
* Global Headers
*
* @var array<string, string>
*/
protected $headers = ['content-type' => 'application/json'];

public function __construct(Cache $cache)
{
$this->cache = $cache;
}

/**
* Get Adapter Name
*
* @return string
*/
public function getName(): string
{
return 'gitea';
}

/**
* Gitea Initialisation with access token from OAuth2 flow.
*
* Note: Gitea uses OAuth2 instead of GitHub's App Installation flow.
* The parameters are adapted to maintain interface compatibility:
* - $installationId is used to pass the access token
* - $privateKey is used to pass the refresh token
* - $githubAppId is used to pass the Gitea instance URL
*/
Comment thread
jaysomani marked this conversation as resolved.
public function initializeVariables(string $installationId, string $privateKey, string $githubAppId): void
Comment thread
jaysomani marked this conversation as resolved.
Outdated
{
$this->accessToken = $installationId;
Comment thread
jaysomani marked this conversation as resolved.
Outdated
$this->refreshToken = $privateKey;
$this->giteaUrl = rtrim($githubAppId, '/');
$this->endpoint = $this->giteaUrl . '/api/v1';
Comment thread
jaysomani marked this conversation as resolved.
Outdated
}

/**
* Generate Access Token
*
* Note: This method is required by the Adapter interface but is not used for Gitea.
* Gitea uses OAuth2 tokens that are provided directly via initializeVariables().
*/
protected function generateAccessToken(string $privateKey, string $githubAppId): void
{
// Not applicable for Gitea - OAuth2 tokens are passed directly
return;
}

/**
* Create new repository
*
* @return array<mixed> Details of new repository
*/
public function createRepository(string $owner, string $repositoryName, bool $private): array
{
$url = "/user/repos";
Comment thread
Meldiron marked this conversation as resolved.
Outdated

$response = $this->call(self::METHOD_POST, $url, ['Authorization' => "token $this->accessToken"], [
'name' => $repositoryName,
'private' => $private,
]);

return $response['body'] ?? [];
}
Comment thread
jaysomani marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Stub methods to satisfy abstract class requirements
// These will be implemented in follow-up PRs

public function searchRepositories(string $owner, int $page, int $per_page, string $search = ''): array
{
throw new Exception("Not implemented yet");
}

public function getRepository(string $owner, string $repositoryName): array
{
throw new Exception("Not implemented yet");
Comment thread
jaysomani marked this conversation as resolved.
}

public function getRepositoryName(string $repositoryId): string
{
throw new Exception("Not implemented yet");
}

public function getRepositoryTree(string $owner, string $repositoryName, string $branch, bool $recursive = false): array
{
throw new Exception("Not implemented yet");
}

public function listRepositoryLanguages(string $owner, string $repositoryName): array
{
throw new Exception("Not implemented yet");
}

public function getRepositoryContent(string $owner, string $repositoryName, string $path, string $ref = ''): array
{
throw new Exception("Not implemented yet");
}

public function listRepositoryContents(string $owner, string $repositoryName, string $path = '', string $ref = ''): array
{
throw new Exception("Not implemented yet");
}

public function deleteRepository(string $owner, string $repositoryName): bool
{
throw new Exception("Not implemented yet");
}

public function createComment(string $owner, string $repositoryName, int $pullRequestNumber, string $comment): string
{
throw new Exception("Not implemented yet");
}

public function getComment(string $owner, string $repositoryName, string $commentId): string
{
throw new Exception("Not implemented yet");
}

public function updateComment(string $owner, string $repositoryName, int $commentId, string $comment): string
{
throw new Exception("Not implemented yet");
}

public function getUser(string $username): array
{
throw new Exception("Not implemented yet");
}

public function getOwnerName(string $installationId): string
{
throw new Exception("Not implemented yet");
}

public function getPullRequest(string $owner, string $repositoryName, int $pullRequestNumber): array
{
throw new Exception("Not implemented yet");
}

public function getPullRequestFromBranch(string $owner, string $repositoryName, string $branch): array
{
throw new Exception("Not implemented yet");
}

public function listBranches(string $owner, string $repositoryName): array
{
throw new Exception("Not implemented yet");
}

public function getCommit(string $owner, string $repositoryName, string $commitHash): array
{
throw new Exception("Not implemented yet");
}

public function getLatestCommit(string $owner, string $repositoryName, string $branch): array
{
throw new Exception("Not implemented yet");
}

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");
}

public function generateCloneCommand(string $owner, string $repositoryName, string $version, string $versionType, string $directory, string $rootDirectory): string
{
throw new Exception("Not implemented yet");
}

public function getEvent(string $event, string $payload): array
{
throw new Exception("Not implemented yet");
}

public function validateWebhookEvent(string $payload, string $signature, string $signatureKey): bool
{
throw new Exception("Not implemented yet");
}
}
120 changes: 120 additions & 0 deletions tests/VCS/Adapter/GiteaTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

namespace Utopia\Tests\VCS\Adapter;

use Utopia\Cache\Adapter\None;
use Utopia\Cache\Cache;
use Utopia\System\System;
use Utopia\Tests\Base;
use Utopia\VCS\Adapter\Git;
use Utopia\VCS\Adapter\Git\Gitea;

Comment thread
Meldiron marked this conversation as resolved.
class GiteaTest extends Base
Comment thread
Meldiron marked this conversation as resolved.
{
private static bool $setupDone = false;
Comment thread
jaysomani marked this conversation as resolved.
Outdated
private static string $accessToken = '';

protected function createVCSAdapter(): Git
{
return new Gitea(new Cache(new None()));
}

public function setUp(): void
{
if (!self::$setupDone) {
$this->setupGitea();
self::$setupDone = true;
}

$this->vcsAdapter = new Gitea(new Cache(new None()));
$this->vcsAdapter->initializeVariables(self::$accessToken, '', System::getEnv('GITEA_URL') ?? 'http://gitea:3000');
}

private function setupGitea(): void
{
$tokenFile = '/data/gitea/token.txt';

if (file_exists($tokenFile)) {
self::$accessToken = trim(file_get_contents($tokenFile));
}
}
Comment thread
Meldiron marked this conversation as resolved.

public function testCreateRepository(): void
{
$owner = System::getEnv('GITEA_TEST_OWNER');
Comment thread
Meldiron marked this conversation as resolved.
Outdated
Comment thread
jaysomani marked this conversation as resolved.
Outdated
$repositoryName = 'test-repo-' . time();

$result = $this->vcsAdapter->createRepository($owner, $repositoryName, false);

$this->assertIsArray($result);
$this->assertArrayHasKey('name', $result);
$this->assertSame($repositoryName, $result['name']);
$this->assertArrayHasKey('owner', $result);
$this->assertSame($owner, $result['owner']['login']);
}

public function testGetComment(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testGetRepositoryName(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testGetRepositoryTree(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testGetRepositoryContent(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testListRepositoryContents(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testGetPullRequest(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testGenerateCloneCommand(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testGenerateCloneCommandWithCommitHash(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testGenerateCloneCommandWithTag(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testUpdateComment(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testGetCommit(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testGetLatestCommit(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}

public function testgetEvent(): void
{
$this->markTestSkipped('Will be implemented in follow-up PR');
}
Comment thread
jaysomani marked this conversation as resolved.
Outdated
}
Loading