Add public bundle manifest package

This commit is contained in:
2026-06-01 20:52:23 +00:00
parent e51bc28b05
commit bb68cb6602
17 changed files with 941 additions and 181 deletions

118
pkg/bundle/build.go Normal file
View File

@@ -0,0 +1,118 @@
package bundle
import (
"fmt"
"os"
"path"
"path/filepath"
"sort"
"time"
)
func BuildManifest(opts BuildOptions) (Manifest, error) {
if opts.Root == "" {
return Manifest{}, fmt.Errorf("root is required")
}
if opts.ID == "" {
return Manifest{}, fmt.Errorf("id is required")
}
explicit := len(opts.Files) > 0
if explicit == opts.Scan {
return Manifest{}, fmt.Errorf("select exactly one file mode")
}
created := opts.Created
if created.IsZero() {
created = time.Now().UTC()
}
paths := append([]string(nil), opts.Files...)
var err error
if opts.Scan {
paths, err = scanSourcePaths(opts.Root)
if err != nil {
return Manifest{}, err
}
}
files := make([]ManifestFile, 0, len(paths))
for _, sourcePath := range paths {
file, err := buildManifestFile(opts.Root, sourcePath)
if err != nil {
return Manifest{}, err
}
files = append(files, file)
}
manifest := Manifest{
SchemaVersion: SchemaVersion,
ID: opts.ID,
Created: created,
Files: files,
}
manifest.Digest = BundleDigest(manifest.Files)
if err := ValidateManifest(manifest); err != nil {
return Manifest{}, err
}
return manifest, nil
}
func scanSourcePaths(root string) ([]string, error) {
var paths []string
err := filepath.WalkDir(root, func(filePath string, entry os.DirEntry, err error) error {
if err != nil {
return err
}
if filePath == root {
return nil
}
relative, err := filepath.Rel(root, filePath)
if err != nil {
return err
}
sourcePath := filepath.ToSlash(relative)
if entry.Type()&os.ModeSymlink != 0 {
return fmt.Errorf("source file %q must be a regular file", sourcePath)
}
if entry.IsDir() {
return nil
}
if path.Base(sourcePath) == ManifestName || path.Base(sourcePath) == distributorStateName {
return nil
}
info, err := entry.Info()
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return fmt.Errorf("source file %q must be a regular file", sourcePath)
}
paths = append(paths, sourcePath)
return nil
})
if err != nil {
return nil, err
}
sort.Strings(paths)
return paths, nil
}
func buildManifestFile(root, sourcePath string) (ManifestFile, error) {
if err := ValidateSourcePath(sourcePath); err != nil {
return ManifestFile{}, fmt.Errorf("source file %q: %w", sourcePath, err)
}
fullPath := filepath.Join(root, filepath.FromSlash(sourcePath))
info, err := os.Lstat(fullPath)
if err != nil {
return ManifestFile{}, fmt.Errorf("source file %q stat: %w", sourcePath, err)
}
if !info.Mode().IsRegular() {
return ManifestFile{}, fmt.Errorf("source file %q must be a regular file", sourcePath)
}
data, err := os.ReadFile(fullPath)
if err != nil {
return ManifestFile{}, fmt.Errorf("source file %q read: %w", sourcePath, err)
}
return ManifestFile{
Path: sourcePath,
SHA256: FileDigest(data),
Size: int64(len(data)),
}, nil
}