259 lines
4.8 KiB
Go
259 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"crypto/md5"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/blang/semver"
|
|
"github.com/spf13/afero"
|
|
"io"
|
|
"meow.tf/deb-simple/deb/archive"
|
|
"path"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
variableRegexp = regexp.MustCompile("(.*?):\\s*(.*)")
|
|
)
|
|
|
|
type PackageFile struct {
|
|
Path string `json:"path"`
|
|
Name string `json:"name"`
|
|
Size int64 `json:"size"`
|
|
MD5Hash string `json:"md5"`
|
|
SHA1Hash string `json:"sha1"`
|
|
SHA256Hash string `json:"sha256"`
|
|
ControlData string `json:"control"`
|
|
Info *Package `json:"info"`
|
|
}
|
|
|
|
type Package struct {
|
|
Package string `json:"package"`
|
|
Version string `json:"version"`
|
|
Architecture string `json:"architecture"`
|
|
}
|
|
|
|
func parsePackageData(ctlData string) *Package {
|
|
res := &Package{}
|
|
|
|
for _, match := range variableRegexp.FindAllStringSubmatch(ctlData, -1) {
|
|
switch match[1] {
|
|
case "Package":
|
|
res.Package = match[2]
|
|
case "Architecture":
|
|
res.Architecture = match[2]
|
|
case "Version":
|
|
res.Version = match[2]
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
type Distro struct {
|
|
Name string `json:"name"`
|
|
Architectures map[string]map[string]*PackageFile `json:"architectures"`
|
|
}
|
|
|
|
var (
|
|
distros = make(map[string]*Distro)
|
|
)
|
|
|
|
func loadCache(dist string) error {
|
|
f, err := fs.Open(path.Join(conf.DistPath(dist), "dist.json"))
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
var distro Distro
|
|
|
|
if err := json.NewDecoder(f).Decode(&distro); err != nil {
|
|
return err
|
|
}
|
|
|
|
distros[dist] = &distro
|
|
|
|
return nil
|
|
}
|
|
|
|
func saveCache(dist *Distro) error {
|
|
f, err := fs.Create(path.Join(conf.DistPath(dist.Name), "dist.json"))
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
if err := json.NewEncoder(f).Encode(dist); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func buildPackageList(config Conf, distro, arch string) (map[string]*PackageFile, error) {
|
|
m := make(map[string]*PackageFile)
|
|
|
|
if err := scanRecursive(config.PoolPath(distro, arch), m); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
func scanRecursive(base string, m map[string]*PackageFile) error {
|
|
dirList, err := afero.ReadDir(fs, base)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, file := range dirList {
|
|
if file.IsDir() {
|
|
if err := scanRecursive(path.Join(base, file.Name()), m); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
|
|
if !strings.HasSuffix(file.Name(), "deb") {
|
|
continue
|
|
}
|
|
|
|
p, err := newPackageFile(fs, path.Join(base, file.Name()))
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if old, exists := m[p.Info.Package]; exists {
|
|
v1, err := semver.Parse(old.Info.Version)
|
|
v2, err2 := semver.Parse(p.Info.Version)
|
|
|
|
if err == nil && err2 == nil && v1.Compare(v2) > 0 {
|
|
// Use old version
|
|
continue
|
|
}
|
|
}
|
|
|
|
m[p.Info.Package] = p
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func newPackageFile(sourceFs afero.Fs, filePath string) (*PackageFile, error) {
|
|
p := &PackageFile{
|
|
Name: path.Base(filePath),
|
|
Path: filePath,
|
|
}
|
|
|
|
var err error
|
|
|
|
stat, err := sourceFs.Stat(filePath)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.Size = stat.Size()
|
|
|
|
f, err := sourceFs.Open(filePath)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
p.ControlData, err = archive.InspectPackage(f)
|
|
|
|
p.Info = parsePackageData(p.ControlData)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if _, err = f.Seek(0, io.SeekStart); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var (
|
|
md5hash = md5.New()
|
|
sha1hash = sha1.New()
|
|
sha256hash = sha256.New()
|
|
)
|
|
|
|
w := io.MultiWriter(md5hash, sha1hash, sha256hash)
|
|
|
|
if _, err := io.Copy(w, f); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.MD5Hash = hex.EncodeToString(md5hash.Sum(nil))
|
|
p.SHA1Hash = hex.EncodeToString(sha1hash.Sum(nil))
|
|
p.SHA256Hash = hex.EncodeToString(sha256hash.Sum(nil))
|
|
|
|
return p, nil
|
|
}
|
|
|
|
func createPackagesCached(config Conf, distro, arch string, packages map[string]*PackageFile) error {
|
|
stdFile, err := fs.Create(path.Join(config.ArchPath(distro, arch), "Packages"))
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create packages: %s", err)
|
|
}
|
|
|
|
defer stdFile.Close()
|
|
|
|
stdOut := bufio.NewWriter(stdFile)
|
|
|
|
// loop through each directory
|
|
// run inspectPackage
|
|
|
|
for _, p := range packages {
|
|
var packBuf bytes.Buffer
|
|
|
|
packBuf.WriteString(p.ControlData)
|
|
|
|
dir := path.Join("pool/main", distro, arch, p.Info.Package[0:1], p.Info.Package, p.Name)
|
|
|
|
fmt.Fprintf(&packBuf, "Filename: %s\n", dir)
|
|
fmt.Fprintf(&packBuf, "Size: %d\n", p.Size)
|
|
|
|
fmt.Fprintf(&packBuf, "MD5sum: %s\n", p.MD5Hash)
|
|
fmt.Fprintf(&packBuf, "SHA1: %s\n", p.SHA1Hash)
|
|
fmt.Fprintf(&packBuf, "SHA256: %s\n", p.SHA256Hash)
|
|
packBuf.WriteString("\n\n")
|
|
|
|
stdOut.Write(packBuf.Bytes())
|
|
}
|
|
|
|
stdOut.Flush()
|
|
|
|
gzipFile, err := fs.Create(path.Join(config.ArchPath(distro, arch), "Packages.gz"))
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create packages.gz: %s", err)
|
|
}
|
|
|
|
defer gzipFile.Close()
|
|
|
|
stdFile.Seek(0, io.SeekStart)
|
|
|
|
if _, err = io.Copy(gzipFile, stdFile); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
} |