Compare commits

...

9 Commits

Author SHA1 Message Date
Alano Terblanche 95992cb0c8
Merge 5667a952c5 into abb8e9b78a 2024-10-21 19:03:12 +01:00
Sebastiaan van Stijn abb8e9b78a
Merge pull request #5546 from thaJeztah/hints_coverage
cli/hints: add tests
2024-10-21 18:08:28 +02:00
Laura Brehm 7029147458
Merge pull request #5557 from thaJeztah/minor_linting_issues 2024-10-21 17:00:40 +01:00
Sebastiaan van Stijn 6b9083776f
cli/command: AddPlatformFlag: suppress unhandled error
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-20 17:51:36 +02:00
Sebastiaan van Stijn fb61156b05
cli/command/registry: fix minor linting issues
- fix camelCase naming of verifyLoginOptions
- suppress unhandled errors that can be ignored

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-20 17:51:12 +02:00
Sebastiaan van Stijn 87acf77aef
cli/hints: add tests
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 00:48:16 +02:00
Alano Terblanche 5667a952c5
feat: use engine API instead of Hub directly
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
2024-10-18 14:42:47 +02:00
Alano Terblanche 5760a3d201
feat: run with hub completion
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
2024-10-11 10:27:41 +02:00
Alano Terblanche f4c164d9b8
feat: pull completion using hub
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
2024-10-11 09:57:32 +02:00
7 changed files with 175 additions and 10 deletions

View File

