diff --git a/cmd/config/cmd/complete/complete.go b/cmd/config/cmd/complete/complete.go new file mode 100644 index 000000000..5f45138cc --- /dev/null +++ b/cmd/config/cmd/complete/complete.go @@ -0,0 +1,68 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package complete + +import ( + "os" + "strings" + + "github.com/posener/complete/v2" + "github.com/posener/complete/v2/predict" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "sigs.k8s.io/kustomize/cmd/config/cmddocs/commands" + "sigs.k8s.io/kustomize/kyaml/errors" +) + +// NewCommand returns a new install-completion command +func NewCommand() *cobra.Command { + return &cobra.Command{ + Use: "install-completion", + Short: commands.CompletionShort, + Long: commands.CompletionLong, + PreRunE: preRunE, + Run: run, + } +} + +func preRunE(cmd *cobra.Command, args []string) error { + // install by default + if os.Getenv("COMP_INSTALL") == "" { + if err := errors.Wrap(os.Setenv("COMP_INSTALL", "1")); err != nil { + return err + } + } + return nil +} + +func run(cmd *cobra.Command, args []string) { + // find the root command + for cmd.Parent() != nil { + cmd = cmd.Parent() + } + + // do completion + Complete(cmd).Complete("kustomize") +} + +// Complete returns a completion command for a cobra command +func Complete(cmd *cobra.Command) *complete.Command { + cc := &complete.Command{ + Flags: map[string]complete.Predictor{}, + Sub: map[string]*complete.Command{}, + } + + // add completion for each subcommand + for i := range cmd.Commands() { + c := cmd.Commands()[i] + name := strings.Split(c.Use, " ")[0] + cc.Sub[name] = Complete(c) + } + + // add completion for each flag + cmd.Flags().VisitAll(func(flag *pflag.Flag) { + cc.Flags[flag.Name] = predict.Nothing + }) + return cc +} diff --git a/cmd/config/cmddocs/commands/docs.go b/cmd/config/cmddocs/commands/docs.go index 88afe9b70..83fa2627e 100644 --- a/cmd/config/cmddocs/commands/docs.go +++ b/cmd/config/cmddocs/commands/docs.go @@ -21,6 +21,23 @@ var CatExamples = ` # unwrap Resource config from a directory in an ResourceList ... | kustomize config cat` +var CompletionShort = `Install shell completion.` +var CompletionLong = ` +Install shell completion for kustomize commands and flags -- supports bash, fish and zsh. + + kustomize install-completion + +Registers the completion command with known shells (e.g. .bashrc, .bash_profile, etc): + + complete -C /Users/USER/go/bin/kustomize kustomize + +Because the completion command is embedded in kustomize directly, there is no need to update +it separately from the kustomize binary. + +To uninstall shell completion run: + + COMP_UNINSTALL=1 kustomize install-completion` + var CountShort = `[Alpha] Count Resources Config from a local directory.` var CountLong = ` [Alpha] Count Resources Config from a local directory. @@ -123,7 +140,7 @@ var RunFnsShort = `[Alpha] Reoncile config functions to Resources.` var RunFnsLong = ` [Alpha] Reconcile config functions to Resources. -run sequentially invokes all config functions in the directly, providing Resources +run sequentially invokes all config functions in the directory, providing Resources in the directory as input to the first function, and writing the output of the last function back to the directory. @@ -140,7 +157,7 @@ order they appear in the file). #### Config Functions: Config functions are specified as Kubernetes types containing a metadata.configFn.container.image - field. This fields tells run how to invoke the container. + field. This field tells run how to invoke the container. Example config function: @@ -160,7 +177,7 @@ order they appear in the file). In the preceding example, 'kustomize config run example/' would identify the function by the metadata.configFn field. It would then write all Resources in the directory to a container stdin (running the gcr.io/example/examplefunction:v1.0.1 image). It - would then writer the container stdout back to example/, replacing the directory + would then write the container stdout back to example/, replacing the directory file contents. See ` + "`" + `kustomize config help docs-fn` + "`" + ` for more details on writing functions. diff --git a/cmd/config/docs/commands/completion.md b/cmd/config/docs/commands/completion.md new file mode 100644 index 000000000..2f9889dea --- /dev/null +++ b/cmd/config/docs/commands/completion.md @@ -0,0 +1,20 @@ +## install-completion + +Install shell completion. + +### Synopsis + +Install shell completion for kustomize commands and flags -- supports bash, fish and zsh. + + kustomize install-completion + +Registers the completion command with known shells (e.g. .bashrc, .bash_profile, etc): + + complete -C /Users/USER/go/bin/kustomize kustomize + +Because the completion command is embedded in kustomize directly, there is no need to update +it separately from the kustomize binary. + +To uninstall shell completion run: + + COMP_UNINSTALL=1 kustomize install-completion diff --git a/cmd/config/go.mod b/cmd/config/go.mod index 623625bc1..5a701abcd 100644 --- a/cmd/config/go.mod +++ b/cmd/config/go.mod @@ -4,8 +4,9 @@ go 1.13 require ( github.com/go-errors/errors v1.0.1 + github.com/posener/complete/v2 v2.0.1-alpha.12 github.com/spf13/cobra v0.0.5 - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.4.0 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect sigs.k8s.io/kustomize/kyaml v0.0.0 diff --git a/cmd/config/go.sum b/cmd/config/go.sum index e3d9d753a..3b39f989c 100644 --- a/cmd/config/go.sum +++ b/cmd/config/go.sum @@ -54,6 +54,10 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -94,6 +98,10 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete/v2 v2.0.1-alpha.12 h1:0wvkuDfHb5vSZlNBYgpEH4XQHpF46MjLPHav8XC77Nc= +github.com/posener/complete/v2 v2.0.1-alpha.12/go.mod h1://JlL91cS2JV7rOl6LVHrRqBXoBUecJu3ILQPgbJiMQ= +github.com/posener/script v1.0.4 h1:nSuXW5ZdmFnQIueLB2s0qvs4oNsUloM1Zydzh75v42w= +github.com/posener/script v1.0.4/go.mod h1:Rg3ijooqulo05aGLyGsHoLmIOUzHUVK19WVgrYBPU/E= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= diff --git a/kustomize/go.sum b/kustomize/go.sum index 2ddaf9666..bca67130c 100644 --- a/kustomize/go.sum +++ b/kustomize/go.sum @@ -146,6 +146,10 @@ github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:Fecb github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -218,6 +222,10 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete/v2 v2.0.1-alpha.12 h1:0wvkuDfHb5vSZlNBYgpEH4XQHpF46MjLPHav8XC77Nc= +github.com/posener/complete/v2 v2.0.1-alpha.12/go.mod h1://JlL91cS2JV7rOl6LVHrRqBXoBUecJu3ILQPgbJiMQ= +github.com/posener/script v1.0.4 h1:nSuXW5ZdmFnQIueLB2s0qvs4oNsUloM1Zydzh75v42w= +github.com/posener/script v1.0.4/go.mod h1:Rg3ijooqulo05aGLyGsHoLmIOUzHUVK19WVgrYBPU/E= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -391,6 +399,8 @@ k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= +sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= +sigs.k8s.io/kustomize/cmd/config v0.0.0-20191206163250-370345093178 h1:6kTHmTk57bi0x/e2Pz8tUWnyvNl2SIE5yyXHW+nY+Go= sigs.k8s.io/kustomize/pluginator/v2 v2.0.0/go.mod h1:zrXhTv8BAKt0egmZX/8AtMOSFUSWM9YuoHvvqz8/eHE= sigs.k8s.io/kustomize/pseudo/k8s v0.1.0 h1:otg4dLFc03c3gzl+2CV8GPGcd1kk8wjXwD+UhhcCn5I= sigs.k8s.io/kustomize/pseudo/k8s v0.1.0/go.mod h1:bl/gVJgYYhJZCZdYU2BfnaKYAlqFkgbJEkpl302jEss= diff --git a/kustomize/internal/commands/commands.go b/kustomize/internal/commands/commands.go index fbff71f61..d15b32567 100644 --- a/kustomize/internal/commands/commands.go +++ b/kustomize/internal/commands/commands.go @@ -13,6 +13,7 @@ import ( "sigs.k8s.io/kustomize/api/k8sdeps/kunstruct" "sigs.k8s.io/kustomize/api/k8sdeps/validator" "sigs.k8s.io/kustomize/api/konfig" + shell_complete "sigs.k8s.io/kustomize/cmd/config/cmd/complete" "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/build" "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/config" "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/create" @@ -36,6 +37,7 @@ See https://sigs.k8s.io/kustomize uf := kunstruct.NewKunstructuredFactoryImpl() v := validator.NewKustValidator() c.AddCommand( + shell_complete.NewCommand(), build.NewCmdBuild(stdOut), edit.NewCmdEdit(fSys, v, uf), create.NewCmdCreate(fSys, uf), diff --git a/kustomize/main.go b/kustomize/main.go index 1bec068fd..fd56bf931 100644 --- a/kustomize/main.go +++ b/kustomize/main.go @@ -6,13 +6,35 @@ package main import ( "os" + "path/filepath" + "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/cmd/config/cmd/complete" "sigs.k8s.io/kustomize/kustomize/v3/internal/commands" ) func main() { - if err := commands.NewDefaultCommand().Execute(); err != nil { + cmd := commands.NewDefaultCommand() + completion(cmd) + + if err := cmd.Execute(); err != nil { os.Exit(1) } os.Exit(0) } + +// completion performs shell completion if kustomize is being called to provide +// shell completion commands. +func completion(cmd *cobra.Command) { + // bash shell completion passes the command name as the first argument + // do this after configuring cmd so it has all the subcommands + if len(os.Args) > 1 { + // use the base name in case kustomize is called with an absolute path + name := filepath.Base(os.Args[1]) + if name == "kustomize" { + // complete calls kustomize with itself as an argument + complete.Complete(cmd).Complete("kustomize") + os.Exit(0) + } + } +}