44 "context"
55 "fmt"
66 "io"
7+ "os"
78
9+ "github.com/containerd/containerd/platforms"
810 "github.com/distribution/reference"
911 "github.com/docker/cli/cli"
1012 "github.com/docker/cli/cli/command"
@@ -14,6 +16,7 @@ import (
1416 registrytypes "github.com/docker/docker/api/types/registry"
1517 "github.com/docker/docker/pkg/jsonmessage"
1618 "github.com/docker/docker/registry"
19+ "github.com/moby/term"
1720 "github.com/pkg/errors"
1821 "github.com/spf13/cobra"
1922)
@@ -23,6 +26,7 @@ type pushOptions struct {
2326 remote string
2427 untrusted bool
2528 quiet bool
29+ platform string
2630}
2731
2832// NewPushCommand creates a new `docker push` command
@@ -48,12 +52,30 @@ func NewPushCommand(dockerCli command.Cli) *cobra.Command {
4852 flags .BoolVarP (& opts .all , "all-tags" , "a" , false , "Push all tags of an image to the repository" )
4953 flags .BoolVarP (& opts .quiet , "quiet" , "q" , false , "Suppress verbose output" )
5054 command .AddTrustSigningFlags (flags , & opts .untrusted , dockerCli .ContentTrustEnabled ())
55+ flags .StringVar (& opts .platform , "platform" , os .Getenv ("DOCKER_DEFAULT_PLATFORM" ),
56+ `Push a platform-specific manifest as a single-platform image to the registry.
57+ 'local': Platform the cli is running on
58+ 'remote': Platform the daemon is running on
59+ 'os[/arch[/variant]]': Explicit platform (eg. linux/amd64)` )
60+ flags .SetAnnotation ("platform" , "version" , []string {"1.45" })
5161
5262 return cmd
5363}
5464
5565// RunPush performs a push against the engine based on the specified options
5666func RunPush (ctx context.Context , dockerCli command.Cli , opts pushOptions ) error {
67+ if opts .platform != "" {
68+ if _ , isTty := term .GetFdInfo (dockerCli .Err ()); isTty {
69+ _ , _ = fmt .Fprint (dockerCli .Err (), "\x1b [1;37m\x1b [1;46m[ NOTE ]\x1b [0m\x1b [0m " )
70+ } else {
71+ _ , _ = fmt .Fprint (dockerCli .Err (), "[ NOTE ] " )
72+ }
73+ _ , _ = fmt .Fprintln (dockerCli .Err (), `Selecting a single platform for the push operation will push the image manifest for that platform only.
74+ This won't push the image index/manifest list which means that other components like Buildkit attestations won't be pushed.
75+ If you want to only push a single platform while preserving the attestations, please build an image with only that platform and push it instead.` )
76+ _ , _ = fmt .Fprintln (dockerCli .Err (), "" ) // Add a newline after the note, separate call to please linter.
77+ }
78+
5779 ref , err := reference .ParseNormalizedNamed (opts .remote )
5880 switch {
5981 case err != nil :
@@ -84,6 +106,7 @@ func RunPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
84106 All : opts .all ,
85107 RegistryAuth : encodedAuth ,
86108 PrivilegeFunc : requestPrivilege ,
109+ Platform : parsePlatform (opts .platform ),
87110 }
88111
89112 responseBody , err := dockerCli .Client ().ImagePush (ctx , reference .FamiliarString (ref ), options )
@@ -106,3 +129,15 @@ func RunPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
106129 }
107130 return jsonmessage .DisplayJSONMessagesToStream (responseBody , dockerCli .Out (), nil )
108131}
132+
133+ func parsePlatform (platform string ) string {
134+ switch platform {
135+ case "local" :
136+ return platforms .DefaultString ()
137+ case "remote" :
138+ // Handled on the server side
139+ return platform
140+ }
141+
142+ return platform
143+ }
0 commit comments