Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,9 @@ func main() {
kong.BindTo(reader, (*io.Reader)(nil)),
)

apiClientRequired := !noAPIClientRequired(strings.Join(os.Args[1:], " "))
predictors := append([]completion.Option{
completion.WithPredictor("file", complete.PredictFiles("*")),
}, clientPredictors(ctx, apiClientRequired)...)
}, clientPredictors(ctx)...)
completion.Register(parser, predictors...)

kongCtx, err := parser.Parse(os.Args[1:])
Expand Down Expand Up @@ -172,7 +171,9 @@ func main() {
kong.BindTo(writer, (*io.Writer)(nil)),
kong.BindTo(reader, (*io.Reader)(nil)),
}
if apiClientRequired {
// Kong exits during Parse for --help and --version, so those cases
// never reach here. Only auth and completions commands remain.
if !noAPIClientRequired(kongCtx.Command()) {
client, err := api.New(
ctx,
cmd.APICluster,
Expand Down Expand Up @@ -217,7 +218,7 @@ func main() {
}
}

func clientPredictors(ctx context.Context, apiClientRequired bool) []completion.Option {
func clientPredictors(ctx context.Context) []completion.Option {
// complete needs all used predictors to be defined, so we just use
// [complete.PredictNothing] for those that would require an API client.
nothing := []completion.Option{
Expand All @@ -227,7 +228,10 @@ func clientPredictors(ctx context.Context, apiClientRequired bool) []completion.
completion.WithPredictor("mysql_databases", complete.PredictNothing),
}

if !apiClientRequired {
// During completion for commands that don't need an API client (auth,
// completions), this still attempts a kubeconfig read. The cost is
// negligible and the predictors are never invoked for those commands.
if os.Getenv("COMP_LINE") == "" {
return nothing
}

Expand All @@ -247,7 +251,8 @@ func clientPredictors(ctx context.Context, apiClientRequired bool) []completion.
}

// noAPIClientRequired returns true if the command does not need to (or can't)
// require an API client.
// require an API client. The command parameter is the resolved command path
// from [kong.Context.Command].
func noAPIClientRequired(command string) bool {
return matchCommand(command, auth.CmdName, format.LoginCommand) ||
matchCommand(command, auth.CmdName, format.LogoutCommand) ||
Expand Down
31 changes: 31 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand All @@ -16,3 +17,33 @@ func TestKongVars(t *testing.T) {
is.NoError(err)
is.NotEmpty(vars)
}

func TestNoAPIClientRequired(t *testing.T) {
t.Parallel()
is := assert.New(t)

// Commands use the resolved format from kong.Context.Command().
// --help and --version are not tested here because Kong exits
// during Parse before noAPIClientRequired is called.
tests := []struct {
command string
expected bool
}{
{"auth login", true},
{"auth login <organization>", true},
{"auth logout", true},
{"auth oidc", true},
{"auth client-credentials", true},
{"completions", true},
{"completions bash", true},
{"get", false},
{"get application", false},
{"get application <name>", false},
{"create application <name>", false},
{"exec application <name>", false},
{"", false},
}
for _, tt := range tests {
is.Equal(tt.expected, noAPIClientRequired(tt.command), "command: %q", tt.command)
}
}
Loading