func (r *Repo) MakePool() error {
	poolRoot := filepath.Join(r.OutRoot, "pool")
	if err := os.MkdirAll(poolRoot, 0755); err != nil {
		return fmt.Errorf("error making pool dir: %v", err)
	}
	for _, dist := range r.Dists {
		for compName, comp := range dist {
			compRoot := filepath.Join(poolRoot, compName)
			if err := os.MkdirAll(compRoot, 0755); err != nil {
				return fmt.Errorf("error making component dir: %v", err)
			}
			for _, d := range comp {
				pkgName := d.Control.MustGet("Package")
				pkgArch := d.Control.MustGet("Architecture")
				pkgVer := d.Control.MustGet("Version")
				letterRoot := filepath.Join(compRoot, getLetter(pkgName))
				if err := os.MkdirAll(letterRoot, 0755); err != nil {
					return fmt.Errorf("error making letter dir: %v", err)
				}
				pkgRoot := filepath.Join(letterRoot, pkgName)
				if err := os.MkdirAll(pkgRoot, 0755); err != nil {
					return fmt.Errorf("error making pkg dir: %v", err)
				}
				pkgFName := filepath.Join(pkgRoot, fmt.Sprintf("%s_%s_%s.deb", pkgName, pkgVer, pkgArch))
				if r.Symlink {
					_ = os.Remove(pkgFName)
					rp, err := filepath.Rel(filepath.Dir(pkgFName), d.Filename)
					if err != nil {
						rp = d.Filename
					}
					if err := os.Symlink(rp, pkgFName); err != nil {
						return fmt.Errorf("error creating package symlink: %v", err)
					}
					continue
				}
				f, err := os.Open(d.Filename)
				if err != nil {
					return fmt.Errorf("error opening package file '%s' for copying: %v", d.Filename, err)
				}
				of, err := os.Create(pkgFName)
				if err != nil {
					f.Close()
					return fmt.Errorf("error opening output package file '%s' for copying: %v", pkgFName, err)
				}
				if _, err = io.Copy(of, f); err != nil {
					of.Close()
					f.Close()
					return fmt.Errorf("error writing package file: %v", err)
				}
				of.Close()
				f.Close()
			}
		}
	}
	return nil
}
MakePool copies the deb files to the pool.
package main
import (
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"
	"golang.org/x/crypto/openpgp/armor"
	"golang.org/x/crypto/openpgp/clearsign"
	"golang.org/x/crypto/openpgp/packet"
	"github.com/ulikunitz/xz"
	"golang.org/x/crypto/openpgp"
)
type Repo struct {
	InRoot             string
	OutRoot            string
	Dists              map[string]map[string][]*Deb  
	GenerateContents   bool
	Symlink            bool
	MaintainerOverride string
	Origin             string
	Description        string
	SignEntity         *openpgp.Entity
}
func NewRepo(in, out string, generateContents bool, maintainerOverride, origin, description, signPrivateKeyAsc string) (*Repo, error) {
	var err error
	if in, err = filepath.Abs(in); err != nil {
		return nil, fmt.Errorf("error resolving in path: %v", err)
	}
	if out, err = filepath.Abs(out); err != nil {
		return nil, fmt.Errorf("error resolving out path: %v", err)
	}
	if _, err := os.Stat(out); err == nil {
		return nil, errors.New("out must not exist")
	}
	block, err := armor.Decode(strings.NewReader(signPrivateKeyAsc))
	if err != nil {
		return nil, fmt.Errorf("could not load private key: could not decode armor: %v", err)
	}
	if block.Type != openpgp.PrivateKeyType {
		return nil, errors.New("could not load private key: no private key in decoded block")
	}
	pr := packet.NewReader(block.Body)
	entity, err := openpgp.ReadEntity(pr)
	if err != nil {
		return nil, fmt.Errorf("could not load private key: could not read entity: %v", err)
	}
	return &Repo{
		InRoot:             in,
		OutRoot:            out,
		Dists:              map[string]map[string][]*Deb{},
		GenerateContents:   generateContents,
		Symlink:            false,
		MaintainerOverride: maintainerOverride,
		Origin:             origin,
		Description:        description,
		SignEntity:         entity,
	}, nil
}
func (r *Repo) Clean() {
	os.RemoveAll(r.OutRoot)
}
func (r *Repo) Scan() error {
	dists := map[string]map[string][]*Deb{}
	dfs, err := ioutil.ReadDir(r.InRoot)
	if err != nil {
		return fmt.Errorf("could not list in dir: %v", err)
	}
	for _, dfi := range dfs {
		if !dfi.IsDir() {
			return fmt.Errorf("could not scan in dir: not a dir: %s", filepath.Join(r.InRoot, dfi.Name()))
		}
		distName, distRoot := dfi.Name(), filepath.Join(r.InRoot, dfi.Name())
		dists[distName] = map[string][]*Deb{}
		if !validateName(distName) {
			return fmt.Errorf("invalid dist name '%s': must match [a-z-]", distName)
		}
		cfs, err := ioutil.ReadDir(distRoot)
		if err != nil {
			return fmt.Errorf("could not list in dir subdir: %v", err)
		}
		for _, cfi := range cfs {
			if !cfi.IsDir() {
				return fmt.Errorf("could not scan in dir: not a dir: %s", filepath.Join(r.InRoot, dfi.Name(), cfi.Name()))
			}
			compName, compRoot := cfi.Name(), filepath.Join(distRoot, cfi.Name())
			dists[distName][compName] = []*Deb{}
			if !validateName(compName) {
				return fmt.Errorf("invalid component name '%s': must match [a-z-]", compName)
			}
			pfs, err := ioutil.ReadDir(compRoot)
			if err != nil {
				return fmt.Errorf("could not list in dir subdir: %v", err)
			}
			for _, pfi := range pfs {
				if pfi.IsDir() || filepath.Ext(pfi.Name()) != ".deb" {
					return fmt.Errorf("could not scan in dir: not a deb file: %s", filepath.Join(r.InRoot, dfi.Name(), cfi.Name(), pfi.Name()))
				}
				pkgFname := filepath.Join(compRoot, pfi.Name())
				d, err := NewDeb(pkgFname, r.GenerateContents)
				if err != nil {
					return fmt.Errorf("could not read deb '%s': %v", pkgFname, err)
				}
				dists[distName][compName] = append(dists[distName][compName], d)
			}
		}
	}
	r.Dists = dists
	return nil
}