A CLI tool to find the latest stable version of an arbitrary project
—
Platform-specific adapters for different hosting services and package repositories. The repository holder system provides a unified interface for accessing version information across diverse platforms, each with their own APIs and data formats.
Central factory class for creating and managing repository holders based on URL patterns and platform hints.
class HolderFactory:
"""
Factory for creating platform-specific repository holders.
Automatically selects appropriate holder based on repository URL patterns,
domain matching, and explicit platform hints. Holders are ordered by
specificity with self-hosted platforms evaluated after primary domains.
"""
HOLDERS: dict[str, type] = {
"wp": WordPressPluginRepoSession,
"sf": SourceForgeRepoSession,
"wiki": WikipediaRepoSession,
"helm_chart": HelmChartRepoSession,
"github": GitHubRepoSession,
"gitlab": GitLabRepoSession,
"bitbucket": BitBucketRepoSession,
"pip": PypiRepoSession,
"hg": MercurialRepoSession,
"gitea": GiteaRepoSession,
"website-feed": FeedRepoSession,
"local": LocalVersionSession,
"system": SystemRepoSession,
}
DEFAULT_HOLDER: str = "github"
@staticmethod
def get_instance_for_repo(repo: str, at: str = None):
"""
Get appropriate holder instance for repository.
Parameters:
- repo: Repository identifier (URL, owner/name, package name)
- at: Optional platform hint to override auto-detection
Returns:
- Holder class for the specified repository
"""Abstract base class defining the common interface implemented by all repository holders.
class BaseProjectHolder:
"""
Abstract base class for all repository holders.
Defines common interface and shared functionality for accessing
version information across different platforms and repositories.
"""
CACHE_DISABLED: bool = False
def __init__(self, repo: str, **kwargs):
"""
Initialize holder for specific repository.
Parameters:
- repo: Repository identifier
- **kwargs: Platform-specific configuration options
"""
def get_latest_version(self, **kwargs) -> dict:
"""
Get latest version information for the repository.
Returns:
- Dictionary containing version and release metadata
"""
def get_versions(self, **kwargs) -> list:
"""
Get all available versions for the repository.
Returns:
- List of version dictionaries ordered by release date
"""Specialized holder for GitHub repositories with support for GitHub Enterprise and advanced filtering options.
class GitHubRepoSession(BaseProjectHolder):
"""
GitHub repository holder supporting public GitHub and GitHub Enterprise.
Features:
- GitHub API v3/v4 integration
- Release and tag-based version discovery
- Asset filtering and download URL generation
- Rate limiting and authentication handling
- Pre-release detection and filtering
"""
def __init__(self, repo: str, hostname: str = "github.com", **kwargs):
"""
Initialize GitHub repository session.
Parameters:
- repo: Repository in format "owner/name"
- hostname: GitHub hostname for Enterprise instances
- **kwargs: Additional GitHub-specific options
"""Holder for GitLab repositories supporting both GitLab.com and self-hosted GitLab instances.
class GitLabRepoSession(BaseProjectHolder):
"""
GitLab repository holder for GitLab.com and self-hosted instances.
Features:
- GitLab API v4 integration
- Project and tag-based version discovery
- Self-hosted GitLab instance support
- Release artifacts and download URLs
"""
def __init__(self, repo: str, hostname: str = "gitlab.com", **kwargs):
"""
Initialize GitLab repository session.
Parameters:
- repo: Repository identifier or full URL
- hostname: GitLab hostname for self-hosted instances
- **kwargs: GitLab-specific options
"""Package repository holder for Python Package Index with comprehensive package metadata support.
class PypiRepoSession(BaseProjectHolder):
"""
PyPI package repository holder.
Features:
- PyPI JSON API integration
- Package version history
- Wheel and source distribution discovery
- Package metadata extraction
- Pre-release version filtering
"""
def __init__(self, package_name: str, **kwargs):
"""
Initialize PyPI package session.
Parameters:
- package_name: PyPI package name
- **kwargs: PyPI-specific options
"""Integration classes for various other platforms and repositories.
class BitBucketRepoSession(BaseProjectHolder):
"""BitBucket repository holder for Atlassian BitBucket."""
class SourceForgeRepoSession(BaseProjectHolder):
"""SourceForge project holder with file release system support."""
class WordPressPluginRepoSession(BaseProjectHolder):
"""WordPress plugin directory holder."""
class WikipediaRepoSession(BaseProjectHolder):
"""Wikipedia software version extraction from software infoboxes."""
class MercurialRepoSession(BaseProjectHolder):
"""Mercurial repository holder for Hg-based projects."""
class GiteaRepoSession(BaseProjectHolder):
"""Gitea repository holder for self-hosted Git service."""
class FeedRepoSession(BaseProjectHolder):
"""RSS/ATOM feed-based version discovery for arbitrary websites."""
class LocalVersionSession(BaseProjectHolder):
"""Local file-based version discovery."""
class SystemRepoSession(BaseProjectHolder):
"""System package manager integration."""
class HelmChartRepoSession(BaseProjectHolder):
"""Helm chart repository holder for Kubernetes packages."""from lastversion.holder_factory import HolderFactory
# Automatic holder selection based on repository URL
holders = [
("numpy/numpy", "github"), # GitHub auto-detected
("requests", "pip"), # Explicit PyPI hint
("gitlab.com/gitlab-org/gitlab", "gitlab"), # GitLab auto-detected
("https://sourceforge.net/projects/sevenzip/", "sf"), # SourceForge
]
for repo, expected in holders:
holder_class = HolderFactory.get_holder_class(repo)
print(f"{repo} → {holder_class.__name__}")from lastversion.repo_holders.github import GitHubRepoSession
from lastversion.repo_holders.pypi import PypiRepoSession
# Direct GitHub repository access
github_holder = GitHubRepoSession("kubernetes/kubernetes")
k8s_versions = github_holder.get_versions()
latest_k8s = github_holder.get_latest_version()
print(f"Latest Kubernetes: {latest_k8s['version']}")
print(f"Total releases: {len(k8s_versions)}")
# Direct PyPI package access
pypi_holder = PypiRepoSession("requests")
requests_info = pypi_holder.get_latest_version()
print(f"Latest requests: {requests_info['version']}")from lastversion.repo_holders.github import GitHubRepoSession
# GitHub Enterprise instance
enterprise_holder = GitHubRepoSession(
"internal/project",
hostname="github.company.com"
)
# Custom authentication can be provided via environment variables
# GITHUB_TOKEN or through holder configuration
latest_version = enterprise_holder.get_latest_version()from lastversion.holder_factory import HolderFactory
def get_filtered_versions(repo, **filters):
"""Get versions with custom filtering applied."""
holder_class = HolderFactory.get_holder_class(repo)
holder = holder_class(repo)
# Get all versions
versions = holder.get_versions()
# Apply filters
if filters.get('pre_ok', False) is False:
versions = [v for v in versions if not v.get('prerelease', False)]
if 'major' in filters:
major_version = filters['major']
versions = [v for v in versions
if v['version'].startswith(major_version)]
return versions
# Usage example
stable_versions = get_filtered_versions("kubernetes/kubernetes", pre_ok=False)
v1_28_versions = get_filtered_versions("kubernetes/kubernetes", major="v1.28")from lastversion.holder_factory import HolderFactory
def compare_versions_across_platforms(package_identifiers):
"""Compare same software across different platforms."""
results = {}
for platform, identifier in package_identifiers.items():
try:
holder_class = HolderFactory.get_holder_class(identifier, at=platform)
holder = holder_class(identifier)
latest = holder.get_latest_version()
results[platform] = latest['version']
except Exception as e:
results[platform] = f"Error: {e}"
return results
# Compare Docker across platforms
docker_platforms = {
"github": "docker/docker",
"pip": "docker", # Docker SDK for Python
}
versions = compare_versions_across_platforms(docker_platforms)
for platform, version in versions.items():
print(f"{platform}: {version}")from lastversion.repo_holders.base import BaseProjectHolder
class CustomRepoSession(BaseProjectHolder):
"""Custom repository holder example."""
def __init__(self, repo, **kwargs):
super().__init__(repo, **kwargs)
self.base_url = kwargs.get('base_url', 'https://api.example.com')
def get_latest_version(self, **kwargs):
"""Implement custom version discovery logic."""
# Custom API integration logic here
return {
'version': '1.0.0',
'date': '2023-01-01',
'download_url': 'https://example.com/download'
}
def get_versions(self, **kwargs):
"""Get all available versions."""
# Custom implementation
return [self.get_latest_version()]
# Register custom holder with factory
HolderFactory.HOLDERS['custom'] = CustomRepoSession
# Use custom holder
custom_version = HolderFactory.get_holder_class('repo', at='custom')from lastversion.holder_factory import HolderFactory
from lastversion.exceptions import BadProjectError, ApiCredentialsError
def robust_version_lookup(repo, platforms=None):
"""Robust version lookup with fallback platforms."""
if platforms is None:
platforms = ['github', 'gitlab', 'pip']
for platform in platforms:
try:
holder_class = HolderFactory.get_holder_class(repo, at=platform)
holder = holder_class(repo)
return holder.get_latest_version()
except BadProjectError:
continue # Try next platform
except ApiCredentialsError as e:
print(f"API credentials issue for {platform}: {e}")
continue
except Exception as e:
print(f"Error with {platform}: {e}")
continue
raise BadProjectError(f"Could not find {repo} on any platform")
# Usage with fallback logic
try:
version_info = robust_version_lookup("some-project")
print(f"Found version: {version_info['version']}")
except BadProjectError as e:
print(f"Project not found: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-lastversion