4.1 KiB
github_rpm remote
Shared classification, TTL and eviction rules: caching.
| Type | Mutable (auto-detected) | Immutable (auto-detected) |
|---|---|---|
github_rpm |
repodata/* (synthesized) |
.rpm (redirected) |
GitHub releases as a yum repo (metadata-only, no precache)
A github_rpm remote turns a GitHub repo's releases into a real dnf/yum
repository without ever caching the packages. It scans releases for .rpm
assets, derives each package's metadata (NEVRA, requires/provides/conflicts/
obsoletes, files, checksum) and synthesizes repodata/ on the fly. Package
metadata comes from a ranged GET of just the RPM header (the header sits at
the front of the file, so the whole package is never downloaded); the sha256
checksum comes from the GitHub asset digest when present, else a one-time
lazy stream. Derived metadata is cached (keyed by asset) so repodata generation
is served from primed DB rows, never a cold on-demand derive.
Each package's <location> points back at the remote, which 302-redirects
the download to the releases_remote — an existing generic github.com remote
that streams the actual bytes. dnf follows the redirect transparently.
Background syncer
A single process-wide background syncer keeps every github_rpm remote's
derived metadata current off the client request path:
- Prime on create. Creating a
github_rpmremote enqueues a background prime scan, so its metadata is derived right away without blocking the create call. The firstdnfrequest is served from cache. If a request arrives before the prime lands, it returns a retryable503(withRetry-After) rather than serving an empty repo or blocking on a multi-minute derive. - Periodic re-check, driven by
mutable_ttl. Each remote is re-checked for new or changed releases no more often than itsmutable_ttl. New/changed assets are derived incrementally; assets already cached are never re-fetched, and assets that disappear upstream are pruned. - ETag / 304 conditional requests. The releases-list
ETagis stored per remote and sent asIf-None-Match; a304 Not Modifiedmeans nothing changed and the syncer derives nothing. GitHub does not count304conditional responses against the rate limit, so an unchanged repo is nearly free — this is the main lever keeping GitHub traffic low. - Global rate limit. Every GitHub call (releases list + each ranged asset
header GET) passes through a single token-bucket limiter shared across all
remotes, so GitHub is never hammered. Configure a token (
password) on the remote for the higher authenticated rate limit (~5000/hr vs ~60/hr unauthenticated). - Multi-replica coordination. State is shared through the database. Before a
periodic scan a replica must atomically claim a per-remote lease
(
github_rpm_sync_state:last_synced_at,etag,sync_lease_owner,sync_lease_expires); only the winner scans. This bounds total GitHub load to ~once permutable_ttlregardless of replica count, and the sharedetaglets any replica issue the conditional request.
# Backend that serves the actual .rpm bytes from github.com.
resource "artifactapi_remote_generic" "github" {
name = "github"
base_url = "https://github.com"
patterns = [
"acme/tools/releases/download/.*\\.rpm$", # allowlist the repo's release assets
]
}
resource "artifactapi_remote_github_rpm" "acme-tools" {
name = "acme-tools"
base_url = "https://api.github.com/repos/acme/tools" # the releases API root
releases_remote = "github" # backend for downloads
mutable_ttl = 3600 # release re-scan interval
# Optional: restrict which release assets become packages (regex on filename).
patterns = [".*\\.x86_64\\.rpm$", ".*\\.noarch\\.rpm$"]
# Optional: a token for private repos / higher API rate limits.
# password = "ghp_..."
}
dnf config: baseurl=https://artifactapi.example/api/v1/remote/acme-tools.
The repo is multi-arch (no $basearch needed) — dnf selects matching packages
from the synthesized metadata.