@ -12,6 +12,7 @@ import (
"github.com/docker/cli/cli/command/completion" "github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/opts" "github.com/docker/cli/opts"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/hub"
"github.com/moby/sys/signal" "github.com/moby/sys/signal"
"github.com/moby/term" "github.com/moby/term"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -37,13 +38,74 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
Short: "Create and run a new container from an image", Short: "Create and run a new container from an image",
Args: cli.RequiresMinArgs(1), Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
copts.Image = args[0] replacer := strings.NewReplacer("(local)", "", "(remote)", "")
copts.Image = replacer.Replace(args[0])
if len(args) > 1 { if len(args) > 1 {
copts.Args = args[1:] copts.Args = args[1:]
} }
return runRun(cmd.Context(), dockerCli, cmd.Flags(), &options, copts) return runRun(cmd.Context(), dockerCli, cmd.Flags(), &options, copts)
}, },
ValidArgsFunction: completion.ImageNames(dockerCli), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
unique := map[string]struct{}{}
localImages, shellComp := completion.ImageNames(dockerCli)(cmd, args, toComplete)
var all []string
if shellComp != cobra.ShellCompDirectiveError {
all = make([]string, 0, len(localImages))
for _, img := range localImages {
unique[img] = struct{}{}
all = append(all, img+"\tlocal")
}
}
if image, tag, found := strings.Cut(toComplete, ":"); found {
remoteTags, err := dockerCli.Client().ImageHubTags(cmd.Context(), image, hub.ImageOptions{
Name: tag,
Ordering: "last_updated",
Page: 0,
PageSize: 25,
})
if err == nil {
if len(all) == 0 {
all = make([]string, 0, len(remoteTags.Results))
}
for _, tag := range remoteTags.Results {
fullName := image + ":" + tag.Name
if _, ok := unique[fullName]; !ok {
all = append(all, fullName+"\tremote")
}
}
}
return all, cobra.ShellCompDirectiveKeepOrder | cobra.ShellCompDirectiveNoFileComp
}
remoteImages, err := dockerCli.Client().ImageHubSearch(cmd.Context(), toComplete, hub.SearchOptions{
From: 0,
Size: 25,
Type: hub.SearchTypeImage,
Order: hub.SearchOrderDesc,
Official: true,
Source: hub.SearchSourceStore,
OpenSource: true,
ExtensionReviewed: true,
})
if err == nil {
if len(all) == 0 {
all = make([]string, 0, len(remoteImages.Results))
}
for _, img := range remoteImages.Results {
if _, ok := unique[img.Name]; !ok {
all = append(all, img.Name+"\tremote")
}
}
}
return all, cobra.ShellCompDirectiveKeepOrder | cobra.ShellCompDirectiveNoFileComp
},
Annotations: map[string]string{ Annotations: map[string]string{
"category-top": "1", "category-top": "1",
"aliases": "docker container run, docker run", "aliases": "docker container run, docker run",

View File

@ -10,6 +10,7 @@ import (
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion" "github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/trust" "github.com/docker/cli/cli/trust"
"github.com/docker/docker/api/types/hub"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -39,7 +40,55 @@ func NewPullCommand(dockerCli command.Cli) *cobra.Command {
"category-top": "5", "category-top": "5",
"aliases": "docker image pull, docker pull", "aliases": "docker image pull, docker pull",
}, },
ValidArgsFunction: completion.NoComplete, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
if image, tag, found := strings.Cut(toComplete, ":"); found {
remoteTags, err := dockerCli.Client().ImageHubTags(cmd.Context(), image, hub.ImageOptions{
Name: tag,
Ordering: "last_updated",
Page: 0,
PageSize: 25,
})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
images := make([]string, 0, len(remoteTags.Results))
for _, tag := range remoteTags.Results {
fullName := image + ":" + tag.Name
images = append(images, fullName+"\t"+tag.LastUpdated.String())
}
return images, cobra.ShellCompDirectiveKeepOrder | cobra.ShellCompDirectiveNoFileComp
}
remoteImages, err := dockerCli.Client().ImageHubSearch(cmd.Context(), toComplete, hub.SearchOptions{
From: 0,
Size: 25,
Type: hub.SearchTypeImage,
Order: hub.SearchOrderDesc,
Official: true,
Source: hub.SearchSourceStore,
OpenSource: true,
ExtensionReviewed: true,
})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
images := make([]string, 0, len(remoteImages.Results))
for _, img := range remoteImages.Results {
categories := make([]string, 0, len(img.Categories))
for _, cat := range img.Categories {
categories = append(categories, cat.Name)
}
images = append(images, img.Name+"\t"+strings.Join(categories, ", "))
}
return images, cobra.ShellCompDirectiveKeepOrder | cobra.ShellCompDirectiveNoFileComp
},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -58,9 +58,9 @@ func NewLoginCommand(dockerCli command.Cli) *cobra.Command {
return cmd return cmd
} }
func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error { func verifyLoginOptions(dockerCli command.Cli, opts *loginOptions) error {
if opts.password != "" { if opts.password != "" {
fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.") _, _ = fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
if opts.passwordStdin { if opts.passwordStdin {
return errors.New("--password and --password-stdin are mutually exclusive") return errors.New("--password and --password-stdin are mutually exclusive")
} }
@ -83,7 +83,7 @@ func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
} }
func runLogin(ctx context.Context, dockerCli command.Cli, opts loginOptions) error { func runLogin(ctx context.Context, dockerCli command.Cli, opts loginOptions) error {
if err := verifyloginOptions(dockerCli, &opts); err != nil { if err := verifyLoginOptions(dockerCli, &opts); err != nil {
return err return err
} }
var ( var (
@ -174,7 +174,7 @@ func loginUser(ctx context.Context, dockerCli command.Cli, opts loginOptions, de
if !errors.Is(err, manager.ErrDeviceLoginStartFail) { if !errors.Is(err, manager.ErrDeviceLoginStartFail) {
return response, err return response, err
} }
fmt.Fprint(dockerCli.Err(), "Failed to start web-based login - falling back to command line login...\n\n") _, _ = fmt.Fprint(dockerCli.Err(), "Failed to start web-based login - falling back to command line login...\n\n")
} }
return loginWithUsernameAndPassword(ctx, dockerCli, opts, defaultUsername, serverAddress) return loginWithUsernameAndPassword(ctx, dockerCli, opts, defaultUsername, serverAddress)

View File

@ -199,7 +199,7 @@ func PruneFilters(dockerCli Cli, pruneFilters filters.Args) filters.Args {
// AddPlatformFlag adds `platform` to a set of flags for API version 1.32 and later. // AddPlatformFlag adds `platform` to a set of flags for API version 1.32 and later.
func AddPlatformFlag(flags *pflag.FlagSet, target *string) { func AddPlatformFlag(flags *pflag.FlagSet, target *string) {
flags.StringVar(target, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable") flags.StringVar(target, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
flags.SetAnnotation("platform", "version", []string{"1.32"}) _ = flags.SetAnnotation("platform", "version", []string{"1.32"})
} }
// ValidateOutputPath validates the output paths of the `export` and `save` commands. // ValidateOutputPath validates the output paths of the `export` and `save` commands.

View File

@ -5,7 +5,9 @@ import (
"strconv" "strconv"
) )
// Enabled returns whether cli hints are enabled or not // Enabled returns whether cli hints are enabled or not. Hints are enabled by
// default, but can be disabled through the "DOCKER_CLI_HINTS" environment
// variable.
func Enabled() bool { func Enabled() bool {
if v := os.Getenv("DOCKER_CLI_HINTS"); v != "" { if v := os.Getenv("DOCKER_CLI_HINTS"); v != "" {
enabled, err := strconv.ParseBool(v) enabled, err := strconv.ParseBool(v)

52
cli/hints/hints_test.go Normal file
View File

@ -0,0 +1,52 @@
package hints
import (
"testing"
"gotest.tools/v3/assert"
)
func TestEnabled(t *testing.T) {
tests := []struct {
doc string
env string
expected bool
}{
{
doc: "default",
expected: true,
},
{
doc: "DOCKER_CLI_HINTS=1",
env: "1",
expected: true,
},
{
doc: "DOCKER_CLI_HINTS=true",
env: "true",
expected: true,
},
{
doc: "DOCKER_CLI_HINTS=0",
env: "0",
expected: false,
},
{
doc: "DOCKER_CLI_HINTS=false",
env: "false",
expected: false,
},
{
doc: "DOCKER_CLI_HINTS=not-a-bool",
env: "not-a-bool",
expected: true,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
t.Setenv("DOCKER_CLI_HINTS", tc.env)
assert.Equal(t, Enabled(), tc.expected)
})
}
}

View File

@ -92,7 +92,7 @@ func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
CompletionOptions: cobra.CompletionOptions{ CompletionOptions: cobra.CompletionOptions{
DisableDefaultCmd: false, DisableDefaultCmd: false,
HiddenDefaultCmd: true, HiddenDefaultCmd: true,
DisableDescriptions: true, DisableDescriptions: false,
}, },
} }
cmd.SetIn(dockerCli.In()) cmd.SetIn(dockerCli.In())