func (p *PublishedRepo) GetSuite() string {
	if p.Suite == "" {
		return p.Distribution
	}
	return p.Suite
}
GetSuite returns default or manual Suite:
package deb
import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"
	"github.com/pborman/uuid"
	"github.com/ugorji/go/codec"
	"github.com/aptly-dev/aptly/aptly"
	"github.com/aptly-dev/aptly/database"
	"github.com/aptly-dev/aptly/pgp"
	"github.com/aptly-dev/aptly/utils"
)
type repoSourceItem struct {
	snapshot *Snapshot
	localRepo *LocalRepo
	packageRefs *PackageRefList
}
type PublishedRepo struct {
	UUID string
	Storage              string
	Prefix               string
	Distribution         string
	Origin               string
	NotAutomatic         string
	ButAutomaticUpgrades string
	Label                string
	Suite                string
	Architectures []string
	SourceKind string
	Sources map[string]string
	Component string
	SourceUUID string `codec:"SnapshotUUID"`
	sourceItems map[string]repoSourceItem
	SkipContents bool
	rePublishing bool
	AcquireByHash bool
}
func ParsePrefix(param string) (storage, prefix string) {
	i := strings.LastIndex(param, ":")
	if i != -1 {
		storage = param[:i]
		prefix = param[i+1:]
		if prefix == "" {
			prefix = "."
		}
	} else {
		prefix = param
	}
	prefix = strings.TrimPrefix(strings.TrimSuffix(prefix, "/"), "/")
	return
}
func walkUpTree(source interface{}, collectionFactory *CollectionFactory) (rootDistributions []string, rootComponents []string) {
	var (
		head    interface{}
		current = []interface{}{source}
	)
	rootComponents = []string{}
	rootDistributions = []string{}
	for len(current) > 0 {
		head, current = current[0], current[1:]
		if snapshot, ok := head.(*Snapshot); ok {
			for _, uuid := range snapshot.SourceIDs {
				if snapshot.SourceKind == SourceRemoteRepo {
					remoteRepo, err := collectionFactory.RemoteRepoCollection().ByUUID(uuid)
					if err != nil {
						continue
					}
					current = append(current, remoteRepo)
				} else if snapshot.SourceKind == SourceLocalRepo {
					localRepo, err := collectionFactory.LocalRepoCollection().ByUUID(uuid)
					if err != nil {
						continue
					}
					current = append(current, localRepo)
				} else if snapshot.SourceKind == SourceSnapshot {
					snap, err := collectionFactory.SnapshotCollection().ByUUID(uuid)
					if err != nil {
						continue
					}
					current = append(current, snap)
				}
			}
		} else if localRepo, ok := head.(*LocalRepo); ok {
			if localRepo.DefaultDistribution != "" {
				rootDistributions = append(rootDistributions, localRepo.DefaultDistribution)
			}
			if localRepo.DefaultComponent != "" {
				rootComponents = append(rootComponents, localRepo.DefaultComponent)
			}
		} else if remoteRepo, ok := head.(*RemoteRepo); ok {
			if remoteRepo.Distribution != "" {
				rootDistributions = append(rootDistributions, remoteRepo.Distribution)
			}
			rootComponents = append(rootComponents, remoteRepo.Components...)
		} else {
			panic("unknown type")
		}
	}
	return
}
func NewPublishedRepo(storage, prefix, distribution string, architectures []string,
	components []string, sources []interface{}, collectionFactory *CollectionFactory) (*PublishedRepo, error) {
	result := &PublishedRepo{
		UUID:          uuid.New(),
		Storage:       storage,
		Architectures: architectures,
		Sources:       make(map[string]string),
		sourceItems:   make(map[string]repoSourceItem),
	}
	if len(sources) == 0 {
		panic("publish with empty sources")
	}
	if len(sources) != len(components) {
		panic("sources and components should be equal in size")
	}
	var (
		discoveredDistributions = []string{}
		source                  interface{}
		component               string
		snapshot                *Snapshot
		localRepo               *LocalRepo
		fields                  = make(map[string][]string)
	)
	source = sources[0]
	switch source.(type) {
	case *Snapshot:
		result.SourceKind = SourceSnapshot
	case *LocalRepo:
		result.SourceKind = SourceLocalRepo
	default:
		panic("unknown source kind")
	}
	for i := range sources {
		component, source = components[i], sources[i]
		if distribution == "" || component == "" {
			rootDistributions, rootComponents := walkUpTree(source, collectionFactory)
			if distribution == "" {
				for i := range rootDistributions {
					rootDistributions[i] = strings.Replace(rootDistributions[i], "/", "-", -1)
				}
				discoveredDistributions = append(discoveredDistributions, rootDistributions...)
			}
			if component == "" {
				sort.Strings(rootComponents)
				if len(rootComponents) > 0 && rootComponents[0] == rootComponents[len(rootComponents)-1] {
					component = rootComponents[0]
				} else if len(sources) == 1 {
					component = "main"
				} else {
					return nil, fmt.Errorf("unable to figure out component name for %s", source)
				}
			}
		}
		_, exists := result.Sources[component]
		if exists {
			return nil, fmt.Errorf("duplicate component name: %s", component)
		}
		if result.SourceKind == SourceSnapshot {
			snapshot = source.(*Snapshot)
			result.Sources[component] = snapshot.UUID
			result.sourceItems[component] = repoSourceItem{snapshot: snapshot}
			if !utils.StrSliceHasItem(fields["Origin"], snapshot.Origin) {
				fields["Origin"] = append(fields["Origin"], snapshot.Origin)
			}
			if !utils.StrSliceHasItem(fields["NotAutomatic"], snapshot.NotAutomatic) {
				fields["NotAutomatic"] = append(fields["NotAutomatic"], snapshot.NotAutomatic)
			}
			if !utils.StrSliceHasItem(fields["ButAutomaticUpgrades"], snapshot.ButAutomaticUpgrades) {
				fields["ButAutomaticUpgrades"] = append(fields["ButAutomaticUpgrades"], snapshot.ButAutomaticUpgrades)
			}
		} else if result.SourceKind == SourceLocalRepo {
			localRepo = source.(*LocalRepo)
			result.Sources[component] = localRepo.UUID
			result.sourceItems[component] = repoSourceItem{localRepo: localRepo, packageRefs: localRepo.RefList()}
		}
	}
	prefix = filepath.Clean(prefix)
	prefix = strings.TrimPrefix(strings.TrimSuffix(prefix, "/"), "/")
	prefix = filepath.Clean(prefix)
	for _, part := range strings.Split(prefix, "/") {
		if part == ".." || part == "dists" || part == "pool" {
			return nil, fmt.Errorf("invalid prefix %s", prefix)
		}
	}
	result.Prefix = prefix
	if distribution == "" {
		sort.Strings(discoveredDistributions)
		if len(discoveredDistributions) > 0 && discoveredDistributions[0] == discoveredDistributions[len(discoveredDistributions)-1] {
			distribution = discoveredDistributions[0]
		} else {
			return nil, fmt.Errorf("unable to guess distribution name, please specify explicitly")
		}
	}
	if strings.Contains(distribution, "/") {
		return nil, fmt.Errorf("invalid distribution %s, '/' is not allowed", distribution)
	}
	result.Distribution = distribution
	if len(fields["Origin"]) == 1 {
		result.Origin = fields["Origin"][0]
	}
	if len(fields["NotAutomatic"]) == 1 {
		result.NotAutomatic = fields["NotAutomatic"][0]
	}
	if len(fields["ButAutomaticUpgrades"]) == 1 {
		result.ButAutomaticUpgrades = fields["ButAutomaticUpgrades"][0]
	}
	return result, nil
}
func (p *PublishedRepo) MarshalJSON() ([]byte, error) {
	type sourceInfo struct {
		Component, Name string
	}
	sources := []sourceInfo{}
	for component, item := range p.sourceItems {
		name := ""
		if item.snapshot != nil {
			name = item.snapshot.Name
		} else if item.localRepo != nil {
			name = item.localRepo.Name
		} else {
			panic("no snapshot/local repo")
		}
		sources = append(sources, sourceInfo{
			Component: component,
			Name:      name,
		})
	}
	return json.Marshal(map[string]interface{}{
		"Architectures":        p.Architectures,
		"Distribution":         p.Distribution,
		"Label":                p.Label,
		"Origin":               p.Origin,
		"Suite":                p.Suite,
		"NotAutomatic":         p.NotAutomatic,
		"ButAutomaticUpgrades": p.ButAutomaticUpgrades,
		"Prefix":               p.Prefix,
		"Path":                 p.GetPath(),
		"SourceKind":           p.SourceKind,
		"Sources":              sources,
		"Storage":              p.Storage,
		"SkipContents":         p.SkipContents,
		"AcquireByHash":        p.AcquireByHash,
	})
}
func (p *PublishedRepo) String() string {
	var sources = []string{}
	for _, component := range p.Components() {
		var source string
		item := p.sourceItems[component]
		if item.snapshot != nil {
			source = item.snapshot.String()
		} else if item.localRepo != nil {
			source = item.localRepo.String()
		} else {
			panic("no snapshot/localRepo")
		}
		sources = append(sources, fmt.Sprintf("{%s: %s}", component, source))
	}
	var extras []string
	var extra string
	if p.Origin != "" {
		extras = append(extras, fmt.Sprintf("origin: %s", p.Origin))
	}
	if p.NotAutomatic != "" {
		extras = append(extras, fmt.Sprintf("notautomatic: %s", p.NotAutomatic))
	}
	if p.ButAutomaticUpgrades != "" {
		extras = append(extras, fmt.Sprintf("butautomaticupgrades: %s", p.ButAutomaticUpgrades))
	}
	if p.Label != "" {
		extras = append(extras, fmt.Sprintf("label: %s", p.Label))
	}
	if p.Suite != "" {
		extras = append(extras, fmt.Sprintf("suite: %s", p.Suite))
	}
	extra = strings.Join(extras, ", ")
	if extra != "" {
		extra = " (" + extra + ")"
	}
	return fmt.Sprintf("%s/%s%s [%s] publishes %s", p.StoragePrefix(), p.Distribution, extra, strings.Join(p.Architectures, ", "),
		strings.Join(sources, ", "))
}
func (p *PublishedRepo) StoragePrefix() string {
	result := p.Prefix
	if p.Storage != "" {
		result = p.Storage + ":" + p.Prefix
	}
	return result
}
func (p *PublishedRepo) Key() []byte {
	return []byte("U" + p.StoragePrefix() + ">>" + p.Distribution)
}
func (p *PublishedRepo) RefKey(component string) []byte {
	return []byte("E" + p.UUID + component)
}
func (p *PublishedRepo) RefList(component string) *PackageRefList {
	item := p.sourceItems[component]
	if p.SourceKind == SourceLocalRepo {
		return item.packageRefs
	}
	if p.SourceKind == SourceSnapshot {
		return item.snapshot.RefList()
	}
	panic("unknown source")
}
func (p *PublishedRepo) Components() []string {
	result := make([]string, 0, len(p.Sources))
	for component := range p.Sources {
		result = append(result, component)
	}
	sort.Strings(result)
	return result
}
func (p *PublishedRepo) UpdateLocalRepo(component string) {
	if p.SourceKind != SourceLocalRepo {
		panic("not local repo publish")
	}
	item := p.sourceItems[component]
	item.packageRefs = item.localRepo.RefList()
	p.sourceItems[component] = item
	p.rePublishing = true
}
func (p *PublishedRepo) UpdateSnapshot(component string, snapshot *Snapshot) {
	if p.SourceKind != SourceSnapshot {
		panic("not snapshot publish")
	}
	item := p.sourceItems[component]
	item.snapshot = snapshot
	p.sourceItems[component] = item
	p.Sources[component] = snapshot.UUID
	p.rePublishing = true
}
func (p *PublishedRepo) Encode() []byte {
	var buf bytes.Buffer
	encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
	encoder.Encode(p)
	return buf.Bytes()
}
func (p *PublishedRepo) Decode(input []byte) error {
	decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
	err := decoder.Decode(p)
	if err != nil {
		return err
	}
	if p.SourceKind == "" {
		p.SourceKind = SourceSnapshot
	}
	if p.Component != "" && p.SourceUUID != "" && len(p.Sources) == 0 {
		p.Sources = map[string]string{p.Component: p.SourceUUID}
		p.Component = ""
		p.SourceUUID = ""
	}
	return nil
}
func (p *PublishedRepo) GetOrigin() string {
	if p.Origin == "" {
		return p.Prefix + " " + p.Distribution
	}
	return p.Origin
}
func (p *PublishedRepo) GetLabel() string {
	if p.Label == "" {
		return p.Prefix + " " + p.Distribution
	}
	return p.Label
}
func (p *PublishedRepo) GetPath() string {
	prefix := p.StoragePrefix()
	if prefix == "" {
		return p.Distribution
	}
	return fmt.Sprintf("%s/%s", prefix, p.Distribution)
}