Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
8 changes: 1 addition & 7 deletions scripts/clone_github_stars.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,7 @@
config = configparser.ConfigParser()
config.read('config.ini')

gitea = GithubToGitea(
config.get('gitea', 'host'),
config.get('gitea', 'token_main_account'),
config.get('gitea', 'token_clone_account'),
config.get('github', 'username'),
config.get('github', 'token'),
)
gitea = GithubToGitea.from_config(config)

print(f"Gitea Version: {gitea.gitea_clone_account.get_version()}")
print(f"API-Token belongs to user: {gitea.user.username}")
Expand Down
102 changes: 92 additions & 10 deletions scripts/core/github_to_gitea.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,119 @@
"""Core helpers for mirroring GitHub repositories to Gitea.

This module previously exposed two almost identical classes – one regular
``GithubToGitea`` and another ``GithubToGiteaCF`` which only differed in the
type of ``Gitea`` client that was instantiated. Maintaining two copies of the
logic made it hard to keep the behaviour in sync and introduced a lot of
repetition. The code has now been consolidated into a single class with optional
Cloudflare Access support.

``GithubToGitea`` accepts optional ``cf_access_client_id`` and
``cf_access_client_secret`` arguments. When provided a ``CFGitea`` client is
used, otherwise the regular ``Gitea`` client is utilised. A convenience
``from_config`` class method is also provided so that scripts can construct the
object without repeating configuration parsing code.
"""

from gitea import AlreadyExistsException, Gitea, Repository, MigrationServices, Organization
from github import Github

try: # pragma: no cover - optional dependency
from core.cf_ready.cf_gitea import CFGitea
except Exception: # ImportError or any other error
Copy link

Copilot AI Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catching Exception is too broad. Consider catching specific exceptions like ImportError or ModuleNotFoundError that would occur when the optional dependency is missing.

Suggested change
except Exception: # ImportError or any other error
except (ImportError, ModuleNotFoundError): # Only catch missing module errors

Copilot uses AI. Check for mistakes.
CFGitea = None


class GithubToGitea:
"""
Custom class to interact with the Gitea API package in an easier way.
TODO: Fix like, everything in here.
"""

def __init__(self,
gitea_url,
gitea_token_main_account,
gitea_token_clone_account,
github_username,
github_token,
):
# Environment Variables
def __init__(
self,
gitea_url,
gitea_token_main_account,
gitea_token_clone_account,
github_username,
github_token,
cf_access_client_id=None,
cf_access_client_secret=None,
):
"""Create a helper around the Gitea and GitHub API clients.

When Cloudflare Access credentials are supplied a ``CFGitea`` client is
instantiated which includes the required headers. Otherwise the normal
``Gitea`` client is used.
"""

self.gitea_url = gitea_url
self.gitea_token_main_account = gitea_token_main_account # Main account key
self.gitea_token_clone_account = gitea_token_clone_account # Test account key
self.github_username = github_username
self.github_token = github_token

# Decide which Gitea implementation to use
use_cf = cf_access_client_id and cf_access_client_secret and CFGitea is not None
gitea_cls = CFGitea if use_cf else Gitea
gitea_kwargs = {}
if use_cf:
gitea_kwargs = {
"cf_access_client_id": cf_access_client_id,
"cf_access_client_secret": cf_access_client_secret,
}

# Create the Gitea API objects here
self.gitea_main_account = Gitea(self.gitea_url, self.gitea_token_main_account)
self.gitea_clone_account = Gitea(self.gitea_url, self.gitea_token_clone_account)
self.gitea_main_account = gitea_cls(self.gitea_url, self.gitea_token_main_account, **gitea_kwargs)
self.gitea_clone_account = gitea_cls(self.gitea_url, self.gitea_token_clone_account, **gitea_kwargs)

# Create the Github API object here
self.github = Github(self.github_token)

# Load the Gitea user
self.user = self.gitea_clone_account.get_user()

@classmethod
def from_config(
cls,
config,
gitea_section="gitea",
github_section="github",
cf_section="cloudflare",
use_cloudflare=False,
):
"""Build ``GithubToGitea`` from a ``ConfigParser`` instance.

Parameters
----------
config:
``configparser.ConfigParser`` instance with loaded values.
gitea_section:
Name of the section containing Gitea credentials.
github_section:
Section containing GitHub credentials.
cf_section:
Section containing Cloudflare Access credentials. Only read when
``use_cloudflare`` is ``True``.
use_cloudflare:
Whether to instantiate using ``CFGitea``.
"""

kwargs = dict(
gitea_url=config.get(gitea_section, "host"),
gitea_token_main_account=config.get(gitea_section, "token_main_account"),
gitea_token_clone_account=config.get(gitea_section, "token_clone_account"),
github_username=config.get(github_section, "username"),
github_token=config.get(github_section, "token"),
)

if use_cloudflare:
kwargs.update(
cf_access_client_id=config.get(cf_section, "cf_access_client_id"),
cf_access_client_secret=config.get(cf_section, "cf_access_client_secret"),
)

return cls(**kwargs)

def find_org_from_gitea(self, github_org):
gitea_organizations = self.gitea_main_account.get_orgs()

Expand Down
139 changes: 0 additions & 139 deletions scripts/core/github_to_gitea_cf.py

This file was deleted.

8 changes: 1 addition & 7 deletions scripts/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@
config = configparser.ConfigParser()
config.read('config.ini')

gitea = GithubToGitea(
config.get('gitea', 'host'),
config.get('gitea', 'token_main_account'),
config.get('gitea', 'token_clone_account'),
config.get('github', 'username'),
config.get('github', 'token')
)
gitea = GithubToGitea.from_config(config)

searched_repos = []
_limit_search_results_to = 10
Expand Down
8 changes: 1 addition & 7 deletions scripts/scrape_organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@
config = configparser.ConfigParser()
config.read('config.ini')

gitea = GithubToGitea(
config.get('gitea', 'host'),
config.get('gitea', 'token_main_account'),
config.get('gitea', 'token_clone_account'),
config.get('github', 'username'),
config.get('github', 'token')
)
gitea = GithubToGitea.from_config(config)

if __name__ == '__main__':
organization_name = None
Expand Down
14 changes: 5 additions & 9 deletions scripts/scrape_organization_cf.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import configparser
import sys

from core.github_to_gitea_cf import GithubToGiteaCF
from core.github_to_gitea import GithubToGitea

config = configparser.ConfigParser()
config.read('config.ini')

gitea = GithubToGiteaCF(
config.get('gitea_private', 'host'),
config.get('gitea_private', 'token_main_account'),
config.get('gitea_private', 'token_clone_account'),
config.get('github', 'username'),
config.get('github', 'token'),
config.get('cloudflare', 'cf_access_client_id'),
config.get('cloudflare', 'cf_access_client_secret'),
gitea = GithubToGitea.from_config(
config,
gitea_section='gitea_private',
use_cloudflare=True,
)

if __name__ == '__main__':
Expand Down