From 847cc8db82003c138d15b4566c8f0f9c6be3a860 Mon Sep 17 00:00:00 2001 From: rsteube Date: Sun, 23 Jan 2022 23:49:23 +0100 Subject: [PATCH] docker-compose: updates from 2.2.3 --- .../cmd/action/path.go | 1 + .../cmd/action/service.go | 105 ++++++------------ .../cmd/action/user.go | 38 +++++++ .../cmd/action/volume.go | 16 +++ .../docker-compose_completer/cmd/build.go | 19 ++-- .../docker-compose_completer/cmd/config.go | 24 ---- .../docker-compose_completer/cmd/convert.go | 39 +++++++ completers/docker-compose_completer/cmd/cp.go | 56 ++++++++++ .../docker-compose_completer/cmd/create.go | 9 +- .../docker-compose_completer/cmd/down.go | 13 +-- .../docker-compose_completer/cmd/events.go | 3 +- .../docker-compose_completer/cmd/exec.go | 15 ++- .../docker-compose_completer/cmd/help.go | 48 -------- .../docker-compose_completer/cmd/images.go | 3 +- .../docker-compose_completer/cmd/kill.go | 12 +- .../docker-compose_completer/cmd/logs.go | 5 +- completers/docker-compose_completer/cmd/ls.go | 25 +++++ .../docker-compose_completer/cmd/pause.go | 1 - .../docker-compose_completer/cmd/port.go | 7 +- completers/docker-compose_completer/cmd/ps.go | 5 +- .../docker-compose_completer/cmd/pull.go | 7 +- .../docker-compose_completer/cmd/push.go | 3 +- .../docker-compose_completer/cmd/restart.go | 5 +- completers/docker-compose_completer/cmd/rm.go | 7 +- .../docker-compose_completer/cmd/root.go | 29 ++--- .../docker-compose_completer/cmd/run.go | 31 +++--- .../docker-compose_completer/cmd/scale.go | 24 ---- .../docker-compose_completer/cmd/stop.go | 3 +- .../docker-compose_completer/cmd/top.go | 1 - .../docker-compose_completer/cmd/unpause.go | 1 - completers/docker-compose_completer/cmd/up.go | 38 ++++--- .../docker-compose_completer/cmd/version.go | 8 +- 32 files changed, 326 insertions(+), 275 deletions(-) create mode 100644 completers/docker-compose_completer/cmd/action/user.go create mode 100644 completers/docker-compose_completer/cmd/action/volume.go delete mode 100644 completers/docker-compose_completer/cmd/config.go create mode 100644 completers/docker-compose_completer/cmd/convert.go create mode 100644 completers/docker-compose_completer/cmd/cp.go delete mode 100644 completers/docker-compose_completer/cmd/help.go create mode 100644 completers/docker-compose_completer/cmd/ls.go delete mode 100644 completers/docker-compose_completer/cmd/scale.go diff --git a/completers/docker-compose_completer/cmd/action/path.go b/completers/docker-compose_completer/cmd/action/path.go index f4fd726242..bb6212cf2d 100644 --- a/completers/docker-compose_completer/cmd/action/path.go +++ b/completers/docker-compose_completer/cmd/action/path.go @@ -8,6 +8,7 @@ import ( ) func ActionServicePath(service string) carapace.Action { + // TODO container index return carapace.ActionCallback(func(c carapace.Context) carapace.Action { path := filepath.Dir(c.CallbackValue) return carapace.ActionMultiParts("/", func(c carapace.Context) carapace.Action { diff --git a/completers/docker-compose_completer/cmd/action/service.go b/completers/docker-compose_completer/cmd/action/service.go index c3bebdfb57..8ec064c85a 100644 --- a/completers/docker-compose_completer/cmd/action/service.go +++ b/completers/docker-compose_completer/cmd/action/service.go @@ -1,89 +1,58 @@ package action import ( - "errors" - "io/ioutil" - "os" - "path/filepath" + "encoding/json" + "fmt" + "strings" "github.com/rsteube/carapace" "github.com/spf13/cobra" - "gopkg.in/yaml.v3" ) -func ActionServices(cmd *cobra.Command) carapace.Action { - return carapace.ActionCallback(func(c carapace.Context) carapace.Action { - if services, _, err := loadFile(cmd); err != nil { - return carapace.ActionMessage(err.Error()) - } else { - return carapace.ActionValues(services...) +type config struct { + Services map[string]struct { + Image string + Build struct { + Context string + Dockerfile string } - }) + } + Volumes map[string]struct { + Name string + } } -func ActionVolumes(cmd *cobra.Command) carapace.Action { - return carapace.ActionCallback(func(c carapace.Context) carapace.Action { - if _, volumes, err := loadFile(cmd); err != nil { +func actionConfig(f func(c config) carapace.Action) carapace.Action { + // TODO support `--files` flag + return carapace.ActionExecCommand("docker-compose", "convert", "--format", "json")(func(output []byte) carapace.Action { + var c config + if err := json.Unmarshal(output, &c); err != nil { return carapace.ActionMessage(err.Error()) - } else { - return carapace.ActionValues(volumes...) } + return f(c) }) } -type compose struct { - Version string - Services map[string]interface{} - Volumes map[string]interface{} -} - -func fileLocation(cmd *cobra.Command) (string, error) { - if flag := cmd.Root().Flag("file"); flag.Changed { - return flag.Value.String(), nil - } else { - if path, err := os.Getwd(); err == nil { - return traverse(path) - } else { - return "", err +func ActionServices(cmd *cobra.Command) carapace.Action { + return actionConfig(func(c config) carapace.Action { + vals := make([]string, 0) + for name, service := range c.Services { + description := service.Image + if strings.TrimSpace(description) == "" { + description = fmt.Sprintf("%v/%v", service.Build.Context, service.Build.Dockerfile) + } + vals = append(vals, name, description) } - } + return carapace.ActionValuesDescribed(vals...) + }) } -func traverse(path string) (string, error) { - if _, err := os.Stat(path + "/docker-compose.yml"); err == nil { - return path + "/docker-compose.yml", nil - } else { - if path == "/" { - return "", errors.New("no docker-compose.yml present") +func ActionContainers(cmd *cobra.Command, status string) carapace.Action { + return carapace.ActionExecCommand("docker-compose", "ps", "--status", status)(func(output []byte) carapace.Action { + lines := strings.Split(string(output), "\n") + if lines[0] != "" { + return carapace.ActionValues(lines[:len(lines)-1]...) } - return traverse(filepath.Dir(path)) - } -} - -func loadFile(cmd *cobra.Command) ([]string, []string, error) { - file, err := fileLocation(cmd) - if err != nil { - return nil, nil, err - } - - yamlFile, err := ioutil.ReadFile(file) - if err != nil { - return nil, nil, err - } - - var content compose - if err = yaml.Unmarshal(yamlFile, &content); err != nil { - return nil, nil, err - } - return keys(content.Services), keys(content.Volumes), nil -} - -func keys(m map[string]interface{}) []string { - keys := make([]string, len(m)) - index := 0 - for key := range m { - keys[index] = key - index += 1 - } - return keys + return carapace.ActionValues() + }) } diff --git a/completers/docker-compose_completer/cmd/action/user.go b/completers/docker-compose_completer/cmd/action/user.go new file mode 100644 index 0000000000..6a79e0b07f --- /dev/null +++ b/completers/docker-compose_completer/cmd/action/user.go @@ -0,0 +1,38 @@ +package action + +import ( + "strings" + + "github.com/rsteube/carapace" + "github.com/spf13/cobra" +) + +func actionContainerExecCommand(cmd *cobra.Command, service string, index string, command string, args ...string) func(f func(output []byte) carapace.Action) carapace.Action { + return func(f func(output []byte) carapace.Action) carapace.Action { + return carapace.ActionExecCommand("docker-compose", append([]string{"exec", "--no-TTY", "--index", index, service, command}, args...)...)(func(output []byte) carapace.Action { + return f(output) + }) + } +} + +// ActionContainerUsers completes container user names +// root (0) +// daemon (1) +func ActionContainerUsers(cmd *cobra.Command, service string, index string) carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + return actionContainerExecCommand(cmd, service, index, "cat", "/etc/passwd")(func(output []byte) carapace.Action { + users := []string{} + for _, entry := range strings.Split(string(output), "\n") { + splitted := strings.Split(entry, ":") + if len(splitted) > 2 { + user := splitted[0] + id := splitted[2] + if len(strings.TrimSpace(user)) > 0 { + users = append(users, user, id) + } + } + } + return carapace.ActionValuesDescribed(users...) + }) + }) +} diff --git a/completers/docker-compose_completer/cmd/action/volume.go b/completers/docker-compose_completer/cmd/action/volume.go new file mode 100644 index 0000000000..3afbf2b17c --- /dev/null +++ b/completers/docker-compose_completer/cmd/action/volume.go @@ -0,0 +1,16 @@ +package action + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" +) + +func ActionVolumes(cmd *cobra.Command) carapace.Action { + return actionConfig(func(c config) carapace.Action { + vals := make([]string, 0) + for name, volume := range c.Volumes { + vals = append(vals, name, volume.Name) + } + return carapace.ActionValuesDescribed(vals...) + }) +} diff --git a/completers/docker-compose_completer/cmd/build.go b/completers/docker-compose_completer/cmd/build.go index 53be9a5fd3..747d5b33ff 100644 --- a/completers/docker-compose_completer/cmd/build.go +++ b/completers/docker-compose_completer/cmd/build.go @@ -14,21 +14,20 @@ var buildCmd = &cobra.Command{ func init() { carapace.Gen(buildCmd).Standalone() - - buildCmd.Flags().String("build-arg", "", "Set build-time variables for services.") - buildCmd.Flags().Bool("compress", false, "Compress the build context using gzip.") - buildCmd.Flags().Bool("force-rm", false, "Always remove intermediate containers.") - buildCmd.Flags().StringP("memory", "m", "", "Set memory limit for the build container.") - buildCmd.Flags().Bool("no-cache", false, "Do not use cache when building the image.") - buildCmd.Flags().Bool("no-rm", false, "Do not remove intermediate containers after a successful build.") - buildCmd.Flags().Bool("parallel", false, "Build images in parallel.") - buildCmd.Flags().String("progress", "", "Set type of progress output (auto, plain, tty).") + buildCmd.Flags().StringArray("build-arg", []string{}, "Set build-time variables for services.") + buildCmd.Flags().Bool("compress", true, "Compress the build context using gzip. DEPRECATED") + buildCmd.Flags().Bool("force-rm", true, "Always remove intermediate containers. DEPRECATED") + buildCmd.Flags().StringP("memory", "m", "", "Set memory limit for the build container. Not supported on buildkit yet.") + buildCmd.Flags().Bool("no-cache", false, "Do not use cache when building the image") + buildCmd.Flags().Bool("no-rm", false, "Do not remove intermediate containers after a successful build. DEPRECATED") + buildCmd.Flags().Bool("parallel", true, "Build images in parallel. DEPRECATED") + buildCmd.Flags().String("progress", "auto", "Set type of progress output (auto, tty, plain, quiet)") buildCmd.Flags().Bool("pull", false, "Always attempt to pull a newer version of the image.") buildCmd.Flags().BoolP("quiet", "q", false, "Don't print anything to STDOUT") rootCmd.AddCommand(buildCmd) carapace.Gen(buildCmd).FlagCompletion(carapace.ActionMap{ - "progress": carapace.ActionValues("auto", "plain", "tty"), + "progress": carapace.ActionValues("auto", "tty", "plain", "quiet"), }) carapace.Gen(buildCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/config.go b/completers/docker-compose_completer/cmd/config.go deleted file mode 100644 index 4d9081d80f..0000000000 --- a/completers/docker-compose_completer/cmd/config.go +++ /dev/null @@ -1,24 +0,0 @@ -package cmd - -import ( - "github.com/rsteube/carapace" - "github.com/spf13/cobra" -) - -var configCmd = &cobra.Command{ - Use: "config", - Short: "Validate and view the Compose file", - Run: func(cmd *cobra.Command, args []string) {}, -} - -func init() { - carapace.Gen(configCmd).Standalone() - - configCmd.Flags().String("hash", "", "Print the service config hash, one per line.") - configCmd.Flags().Bool("no-interpolate", false, "Don't interpolate environment variables") - configCmd.Flags().BoolP("quiet", "q", false, "Only validate the configuration, don't print") - configCmd.Flags().Bool("resolve-image-digests", false, "Pin image tags to digests.") - configCmd.Flags().Bool("services", false, "Print the service names, one per line.") - configCmd.Flags().Bool("volumes", false, "Print the volume names, one per line.") - rootCmd.AddCommand(configCmd) -} diff --git a/completers/docker-compose_completer/cmd/convert.go b/completers/docker-compose_completer/cmd/convert.go new file mode 100644 index 0000000000..87b619312f --- /dev/null +++ b/completers/docker-compose_completer/cmd/convert.go @@ -0,0 +1,39 @@ +package cmd + +import ( + "github.com/rsteube/carapace" + "github.com/rsteube/carapace-bin/completers/docker-compose_completer/cmd/action" + "github.com/spf13/cobra" +) + +var convertCmd = &cobra.Command{ + Use: "convert", + Short: "Converts the compose file to platform's canonical format", + Run: func(cmd *cobra.Command, args []string) {}, +} + +func init() { + carapace.Gen(convertCmd).Standalone() + convertCmd.Flags().String("format", "yaml", "Format the output. Values: [yaml | json]") + convertCmd.Flags().String("hash", "", "Print the service config hash, one per line.") + convertCmd.Flags().Bool("images", false, "Print the image names, one per line.") + convertCmd.Flags().Bool("no-interpolate", false, "Don't interpolate environment variables.") + convertCmd.Flags().Bool("no-normalize", false, "Don't normalize compose model.") + convertCmd.Flags().StringP("output", "o", "", "Save to file (default to stdout)") + convertCmd.Flags().Bool("profiles", false, "Print the profile names, one per line.") + convertCmd.Flags().BoolP("quiet", "q", false, "Only validate the configuration, don't print anything.") + convertCmd.Flags().Bool("resolve-image-digests", false, "Pin image tags to digests.") + convertCmd.Flags().Bool("services", false, "Print the service names, one per line.") + convertCmd.Flags().Bool("volumes", false, "Print the volume names, one per line.") + rootCmd.AddCommand(convertCmd) + + carapace.Gen(convertCmd).FlagCompletion(carapace.ActionMap{ + "format": carapace.ActionValues("yaml", "json"), + "hash": action.ActionServices(convertCmd), + "output": carapace.ActionFiles(), + }) + + carapace.Gen(convertCmd).PositionalCompletion( + action.ActionServices(convertCmd), + ) +} diff --git a/completers/docker-compose_completer/cmd/cp.go b/completers/docker-compose_completer/cmd/cp.go new file mode 100644 index 0000000000..ab4e4e3de0 --- /dev/null +++ b/completers/docker-compose_completer/cmd/cp.go @@ -0,0 +1,56 @@ +package cmd + +import ( + "github.com/rsteube/carapace" + "github.com/rsteube/carapace-bin/completers/docker-compose_completer/cmd/action" + "github.com/rsteube/carapace-bin/pkg/util" + "github.com/spf13/cobra" +) + +var cpCmd = &cobra.Command{ + Use: "cp", + Short: "Copy files/folders between a service container and the local filesystem", + Run: func(cmd *cobra.Command, args []string) {}, +} + +func init() { + carapace.Gen(cpCmd).Standalone() + cpCmd.Flags().Bool("all", false, "Copy to all the containers of the service.") + cpCmd.Flags().BoolP("archive", "a", false, "Archive mode (copy all uid/gid information)") + cpCmd.Flags().BoolP("follow-link", "L", false, "Always follow symbol link in SRC_PATH") + cpCmd.Flags().Int("index", 1, "Index of the container if there are multiple instances of a service [default: 1].") + rootCmd.AddCommand(cpCmd) + + carapace.Gen(cpCmd).PositionalCompletion( + carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if util.HasPathPrefix(c.CallbackValue) { + return carapace.ActionFiles() + } + return carapace.ActionMultiParts(":", func(c carapace.Context) carapace.Action { + switch len(c.Parts) { + case 0: + return action.ActionServices(cpCmd).Invoke(c).Suffix(":").ToA() + case 1: + return action.ActionServicePath(c.Parts[0]) // TODO index + default: + return carapace.ActionValues() + } + }) + }), + carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if !util.HasPathPrefix(c.Args[0]) { + return carapace.ActionFiles() + } + return carapace.ActionMultiParts(":", func(c carapace.Context) carapace.Action { + switch len(c.Parts) { + case 0: + return action.ActionServices(cpCmd).Invoke(c).Suffix(":").ToA() + case 1: + return action.ActionServicePath(c.Parts[0]) // TODO index + default: + return carapace.ActionValues() + } + }) + }), + ) +} diff --git a/completers/docker-compose_completer/cmd/create.go b/completers/docker-compose_completer/cmd/create.go index feea73fdc7..27c0ee3292 100644 --- a/completers/docker-compose_completer/cmd/create.go +++ b/completers/docker-compose_completer/cmd/create.go @@ -8,17 +8,16 @@ import ( var createCmd = &cobra.Command{ Use: "create", - Short: "Create services", + Short: "Creates containers for a service.", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(createCmd).Standalone() - - createCmd.Flags().Bool("build", false, "Build images before creating containers.") - createCmd.Flags().Bool("force-recreate", false, "Recreate containers even if their configuration and") + createCmd.Flags().Bool("build", false, "Build images before starting containers.") + createCmd.Flags().Bool("force-recreate", false, "Recreate containers even if their configuration and image haven't changed.") createCmd.Flags().Bool("no-build", false, "Don't build an image, even if it's missing.") - createCmd.Flags().Bool("no-recreate", false, "If containers already exist, don't recreate them.") + createCmd.Flags().Bool("no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.") rootCmd.AddCommand(createCmd) carapace.Gen(createCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/down.go b/completers/docker-compose_completer/cmd/down.go index 62ecd99544..f1de1b7c18 100644 --- a/completers/docker-compose_completer/cmd/down.go +++ b/completers/docker-compose_completer/cmd/down.go @@ -2,23 +2,21 @@ package cmd import ( "github.com/rsteube/carapace" - "github.com/rsteube/carapace-bin/completers/docker-compose_completer/cmd/action" "github.com/spf13/cobra" ) var downCmd = &cobra.Command{ Use: "down", - Short: "Stop and remove containers, networks, images, and volumes", + Short: "Stop and remove containers, networks", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(downCmd).Standalone() - - downCmd.Flags().Bool("remove-orphans", false, "Remove containers for services not defined in the") - downCmd.Flags().String("rmi", "", "Remove images. Type must be one of:") - downCmd.Flags().StringP("timeout", "t", "", "Specify a shutdown timeout in seconds.") - downCmd.Flags().BoolP("volumes", "v", false, "Remove named volumes declared in the `volumes`") + downCmd.Flags().Bool("remove-orphans", false, "Remove containers for services not defined in the Compose file.") + downCmd.Flags().String("rmi", "", "Remove images used by services. \"local\" remove only images that don't have a custom tag (\"local\"|\"all\")") + downCmd.Flags().IntP("timeout", "t", 10, "Specify a shutdown timeout in seconds") + downCmd.Flags().BoolP("volumes", "v", false, " Remove named volumes declared in the `volumes` section of the Compose file and anonymous volumes attached to containers.") rootCmd.AddCommand(downCmd) carapace.Gen(downCmd).FlagCompletion(carapace.ActionMap{ @@ -26,6 +24,5 @@ func init() { "all", "Remove all images used by any service.", "local", "Remove only images that don't have a custom tag set by the image field.", ), - "volumes": action.ActionVolumes(downCmd), }) } diff --git a/completers/docker-compose_completer/cmd/events.go b/completers/docker-compose_completer/cmd/events.go index 3176fe732a..ce43ba7574 100644 --- a/completers/docker-compose_completer/cmd/events.go +++ b/completers/docker-compose_completer/cmd/events.go @@ -8,13 +8,12 @@ import ( var eventsCmd = &cobra.Command{ Use: "events", - Short: "Receive real time events from containers", + Short: "Receive real time events from containers.", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(eventsCmd).Standalone() - eventsCmd.Flags().Bool("json", false, "Output events as a stream of json objects") rootCmd.AddCommand(eventsCmd) diff --git a/completers/docker-compose_completer/cmd/exec.go b/completers/docker-compose_completer/cmd/exec.go index 6c8053ccf0..ea0d91cc72 100644 --- a/completers/docker-compose_completer/cmd/exec.go +++ b/completers/docker-compose_completer/cmd/exec.go @@ -3,28 +3,33 @@ package cmd import ( "github.com/rsteube/carapace" "github.com/rsteube/carapace-bin/completers/docker-compose_completer/cmd/action" - "github.com/rsteube/carapace-bin/pkg/actions/os" "github.com/spf13/cobra" ) var execCmd = &cobra.Command{ Use: "exec", - Short: "Execute a command in a running container", + Short: "Execute a command in a running container.", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(execCmd).Standalone() - execCmd.Flags().BoolS("T", "T", false, "Disable pseudo-tty allocation. By default `docker-compose exec`") execCmd.Flags().BoolP("detach", "d", false, "Detached mode: Run command in the background.") - execCmd.Flags().String("index", "", "index of the container if there are multiple") + execCmd.Flags().StringArrayP("env", "e", []string{}, "Set environment variables") + execCmd.Flags().String("index", "1", "index of the container if there are multiple instances of a service [default: 1].") + execCmd.Flags().BoolP("no-TTY", "T", false, "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.") execCmd.Flags().Bool("privileged", false, "Give extended privileges to the process.") execCmd.Flags().StringP("user", "u", "", "Run the command as this user.") rootCmd.AddCommand(execCmd) carapace.Gen(execCmd).FlagCompletion(carapace.ActionMap{ - "user": os.ActionUsers(), + "user": carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if len(c.Args) > 0 { + return action.ActionContainerUsers(execCmd, c.Args[0], execCmd.Flag("index").Value.String()) + } + return carapace.ActionValues() + }), }) carapace.Gen(execCmd).PositionalCompletion( diff --git a/completers/docker-compose_completer/cmd/help.go b/completers/docker-compose_completer/cmd/help.go deleted file mode 100644 index 3bfb6a36e5..0000000000 --- a/completers/docker-compose_completer/cmd/help.go +++ /dev/null @@ -1,48 +0,0 @@ -package cmd - -import ( - "github.com/rsteube/carapace" - "github.com/spf13/cobra" -) - -var helpCmd = &cobra.Command{ - Use: "help", - Short: "Get help on a command", - Run: func(cmd *cobra.Command, args []string) {}, -} - -func init() { - carapace.Gen(helpCmd).Standalone() - - rootCmd.AddCommand(helpCmd) - - carapace.Gen(helpCmd).PositionalCompletion( - carapace.ActionValuesDescribed( - "build", "Build or rebuild services", - "config", "Validate and view the Compose file", - "create", "Create services", - "down", "Stop and remove containers, networks, images, and volumes", - "events", "Receive real time events from containers", - "exec", "Execute a command in a running container", - "help", "Get help on a command", - "images", "List images", - "kill", "Kill containers", - "logs", "View output from containers", - "pause", "Pause services", - "port", "Print the public port for a port binding", - "ps", "List containers", - "pull", "Pull service images", - "push", "Push service images", - "restart", "Restart services", - "rm", "Remove stopped containers", - "run", "Run a one-off command", - "scale", "Set number of containers for a service", - "start", "Start services", - "stop", "Stop services", - "top", "Display the running processes", - "unpause", "Unpause services", - "up", "Create and start containers", - "version", "Show the Docker-Compose version information", - ), - ) -} diff --git a/completers/docker-compose_completer/cmd/images.go b/completers/docker-compose_completer/cmd/images.go index ccd4f915f3..814f81fb19 100644 --- a/completers/docker-compose_completer/cmd/images.go +++ b/completers/docker-compose_completer/cmd/images.go @@ -8,13 +8,12 @@ import ( var imagesCmd = &cobra.Command{ Use: "images", - Short: "List images", + Short: "List images used by the created containers", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(imagesCmd).Standalone() - imagesCmd.Flags().BoolP("quiet", "q", false, "Only display IDs") rootCmd.AddCommand(imagesCmd) diff --git a/completers/docker-compose_completer/cmd/kill.go b/completers/docker-compose_completer/cmd/kill.go index ecddf5d3ee..e21a997448 100644 --- a/completers/docker-compose_completer/cmd/kill.go +++ b/completers/docker-compose_completer/cmd/kill.go @@ -2,23 +2,27 @@ package cmd import ( "github.com/rsteube/carapace" + "github.com/rsteube/carapace-bin/completers/docker-compose_completer/cmd/action" "github.com/rsteube/carapace-bin/pkg/actions/os" "github.com/spf13/cobra" ) var killCmd = &cobra.Command{ Use: "kill", - Short: "Kill containers", + Short: "Force stop service containers.", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(killCmd).Standalone() - - killCmd.Flags().StringS("s", "s", "", "SIGNAL to send to the container.") + killCmd.Flags().StringP("signal", "s", "SIGKILL", "SIGNAL to send to the container.") rootCmd.AddCommand(killCmd) carapace.Gen(killCmd).FlagCompletion(carapace.ActionMap{ - "s": os.ActionKillSignals(), + "signal": os.ActionKillSignals(), }) + + carapace.Gen(killCmd).PositionalAnyCompletion( + action.ActionServices(killCmd), + ) } diff --git a/completers/docker-compose_completer/cmd/logs.go b/completers/docker-compose_completer/cmd/logs.go index 517ba71070..1584a015f1 100644 --- a/completers/docker-compose_completer/cmd/logs.go +++ b/completers/docker-compose_completer/cmd/logs.go @@ -14,10 +14,11 @@ var logsCmd = &cobra.Command{ func init() { carapace.Gen(logsCmd).Standalone() - logsCmd.Flags().BoolP("follow", "f", false, "Follow log output.") logsCmd.Flags().Bool("no-color", false, "Produce monochrome output.") - logsCmd.Flags().String("tail", "", "Number of lines to show from the end of the logs") + logsCmd.Flags().Bool("no-log-prefix", false, "Don't print prefix in logs.") + logsCmd.Flags().String("since", "", "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)") + logsCmd.Flags().String("tail", "all", "Number of lines to show from the end of the logs for each container.") logsCmd.Flags().BoolP("timestamps", "t", false, "Show timestamps.") rootCmd.AddCommand(logsCmd) diff --git a/completers/docker-compose_completer/cmd/ls.go b/completers/docker-compose_completer/cmd/ls.go new file mode 100644 index 0000000000..1681c4cb6a --- /dev/null +++ b/completers/docker-compose_completer/cmd/ls.go @@ -0,0 +1,25 @@ +package cmd + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" +) + +var lsCmd = &cobra.Command{ + Use: "ls", + Short: "List running compose projects", + Run: func(cmd *cobra.Command, args []string) {}, +} + +func init() { + carapace.Gen(lsCmd).Standalone() + lsCmd.Flags().BoolP("all", "a", false, "Show all stopped Compose projects") + lsCmd.Flags().String("filter", "", "Filter output based on conditions provided.") + lsCmd.Flags().String("format", "pretty", "Format the output. Values: [pretty | json].") + lsCmd.Flags().BoolP("quiet", "q", false, "Only display IDs.") + rootCmd.AddCommand(lsCmd) + + carapace.Gen(lsCmd).FlagCompletion(carapace.ActionMap{ + "format": carapace.ActionValues("pretty", "json"), + }) +} diff --git a/completers/docker-compose_completer/cmd/pause.go b/completers/docker-compose_completer/cmd/pause.go index 7f34c3d626..6fa6e9718c 100644 --- a/completers/docker-compose_completer/cmd/pause.go +++ b/completers/docker-compose_completer/cmd/pause.go @@ -14,7 +14,6 @@ var pauseCmd = &cobra.Command{ func init() { carapace.Gen(pauseCmd).Standalone() - rootCmd.AddCommand(pauseCmd) carapace.Gen(pauseCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/port.go b/completers/docker-compose_completer/cmd/port.go index f6cabedfca..c11034b9c9 100644 --- a/completers/docker-compose_completer/cmd/port.go +++ b/completers/docker-compose_completer/cmd/port.go @@ -8,15 +8,14 @@ import ( var portCmd = &cobra.Command{ Use: "port", - Short: "Print the public port for a port binding", + Short: "Print the public port for a port binding.", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(portCmd).Standalone() - - portCmd.Flags().String("index", "", "index of the container if there are multiple") - portCmd.Flags().String("protocol", "", "tcp or udp [default: tcp]") + portCmd.Flags().Int("index", 1, "index of the container if service has multiple replicas") + portCmd.Flags().String("protocol", "tcp", "tcp or udp") rootCmd.AddCommand(portCmd) carapace.Gen(portCmd).FlagCompletion(carapace.ActionMap{ diff --git a/completers/docker-compose_completer/cmd/ps.go b/completers/docker-compose_completer/cmd/ps.go index 7b5fcebac4..cf4b313815 100644 --- a/completers/docker-compose_completer/cmd/ps.go +++ b/completers/docker-compose_completer/cmd/ps.go @@ -14,11 +14,12 @@ var psCmd = &cobra.Command{ func init() { carapace.Gen(psCmd).Standalone() - psCmd.Flags().BoolP("all", "a", false, "Show all stopped containers (including those created by the run command)") - psCmd.Flags().String("filter", "", "Filter services by a property") + psCmd.Flags().String("filter", "", "Filter services by a property. Deprecated, use --status instead") + psCmd.Flags().String("format", "pretty", "Format the output. Values: [pretty | json]") psCmd.Flags().BoolP("quiet", "q", false, "Only display IDs") psCmd.Flags().Bool("services", false, "Display services") + psCmd.Flags().StringArray("status", []string{}, "Filter services by status. Values: [paused | restarting | removing | running | dead | created | exited]") rootCmd.AddCommand(psCmd) carapace.Gen(psCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/pull.go b/completers/docker-compose_completer/cmd/pull.go index bb6721ad83..f31b9e7617 100644 --- a/completers/docker-compose_completer/cmd/pull.go +++ b/completers/docker-compose_completer/cmd/pull.go @@ -14,11 +14,10 @@ var pullCmd = &cobra.Command{ func init() { carapace.Gen(pullCmd).Standalone() - - pullCmd.Flags().Bool("ignore-pull-failures", false, "Pull what it can and ignores images with pull failures.") + pullCmd.Flags().Bool("ignore-pull-failures", false, "Pull what it can and ignores images with pull failures") pullCmd.Flags().Bool("include-deps", false, "Also pull services declared as dependencies") - pullCmd.Flags().Bool("no-parallel", false, "Disable parallel pulling.") - pullCmd.Flags().Bool("parallel", false, "Deprecated, pull multiple images in parallel (enabled by default).") + pullCmd.Flags().Bool("no-parallel", true, "DEPRECATED disable parallel pulling.") + pullCmd.Flags().Bool("parallel", true, "DEPRECATED pull multiple images in parallel.") pullCmd.Flags().BoolP("quiet", "q", false, "Pull without printing progress information") rootCmd.AddCommand(pullCmd) diff --git a/completers/docker-compose_completer/cmd/push.go b/completers/docker-compose_completer/cmd/push.go index a2f77a4cf5..241a62d8f1 100644 --- a/completers/docker-compose_completer/cmd/push.go +++ b/completers/docker-compose_completer/cmd/push.go @@ -14,8 +14,7 @@ var pushCmd = &cobra.Command{ func init() { carapace.Gen(pushCmd).Standalone() - - pushCmd.Flags().Bool("ignore-push-failures", false, "Push what it can and ignores images with push failures.") + pushCmd.Flags().Bool("ignore-push-failures", false, "Push what it can and ignores images with push failures") rootCmd.AddCommand(pushCmd) carapace.Gen(pushCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/restart.go b/completers/docker-compose_completer/cmd/restart.go index 37cf0b9d11..107e8aeb75 100644 --- a/completers/docker-compose_completer/cmd/restart.go +++ b/completers/docker-compose_completer/cmd/restart.go @@ -8,14 +8,13 @@ import ( var restartCmd = &cobra.Command{ Use: "restart", - Short: "Restart services", + Short: "Restart containers", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(restartCmd).Standalone() - - restartCmd.Flags().StringP("timeout", "t", "", "Specify a shutdown timeout in seconds.") + restartCmd.Flags().IntP("timeout", "t", 10, "Specify a shutdown timeout in seconds") rootCmd.AddCommand(restartCmd) carapace.Gen(restartCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/rm.go b/completers/docker-compose_completer/cmd/rm.go index 1236fe538e..8392da2b78 100644 --- a/completers/docker-compose_completer/cmd/rm.go +++ b/completers/docker-compose_completer/cmd/rm.go @@ -8,17 +8,16 @@ import ( var rmCmd = &cobra.Command{ Use: "rm", - Short: "Remove stopped containers", + Short: "Removes stopped service containers", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(rmCmd).Standalone() - - rmCmd.Flags().BoolP("all", "a", false, "Deprecated - no effect.") + rmCmd.Flags().BoolP("all", "a", false, "Deprecated - no effect") rmCmd.Flags().BoolP("force", "f", false, "Don't ask to confirm removal") rmCmd.Flags().BoolP("stop", "s", false, "Stop the containers, if required, before removing") - rmCmd.Flags().BoolS("v", "v", false, "Remove any anonymous volumes attached to containers") + rmCmd.Flags().BoolP("volumes", "v", false, "Remove any anonymous volumes attached to containers") rootCmd.AddCommand(rmCmd) carapace.Gen(rmCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/root.go b/completers/docker-compose_completer/cmd/root.go index 1e2eda170e..41c8a4870b 100644 --- a/completers/docker-compose_completer/cmd/root.go +++ b/completers/docker-compose_completer/cmd/root.go @@ -15,33 +15,22 @@ var rootCmd = &cobra.Command{ func Execute() error { return rootCmd.Execute() } + func init() { carapace.Gen(rootCmd).Standalone() - - rootCmd.Flags().Bool("compatibility", false, "If set, Compose will attempt to convert keys") - rootCmd.Flags().StringP("context", "c", "", "Specify a context name") - rootCmd.Flags().String("env-file", "", "Specify an alternate environment file") - rootCmd.Flags().StringP("file", "f", "", "Specify an alternate compose file") - rootCmd.Flags().StringP("host", "H", "", "Daemon socket to connect to") - rootCmd.Flags().String("log-level", "", "Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)") - rootCmd.Flags().Bool("no-ansi", false, "Do not print ANSI control characters") + rootCmd.Flags().String("ansi", "auto", "Control when to print ANSI control characters (\"never\"|\"always\"|\"auto\")") + rootCmd.Flags().Bool("compatibility", false, "Run compose in backward compatibility mode") + rootCmd.Flags().String("env-file", "", "Specify an alternate environment file.") + rootCmd.Flags().StringArrayP("file", "f", []string{}, "Compose configuration files") + rootCmd.Flags().Bool("no-ansi", false, "Do not print ANSI control characters (DEPRECATED)") + rootCmd.Flags().StringArray("profile", []string{}, "Specify a profile to enable") rootCmd.Flags().String("project-directory", "", "Specify an alternate working directory") - rootCmd.Flags().StringP("project-name", "p", "", "Specify an alternate project name") - rootCmd.Flags().Bool("skip-hostname-check", false, "Don't check the daemon's hostname against the") - rootCmd.Flags().Bool("tls", false, "Use TLS; implied by --tlsverify") - rootCmd.Flags().String("tlscacert", "", "Trust certs signed only by this CA") - rootCmd.Flags().String("tlscert", "", "Path to TLS certificate file") - rootCmd.Flags().String("tlskey", "", "Path to TLS key file") - rootCmd.Flags().Bool("tlsverify", false, "Use TLS and verify the remote") - rootCmd.Flags().Bool("verbose", false, "Show more output") - rootCmd.Flags().BoolP("version", "v", false, "Print version and exit") + rootCmd.Flags().StringP("project-name", "p", "", "Project name") carapace.Gen(rootCmd).FlagCompletion(carapace.ActionMap{ + "ansi": carapace.ActionValues("never", "always", "auto"), "env-file": carapace.ActionFiles(), "file": carapace.ActionFiles(), - "log-level": carapace.ActionValues("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"), "project-directory": carapace.ActionDirectories(), - "tlscert": carapace.ActionFiles(".crt"), - "tlskey": carapace.ActionFiles(".key"), }) } diff --git a/completers/docker-compose_completer/cmd/run.go b/completers/docker-compose_completer/cmd/run.go index 7474c0f55b..def1386371 100644 --- a/completers/docker-compose_completer/cmd/run.go +++ b/completers/docker-compose_completer/cmd/run.go @@ -8,29 +8,34 @@ import ( var runCmd = &cobra.Command{ Use: "run", - Short: "Run a one-off command", + Short: "Run a one-off command on a service.", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(runCmd).Standalone() - - runCmd.Flags().BoolS("T", "T", false, "Disable pseudo-tty allocation. By default `docker-compose run`") - runCmd.Flags().BoolP("detach", "d", false, "Detached mode: Run container in the background, print") - runCmd.Flags().StringS("e", "e", "", "Set an environment variable (can be used multiple times)") - runCmd.Flags().String("entrypoint", "", "Override the entrypoint of the image.") - runCmd.Flags().StringP("label", "l", "", "Add or override a label (can be used multiple times)") - runCmd.Flags().String("name", "", "Assign a name to the container") + runCmd.Flags().BoolP("detach", "d", false, "Run container in background and print container ID") + runCmd.Flags().String("entrypoint", "", "Override the entrypoint of the image") + runCmd.Flags().StringArrayP("env", "e", []string{}, "Set environment variables") + runCmd.Flags().StringArrayP("label", "l", []string{}, "Add or override a label") + runCmd.Flags().String("name", "", " Assign a name to the container") + runCmd.Flags().BoolP("no-TTY", "T", false, "Disable pseudo-noTty allocation. By default docker compose run allocates a TTY") runCmd.Flags().Bool("no-deps", false, "Don't start linked services.") - runCmd.Flags().StringP("publish", "p", "", "Publish a container's port(s) to the host") - runCmd.Flags().Bool("rm", false, "Remove container after run. Ignored in detached mode.") - runCmd.Flags().Bool("service-ports", false, "Run command with the service's ports enabled and mapped") - runCmd.Flags().Bool("use-aliases", false, "Use the service's network aliases in the network(s) the") + runCmd.Flags().StringArrayP("publish", "p", []string{}, "Publish a container's port(s) to the host.") + runCmd.Flags().Bool("quiet-pull", false, "Pull without printing progress information.") + runCmd.Flags().Bool("rm", false, "Automatically remove the container when it exits") + runCmd.Flags().Bool("service-ports", false, "Run command with the service's ports enabled and mapped to the host.") + runCmd.Flags().Bool("use-aliases", false, "Use the service's network useAliases in the network(s) the container connects to.") runCmd.Flags().StringP("user", "u", "", "Run as specified username or uid") - runCmd.Flags().StringP("volume", "v", "", "Bind mount a volume (default [])") + runCmd.Flags().StringArrayP("volume", "v", []string{}, "Bind mount a volume.") runCmd.Flags().StringP("workdir", "w", "", "Working directory inside the container") rootCmd.AddCommand(runCmd) + // TODO flag completion + carapace.Gen(runCmd).FlagCompletion(carapace.ActionMap{ + "volume": action.ActionVolumes(runCmd), + }) + carapace.Gen(runCmd).PositionalCompletion( action.ActionServices(runCmd), ) diff --git a/completers/docker-compose_completer/cmd/scale.go b/completers/docker-compose_completer/cmd/scale.go deleted file mode 100644 index 8c43299827..0000000000 --- a/completers/docker-compose_completer/cmd/scale.go +++ /dev/null @@ -1,24 +0,0 @@ -package cmd - -import ( - "github.com/rsteube/carapace" - "github.com/rsteube/carapace-bin/completers/docker-compose_completer/cmd/action" - "github.com/spf13/cobra" -) - -var scaleCmd = &cobra.Command{ - Use: "scale", - Short: "Set number of containers for a service", - Run: func(cmd *cobra.Command, args []string) {}, -} - -func init() { - carapace.Gen(scaleCmd).Standalone() - - scaleCmd.Flags().StringP("timeout", "t", "", "Specify a shutdown timeout in seconds.") - rootCmd.AddCommand(scaleCmd) - - carapace.Gen(scaleCmd).PositionalAnyCompletion( - action.ActionServices(scaleCmd), // TODO Multiparts Action/ or simply add = suffix - ) -} diff --git a/completers/docker-compose_completer/cmd/stop.go b/completers/docker-compose_completer/cmd/stop.go index 16c11ec599..f86c4d42fe 100644 --- a/completers/docker-compose_completer/cmd/stop.go +++ b/completers/docker-compose_completer/cmd/stop.go @@ -14,8 +14,7 @@ var stopCmd = &cobra.Command{ func init() { carapace.Gen(stopCmd).Standalone() - - stopCmd.Flags().StringP("timeout", "t", "", "Specify a shutdown timeout in seconds.") + stopCmd.Flags().IntP("timeout", "t", 10, "Specify a shutdown timeout in seconds") rootCmd.AddCommand(stopCmd) carapace.Gen(stopCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/top.go b/completers/docker-compose_completer/cmd/top.go index f058eee013..821d6c2d31 100644 --- a/completers/docker-compose_completer/cmd/top.go +++ b/completers/docker-compose_completer/cmd/top.go @@ -14,7 +14,6 @@ var topCmd = &cobra.Command{ func init() { carapace.Gen(topCmd).Standalone() - rootCmd.AddCommand(topCmd) carapace.Gen(topCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/unpause.go b/completers/docker-compose_completer/cmd/unpause.go index fc74c71e61..4f165aa800 100644 --- a/completers/docker-compose_completer/cmd/unpause.go +++ b/completers/docker-compose_completer/cmd/unpause.go @@ -14,7 +14,6 @@ var unpauseCmd = &cobra.Command{ func init() { carapace.Gen(unpauseCmd).Standalone() - rootCmd.AddCommand(unpauseCmd) carapace.Gen(unpauseCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/up.go b/completers/docker-compose_completer/cmd/up.go index 33c8c2ebdc..d0843bec8d 100644 --- a/completers/docker-compose_completer/cmd/up.go +++ b/completers/docker-compose_completer/cmd/up.go @@ -14,29 +14,39 @@ var upCmd = &cobra.Command{ func init() { carapace.Gen(upCmd).Standalone() - - upCmd.Flags().Bool("abort-on-container-exit", false, "Stops all containers if any container was") - upCmd.Flags().Bool("abort-on-container-exit.", false, "") - upCmd.Flags().Bool("always-recreate-deps", false, "Recreate dependent containers.") - upCmd.Flags().Bool("attach-dependencies", false, "Attach to dependent containers") + upCmd.Flags().Bool("abort-on-container-exit", false, "Stops all containers if any container was stopped. Incompatible with -d") + upCmd.Flags().Bool("always-recreate-deps", false, "Recreate dependent containers. Incompatible with --no-recreate.") + upCmd.Flags().StringArray("attach", []string{}, "Attach to service output.") + upCmd.Flags().Bool("attach-dependencies", false, "Attach to dependent containers.") upCmd.Flags().Bool("build", false, "Build images before starting containers.") - upCmd.Flags().BoolP("detach", "d", false, "Detached mode: Run containers in the background,") - upCmd.Flags().String("exit-code-from", "", "Return the exit code of the selected service") - upCmd.Flags().Bool("force-recreate", false, "Recreate containers even if their configuration") + upCmd.Flags().BoolP("detach", "d", false, "Detached mode: Run containers in the background") + upCmd.Flags().String("exit-code-from", "", "Return the exit code of the selected service container. Implies --abort-on-container-exit") + upCmd.Flags().Bool("force-recreate", false, "Recreate containers even if their configuration and image haven't changed.") upCmd.Flags().Bool("no-build", false, "Don't build an image, even if it's missing.") upCmd.Flags().Bool("no-color", false, "Produce monochrome output.") upCmd.Flags().Bool("no-deps", false, "Don't start linked services.") - upCmd.Flags().Bool("no-recreate", false, "If containers already exist, don't recreate") + upCmd.Flags().Bool("no-log-prefix", false, "Don't print prefix in logs.") + upCmd.Flags().Bool("no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.") upCmd.Flags().Bool("no-start", false, "Don't start the services after creating them.") - upCmd.Flags().Bool("quiet-pull", false, "Pull without printing progress information") - upCmd.Flags().Bool("remove-orphans", false, "Remove containers for services not defined") - upCmd.Flags().BoolP("renew-anon-volumes", "V", false, "Recreate anonymous volumes instead of retrieving") - upCmd.Flags().String("scale", "", "Scale SERVICE to NUM instances. Overrides the") - upCmd.Flags().StringP("timeout", "t", "", "Use this timeout in seconds for container") + upCmd.Flags().Bool("quiet-pull", false, "Pull without printing progress information.") + upCmd.Flags().Bool("remove-orphans", false, "Remove containers for services not defined in the Compose file.") + upCmd.Flags().BoolP("renew-anon-volumes", "V", false, "Recreate anonymous volumes instead of retrieving data from the previous containers.") + upCmd.Flags().StringArray("scale", []string{}, "Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present.") + upCmd.Flags().IntP("timeout", "t", 10, "Use this timeout in seconds for container shutdown when attached or when containers are already running.") + upCmd.Flags().Bool("wait", false, "Wait for services to be running|healthy. Implies detached mode.") rootCmd.AddCommand(upCmd) carapace.Gen(upCmd).FlagCompletion(carapace.ActionMap{ + "attach": action.ActionServices(upCmd), "exit-code-from": action.ActionServices(upCmd), + "scale": carapace.ActionMultiParts("=", func(c carapace.Context) carapace.Action { + switch len(c.Parts) { + case 0: + return action.ActionServices(upCmd).Invoke(c).Suffix("=").ToA() + default: + return carapace.ActionValues() + } + }), }) carapace.Gen(upCmd).PositionalAnyCompletion( diff --git a/completers/docker-compose_completer/cmd/version.go b/completers/docker-compose_completer/cmd/version.go index fa49fa5f49..8dbd000d28 100644 --- a/completers/docker-compose_completer/cmd/version.go +++ b/completers/docker-compose_completer/cmd/version.go @@ -7,13 +7,17 @@ import ( var versionCmd = &cobra.Command{ Use: "version", - Short: "Show the Docker-Compose version information", + Short: "Show the Docker Compose version information", Run: func(cmd *cobra.Command, args []string) {}, } func init() { carapace.Gen(versionCmd).Standalone() - + versionCmd.Flags().StringP("format", "f", "", "Format the output. Values: [pretty | json]. (Default: pretty)") versionCmd.Flags().Bool("short", false, "Shows only Compose's version number.") rootCmd.AddCommand(versionCmd) + + carapace.Gen(versionCmd).FlagCompletion(carapace.ActionMap{ + "format": carapace.ActionValues("pretty", "json"), + }) }