155 lines
4.0 KiB
Go
155 lines
4.0 KiB
Go
package debanator
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"pault.ag/go/debian/control"
|
|
"pault.ag/go/debian/deb"
|
|
"pault.ag/go/debian/dependency"
|
|
"pault.ag/go/debian/version"
|
|
|
|
"golang.org/x/exp/maps"
|
|
)
|
|
|
|
// A backend to search for packages in
|
|
type Backend interface {
|
|
GetPackages()
|
|
}
|
|
|
|
|
|
type FileBackend struct {
|
|
path string
|
|
}
|
|
|
|
|
|
func NewFileBackend(path string) FileBackend {
|
|
return FileBackend{path}
|
|
}
|
|
|
|
func BinaryIndexFromDeb(p string, basePath string) (*control.BinaryIndex, error) {
|
|
f, err := os.Open(p)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open file: %w", err)
|
|
}
|
|
defer f.Close()
|
|
debFile, err := deb.Load(f, p)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read deb: %w", err)
|
|
}
|
|
md5sum := md5.New()
|
|
sha1sum := sha1.New()
|
|
sha256sum := sha256.New()
|
|
hashWriter := io.MultiWriter(md5sum, sha1sum, sha256sum)
|
|
size, err := io.Copy(hashWriter, f)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("hash file: %w", err)
|
|
}
|
|
bi := control.BinaryIndex{
|
|
Paragraph: control.Paragraph{
|
|
Values: make(map[string]string),
|
|
},
|
|
Package: debFile.Control.Package,
|
|
Source: debFile.Control.Source,
|
|
Version: debFile.Control.Version,
|
|
InstalledSize: fmt.Sprintf("%d", debFile.Control.InstalledSize),
|
|
Size: strconv.Itoa(int(size)),
|
|
Maintainer: debFile.Control.Maintainer,
|
|
Architecture: debFile.Control.Architecture,
|
|
MultiArch: debFile.Control.MultiArch,
|
|
Description: debFile.Control.Description,
|
|
Homepage: debFile.Control.Homepage,
|
|
Section: debFile.Control.Section,
|
|
// FIXME: gross, make this more centrally managed somehow
|
|
Filename: path.Join("pool/main", strings.TrimPrefix(p, basePath)),
|
|
Priority: debFile.Control.Priority,
|
|
MD5sum: fmt.Sprintf("%x", md5sum.Sum(nil)),
|
|
SHA1: fmt.Sprintf("%x", sha1sum.Sum(nil)),
|
|
SHA256: fmt.Sprintf("%x", sha256sum.Sum(nil)),
|
|
}
|
|
if debFile.Control.Depends.String() != "" {
|
|
bi.Paragraph.Set("Depends", debFile.Control.Depends.String())
|
|
}
|
|
if debFile.Control.Recommends.String() != "" {
|
|
bi.Paragraph.Set("Recommends", debFile.Control.Recommends.String())
|
|
}
|
|
if debFile.Control.Suggests.String() != "" {
|
|
bi.Paragraph.Set("Suggests", debFile.Control.Suggests.String())
|
|
}
|
|
if debFile.Control.Breaks.String() != "" {
|
|
bi.Paragraph.Set("Breaks", debFile.Control.Breaks.String())
|
|
}
|
|
if debFile.Control.Replaces.String() != "" {
|
|
bi.Paragraph.Set("Replaces", debFile.Control.Replaces.String())
|
|
}
|
|
if debFile.Control.BuiltUsing.String() != "" {
|
|
bi.Paragraph.Set("BuiltUsing", debFile.Control.BuiltUsing.String())
|
|
}
|
|
return &bi, nil
|
|
}
|
|
|
|
func ScanDebs(debpath string) Repo {
|
|
var debs []string
|
|
fs.WalkDir(os.DirFS(debpath), ".", func(path string, dir fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"path": path,
|
|
"error": err,
|
|
}).Warn("Error scanning for debs")
|
|
return nil
|
|
}
|
|
if !dir.IsDir() && strings.HasSuffix(dir.Name(), ".deb"){
|
|
debs = append(debs, path)
|
|
}
|
|
return nil
|
|
})
|
|
packs := make(map[string]LogicalPackage)
|
|
for _, d := range debs {
|
|
p := path.Join(debpath, d)
|
|
bi, err := BinaryIndexFromDeb(p, debpath)
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"path": p,
|
|
"error": err,
|
|
}).Error("Error processing deb file")
|
|
continue
|
|
}
|
|
|
|
packageName := bi.Package
|
|
if _, ok := packs[packageName]; !ok {
|
|
packs[packageName] = LogicalPackage{
|
|
Name: packageName,
|
|
Arches: make(map[dependency.Arch]map[version.Version]control.BinaryIndex),
|
|
}
|
|
}
|
|
pack := packs[packageName]
|
|
if _, ok := pack.Arches[bi.Architecture]; !ok {
|
|
pack.Arches[bi.Architecture] = make(map[version.Version]control.BinaryIndex)
|
|
}
|
|
arch := pack.Arches[bi.Architecture]
|
|
if _, ok := arch[bi.Version]; !ok {
|
|
arch[bi.Version] = *bi
|
|
} else {
|
|
log.WithFields(log.Fields{
|
|
"package": packageName,
|
|
"arch": arch,
|
|
"version": bi.Version.String(),
|
|
}).Warn("Duplicate package/arch/version found, ignoring...")
|
|
}
|
|
}
|
|
|
|
return Repo{
|
|
packages: maps.Values(packs),
|
|
cache: make(map[string]hashedFile),
|
|
}
|
|
}
|