feat: github_deb metadata-only package type

Add the Debian/apt analog of github_rpm: a metadata-only remote that scans
a GitHub repo's releases for .deb assets, derives per-asset control metadata
via a ranged prefix fetch (never downloading whole packages), synthesizes a
flat apt repository, and redirects .deb downloads to a backend releases_remote.

- Add PackageGitHubDeb to the package-type enum + validity map.
- Add the github_deb provider (internal/provider/deb/github.go): ServeRemote
  serves Packages/Packages.gz/Release, 404s the signed index variants
  (consumed via [trusted=yes]), and 302-redirects *.deb to the releases_remote;
  deriveAsset ranged-GETs the ar prefix, locates control.tar.*, and parses the
  control paragraph, doubling the range on truncation; sha256 comes from the
  asset digest when present, else a one-time full stream.
- Add the github_deb background Syncer (internal/provider/deb/syncer.go): its
  own worker pool, shared rate limiter, deduped queue, and DB-lease-gated scans.
- Add github_deb_sync_state table plus ListGitHubDebRemotes/Claim/Release DB
  helpers (separate from the rpm ones).
- Prime github_deb remotes on create and run the deb syncer alongside the rpm
  one; route prime-on-create by package type.
- Reuse the deb apt-index generators and control parser; skip empty hash lines
  in the Packages index so a SHA256-only metadata entry is valid.
This commit is contained in:
2026-08-11 22:21:05 +10:00
parent 60f008debc
commit b2a6be8eb5
11 changed files with 1889 additions and 14 deletions
+11 -9
View File
@@ -12,18 +12,20 @@ import (
)
// Primer enqueues a background metadata prime for a newly created remote so the
// create call never blocks on a derive. *rpm.Syncer satisfies it.
// create call never blocks on a derive. *rpm.Syncer and *deb.Syncer satisfy it.
type Primer interface {
EnqueuePrime(remote models.Remote)
}
type RemotesHandler struct {
db *database.DB
primer Primer
db *database.DB
primers map[models.PackageType]Primer
}
func NewRemotesHandler(db *database.DB, primer Primer) *RemotesHandler {
return &RemotesHandler{db: db, primer: primer}
// NewRemotesHandler wires the handler to the per-type metadata primers. primers
// may be nil; a package type with no registered primer simply skips priming.
func NewRemotesHandler(db *database.DB, primers map[models.PackageType]Primer) *RemotesHandler {
return &RemotesHandler{db: db, primers: primers}
}
func (h *RemotesHandler) Routes() chi.Router {
@@ -84,10 +86,10 @@ func (h *RemotesHandler) create(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Prime a github_rpm remote's metadata in the background so its first
// repodata request is served from cache instead of a cold on-demand derive.
if h.primer != nil && remote.PackageType == models.PackageGitHubRPM {
h.primer.EnqueuePrime(remote)
// Prime a metadata-only remote (github_rpm/github_deb) in the background so
// its first index request is served from cache instead of a cold derive.
if primer := h.primers[remote.PackageType]; primer != nil {
primer.EnqueuePrime(remote)
}
writeJSON(w, http.StatusCreated, remote)
}