129 lines
3.6 KiB
Go
129 lines
3.6 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
producerbundle "gitea.maximumdirect.net/eric/distributor/pkg/bundle"
|
|
)
|
|
|
|
type ManifestCreateOptions struct {
|
|
Root string
|
|
ID string
|
|
Created string
|
|
Files []string
|
|
Overwrite bool
|
|
Stdout io.Writer
|
|
OutputFormat OutputFormat
|
|
}
|
|
|
|
func ManifestCreate(ctx context.Context, options ManifestCreateOptions) error {
|
|
if err := ValidateOutputFormat(options.OutputFormat); err != nil {
|
|
return err
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
if options.Root == "" {
|
|
return fmt.Errorf("manifest create command requires a bundle path")
|
|
}
|
|
if options.ID == "" {
|
|
return fmt.Errorf("manifest create command requires --id")
|
|
}
|
|
|
|
created, err := parseOptionalCreated(options.Created)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
files := normalizeManifestFiles(options.Files)
|
|
buildOptions := producerbundle.BuildOptions{
|
|
Root: options.Root,
|
|
ID: options.ID,
|
|
Created: created,
|
|
Files: files,
|
|
Scan: len(files) == 0,
|
|
}
|
|
manifest, err := producerbundle.BuildManifest(buildOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := producerbundle.WriteManifest(options.Root, manifest, producerbundle.WriteManifestOptions{Overwrite: options.Overwrite}); err != nil {
|
|
return err
|
|
}
|
|
loaded, err := producerbundle.LoadManifest(options.Root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := producerbundle.ValidateBundle(options.Root, loaded); err != nil {
|
|
return err
|
|
}
|
|
|
|
result := manifestCreateResultFromManifest(options.Root, loaded)
|
|
if IsJSONOutput(options.OutputFormat) {
|
|
return WriteJSONEnvelope(options.Stdout, "manifest create", true, nil, result, nil)
|
|
}
|
|
if options.Stdout != nil {
|
|
_, err = fmt.Fprintf(options.Stdout, "created %s\nbundle: %s\nfiles: %d\ndigest: %s\n", producerbundle.ManifestName, result.ID, result.FileCount, result.Digest)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func parseOptionalCreated(value string) (time.Time, error) {
|
|
if value == "" {
|
|
return time.Time{}, nil
|
|
}
|
|
created, err := time.Parse(time.RFC3339, value)
|
|
if err != nil {
|
|
return time.Time{}, fmt.Errorf("created must be RFC3339: %w", err)
|
|
}
|
|
return created, nil
|
|
}
|
|
|
|
func normalizeManifestFiles(files []string) []string {
|
|
normalized := make([]string, 0, len(files))
|
|
for _, file := range files {
|
|
normalized = append(normalized, filepath.ToSlash(filepath.Clean(strings.ReplaceAll(file, "\\", string(filepath.Separator)))))
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
type manifestCreateResult struct {
|
|
ManifestPath string `json:"manifest_path"`
|
|
Root string `json:"root"`
|
|
ID string `json:"id"`
|
|
Created string `json:"created"`
|
|
Digest string `json:"digest"`
|
|
FileCount int `json:"file_count"`
|
|
Files []manifestCreateFileResult `json:"files"`
|
|
}
|
|
|
|
type manifestCreateFileResult struct {
|
|
Path string `json:"path"`
|
|
SHA256 string `json:"sha256"`
|
|
Size int64 `json:"size"`
|
|
}
|
|
|
|
func manifestCreateResultFromManifest(root string, manifest producerbundle.Manifest) manifestCreateResult {
|
|
result := manifestCreateResult{
|
|
ManifestPath: filepath.ToSlash(filepath.Join(root, producerbundle.ManifestName)),
|
|
Root: filepath.ToSlash(root),
|
|
ID: manifest.ID,
|
|
Created: manifest.Created.Format(time.RFC3339),
|
|
Digest: manifest.Digest,
|
|
FileCount: len(manifest.Files),
|
|
Files: make([]manifestCreateFileResult, 0, len(manifest.Files)),
|
|
}
|
|
for _, file := range manifest.Files {
|
|
result.Files = append(result.Files, manifestCreateFileResult{
|
|
Path: file.Path,
|
|
SHA256: file.SHA256,
|
|
Size: file.Size,
|
|
})
|
|
}
|
|
return result
|
|
}
|