0
# Repository Holders
1
2
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.
3
4
## Capabilities
5
6
### Holder Factory
7
8
Central factory class for creating and managing repository holders based on URL patterns and platform hints.
9
10
```python { .api }
11
class HolderFactory:
12
"""
13
Factory for creating platform-specific repository holders.
14
15
Automatically selects appropriate holder based on repository URL patterns,
16
domain matching, and explicit platform hints. Holders are ordered by
17
specificity with self-hosted platforms evaluated after primary domains.
18
"""
19
20
HOLDERS: dict[str, type] = {
21
"wp": WordPressPluginRepoSession,
22
"sf": SourceForgeRepoSession,
23
"wiki": WikipediaRepoSession,
24
"helm_chart": HelmChartRepoSession,
25
"github": GitHubRepoSession,
26
"gitlab": GitLabRepoSession,
27
"bitbucket": BitBucketRepoSession,
28
"pip": PypiRepoSession,
29
"hg": MercurialRepoSession,
30
"gitea": GiteaRepoSession,
31
"website-feed": FeedRepoSession,
32
"local": LocalVersionSession,
33
"system": SystemRepoSession,
34
}
35
36
DEFAULT_HOLDER: str = "github"
37
38
@staticmethod
39
def get_instance_for_repo(repo: str, at: str = None):
40
"""
41
Get appropriate holder instance for repository.
42
43
Parameters:
44
- repo: Repository identifier (URL, owner/name, package name)
45
- at: Optional platform hint to override auto-detection
46
47
Returns:
48
- Holder class for the specified repository
49
"""
50
```
51
52
### Base Holder Interface
53
54
Abstract base class defining the common interface implemented by all repository holders.
55
56
```python { .api }
57
class BaseProjectHolder:
58
"""
59
Abstract base class for all repository holders.
60
61
Defines common interface and shared functionality for accessing
62
version information across different platforms and repositories.
63
"""
64
65
CACHE_DISABLED: bool = False
66
67
def __init__(self, repo: str, **kwargs):
68
"""
69
Initialize holder for specific repository.
70
71
Parameters:
72
- repo: Repository identifier
73
- **kwargs: Platform-specific configuration options
74
"""
75
76
def get_latest_version(self, **kwargs) -> dict:
77
"""
78
Get latest version information for the repository.
79
80
Returns:
81
- Dictionary containing version and release metadata
82
"""
83
84
def get_versions(self, **kwargs) -> list:
85
"""
86
Get all available versions for the repository.
87
88
Returns:
89
- List of version dictionaries ordered by release date
90
"""
91
```
92
93
### GitHub Integration
94
95
Specialized holder for GitHub repositories with support for GitHub Enterprise and advanced filtering options.
96
97
```python { .api }
98
class GitHubRepoSession(BaseProjectHolder):
99
"""
100
GitHub repository holder supporting public GitHub and GitHub Enterprise.
101
102
Features:
103
- GitHub API v3/v4 integration
104
- Release and tag-based version discovery
105
- Asset filtering and download URL generation
106
- Rate limiting and authentication handling
107
- Pre-release detection and filtering
108
"""
109
110
def __init__(self, repo: str, hostname: str = "github.com", **kwargs):
111
"""
112
Initialize GitHub repository session.
113
114
Parameters:
115
- repo: Repository in format "owner/name"
116
- hostname: GitHub hostname for Enterprise instances
117
- **kwargs: Additional GitHub-specific options
118
"""
119
```
120
121
### GitLab Integration
122
123
Holder for GitLab repositories supporting both GitLab.com and self-hosted GitLab instances.
124
125
```python { .api }
126
class GitLabRepoSession(BaseProjectHolder):
127
"""
128
GitLab repository holder for GitLab.com and self-hosted instances.
129
130
Features:
131
- GitLab API v4 integration
132
- Project and tag-based version discovery
133
- Self-hosted GitLab instance support
134
- Release artifacts and download URLs
135
"""
136
137
def __init__(self, repo: str, hostname: str = "gitlab.com", **kwargs):
138
"""
139
Initialize GitLab repository session.
140
141
Parameters:
142
- repo: Repository identifier or full URL
143
- hostname: GitLab hostname for self-hosted instances
144
- **kwargs: GitLab-specific options
145
"""
146
```
147
148
### PyPI Integration
149
150
Package repository holder for Python Package Index with comprehensive package metadata support.
151
152
```python { .api }
153
class PypiRepoSession(BaseProjectHolder):
154
"""
155
PyPI package repository holder.
156
157
Features:
158
- PyPI JSON API integration
159
- Package version history
160
- Wheel and source distribution discovery
161
- Package metadata extraction
162
- Pre-release version filtering
163
"""
164
165
def __init__(self, package_name: str, **kwargs):
166
"""
167
Initialize PyPI package session.
168
169
Parameters:
170
- package_name: PyPI package name
171
- **kwargs: PyPI-specific options
172
"""
173
```
174
175
### Additional Platform Holders
176
177
Integration classes for various other platforms and repositories.
178
179
```python { .api }
180
class BitBucketRepoSession(BaseProjectHolder):
181
"""BitBucket repository holder for Atlassian BitBucket."""
182
183
class SourceForgeRepoSession(BaseProjectHolder):
184
"""SourceForge project holder with file release system support."""
185
186
class WordPressPluginRepoSession(BaseProjectHolder):
187
"""WordPress plugin directory holder."""
188
189
class WikipediaRepoSession(BaseProjectHolder):
190
"""Wikipedia software version extraction from software infoboxes."""
191
192
class MercurialRepoSession(BaseProjectHolder):
193
"""Mercurial repository holder for Hg-based projects."""
194
195
class GiteaRepoSession(BaseProjectHolder):
196
"""Gitea repository holder for self-hosted Git service."""
197
198
class FeedRepoSession(BaseProjectHolder):
199
"""RSS/ATOM feed-based version discovery for arbitrary websites."""
200
201
class LocalVersionSession(BaseProjectHolder):
202
"""Local file-based version discovery."""
203
204
class SystemRepoSession(BaseProjectHolder):
205
"""System package manager integration."""
206
207
class HelmChartRepoSession(BaseProjectHolder):
208
"""Helm chart repository holder for Kubernetes packages."""
209
```
210
211
## Usage Examples
212
213
### Automatic Platform Detection
214
215
```python
216
from lastversion.holder_factory import HolderFactory
217
218
# Automatic holder selection based on repository URL
219
holders = [
220
("numpy/numpy", "github"), # GitHub auto-detected
221
("requests", "pip"), # Explicit PyPI hint
222
("gitlab.com/gitlab-org/gitlab", "gitlab"), # GitLab auto-detected
223
("https://sourceforge.net/projects/sevenzip/", "sf"), # SourceForge
224
]
225
226
for repo, expected in holders:
227
holder_class = HolderFactory.get_holder_class(repo)
228
print(f"{repo} → {holder_class.__name__}")
229
```
230
231
### Direct Holder Usage
232
233
```python
234
from lastversion.repo_holders.github import GitHubRepoSession
235
from lastversion.repo_holders.pypi import PypiRepoSession
236
237
# Direct GitHub repository access
238
github_holder = GitHubRepoSession("kubernetes/kubernetes")
239
k8s_versions = github_holder.get_versions()
240
latest_k8s = github_holder.get_latest_version()
241
242
print(f"Latest Kubernetes: {latest_k8s['version']}")
243
print(f"Total releases: {len(k8s_versions)}")
244
245
# Direct PyPI package access
246
pypi_holder = PypiRepoSession("requests")
247
requests_info = pypi_holder.get_latest_version()
248
print(f"Latest requests: {requests_info['version']}")
249
```
250
251
### GitHub Enterprise Integration
252
253
```python
254
from lastversion.repo_holders.github import GitHubRepoSession
255
256
# GitHub Enterprise instance
257
enterprise_holder = GitHubRepoSession(
258
"internal/project",
259
hostname="github.company.com"
260
)
261
262
# Custom authentication can be provided via environment variables
263
# GITHUB_TOKEN or through holder configuration
264
latest_version = enterprise_holder.get_latest_version()
265
```
266
267
### Advanced Filtering with Holders
268
269
```python
270
from lastversion.holder_factory import HolderFactory
271
272
def get_filtered_versions(repo, **filters):
273
"""Get versions with custom filtering applied."""
274
holder_class = HolderFactory.get_holder_class(repo)
275
holder = holder_class(repo)
276
277
# Get all versions
278
versions = holder.get_versions()
279
280
# Apply filters
281
if filters.get('pre_ok', False) is False:
282
versions = [v for v in versions if not v.get('prerelease', False)]
283
284
if 'major' in filters:
285
major_version = filters['major']
286
versions = [v for v in versions
287
if v['version'].startswith(major_version)]
288
289
return versions
290
291
# Usage example
292
stable_versions = get_filtered_versions("kubernetes/kubernetes", pre_ok=False)
293
v1_28_versions = get_filtered_versions("kubernetes/kubernetes", major="v1.28")
294
```
295
296
### Multi-Platform Version Comparison
297
298
```python
299
from lastversion.holder_factory import HolderFactory
300
301
def compare_versions_across_platforms(package_identifiers):
302
"""Compare same software across different platforms."""
303
results = {}
304
305
for platform, identifier in package_identifiers.items():
306
try:
307
holder_class = HolderFactory.get_holder_class(identifier, at=platform)
308
holder = holder_class(identifier)
309
latest = holder.get_latest_version()
310
results[platform] = latest['version']
311
except Exception as e:
312
results[platform] = f"Error: {e}"
313
314
return results
315
316
# Compare Docker across platforms
317
docker_platforms = {
318
"github": "docker/docker",
319
"pip": "docker", # Docker SDK for Python
320
}
321
322
versions = compare_versions_across_platforms(docker_platforms)
323
for platform, version in versions.items():
324
print(f"{platform}: {version}")
325
```
326
327
### Custom Holder Development
328
329
```python
330
from lastversion.repo_holders.base import BaseProjectHolder
331
332
class CustomRepoSession(BaseProjectHolder):
333
"""Custom repository holder example."""
334
335
def __init__(self, repo, **kwargs):
336
super().__init__(repo, **kwargs)
337
self.base_url = kwargs.get('base_url', 'https://api.example.com')
338
339
def get_latest_version(self, **kwargs):
340
"""Implement custom version discovery logic."""
341
# Custom API integration logic here
342
return {
343
'version': '1.0.0',
344
'date': '2023-01-01',
345
'download_url': 'https://example.com/download'
346
}
347
348
def get_versions(self, **kwargs):
349
"""Get all available versions."""
350
# Custom implementation
351
return [self.get_latest_version()]
352
353
# Register custom holder with factory
354
HolderFactory.HOLDERS['custom'] = CustomRepoSession
355
356
# Use custom holder
357
custom_version = HolderFactory.get_holder_class('repo', at='custom')
358
```
359
360
### Error Handling and Fallbacks
361
362
```python
363
from lastversion.holder_factory import HolderFactory
364
from lastversion.exceptions import BadProjectError, ApiCredentialsError
365
366
def robust_version_lookup(repo, platforms=None):
367
"""Robust version lookup with fallback platforms."""
368
if platforms is None:
369
platforms = ['github', 'gitlab', 'pip']
370
371
for platform in platforms:
372
try:
373
holder_class = HolderFactory.get_holder_class(repo, at=platform)
374
holder = holder_class(repo)
375
return holder.get_latest_version()
376
except BadProjectError:
377
continue # Try next platform
378
except ApiCredentialsError as e:
379
print(f"API credentials issue for {platform}: {e}")
380
continue
381
except Exception as e:
382
print(f"Error with {platform}: {e}")
383
continue
384
385
raise BadProjectError(f"Could not find {repo} on any platform")
386
387
# Usage with fallback logic
388
try:
389
version_info = robust_version_lookup("some-project")
390
print(f"Found version: {version_info['version']}")
391
except BadProjectError as e:
392
print(f"Project not found: {e}")
393
```