Skip to content

Commit 1f183ab

Browse files
committed
feat(push): add Checking/Pushing two-phase progress display
- Remove prompt parameter from pushIfNotExist, phases now hardcoded - Phase 1: show "Checking blob" with nil reader during dst.Exists() - Phase 2: reset to "Pushing blob" with content reader for upload - Add pb.Abort on Exists() and PullBlob() error paths - Manifest/config pushes unchanged (small payloads, no phases needed) Signed-off-by: Zhao Chen <zhaochen.zju@gmail.com> Signed-off-by: Zhao Chen <winters.zc@antgroup.com>
1 parent 8f6212d commit 1f183ab

1 file changed

Lines changed: 16 additions & 7 deletions

File tree

pkg/backend/push.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func (b *backend) Push(ctx context.Context, target string, cfg *config.Push) err
9191

9292
return retry.Do(func() error {
9393
logrus.Debugf("push: processing layer %s", layer.Digest)
94-
if err := pushIfNotExist(gctx, pb, internalpb.NormalizePrompt("Copying blob"), src, dst, layer, repo, tag); err != nil {
94+
if err := pushIfNotExist(gctx, pb, src, dst, layer, repo, tag); err != nil {
9595
return err
9696
}
9797
logrus.Debugf("push: successfully processed layer %s", layer.Digest)
@@ -106,14 +106,14 @@ func (b *backend) Push(ctx context.Context, target string, cfg *config.Push) err
106106

107107
// copy the config.
108108
if err := retry.Do(func() error {
109-
return pushIfNotExist(ctx, pb, internalpb.NormalizePrompt("Copying config"), src, dst, manifest.Config, repo, tag)
109+
return pushIfNotExist(ctx, pb, src, dst, manifest.Config, repo, tag)
110110
}, append(defaultRetryOpts, retry.Context(ctx))...); err != nil {
111111
return fmt.Errorf("failed to push config to remote: %w", err)
112112
}
113113

114114
// copy the manifest.
115115
if err := retry.Do(func() error {
116-
return pushIfNotExist(ctx, pb, internalpb.NormalizePrompt("Copying manifest"), src, dst, ocispec.Descriptor{
116+
return pushIfNotExist(ctx, pb, src, dst, ocispec.Descriptor{
117117
MediaType: manifest.MediaType,
118118
Size: int64(len(manifestRaw)),
119119
Digest: godigest.FromBytes(manifestRaw),
@@ -128,15 +128,22 @@ func (b *backend) Push(ctx context.Context, target string, cfg *config.Push) err
128128
}
129129

130130
// pushIfNotExist copies the content from the src storage to the dst storage if the content does not exist.
131-
func pushIfNotExist(ctx context.Context, pb *internalpb.ProgressBar, prompt string, src storage.Storage, dst *remote.Repository, desc ocispec.Descriptor, repo, tag string) error {
131+
func pushIfNotExist(ctx context.Context, pb *internalpb.ProgressBar, src storage.Storage, dst *remote.Repository, desc ocispec.Descriptor, repo, tag string) error {
132+
// Phase 1: show "Checking" during existence check (blob layers only).
133+
// Bar is created with nil reader — shows 0 B / total at 0 speed to indicate waiting state.
134+
if desc.MediaType != ocispec.MediaTypeImageManifest {
135+
pb.Add(internalpb.NormalizePrompt("Checking blob"), desc.Digest.String(), desc.Size, nil)
136+
}
137+
132138
// check whether the content exists in the destination storage.
133139
exist, err := dst.Exists(ctx, desc)
134140
if err != nil {
141+
pb.Abort(desc.Digest.String(), err)
135142
return err
136143
}
137144

138145
if exist {
139-
pb.Add(prompt, desc.Digest.String(), desc.Size, bytes.NewReader([]byte{}))
146+
pb.Add(internalpb.NormalizePrompt("Skipped blob"), desc.Digest.String(), desc.Size, bytes.NewReader([]byte{}))
140147
// if the descriptor is the manifest, should check the tag existence as well.
141148
if desc.MediaType == ocispec.MediaTypeImageManifest {
142149
_, _, err := dst.FetchReference(ctx, tag)
@@ -157,7 +164,7 @@ func pushIfNotExist(ctx context.Context, pb *internalpb.ProgressBar, prompt stri
157164
// push the content to the destination, and wrap the content reader for progress bar,
158165
// manifest should use dst.Manifests().Push, others should use dst.Blobs().Push.
159166
if desc.MediaType == ocispec.MediaTypeImageManifest {
160-
reader := pb.Add(prompt, desc.Digest.String(), desc.Size, bytes.NewReader(desc.Data))
167+
reader := pb.Add(internalpb.NormalizePrompt("Copying manifest"), desc.Digest.String(), desc.Size, bytes.NewReader(desc.Data))
161168
if err := dst.Manifests().Push(ctx, desc, reader); err != nil {
162169
err = fmt.Errorf("failed to push manifest %s, err: %w", desc.Digest.String(), err)
163170
pb.Abort(desc.Digest.String(), err)
@@ -174,10 +181,12 @@ func pushIfNotExist(ctx context.Context, pb *internalpb.ProgressBar, prompt stri
174181
// fetch the content from the source storage.
175182
content, err := src.PullBlob(ctx, repo, desc.Digest.String())
176183
if err != nil {
184+
pb.Abort(desc.Digest.String(), err)
177185
return err
178186
}
179187

180-
reader := pb.Add(prompt, desc.Digest.String(), desc.Size, content)
188+
// Phase 2: reset to "Pushing" for actual upload.
189+
reader := pb.Reset(internalpb.NormalizePrompt("Pushing blob"), desc.Digest.String(), desc.Size, content)
181190
// resolve issue: https://github.com/modelpack/modctl/issues/50
182191
// wrap the content to the NopCloser, because the implementation of the distribution will
183192
// always return the error when Close() is called.

0 commit comments

Comments
 (0)