From e4e72bc52abc504ca38a11058453daae221eb058 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 02:39:22 +0000 Subject: [PATCH] Support stacked ansible verbosity flags --- cmd/ansible/ansible.go | 38 +++++++++++++++++++++++++++++++++---- cmd/ansible/ansible_test.go | 18 ++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/cmd/ansible/ansible.go b/cmd/ansible/ansible.go index c7a9c6e..1c23aac 100644 --- a/cmd/ansible/ansible.go +++ b/cmd/ansible/ansible.go @@ -2,6 +2,9 @@ package anscmd import ( "context" + "os" + "strconv" + "strings" "time" "dappco.re/go/core" @@ -43,6 +46,36 @@ func firstBool(opts core.Options, keys ...string) bool { return false } +// verbosityLevel resolves the effective verbosity from parsed options and the +// raw command line arguments. The core CLI parser does not preserve repeated +// `-v` tokens, so we count them from os.Args as a fallback. +func verbosityLevel(opts core.Options, rawArgs []string) int { + level := opts.Int("verbose") + if firstBool(opts, "v") && level < 1 { + level = 1 + } + + for _, arg := range rawArgs { + switch { + case arg == "-v" || arg == "--verbose": + level++ + case strings.HasPrefix(arg, "--verbose="): + if n, err := strconv.Atoi(strings.TrimPrefix(arg, "--verbose=")); err == nil && n > level { + level = n + } + case strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--"): + short := strings.TrimPrefix(arg, "-") + if short != "" && strings.Trim(short, "v") == "" { + if n := len([]rune(short)); n > level { + level = n + } + } + } + } + + return level +} + // extraVars collects all repeated extra-vars values from Options. func extraVars(opts core.Options) map[string]string { vars := make(map[string]string) @@ -119,10 +152,7 @@ func runAnsible(opts core.Options) core.Result { executor.Limit = firstString(opts, "limit", "l") executor.CheckMode = opts.Bool("check") executor.Diff = opts.Bool("diff") - executor.Verbose = opts.Int("verbose") - if firstBool(opts, "v") && executor.Verbose < 1 { - executor.Verbose = 1 - } + executor.Verbose = verbosityLevel(opts, os.Args[1:]) if tags := firstString(opts, "tags", "t"); tags != "" { executor.Tags = split(tags, ",") diff --git a/cmd/ansible/ansible_test.go b/cmd/ansible/ansible_test.go index c3565fd..ceedd7e 100644 --- a/cmd/ansible/ansible_test.go +++ b/cmd/ansible/ansible_test.go @@ -69,6 +69,24 @@ func TestFirstBool_Good_UsesAlias(t *testing.T) { assert.True(t, firstBool(opts, "verbose", "v")) } +func TestVerbosityLevel_Good_CountsStackedShortFlags(t *testing.T) { + opts := core.NewOptions() + + assert.Equal(t, 3, verbosityLevel(opts, []string{"-vvv"})) +} + +func TestVerbosityLevel_Good_CountsLongForm(t *testing.T) { + opts := core.NewOptions() + + assert.Equal(t, 1, verbosityLevel(opts, []string{"--verbose"})) +} + +func TestVerbosityLevel_Good_PreservesExplicitNumericLevel(t *testing.T) { + opts := core.NewOptions(core.Option{Key: "verbose", Value: 2}) + + assert.Equal(t, 2, verbosityLevel(opts, nil)) +} + func TestTestKeyFile_Good_PrefersExplicitKey(t *testing.T) { opts := core.NewOptions( core.Option{Key: "key", Value: "/tmp/id_ed25519"},