diff --git a/cmd/oras/internal/display/print.go b/cmd/oras/internal/display/print.go index 6bbbb757d..48374a327 100644 --- a/cmd/oras/internal/display/print.go +++ b/cmd/oras/internal/display/print.go @@ -25,7 +25,6 @@ import ( "oras.land/oras-go/v2" "oras.land/oras-go/v2/content" "oras.land/oras-go/v2/registry" - "oras.land/oras/cmd/oras/internal/display/track" ) var printLock sync.Mutex @@ -58,22 +57,6 @@ func PrintStatus(desc ocispec.Descriptor, status string, verbose bool) error { return Print(status, ShortDigest(desc), name) } -// PrintStatusWithoutTrackable prints transfer status if trackable is no set. -func PrintStatusWithoutTrackable(desc ocispec.Descriptor, status string, verbose bool, trackable track.Trackable) error { - if trackable != nil { - return nil - } - return PrintStatus(desc, status, verbose) -} - -// PrintStatusWithTrackable prints transfer status if trackable is set. -func PrintStatusWithTrackable(desc ocispec.Descriptor, status string, verbose bool, trackable track.Trackable) error { - if trackable == nil { - return PrintStatus(desc, status, verbose) - } - return trackable.Prompt(desc, status) -} - // PrintSuccessorStatus prints transfer status of successors. func PrintSuccessorStatus(ctx context.Context, desc ocispec.Descriptor, status string, fetcher content.Fetcher, committed *sync.Map, verbose bool) error { successors, err := content.Successors(ctx, fetcher, desc) diff --git a/cmd/oras/internal/display/track/target.go b/cmd/oras/internal/display/track/target.go index ca7805c2e..f3d3be6f3 100644 --- a/cmd/oras/internal/display/track/target.go +++ b/cmd/oras/internal/display/track/target.go @@ -26,35 +26,25 @@ import ( "oras.land/oras-go/v2" "oras.land/oras-go/v2/content" "oras.land/oras-go/v2/registry" + "oras.land/oras/cmd/oras/internal/display" "oras.land/oras/cmd/oras/internal/display/progress" ) // Trackable can be tracked and supprots explicit prompting and stoping. -type Trackable interface { - Prompt(desc ocispec.Descriptor, prompt string) error - Close() error -} - -// Target is a wrapper for oras.Target with tracked pushing. -type Target interface { - oras.GraphTarget - Trackable -} - -type target struct { +type Target struct { oras.Target manager progress.Manager actionPrompt string donePrompt string } -func NewTarget(t oras.Target, actionPrompt, donePrompt string, tty *os.File) (Target, error) { +func NewTarget(t oras.Target, actionPrompt, donePrompt string, tty *os.File) (*Target, error) { manager, err := progress.NewManager(tty) if err != nil { return nil, err } - return &target{ + return &Target{ Target: t, manager: manager, actionPrompt: actionPrompt, @@ -62,7 +52,7 @@ func NewTarget(t oras.Target, actionPrompt, donePrompt string, tty *os.File) (Ta }, nil } -func (t *target) Push(ctx context.Context, expected ocispec.Descriptor, content io.Reader) error { +func (t *Target) Push(ctx context.Context, expected ocispec.Descriptor, content io.Reader) error { r, err := managedReader(content, expected, t.manager, t.actionPrompt, t.donePrompt) if err != nil { return err @@ -76,7 +66,7 @@ func (t *target) Push(ctx context.Context, expected ocispec.Descriptor, content return nil } -func (t *target) PushReference(ctx context.Context, expected ocispec.Descriptor, content io.Reader, reference string) error { +func (t *Target) PushReference(ctx context.Context, expected ocispec.Descriptor, content io.Reader, reference string) error { r, err := managedReader(content, expected, t.manager, t.actionPrompt, t.donePrompt) if err != nil { return err @@ -98,21 +88,25 @@ func (t *target) PushReference(ctx context.Context, expected ocispec.Descriptor, return nil } -func (t *target) Predecessors(ctx context.Context, node ocispec.Descriptor) ([]ocispec.Descriptor, error) { +func (t *Target) Predecessors(ctx context.Context, node ocispec.Descriptor) ([]ocispec.Descriptor, error) { if p, ok := t.Target.(content.PredecessorFinder); ok { return p.Predecessors(ctx, node) } - return nil, fmt.Errorf("target %v does not support Predecessors", reflect.TypeOf(t.Target)) + return nil, fmt.Errorf("Target %v does not support Predecessors", reflect.TypeOf(t.Target)) } -func (t *target) Close() error { - if err := t.manager.Close(); err != nil { - return err - } - return nil +// Close closes the Target to stop tracking. +func (t *Target) Close() error { + return t.manager.Close() } -func (t *target) Prompt(desc ocispec.Descriptor, prompt string) error { +// Prompt prompts the user with the provided prompt and descriptor. +// If Target is not set, only prints status. +func (t *Target) Prompt(desc ocispec.Descriptor, prompt string, verbose bool) error { + if t == nil { + display.PrintStatus(desc, prompt, verbose) + return nil + } status, err := t.manager.Add() if err != nil { return err diff --git a/cmd/oras/root/cp.go b/cmd/oras/root/cp.go index 19bbd589f..2f4fafed0 100644 --- a/cmd/oras/root/cp.go +++ b/cmd/oras/root/cp.go @@ -143,17 +143,8 @@ func runCopy(ctx context.Context, opts copyOptions) error { } func doCopy(ctx context.Context, src option.ReadOnlyGraphTagFinderTarget, dst oras.GraphTarget, opts copyOptions) (ocispec.Descriptor, error) { - var trackable track.Trackable - if opts.TTY != nil { - target, err := track.NewTarget(dst, "Copying ", "Copied ", opts.TTY) - if err != nil { - return ocispec.Descriptor{}, err - } - defer target.Close() - trackable = target - dst = target - } - + var tracked *track.Target + var err error // Prepare copy options committed := &sync.Map{} extendedCopyOptions := oras.DefaultExtendedCopyOptions @@ -161,24 +152,35 @@ func doCopy(ctx context.Context, src option.ReadOnlyGraphTagFinderTarget, dst or extendedCopyOptions.FindPredecessors = func(ctx context.Context, src content.ReadOnlyGraphStorage, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { return graph.Referrers(ctx, src, desc, "") } - extendedCopyOptions.PreCopy = func(ctx context.Context, desc ocispec.Descriptor) error { - return display.PrintStatusWithoutTrackable(desc, "Copying", opts.Verbose, trackable) - } extendedCopyOptions.PostCopy = func(ctx context.Context, desc ocispec.Descriptor) error { committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) - var err error - if err = display.PrintSuccessorStatus(ctx, desc, "Skipped", dst, committed, opts.Verbose); err != nil { - return err - } - return display.PrintStatusWithoutTrackable(desc, "Copied ", opts.Verbose, trackable) + return display.PrintSuccessorStatus(ctx, desc, "Skipped", dst, committed, opts.Verbose) } extendedCopyOptions.OnCopySkipped = func(ctx context.Context, desc ocispec.Descriptor) error { committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) - return display.PrintStatusWithTrackable(desc, "Exists ", opts.Verbose, trackable) + return tracked.Prompt(desc, "Exists ", opts.Verbose) + } + if opts.TTY != nil { + tracked, err = track.NewTarget(dst, "Copying ", "Copied ", opts.TTY) + if err != nil { + return ocispec.Descriptor{}, err + } + defer tracked.Close() + dst = tracked + } else { + extendedCopyOptions.PreCopy = func(ctx context.Context, desc ocispec.Descriptor) error { + return display.PrintStatus(desc, "Copying", opts.Verbose) + } + postCopy := extendedCopyOptions.PostCopy + extendedCopyOptions.PostCopy = func(ctx context.Context, desc ocispec.Descriptor) error { + if err := postCopy(ctx, desc); err != nil { + return err + } + return display.PrintStatus(desc, "Copied ", opts.Verbose) + } } var desc ocispec.Descriptor - var err error rOpts := oras.DefaultResolveOptions rOpts.TargetPlatform = opts.Platform.Platform if opts.recursive {