Skip to content

repository

appimage_updater.repositories.gitlab.repository

GitLab repository implementation for AppImage Updater.

This module provides the GitLab-specific implementation of the repository interface, supporting both gitlab.com and self-hosted GitLab instances.

GitLabRepository(timeout=30, user_agent=None, auth=None, token=None, **kwargs)

GitLab repository implementation following the abstract base interface.

Parameters:

Name Type Description Default
timeout int

Request timeout in seconds

30
user_agent str | None

Custom user agent string

None
auth GitLabAuth | None

GitLabAuth instance for authentication

None
token str | None

Explicit GitLab token (creates auth if provided)

None
**kwargs Any

Additional configuration options

{}
Source code in src/appimage_updater/repositories/gitlab/repository.py
def __init__(
    self,
    timeout: int = 30,
    user_agent: str | None = None,
    auth: GitLabAuth | None = None,
    token: str | None = None,
    **kwargs: Any,
) -> None:
    """Initialize GitLab repository client.

    Args:
        timeout: Request timeout in seconds
        user_agent: Custom user agent string
        auth: GitLabAuth instance for authentication
        token: Explicit GitLab token (creates auth if provided)
        **kwargs: Additional configuration options
    """
    super().__init__(timeout=timeout, user_agent=user_agent, **kwargs)

    # Initialize authentication
    if token and not auth:
        auth = GitLabAuth(token=token)
    self._auth = auth or GitLabAuth()

    # Initialize GitLab client
    self._gitlab_client = GitLabClient(timeout=timeout, user_agent=user_agent, auth=self._auth)

    logger.debug(f"GitLab repository client initialized (authenticated: {self._auth.is_authenticated()})")

detect_repository_type(url)

Check if this client can handle the given repository URL.

Parameters:

Name Type Description Default
url str

Repository URL to check

required

Returns:

Type Description
bool

True if this is a GitLab URL, False otherwise

Source code in src/appimage_updater/repositories/gitlab/repository.py
def detect_repository_type(self, url: str) -> bool:
    """Check if this client can handle the given repository URL.

    Args:
        url: Repository URL to check

    Returns:
        True if this is a GitLab URL, False otherwise
    """
    # Check for gitlab.com URLs
    if "gitlab.com" in url.lower():
        return True

    # Check for common GitLab URL patterns
    gitlab_patterns = [
        r"gitlab\.",  # gitlab.example.com
        r"/gitlab/",  # example.com/gitlab/
        r"git\..*\.com",  # git.company.com
    ]

    return any(re.search(pattern, url, re.IGNORECASE) for pattern in gitlab_patterns)

generate_pattern_from_releases(url) async

Generate file pattern from actual releases.

Parameters:

Name Type Description Default
url str

Repository URL to analyze for release patterns

required

Returns:

Type Description
str | None

str | None: Regex pattern string if successful, None otherwise

Source code in src/appimage_updater/repositories/gitlab/repository.py
async def generate_pattern_from_releases(self, url: str) -> str | None:
    """Generate file pattern from actual releases.

    Args:
        url: Repository URL to analyze for release patterns

    Returns:
        str | None: Regex pattern string if successful, None otherwise
    """
    try:
        asset_names = await self._collect_asset_names_from_releases(url)
        return self._generate_pattern_from_names(asset_names) if asset_names else None
    except Exception as e:
        logger.debug(f"Failed to generate pattern from GitLab releases: {e}")
        return None

get_latest_release(repo_url) async

Get the latest stable release for a repository.

Parameters:

Name Type Description Default
repo_url str

Repository URL

required

Returns:

Type Description
Release

Release object with release information

Raises:

Type Description
RepositoryError

If the operation fails

Source code in src/appimage_updater/repositories/gitlab/repository.py
async def get_latest_release(self, repo_url: str) -> Release:
    """Get the latest stable release for a repository.

    Args:
        repo_url: Repository URL

    Returns:
        Release object with release information

    Raises:
        RepositoryError: If the operation fails
    """
    try:
        owner, repo = self.parse_repo_url(repo_url)
        base_url = self._get_base_url(repo_url)

        gitlab_release = await self._gitlab_client.get_latest_release(owner, repo, base_url)
        return self._map_gitlab_release_to_release(gitlab_release)

    except GitLabClientError as e:
        raise RepositoryError(f"Failed to get latest release from GitLab: {e}") from e

get_latest_release_including_prerelease(repo_url) async

Get the latest release including prereleases.

For GitLab, we'll get all recent releases and find the most recent one (which may be a prerelease).

Parameters:

Name Type Description Default
repo_url str

Repository URL

required

Returns:

Type Description
Release

Release object with release information

Raises:

Type Description
RepositoryError

If the operation fails

