Skip to content

Commit

Permalink
Add base support for forgejo
Browse files Browse the repository at this point in the history
  • Loading branch information
majamassarini committed Feb 12, 2025
1 parent 4a3d53f commit 0ecd191
Show file tree
Hide file tree
Showing 16 changed files with 1,534 additions and 6 deletions.
2 changes: 2 additions & 0 deletions ogr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
get_service_class,
get_service_class_or_none,
)
from ogr.services.forgejo import ForgejoService
from ogr.services.github import GithubService
from ogr.services.gitlab import GitlabService
from ogr.services.pagure import PagureService
Expand All @@ -26,6 +27,7 @@
GithubService.__name__,
PagureService.__name__,
GitlabService.__name__,
ForgejoService.__name__,
AuthMethod.__name__,
get_project.__name__,
get_service_class.__name__,
Expand Down
16 changes: 14 additions & 2 deletions ogr/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@

import github
import gitlab
import pyforgejo
import pyforgejo.core
import requests

from ogr.deprecation import deprecate_and_set_removal
from ogr.exceptions import (
APIException,
ForgejoAPIException,
GitForgeInternalError,
GithubAPIException,
GitlabAPIException,
Expand Down Expand Up @@ -59,7 +62,11 @@ def __check_for_internal_failure(ex: APIException):


def __wrap_exception(
ex: Union[github.GithubException, gitlab.GitlabError],
ex: Union[
github.GithubException,
gitlab.GitlabError,
pyforgejo.core.api_error.ApiError,
],
) -> APIException:
"""
Wraps uncaught exception in one of ogr exceptions.
Expand All @@ -76,6 +83,7 @@ def __wrap_exception(
MAPPING = {
github.GithubException: GithubAPIException,
gitlab.GitlabError: GitlabAPIException,
pyforgejo.core.api_error.ApiError: ForgejoAPIException,
}

for caught_exception, ogr_exception in MAPPING.items():
Expand Down Expand Up @@ -114,7 +122,11 @@ def wrapper(*args, **kwargs):
) from ex
except APIException as ex:
__check_for_internal_failure(ex)
except (github.GithubException, gitlab.GitlabError) as ex:
except (
github.GithubException,
gitlab.GitlabError,
pyforgejo.core.api_error.ApiError,
) as ex:
__check_for_internal_failure(__wrap_exception(ex))

return wrapper
Expand Down
15 changes: 15 additions & 0 deletions ogr/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import github
import gitlab
import pyforgejo
import pyforgejo.core


class OgrException(Exception):
Expand Down Expand Up @@ -66,6 +68,19 @@ def response_code(self):
return self.__cause__.response_code


class ForgejoAPIException(APIException):
"""Exception related to Forgejo API."""

@property
def response_code(self):
if self.__cause__ is None or not isinstance(
self.__cause__,
pyforgejo.core.api_error.ApiError,
):
return None
return self.__cause__.status_code


class OperationNotSupported(OgrException):
"""Raise when the operation is not supported by the backend."""

Expand Down
14 changes: 14 additions & 0 deletions ogr/services/forgejo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

from ogr.services.forgejo.issue import ForgejoIssue
from ogr.services.forgejo.project import ForgejoProject
from ogr.services.forgejo.pull_request import ForgejoPullRequest
from ogr.services.forgejo.service import ForgejoService

__all__ = [
ForgejoPullRequest.__name__,
ForgejoIssue.__name__,
ForgejoProject.__name__,
ForgejoService.__name__,
]
10 changes: 10 additions & 0 deletions ogr/services/forgejo/issue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

from ogr.services import forgejo
from ogr.services.base import BaseIssue


class ForgejoIssue(BaseIssue):
def __init__(self, raw_issue, project: "forgejo.ForgejoProject"):
super().__init__(raw_issue, project)
35 changes: 35 additions & 0 deletions ogr/services/forgejo/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT


from ogr.services import forgejo
from ogr.services.base import BaseGitProject


class ForgejoProject(BaseGitProject):
service: "forgejo.ForgejoService"

def __init__(
self,
repo: str,
service: "forgejo.ForgejoService",
namespace: str,
**kwargs,
):
super().__init__(repo, service, namespace)
self._forgejo_repo = None

@property
def forgejo_repo(self):
if not self._forgejo_repo:
if self.namespace:
self._forgejo_repo = self.service.api.repository.repo_get(
owner=self.namespace,
repo=self.repo,
)
else:
self._forgejo_repo = self.service.api.repository.repo_get(
owner=self.service.user.get_username(),
repo=self.repo,
)
return self._forgejo_repo
16 changes: 16 additions & 0 deletions ogr/services/forgejo/pull_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

from typing import Any

from ogr.services import forgejo
from ogr.services.base import BasePullRequest


class ForgejoPullRequest(BasePullRequest):
def __init__(
self,
raw_pr: Any,
project: "forgejo.ForgejoProject",
):
super().__init__(raw_pr, project)
89 changes: 89 additions & 0 deletions ogr/services/forgejo/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

from typing import Optional
from urllib.parse import urlparse

from pyforgejo import PyforgejoApi

from ogr.abstract import GitUser
from ogr.exceptions import OgrException
from ogr.factory import use_for_service
from ogr.services.base import BaseGitService
from ogr.services.forgejo.project import ForgejoProject
from ogr.services.forgejo.user import ForgejoUser


@use_for_service("forgejo")
class ForgejoService(BaseGitService):
version = "/api/v1"

def __init__(
self,
instance_url: str = "https://forgejo.org",
api_key: Optional[str] = None,
**kwargs,
):
super().__init__()
self.instance_url = instance_url + self.version
self._token = f"token {api_key}"
self._api = None

@property
def api(self):
if not self._api:
self._api = PyforgejoApi(base_url=self.instance_url, api_key=self._token)
return self._api

def get_project( # type: ignore[override]
self,
repo: str,
namespace: str,
**kwargs,
) -> "ForgejoProject":
return ForgejoProject(
repo=repo,
namespace=namespace,
service=self,
**kwargs,
)

@property
def user(self) -> GitUser:
return ForgejoUser(self)

def project_create(
self,
repo: str,
namespace: Optional[str] = None,
description: Optional[str] = None,
) -> "ForgejoProject":
if namespace:
new_repo = self.api.organization.create_org_repo(
org=namespace,
name=repo,
description=description,
)
else:
new_repo = self.api.repository.create_current_user_repo(
name=repo,
description=description,
)
return ForgejoProject(
repo=repo,
namespace=namespace,
service=self,
github_repo=new_repo,
)

def get_project_from_url(self, url: str) -> "ForgejoProject":
parsed_url = urlparse(url)
path_parts = parsed_url.path.strip("/").split("/")

if len(path_parts) < 2:
raise OgrException(f"Invalid Forgejo URL: {url}")

namespace = path_parts[0]
repo = path_parts[1]

return self.get_project(repo=repo, namespace=namespace)
26 changes: 26 additions & 0 deletions ogr/services/forgejo/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT


from ogr.services import forgejo
from ogr.services.base import BaseGitUser


class ForgejoUser(BaseGitUser):
service: "forgejo.ForgejoService"

def __init__(self, service: "forgejo.ForgejoService") -> None:
super().__init__(service=service)
self._forgejo_user = None

def __str__(self) -> str:
return f'ForgejoUser(username="{self.get_username()}")'

@property
def forgejo_user(self):
if not self._forgejo_user:
self._forgejo_user = self.service.api.user.get_current()
return self._forgejo_user

def get_username(self) -> str:
return self.forgejo_user.login
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dependencies = [
"PyYAML",
"requests",
"urllib3",
"pyforgejo",
]

[project.urls]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
_requre:
DataTypes: 1
key_strategy: StorageKeysInspectSimple
version_storage_file: 3
30 changes: 26 additions & 4 deletions tests/integration/factory/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
from requre.online_replacing import record_requests_for_all_methods
from requre.utils import get_datafile_filename

from ogr import GithubService, GitlabService, PagureService, get_project
from ogr import ForgejoService, GithubService, GitlabService, PagureService, get_project
from ogr.services.forgejo import ForgejoProject
from ogr.services.github import GithubProject
from ogr.services.gitlab import GitlabProject
from ogr.services.pagure import PagureProject
Expand All @@ -20,17 +21,20 @@ def setUp(self):
self._github_service = None
self._pagure_service = None
self._gitlab_service = None
self._forgejo_service = None
self.github_token = os.environ.get("GITHUB_TOKEN")
self.pagure_token = os.environ.get("PAGURE_TOKEN")
self.gitlab_token = os.environ.get("GITLAB_TOKEN") or "some_token"
self.forgejo_token = os.environ.get("FORGEJO_TOKEN")
if not Path(get_datafile_filename(obj=self)).exists() and (
not self.github_token
or not self.pagure_token
or not os.environ.get("GITLAB_TOKEN")
and not self.pagure_token
and not os.environ.get("GITLAB_TOKEN")
and not self.forgejo_token
):
raise OSError(
"You are in requre write mode, please set GITHUB_TOKEN PAGURE_TOKEN"
" GITLAB_TOKEN env variables",
" GITLAB_TOKEN FORGEJO_TOKEN env variables",
)

@property
Expand All @@ -54,12 +58,22 @@ def gitlab_service(self):
)
return self._gitlab_service

@property
def forgejo_service(self):
if not self._forgejo_service:
self._forgejo_service = ForgejoService(
instance_url="https://v10.next.forgejo.org", # a test server
api_key=self.forgejo_token,
)
return self._forgejo_service

@property
def custom_instances(self):
return [
self.github_service,
self.pagure_service,
self.gitlab_service,
self.forgejo_service,
]

def test_get_project_github(self):
Expand Down Expand Up @@ -88,3 +102,11 @@ def test_get_project_gitlab(self):
)
assert isinstance(project, GitlabProject)
assert project.gitlab_repo

def test_get_project_forgejo(self):
project = get_project(
url="https://v10.next.forgejo.org/packit/test",
custom_instances=self.custom_instances,
)
assert isinstance(project, ForgejoProject)
assert project.forgejo_repo
2 changes: 2 additions & 0 deletions tests/integration/forgejo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT
17 changes: 17 additions & 0 deletions tests/integration/forgejo/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

import os

import pytest

from ogr.services.forgejo import ForgejoService


@pytest.fixture
def service():
api_key = os.environ.get("FORGEJO_TOKEN")
return ForgejoService(
instance_url="https://v10.next.forgejo.org",
api_key=api_key,
)
Loading

0 comments on commit 0ecd191

Please sign in to comment.