Some checks failed
Go package / build (push) Has been cancelled
- Classify core.db.sig as an Arch entry point so it is gated on the database it signs. Served as a plain file it could be revalidated on its own, and pacman fetching core.db then core.db.sig would receive a rotated signature paired with the database the mirror still holds, failing verification on every client until the next crawl. - Keep a repository registered when its crawl verified and published the tree but the requested entry point is absent upstream. Deregistering on that miss dropped every member file to the generic path, which refreshes files individually with no checksum or signature check. - Drop the generic state entry for a file a registered repository now owns instead of evicting the file. The entry is left by a request that predates the registration and nothing refreshes it, so expiry deleted a file out of the verified tree and forced the next request into a synchronous crawl. - Release 0.1.2. Claude-Session: https://claude.ai/code/session_01FkruwxzDGY4BoXp1Zzoott
100 lines
2.6 KiB
Go
100 lines
2.6 KiB
Go
package server
|
|
|
|
import (
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/grmrgecko/repo-sync/mirror"
|
|
)
|
|
|
|
// kindGeneric marks request paths that are not repository entry points.
|
|
const kindGeneric = "generic"
|
|
|
|
// resource is a crawlable unit derived from a request path.
|
|
type resource struct {
|
|
// Kind is a RepoType string, or kindGeneric.
|
|
Kind string
|
|
// Key uniquely identifies the resource in state and the lock map.
|
|
Key string
|
|
// Path is the repository request path (the suite directory for deb).
|
|
Path string
|
|
// Root is the request path prefix covering every file the resource
|
|
// owns; deb uses the archive root so pool requests match.
|
|
Root string
|
|
// ReqPath preserves the original request path.
|
|
ReqPath string
|
|
}
|
|
|
|
// classifyRequest maps a request path onto a crawlable resource. Only
|
|
// repository entry-point metadata registers a repository; every other path
|
|
// is generic and served on demand.
|
|
func classifyRequest(reqPath string) resource {
|
|
clean := path.Clean("/" + strings.TrimPrefix(reqPath, "/"))
|
|
base := path.Base(clean)
|
|
dir := path.Dir(clean)
|
|
|
|
// RPM repositories are identified by any repodata request.
|
|
if idx := strings.Index(clean, "/repodata/"); idx >= 0 {
|
|
root := clean[:idx]
|
|
if root == "" {
|
|
root = "/"
|
|
}
|
|
return resource{
|
|
Kind: string(mirror.RepoRPM),
|
|
Key: string(mirror.RepoRPM) + ":" + root,
|
|
Path: root,
|
|
Root: root,
|
|
ReqPath: clean,
|
|
}
|
|
}
|
|
|
|
// Apt suites are identified by their release files; the archive root
|
|
// above dists also covers the pool.
|
|
if base == "InRelease" || base == "Release" || base == "Release.gpg" {
|
|
root := dir
|
|
if idx := strings.Index(clean, "/dists/"); idx >= 0 {
|
|
root = clean[:idx]
|
|
if root == "" {
|
|
root = "/"
|
|
}
|
|
}
|
|
return resource{
|
|
Kind: string(mirror.RepoDeb),
|
|
Key: string(mirror.RepoDeb) + ":" + dir,
|
|
Path: dir,
|
|
Root: root,
|
|
ReqPath: clean,
|
|
}
|
|
}
|
|
|
|
// Pacman repositories are identified by their database file. Its
|
|
// detached signature is an entry point too: served as a plain file it
|
|
// could be refreshed apart from the database it signs.
|
|
if strings.HasSuffix(base, ".db") || strings.HasSuffix(base, ".db.sig") {
|
|
return resource{
|
|
Kind: string(mirror.RepoArch),
|
|
Key: string(mirror.RepoArch) + ":" + dir,
|
|
Path: dir,
|
|
Root: dir,
|
|
ReqPath: clean,
|
|
}
|
|
}
|
|
|
|
// Alpine repositories are identified by their index archive.
|
|
if base == mirror.APKIndexName {
|
|
return resource{
|
|
Kind: string(mirror.RepoApk),
|
|
Key: string(mirror.RepoApk) + ":" + dir,
|
|
Path: dir,
|
|
Root: dir,
|
|
ReqPath: clean,
|
|
}
|
|
}
|
|
|
|
return resource{
|
|
Kind: kindGeneric,
|
|
Key: kindGeneric + ":" + clean,
|
|
Path: clean,
|
|
ReqPath: clean,
|
|
}
|
|
}
|