Source code in src/appimage_updater/repositories/gitlab/repository.py
async def get_latest_release_including_prerelease(self, repo_url: str) -> Release:
    """Get the latest release including prereleases.

    For GitLab, we'll get all recent releases and find the most recent one
    (which may be a prerelease).

    Args:
        repo_url: Repository URL

    Returns:
        Release object with release information

    Raises:
        RepositoryError: If the operation fails
    """
    try:
        owner, repo = self.parse_repo_url(repo_url)
        base_url = self._get_base_url(repo_url)

        # Get recent releases and return the first (most recent) one
        releases = await self._gitlab_client.get_releases(owner, repo, base_url, limit=1)

        if not releases:
            raise RepositoryError(f"No releases found for GitLab repository: {repo_url}")

        return self._map_gitlab_release_to_release(releases[0])

    except GitLabClientError as e:
        raise RepositoryError(f"Failed to get latest release (including prerelease) from GitLab: {e}") from e

get_releases(repo_url, limit=10) async

Get recent releases for a repository.

Parameters:

Name Type Description Default
repo_url str

Repository URL

required
limit int

Maximum number of releases to fetch

10

Returns:

Type Description
list[Release]

List of Release objects

Raises:

Type Description
RepositoryError

If the operation fails

Source code in src/appimage_updater/repositories/gitlab/repository.py
async def get_releases(self, repo_url: str, limit: int = 10) -> list[Release]:
    """Get recent releases for a repository.

    Args:
        repo_url: Repository URL
        limit: Maximum number of releases to fetch

    Returns:
        List of Release objects

    Raises:
        RepositoryError: If the operation fails
    """
    try:
        owner, repo = self.parse_repo_url(repo_url)
        base_url = self._get_base_url(repo_url)

        gitlab_releases = await self._gitlab_client.get_releases(owner, repo, base_url, limit)
        return [self._map_gitlab_release_to_release(release) for release in gitlab_releases]

    except GitLabClientError as e:
        raise RepositoryError(f"Failed to get releases from GitLab: {e}") from e

normalize_repo_url(url)

Normalize repository URL and detect if it was corrected.

Parameters:

Name Type Description Default
url str

Repository URL to normalize

required

Returns:

Type Description
tuple[str, bool]

Tuple of (normalized_url, was_corrected)

Source code in src/appimage_updater/repositories/gitlab/repository.py
def normalize_repo_url(self, url: str) -> tuple[str, bool]:
    """Normalize repository URL and detect if it was corrected.

    Args:
        url: Repository URL to normalize

    Returns:
        Tuple of (normalized_url, was_corrected)
    """
    original_url = url
    url, corrected_suffix = self._remove_git_suffix(url)
    url, corrected_slash = self._remove_trailing_slashes(url)
    url, corrected_protocol = self._ensure_https_protocol(url)
    url, corrected_domain = self._normalize_gitlab_domain(url, original_url)

    was_corrected = any([corrected_suffix, corrected_slash, corrected_protocol, corrected_domain])

    logger.debug(f"Normalized GitLab URL: {original_url} -> {url} (corrected: {was_corrected})")
    return url, was_corrected

parse_repo_url(url)

Parse repository URL to extract owner and repo name.

Parameters:

Name Type Description Default
url str

Repository URL to parse

required

Returns:

Type Description
tuple[str, str]

Tuple of (owner, repo_name) where owner may contain slashes for nested groups

Raises:

Type Description
RepositoryError

If URL format is invalid or parsing fails

ValueError

If URL is empty or whitespace only

Source code in src/appimage_updater/repositories/gitlab/repository.py
def parse_repo_url(self, url: str) -> tuple[str, str]:
    """Parse repository URL to extract owner and repo name.

    Args:
        url: Repository URL to parse

    Returns:
        Tuple of (owner, repo_name) where owner may contain slashes for nested groups

    Raises:
        RepositoryError: If URL format is invalid or parsing fails
        ValueError: If URL is empty or whitespace only
    """
    if not url or not url.strip():
        raise ValueError("URL cannot be empty")

    try:
        url = self._clean_git_url(url)
        path_parts = self._extract_path_parts(url)
        self._validate_path_parts(path_parts, url)
        owner, repo = self._extract_owner_and_repo(path_parts)

        logger.debug(f"Parsed GitLab URL {url} -> owner='{owner}', repo='{repo}'")
        return owner, repo

    except RepositoryError:
        raise
    except Exception as e:
        raise RepositoryError(f"Failed to parse GitLab URL {url}: {e}") from e

should_enable_prerelease(url) async

Check if prerelease should be automatically enabled for a repository.

Parameters:

Name Type Description Default
url str

Repository URL

required

Returns:

Type Description
bool

True if only prereleases are found, False if stable releases exist

Source code in src/appimage_updater/repositories/gitlab/repository.py
async def should_enable_prerelease(self, url: str) -> bool:
    """Check if prerelease should be automatically enabled for a repository.

    Args:
        url: Repository URL

    Returns:
        True if only prereleases are found, False if stable releases exist
    """
    try:
        owner, repo = self.parse_repo_url(url)
        base_url = self._get_base_url(url)

        return await self._gitlab_client.should_enable_prerelease(owner, repo, base_url)

    except GitLabClientError as e:
        logger.debug(f"Could not determine prerelease status for {url}: {e}")
        return False