Files
distributor/internal/bundle/discover.go

83 lines
1.8 KiB
Go

package bundle
import (
"context"
"fmt"
"path"
"sort"
"strings"
"gitea.maximumdirect.net/eric/distributor/internal/storage"
)
func Discover(ctx context.Context, backend storage.Backend, sourceRoot string) ([]Bundle, error) {
if err := storage.ValidatePrefix(sourceRoot); err != nil {
return nil, err
}
entries, err := storage.List(ctx, backend, sourceRoot, storage.WalkOptions{Recursive: true})
if err != nil {
return nil, err
}
var roots []string
for _, entry := range entries {
if entry.Type == storage.EntryTypeDirectory {
continue
}
if path.Base(entry.Path) == ManifestName {
roots = append(roots, path.Dir(entry.Path))
}
}
for index, root := range roots {
if root == "." {
roots[index] = ""
}
}
sort.Strings(roots)
if len(roots) == 0 {
return nil, fmt.Errorf("no bundles found under %q", displayRoot(sourceRoot))
}
if err := rejectNestedRoots(roots); err != nil {
return nil, err
}
bundles := make([]Bundle, 0, len(roots))
for _, root := range roots {
relativeRoot := relativeToSource(sourceRoot, root)
sourceBundle, err := validateAt(ctx, backend, root, relativeRoot)
if err != nil {
return nil, err
}
bundles = append(bundles, sourceBundle)
}
return bundles, nil
}
func rejectNestedRoots(roots []string) error {
for index, root := range roots {
for _, candidate := range roots[index+1:] {
if isAncestor(root, candidate) {
return fmt.Errorf("nested manifest %q under bundle %q", displayRoot(candidate), displayRoot(root))
}
}
}
return nil
}
func isAncestor(root, candidate string) bool {
if root == "" {
return candidate != ""
}
return strings.HasPrefix(candidate, root+"/")
}
func relativeToSource(sourceRoot, bundleRoot string) string {
if sourceRoot == "" {
return bundleRoot
}
if bundleRoot == sourceRoot {
return ""
}
return strings.TrimPrefix(bundleRoot, sourceRoot+"/")
}