Cobra allows for aliases to be defined for a command, but only allows these
to be defined at the same level (for example, `docker image ls` as alias for
`docker image list`). Our CLI has some commands that are available both as a
top-level shorthand as well as `docker <object> <verb>` subcommands. For example,
`docker ps` is a shorthand for `docker container ps` / `docker container ls`.
This patch introduces a custom "aliases" annotation that can be used to print
all available aliases for a command. While this requires these aliases to be
defined manually, in practice the list of aliases rarely changes, so maintenance
should be minimal.
As a convention, we could consider the first command in this list to be the
canonical command, so that we can use this information to add redirects in
our documentation in future.
Before this patch:
docker images --help
Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]
List images
Options:
-a, --all Show all images (default hides intermediate images)
...
With this patch:
docker images --help
Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]
List images
Aliases:
docker image ls, docker image list, docker images
Options:
-a, --all Show all images (default hides intermediate images)
...
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
110 lines
3.1 KiB
Go
110 lines
3.1 KiB
Go
package image
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/docker/cli/cli"
|
|
"github.com/docker/cli/cli/command"
|
|
"github.com/docker/cli/cli/command/completion"
|
|
"github.com/docker/cli/cli/streams"
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/pkg/jsonmessage"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
type pushOptions struct {
|
|
all bool
|
|
remote string
|
|
untrusted bool
|
|
quiet bool
|
|
}
|
|
|
|
// NewPushCommand creates a new `docker push` command
|
|
func NewPushCommand(dockerCli command.Cli) *cobra.Command {
|
|
var opts pushOptions
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "push [OPTIONS] NAME[:TAG]",
|
|
Short: "Upload an image to a registry",
|
|
Args: cli.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
opts.remote = args[0]
|
|
return RunPush(dockerCli, opts)
|
|
},
|
|
Annotations: map[string]string{
|
|
"category-top": "6",
|
|
"aliases": "docker image push, docker push",
|
|
},
|
|
ValidArgsFunction: completion.ImageNames(dockerCli),
|
|
}
|
|
|
|
flags := cmd.Flags()
|
|
flags.BoolVarP(&opts.all, "all-tags", "a", false, "Push all tags of an image to the repository")
|
|
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress verbose output")
|
|
command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled())
|
|
|
|
return cmd
|
|
}
|
|
|
|
// RunPush performs a push against the engine based on the specified options
|
|
func RunPush(dockerCli command.Cli, opts pushOptions) error {
|
|
ref, err := reference.ParseNormalizedNamed(opts.remote)
|
|
switch {
|
|
case err != nil:
|
|
return err
|
|
case opts.all && !reference.IsNameOnly(ref):
|
|
return errors.New("tag can't be used with --all-tags/-a")
|
|
case !opts.all && reference.IsNameOnly(ref):
|
|
ref = reference.TagNameOnly(ref)
|
|
if tagged, ok := ref.(reference.Tagged); ok && !opts.quiet {
|
|
_, _ = fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", tagged.Tag())
|
|
}
|
|
}
|
|
|
|
// Resolve the Repository name from fqn to RepositoryInfo
|
|
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
// Resolve the Auth config relevant for this server
|
|
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
|
|
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "push")
|
|
options := types.ImagePushOptions{
|
|
All: opts.all,
|
|
RegistryAuth: encodedAuth,
|
|
PrivilegeFunc: requestPrivilege,
|
|
}
|
|
|
|
responseBody, err := dockerCli.Client().ImagePush(ctx, reference.FamiliarString(ref), options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer responseBody.Close()
|
|
if !opts.untrusted {
|
|
// TODO PushTrustedReference currently doesn't respect `--quiet`
|
|
return PushTrustedReference(dockerCli, repoInfo, ref, authConfig, responseBody)
|
|
}
|
|
|
|
if opts.quiet {
|
|
err = jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(io.Discard), nil)
|
|
if err == nil {
|
|
fmt.Fprintln(dockerCli.Out(), ref.String())
|
|
}
|
|
return err
|
|
}
|
|
return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil)
|
|
}
|