@@ -2,6 +2,7 @@ package image
22
33import (
44 "context"
5+ "encoding/json"
56 "fmt"
67 "io"
78 "os"
@@ -12,6 +13,7 @@ import (
1213 "github.com/docker/cli/cli/command"
1314 "github.com/docker/cli/cli/command/completion"
1415 "github.com/docker/cli/cli/streams"
16+ "github.com/docker/docker/api/types/auxprogress"
1517 "github.com/docker/docker/api/types/image"
1618 registrytypes "github.com/docker/docker/api/types/registry"
1719 "github.com/docker/docker/pkg/jsonmessage"
@@ -73,9 +75,8 @@ func RunPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
7375
7476 printNote (dockerCli , `Selecting a single platform will only push one matching image manifest from a multi-platform image index.
7577This means that any other components attached to the multi-platform image index (like Buildkit attestations) won't be pushed.
76- If you want to only push a single platform image while preserving the attestations, please build an image with only that platform and push it instead.
77- Example: echo "FROM %s" | docker build - --platform %s -t <NEW-TAG>
78- ` , opts .remote , opts .platform )
78+ If you want to only push a single platform image while preserving the attestations, please use 'docker convert\n'
79+ ` )
7980 }
8081
8182 ref , err := reference .ParseNormalizedNamed (opts .remote )
@@ -116,20 +117,71 @@ Example: echo "FROM %s" | docker build - --platform %s -t <NEW-TAG>
116117 return err
117118 }
118119
120+ defer func () {
121+ for _ , note := range notes {
122+ fmt .Fprintln (dockerCli .Err (), "" )
123+ printNote (dockerCli , note )
124+ }
125+ }()
126+
119127 defer responseBody .Close ()
120128 if ! opts .untrusted {
121129 // TODO PushTrustedReference currently doesn't respect `--quiet`
122130 return PushTrustedReference (dockerCli , repoInfo , ref , authConfig , responseBody )
123131 }
124132
125133 if opts .quiet {
126- err = jsonmessage .DisplayJSONMessagesToStream (responseBody , streams .NewOut (io .Discard ), nil )
134+ err = jsonmessage .DisplayJSONMessagesToStream (responseBody , streams .NewOut (io .Discard ), handleAux ( dockerCli ) )
127135 if err == nil {
128136 fmt .Fprintln (dockerCli .Out (), ref .String ())
129137 }
130138 return err
131139 }
132- return jsonmessage .DisplayJSONMessagesToStream (responseBody , dockerCli .Out (), nil )
140+ return jsonmessage .DisplayJSONMessagesToStream (responseBody , dockerCli .Out (), handleAux (dockerCli ))
141+ }
142+
143+ var notes []string
144+
145+ func handleAux (dockerCli command.Cli ) func (jm jsonmessage.JSONMessage ) {
146+ return func (jm jsonmessage.JSONMessage ) {
147+ b := []byte (* jm .Aux )
148+
149+ var stripped auxprogress.ManifestPushedInsteadOfIndex
150+ err := json .Unmarshal (b , & stripped )
151+ if err == nil {
152+ if stripped .ManifestPushedInsteadOfIndex {
153+ note := fmt .Sprintf ("Not all multiplatform-content is present and only the available single-platform image was pushed\n %s -> %s" ,
154+ red (stripped .OriginalIndex .Digest .String ()),
155+ green (stripped .SelectedManifest .Digest .String ()))
156+ notes = append (notes , note )
157+ }
158+ }
159+
160+ var missing auxprogress.ContentMissing
161+ err = json .Unmarshal (b , & missing )
162+ if missing .ContentMissing {
163+ note := `You're trying to push a manifest list/index which
164+ references multiple platform specific manifests, but not all of them are available locally
165+ or available to the remote repository.
166+
167+ Make sure you have all the referenced content and try again.
168+
169+ You can also push only a single platform specific manifest directly by specifying the platform you want to push with the --platform flag.`
170+ notes = append (notes , note )
171+ }
172+ }
173+ }
174+
175+ func red (text string ) string {
176+ return fmt .Sprintf ("\033 [31m%s\033 [0m" , text )
177+ }
178+
179+ func green (text string ) string {
180+ return fmt .Sprintf ("\033 [32m%s\033 [0m" , text )
181+ }
182+
183+ func bold (text string ) string {
184+ return fmt .Sprintf ("\033 [1m%s\033 [0m" , text )
133185}
134186
135187func printNote (dockerCli command.Cli , format string , args ... any ) {
@@ -138,5 +190,5 @@ func printNote(dockerCli command.Cli, format string, args ...any) {
138190 } else {
139191 _ , _ = fmt .Fprint (dockerCli .Err (), "[ NOTE ] " )
140192 }
141- _ , _ = fmt .Fprintf (dockerCli .Err (), format + " \n \n " , args ... )
193+ _ , _ = fmt .Fprintf (dockerCli .Err (), bold ( format ) + " \n " , args ... )
142194}
0 commit comments