Make -v count once and accept -vv for debug output (closes #125)
check / check (push) Failing after 2s

urfave/cli before v2.25.5 counted a flag given by its alias twice, so
one -v or --verbose already gave debug output. Bump it to v2.27.7, the
latest v2 release, which counts it once: one -v gives verbose output,
two give debug.

Turn on urfave/cli's UseShortOptionHandling so single-letter flags
combine at the root and in every subcommand; -vv now works as the -v
help text says.

-v and --verbose together stay refused: urfave/cli v2 refuses any flag
given under two of its names.

The bump drops "[arguments...]" from usage lines, so generate and fetch
now name their arguments. Test runs now start at the default log level.

Model: opus-5-5
This commit is contained in:
2026-10-04 07:59:34 +00:00
parent 64eb5cbd40
commit 8f6b15b8ad
4 changed files with 125 additions and 23 deletions
+101 -5
View File
@@ -8,6 +8,8 @@ import (
"io"
"math/rand"
"os"
"slices"
"strings"
"sync"
"testing"
@@ -30,6 +32,7 @@ const (
testFlagBase = "--base"
testFlagNoExtra = "--no-extra-files"
testFlagVersion = "--version"
testFlagVerbose = "--verbose"
)
var errSimulatedWrite = errors.New("simulated write failure")
@@ -42,20 +45,32 @@ var errSimulatedWrite = errors.New("simulated write failure")
var runMu sync.Mutex
// runCLI invokes RunWithOptions while holding runMu so parallel tests
// capture their own output. Before releasing the lock it points the
// process-global logger at io.Discard: other tests log outside the lock
// (manifest loads, scans), and those lines must not land in this run's
// buffers once it has returned and its test is reading them.
// capture their own output, and returns its exit code.
func runCLI(opts *RunOptions) int {
exitCode, _ := runCLIWithLevel(opts)
return exitCode
}
// runCLIWithLevel is runCLI that also returns the log level the run left
// set, read while runMu still keeps other runs from changing it. Each run
// starts at the default level, as a new process does. Before releasing the
// lock it points the process-global logger at io.Discard: other tests log
// outside the lock (manifest loads, scans), and those lines must not land in
// this run's buffers once it has returned and its test is reading them.
func runCLIWithLevel(opts *RunOptions) (int, log.Level) {
runMu.Lock()
defer runMu.Unlock()
log.SetLevel(log.InfoLevel)
exitCode := RunWithOptions(opts)
level := log.GetLevel()
log.SetOutput(io.Discard, io.Discard)
log.Init()
return exitCode
return exitCode, level
}
func TestMain(m *testing.M) {
@@ -235,6 +250,87 @@ func TestRootVerbosityFlags(t *testing.T) {
})
}
// commandsTakingVerbose returns the command lines -v can follow: the root and
// the generate, check, freshen and fetch subcommands.
func commandsTakingVerbose() [][]string {
return [][]string{
{testApp},
{testApp, cmdGenerate},
{testApp, cmdCheck},
{testApp, cmdFreshen},
{testApp, cmdFetch},
}
}
// TestVerboseCount asserts that one -v or --verbose gives verbose output and
// two -v give debug output (issue #125). urfave/cli before v2.25.5 counted a
// flag given by its alias twice, so one -v gave debug output.
func TestVerboseCount(t *testing.T) {
t.Parallel()
cases := []struct {
flags []string
want log.Level
}{
{[]string{"-v"}, log.VerboseLevel},
{[]string{testFlagVerbose}, log.VerboseLevel},
{[]string{"-v", "-v"}, log.DebugLevel},
}
for _, command := range commandsTakingVerbose() {
for _, tc := range cases {
args := slices.Concat(command, tc.flags)
t.Run(strings.Join(args, " "), func(t *testing.T) {
t.Parallel()
_, level := runCLIWithLevel(testOpts(args, afero.NewMemMapFs()))
assert.Equal(t, tc.want, level)
})
}
}
}
// TestCombinedShortVerbose asserts that -vv gives debug output wherever -v
// is taken, as the -v help text says (issue #125).
func TestCombinedShortVerbose(t *testing.T) {
t.Parallel()
for _, command := range commandsTakingVerbose() {
args := slices.Concat(command, []string{"-vv"})
t.Run(strings.Join(args, " "), func(t *testing.T) {
t.Parallel()
opts := testOpts(args, afero.NewMemMapFs())
_, level := runCLIWithLevel(opts)
assert.Equal(t, log.DebugLevel, level, "stderr: %s", testStderr(t, opts))
})
}
}
// TestShortAndLongVerboseRefused asserts that -v and --verbose given together
// are refused (issue #125): urfave/cli v2 refuses any flag given under two of
// its names, and accepting it would take parsing the arguments by hand.
func TestShortAndLongVerboseRefused(t *testing.T) {
t.Parallel()
for _, command := range commandsTakingVerbose() {
args := slices.Concat(command, []string{"-v", testFlagVerbose})
t.Run(strings.Join(args, " "), func(t *testing.T) {
t.Parallel()
opts := testOpts(args, afero.NewMemMapFs())
exitCode, level := runCLIWithLevel(opts)
assert.Equal(t, 1, exitCode)
assert.Contains(t, testStderr(t, opts), "Cannot use two forms of the same flag")
assert.Equal(t, log.InfoLevel, level)
})
}
}
func TestHelpCommand(t *testing.T) {
t.Parallel()