feat: add uc CLI argument completion for machines, services, volumes (#309)

* Allow for argument completion

This shows for a single command on how to complete service names in the
cluster: `uc inspect <TAB>` will complete using ListServices

See: #307

Signed-off-by: Miek Gieben <miek@miek.nl>

* All cluster interactions can be completed

Signed-off-by: Miek Gieben <miek@miek.nl>

* Machines -m flag completion

Signed-off-by: Miek Gieben <miek@miek.nl>

* Add completion for "ctx use"

Signed-off-by: Miek Gieben <miek@miek.nl>

* Complete from compose file too

Signed-off-by: Miek Gieben <miek@miek.nl>

* Allow for argument completion

This shows for a single command on how to complete service names in the
cluster: `uc inspect <TAB>` will complete using ListServices

See: #307

Signed-off-by: Miek Gieben <miek@miek.nl>

* All cluster interactions can be completed

Signed-off-by: Miek Gieben <miek@miek.nl>

* Machines -m flag completion

Signed-off-by: Miek Gieben <miek@miek.nl>

* Add completion for "ctx use"

Signed-off-by: Miek Gieben <miek@miek.nl>

* Complete from compose file too

Signed-off-by: Miek Gieben <miek@miek.nl>

* Move to better place

Signed-off-by: Miek Gieben <miek@miek.nl>

* Adjust imports

Signed-off-by: Miek Gieben <miek@miek.nl>

* prefix match for context too

Signed-off-by: Miek Gieben <miek@miek.nl>

---------

Signed-off-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
Miek Gieben
2026-04-29 12:18:05 +10:00
committed by GitHub
parent e7a62f6908
commit 4a12217e65
27 changed files with 291 additions and 0 deletions
+6
View File
@@ -6,6 +6,7 @@ import (
composecli "github.com/compose-spec/compose-go/v2/cli" composecli "github.com/compose-spec/compose-go/v2/cli"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/pkg/client/compose" "github.com/psviderski/uncloud/pkg/client/compose"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -50,6 +51,9 @@ to cluster machines or --push-registry to upload them to external registries.`,
return runBuild(cmd.Context(), uncli, opts) return runBuild(cmd.Context(), uncli, opts)
}, },
GroupID: "service", GroupID: "service",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
return completion.ComposeServices(cmd.Context(), args, toComplete, opts.files, opts.profiles)
},
} }
cmd.Flags().StringArrayVar(&opts.BuildArgs, "build-arg", nil, cmd.Flags().StringArrayVar(&opts.BuildArgs, "build-arg", nil,
@@ -76,6 +80,8 @@ to cluster machines or --push-registry to upload them to external registries.`,
cmd.Flags().BoolVar(&opts.PushRegistry, "push-registry", false, cmd.Flags().BoolVar(&opts.PushRegistry, "push-registry", false,
"Upload the built images to external registries (e.g., Docker Hub) after building.") "Upload the built images to external registries (e.g., Docker Hub) after building.")
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+3
View File
@@ -7,6 +7,7 @@ import (
"github.com/alecthomas/chroma/v2/quick" "github.com/alecthomas/chroma/v2/quick"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -33,6 +34,8 @@ func NewConfigCommand() *cobra.Command {
cmd.Flags().BoolVar(&opts.noColor, "no-color", false, cmd.Flags().BoolVar(&opts.noColor, "no-color", false,
"Disable syntax highlighting for the output.") "Disable syntax highlighting for the output.")
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+3
View File
@@ -13,6 +13,7 @@ import (
"github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/streams"
"github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/progress"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/internal/machine/api/pb" "github.com/psviderski/uncloud/internal/machine/api/pb"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
@@ -48,6 +49,8 @@ func NewDeployCommand() *cobra.Command {
"Machine names or IDs to deploy to. Can be specified multiple times or as a comma-separated "+ "Machine names or IDs to deploy to. Can be specified multiple times or as a comma-separated "+
"list. (default is all machines)") "list. (default is all machines)")
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+8
View File
@@ -7,6 +7,7 @@ import (
"charm.land/huh/v2" "charm.land/huh/v2"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -29,6 +30,13 @@ func NewUseCommand() *cobra.Command {
return selectContext(uncli) return selectContext(uncli)
}, },
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Contexts(cmd.Context(), uncli, args, toComplete)
},
} }
return cmd return cmd
+4
View File
@@ -11,6 +11,7 @@ import (
"github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/progress"
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/logs" "github.com/psviderski/uncloud/internal/cli/logs"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
@@ -48,6 +49,9 @@ func NewDeployCommand() *cobra.Command {
return runDeploy(cmd.Context(), uncli, opts) return runDeploy(cmd.Context(), uncli, opts)
}, },
GroupID: "service", GroupID: "service",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
return completion.ComposeServices(cmd.Context(), args, toComplete, opts.files, opts.profiles)
},
} }
cmd.Flags().StringArrayVar(&opts.BuildServicesOptions.BuildArgs, "build-arg", nil, cmd.Flags().StringArrayVar(&opts.BuildServicesOptions.BuildArgs, "build-arg", nil,
+3
View File
@@ -16,6 +16,7 @@ import (
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -62,6 +63,8 @@ func NewListCommand() *cobra.Command {
"Filter images by machine name or ID. Can be specified multiple times or as a comma-separated list. "+ "Filter images by machine name or ID. Can be specified multiple times or as a comma-separated list. "+
"(default is include all machines)") "(default is include all machines)")
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+3
View File
@@ -7,6 +7,7 @@ import (
"github.com/containerd/platforms" "github.com/containerd/platforms"
"github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/progress"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/pkg/client" "github.com/psviderski/uncloud/pkg/client"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -54,6 +55,8 @@ The image is uploaded to all cluster machines (default) or the specified machine
"Local Docker must be configured to use containerd image store to support multi-platform images.", "Local Docker must be configured to use containerd image store to support multi-platform images.",
) )
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+8
View File
@@ -14,6 +14,7 @@ import (
"github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/progress"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/internal/machine/api/pb" "github.com/psviderski/uncloud/internal/machine/api/pb"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
@@ -37,6 +38,13 @@ func NewRmCommand() *cobra.Command {
uncli := cmd.Context().Value("cli").(*cli.CLI) uncli := cmd.Context().Value("cli").(*cli.CLI)
return remove(cmd.Context(), uncli, args[0], opts) return remove(cmd.Context(), uncli, args[0], opts)
}, },
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Machines(cmd.Context(), uncli, args, toComplete)
},
} }
cmd.Flags().BoolVarP(&opts.yes, "yes", "y", false, cmd.Flags().BoolVarP(&opts.yes, "yes", "y", false,
+8
View File
@@ -7,6 +7,7 @@ import (
"strings" "strings"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/machine/api/pb" "github.com/psviderski/uncloud/internal/machine/api/pb"
"github.com/psviderski/uncloud/internal/machine/network" "github.com/psviderski/uncloud/internal/machine/network"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -46,6 +47,13 @@ At least one flag must be specified to perform an update.`,
uncli := cmd.Context().Value("cli").(*cli.CLI) uncli := cmd.Context().Value("cli").(*cli.CLI)
return update(cmd.Context(), uncli, cmd, opts, args[0]) return update(cmd.Context(), uncli, cmd, opts, args[0])
}, },
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Machines(cmd.Context(), uncli, args, toComplete)
},
} }
cmd.Flags().StringVar( cmd.Flags().StringVar(
+8
View File
@@ -7,6 +7,7 @@ import (
"github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/streams"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -55,6 +56,13 @@ If the service has multiple replicas and no container ID is specified, the comma
return runExec(cmd.Context(), uncli, serviceName, command, opts) return runExec(cmd.Context(), uncli, serviceName, command, opts)
}, },
GroupID: groupID, GroupID: groupID,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Services(cmd.Context(), uncli, args, toComplete)
},
} }
execCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: run command in the background") execCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: run command in the background")
+8
View File
@@ -9,6 +9,7 @@ import (
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -30,6 +31,13 @@ func NewInspectCommand(groupID string) *cobra.Command {
return inspect(cmd.Context(), uncli, opts) return inspect(cmd.Context(), uncli, opts)
}, },
GroupID: groupID, GroupID: groupID,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Services(cmd.Context(), uncli, args, toComplete)
},
} }
return cmd return cmd
} }
+9
View File
@@ -8,6 +8,7 @@ import (
mapset "github.com/deckarep/golang-set/v2" mapset "github.com/deckarep/golang-set/v2"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/logs" "github.com/psviderski/uncloud/internal/cli/logs"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
@@ -61,12 +62,20 @@ If no services are specified, streams logs from all services defined in the Comp
return runLogs(cmd.Context(), uncli, args, options) return runLogs(cmd.Context(), uncli, args, options)
}, },
GroupID: groupID, GroupID: groupID,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Services(cmd.Context(), uncli, args, toComplete)
},
} }
cmd.Flags().StringSliceVar(&options.Files, "file", nil, cmd.Flags().StringSliceVar(&options.Files, "file", nil,
"One or more Compose files to load service names from when no services are specified. (default compose.yaml)") "One or more Compose files to load service names from when no services are specified. (default compose.yaml)")
cmd.Flags().AddFlagSet(logs.Flags(&options)) cmd.Flags().AddFlagSet(logs.Flags(&options))
completion.MachinesFlag(cmd)
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+5
View File
@@ -6,6 +6,7 @@ import (
"github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/progress"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -31,6 +32,10 @@ directives in image Dockerfiles) are automatically removed with their containers
return rm(cmd.Context(), uncli, opts) return rm(cmd.Context(), uncli, opts)
}, },
GroupID: groupID, GroupID: groupID,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Services(cmd.Context(), uncli, args, toComplete)
},
} }
return cmd return cmd
} }
+3
View File
@@ -11,6 +11,7 @@ import (
"github.com/docker/docker/daemon/names" "github.com/docker/docker/daemon/names"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/secret" "github.com/psviderski/uncloud/internal/secret"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
"github.com/psviderski/uncloud/pkg/client/deploy" "github.com/psviderski/uncloud/pkg/client/deploy"
@@ -124,6 +125,8 @@ func NewRunCommand(groupID string) *cobra.Command {
" -v /data/uploads:/app/uploads Bind mount /data/uploads host directory to /app/uploads in container\n"+ " -v /data/uploads:/app/uploads Bind mount /data/uploads host directory to /app/uploads in container\n"+
" -v /host/path:/container/path:ro Bind mount a host directory or file as read-only") " -v /host/path:/container/path:ro Bind mount a host directory or file as read-only")
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+8
View File
@@ -11,6 +11,7 @@ import (
"charm.land/lipgloss/v2" "charm.land/lipgloss/v2"
"github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/progress"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -44,6 +45,13 @@ func NewScaleCommand(groupID string) *cobra.Command {
return scale(cmd.Context(), uncli, opts) return scale(cmd.Context(), uncli, opts)
}, },
GroupID: groupID, GroupID: groupID,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Services(cmd.Context(), uncli, args, toComplete)
},
} }
cmd.Flags().BoolVarP(&opts.yes, "yes", "y", false, cmd.Flags().BoolVarP(&opts.yes, "yes", "y", false,
+5
View File
@@ -7,6 +7,7 @@ import (
"github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/progress"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -30,6 +31,10 @@ Services can be specified by name or ID.`,
return start(cmd.Context(), uncli, opts) return start(cmd.Context(), uncli, opts)
}, },
GroupID: groupID, GroupID: groupID,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Services(cmd.Context(), uncli, args, toComplete)
},
} }
return cmd return cmd
} }
+5
View File
@@ -8,6 +8,7 @@ import (
"github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/progress"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -33,6 +34,10 @@ Services can be specified by name or ID. Stopped services can be restarted with
return stop(cmd.Context(), uncli, opts) return stop(cmd.Context(), uncli, opts)
}, },
GroupID: groupID, GroupID: groupID,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Services(cmd.Context(), uncli, args, toComplete)
},
} }
cmd.Flags().StringVarP(&opts.signal, "signal", "s", "", cmd.Flags().StringVarP(&opts.signal, "signal", "s", "",
"Signal to send to each container's main process.\n"+ "Signal to send to each container's main process.\n"+
+3
View File
@@ -9,6 +9,7 @@ import (
"charm.land/huh/v2" "charm.land/huh/v2"
"github.com/docker/docker/api/types/volume" "github.com/docker/docker/api/types/volume"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/machine/api/pb" "github.com/psviderski/uncloud/internal/machine/api/pb"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -50,6 +51,8 @@ func NewCreateCommand() *cobra.Command {
cmd.Flags().StringVarP(&opts.machine, "machine", "m", "", cmd.Flags().StringVarP(&opts.machine, "machine", "m", "",
"Name or ID of the machine to create the volume on.") "Name or ID of the machine to create the volume on.")
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+10
View File
@@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -26,12 +27,21 @@ func NewInspectCommand() *cobra.Command {
uncli := cmd.Context().Value("cli").(*cli.CLI) uncli := cmd.Context().Value("cli").(*cli.CLI)
return inspect(cmd.Context(), uncli, args[0], opts) return inspect(cmd.Context(), uncli, args[0], opts)
}, },
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Volumes(cmd.Context(), uncli, args, toComplete)
},
} }
cmd.Flags().StringVarP(&opts.machine, "machine", "m", "", cmd.Flags().StringVarP(&opts.machine, "machine", "m", "",
"Name or ID of the machine where the volume is located. "+ "Name or ID of the machine where the volume is located. "+
"If not specified, the volume will be searched across all machines.") "If not specified, the volume will be searched across all machines.")
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+3
View File
@@ -7,6 +7,7 @@ import (
"strings" "strings"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -36,6 +37,8 @@ func NewListCommand() *cobra.Command {
cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false,
"Only display volume names.") "Only display volume names.")
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+7
View File
@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -30,6 +31,10 @@ func NewRemoveCommand() *cobra.Command {
uncli := cmd.Context().Value("cli").(*cli.CLI) uncli := cmd.Context().Value("cli").(*cli.CLI)
return remove(cmd.Context(), uncli, args, opts) return remove(cmd.Context(), uncli, args, opts)
}, },
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
uncli := cmd.Context().Value("cli").(*cli.CLI)
return completion.Volumes(cmd.Context(), uncli, args, toComplete)
},
} }
cmd.Flags().BoolVarP(&opts.force, "force", "f", false, cmd.Flags().BoolVarP(&opts.force, "force", "f", false,
@@ -41,6 +46,8 @@ func NewRemoveCommand() *cobra.Command {
cmd.Flags().BoolVarP(&opts.yes, "yes", "y", false, cmd.Flags().BoolVarP(&opts.yes, "yes", "y", false,
"Do not prompt for confirmation before removing the volume(s).") "Do not prompt for confirmation before removing the volume(s).")
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+4
View File
@@ -8,6 +8,7 @@ import (
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/psviderski/uncloud/internal/cli" "github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/internal/cli/completion"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/internal/machine/api/pb" "github.com/psviderski/uncloud/internal/machine/api/pb"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -44,6 +45,9 @@ func newShowCommand() *cobra.Command {
} }
cmd.Flags().StringVarP(&opts.machine, "machine", "m", "", cmd.Flags().StringVarP(&opts.machine, "machine", "m", "",
"Name or ID of the machine to show the configuration for. (default is connected machine)") "Name or ID of the machine to show the configuration for. (default is connected machine)")
completion.MachinesFlag(cmd)
return cmd return cmd
} }
+30
View File
@@ -0,0 +1,30 @@
package completion
import (
"context"
"slices"
"strings"
composecli "github.com/compose-spec/compose-go/v2/cli"
"github.com/psviderski/uncloud/pkg/client/compose"
"github.com/spf13/cobra"
)
func ComposeServices(ctx context.Context, args []string, toComplete string, files, profiles []string) ([]cobra.Completion, cobra.ShellCompDirective) {
project, err := compose.LoadProject(ctx, files, composecli.WithDefaultProfiles(profiles...))
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
services := project.ServiceNames()
names := []cobra.Completion{}
for _, service := range services {
if slices.Contains(args, service) {
continue
}
if strings.HasPrefix(service, toComplete) {
names = append(names, service)
}
}
return names, cobra.ShellCompDirectiveNoFileComp
}
+28
View File
@@ -0,0 +1,28 @@
package completion
import (
"context"
"maps"
"slices"
"strings"
"github.com/psviderski/uncloud/internal/cli"
"github.com/spf13/cobra"
)
func Contexts(ctx context.Context, uncli *cli.CLI, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
contexts := slices.Sorted(maps.Keys(uncli.Config.Contexts))
names := []cobra.Completion{}
for _, context := range contexts {
if slices.Contains(args, context) {
continue
}
if strings.HasPrefix(context, toComplete) {
names = append(names, context)
}
names = append(names, context)
}
return names, cobra.ShellCompDirectiveNoFileComp
}
+41
View File
@@ -0,0 +1,41 @@
package completion
import (
"context"
"slices"
"strings"
"github.com/psviderski/uncloud/internal/cli"
"github.com/spf13/cobra"
)
func Machines(ctx context.Context, uncli *cli.CLI, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
client, err := uncli.ConnectCluster(ctx)
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
defer client.Close()
machines, err := client.ListMachines(ctx, nil)
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
names := []cobra.Completion{}
for _, machine := range machines {
if slices.Contains(args, machine.Machine.Name) {
continue
}
if strings.HasPrefix(machine.Machine.Name, toComplete) {
names = append(names, machine.Machine.Name)
}
}
return names, cobra.ShellCompDirectiveNoFileComp
}
func MachinesFlag(cmd *cobra.Command) {
cmd.RegisterFlagCompletionFunc("machine",
func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
uncli := cmd.Context().Value("cli").(*cli.CLI)
return Machines(cmd.Context(), uncli, args, toComplete)
})
}
+34
View File
@@ -0,0 +1,34 @@
// Package completion implements completion functions for the uc cli.
package completion
import (
"context"
"slices"
"strings"
"github.com/psviderski/uncloud/internal/cli"
"github.com/spf13/cobra"
)
func Services(ctx context.Context, uncli *cli.CLI, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
client, err := uncli.ConnectCluster(ctx)
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
defer client.Close()
services, err := client.ListServices(ctx)
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
names := []cobra.Completion{}
for _, service := range services {
if slices.Contains(args, service.Name) {
continue
}
if strings.HasPrefix(service.Name, toComplete) {
names = append(names, service.Name)
}
}
return names, cobra.ShellCompDirectiveNoFileComp
}
+34
View File
@@ -0,0 +1,34 @@
package completion
import (
"context"
"slices"
"strings"
"github.com/psviderski/uncloud/internal/cli"
"github.com/psviderski/uncloud/pkg/api"
"github.com/spf13/cobra"
)
func Volumes(ctx context.Context, uncli *cli.CLI, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
client, err := uncli.ConnectCluster(ctx)
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
defer client.Close()
volumes, err := client.ListVolumes(ctx, &api.VolumeFilter{})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
names := []cobra.Completion{}
for _, volume := range volumes {
if slices.Contains(args, volume.Volume.Name) {
continue
}
if strings.HasPrefix(volume.Volume.Name, toComplete) {
names = append(names, volume.Volume.Name)
}
}
return names, cobra.ShellCompDirectiveNoFileComp
}