package arch import ( "archive/tar" "bufio" "bytes" "compress/gzip" "context" "fmt" "io" "strings" "time" "tangled.org/odd.computer/isomorph/internal/fetch" ) // pacman's repository databases (core.db, extra.db, multilib.db) list every binary package of a // repository with its pkgbase, version and url in one small download, so they enumerate the // distribution without touching GitLab. They carry no source URLs; those come from .SRCINFO. // DefaultMirror serves the repository databases. const DefaultMirror = "https://geo.mirror.pkgbuild.com" // SyncRepos are the repositories of the released distribution. var SyncRepos = []string{"core", "extra", "multilib"} // SyncPackage is one binary package entry of a repository database. type SyncPackage struct { Repo string Name string Base string Version string URL string } // SyncDB downloads and parses the x86_64 database of repo. func (r *Resolver) SyncDB(ctx context.Context, repo string) ([]SyncPackage, error) { mirror := r.Mirror if mirror == "" { mirror = DefaultMirror } b, err := r.c.Get(ctx, mirror+"/"+repo+"/os/x86_64/"+repo+".db", fetch.WithTTL(6*time.Hour)) if err != nil { return nil, fmt.Errorf("arch: %s.db: %w", repo, err) } return ParseSyncDB(b, repo) } // ParseSyncDB reads a gzip-compressed repository database. func ParseSyncDB(b []byte, repo string) ([]SyncPackage, error) { zr, err := gzip.NewReader(bytes.NewReader(b)) if err != nil { return nil, fmt.Errorf("arch: %s.db: %w", repo, err) } tr := tar.NewReader(zr) var out []SyncPackage for { h, err := tr.Next() if err == io.EOF { break } if err != nil { return nil, fmt.Errorf("arch: %s.db: %w", repo, err) } if h.Typeflag != tar.TypeReg || !strings.HasSuffix(h.Name, "/desc") { continue } fields := map[string]string{} sc := bufio.NewScanner(io.LimitReader(tr, 1<<20)) sc.Buffer(make([]byte, 64<<10), 1<<20) key := "" for sc.Scan() { line := sc.Text() switch { case strings.HasPrefix(line, "%") && strings.HasSuffix(line, "%"): key = strings.Trim(line, "%") case line == "": key = "" case key != "" && fields[key] == "": fields[key] = line } } p := SyncPackage{Repo: repo, Name: fields["NAME"], Base: fields["BASE"], Version: fields["VERSION"], URL: fields["URL"]} if p.Base == "" { p.Base = p.Name } if p.Name != "" { out = append(out, p) } } return out, nil }