From 2d927bbb78c041ee62106877172fe3e79dbd6f3f Mon Sep 17 00:00:00 2001 From: JSap0914 Date: Tue, 14 Jul 2026 18:04:31 +0900 Subject: [PATCH 1/2] fix: treat arguments after delimiter as paths --- main.go | 17 +++++++------- main_test.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/main.go b/main.go index 323b056..5bc1258 100644 --- a/main.go +++ b/main.go @@ -49,12 +49,16 @@ func catchPanic(err any) { func preprocessArgs() { rearrangeArgs() + argsBeforeDelimiter := os.Args + if i := slices.Index(os.Args, "--"); i >= 0 { + argsBeforeDelimiter = os.Args[:i] + } // normal logic // load config if the args do not contains -no-config - if !slices.ContainsFunc(os.Args, hasNoConfig) { + if !slices.ContainsFunc(argsBeforeDelimiter, hasNoConfig) { defaultArgs, err := config.Load() // if successfully load config and **the config.Args do not contain -no-config** - if err == nil && !slices.ContainsFunc(defaultArgs.Args, hasNoConfig) { + if err == nil && defaultArgs != nil && !slices.ContainsFunc(defaultArgs.Args, hasNoConfig) { os.Args = slices.Insert(os.Args, 1, defaultArgs.Args...) } else if err != nil { // if failed to load config // if it's read error @@ -66,7 +70,7 @@ func preprocessArgs() { } else { // contains -no-config // remove it before the cli.G starts - os.Args = slices.DeleteFunc(os.Args, hasNoConfig) + os.Args = append(slices.DeleteFunc(argsBeforeDelimiter, hasNoConfig), os.Args[len(argsBeforeDelimiter):]...) } } @@ -86,11 +90,8 @@ func separateArgs(args []string) (flags, paths []string) { arg := args[i] if arg == "--" { hasDoubleDash = true - if i+1 < len(args) { - paths = append(paths, args[i+1]) - i++ - } - continue + paths = append(paths, args[i+1:]...) + break } if strings.HasPrefix(arg, "--") { i = handleLongFlag(arg, args, i, &flags, &expectValue, flagsWithArgs) diff --git a/main_test.go b/main_test.go index 3133d62..24c5ff8 100644 --- a/main_test.go +++ b/main_test.go @@ -73,6 +73,61 @@ func Test_preprocessArgs(t *testing.T) { assert.Equal(t, 2, len(os.Args)) } +func TestPreprocessArgs_DelimiterProtectsNoConfigPaths(t *testing.T) { + originalArgs := os.Args + t.Cleanup(func() { os.Args = originalArgs }) + tests := []struct { + name string + args []string + expectConfigLoad bool + expectedArgs []string + }{ + { + name: "suffix paths do not disable config", + args: []string{"g", "--", "--no-config", "-no-config"}, + expectConfigLoad: true, + expectedArgs: []string{"g", "--", "--no-config", "-no-config"}, + }, + { + name: "prefix flag disables config without removing suffix paths", + args: []string{"g", "--no-config", "--", "--no-config", "-no-config"}, + expectConfigLoad: false, + expectedArgs: []string{"g", "--", "--no-config", "-no-config"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + configLoaded := false + patch := gomonkey.ApplyFunc(config.Load, func() (*config.Config, error) { + configLoaded = true + return &config.Config{}, nil + }) + defer patch.Reset() + os.Args = append([]string{}, tt.args...) + + preprocessArgs() + + assert.Equal(t, tt.expectConfigLoad, configLoaded) + require.Equal(t, tt.expectedArgs, os.Args) + }) + } +} + +func TestPreprocessArgs_NilConfigKeepsDelimiterPaths(t *testing.T) { + originalArgs := os.Args + t.Cleanup(func() { os.Args = originalArgs }) + patch := gomonkey.ApplyFunc(config.Load, func() (*config.Config, error) { + return nil, nil + }) + t.Cleanup(patch.Reset) + os.Args = []string{"g", "--", "--no-config"} + + assert.NotPanics(t, preprocessArgs) + + require.Equal(t, []string{"g", "--", "--no-config"}, os.Args) +} + func TestSeparateArgs(t *testing.T) { originalFlags := cli.G.Flags defer func() { cli.G.Flags = originalFlags }() @@ -118,6 +173,12 @@ func TestSeparateArgs(t *testing.T) { expectedFlags: []string{"--all", "--"}, expectedPaths: []string{"dir1", "--sort", "name"}, }, + { + name: "Delimiter protects all following paths", + args: []string{"--", "a", "--bad"}, + expectedFlags: []string{"--"}, + expectedPaths: []string{"a", "--bad"}, + }, { name: "Short flags", args: []string{"-a", "-s", "name", "dir1"}, @@ -133,8 +194,8 @@ func TestSeparateArgs(t *testing.T) { { name: "Complex case with double dash", args: []string{"--all", "dir1", "--term-width", "100", "-s", "name", "--", "-a", "-a", "-l", "dir2", "--", "--fake-flag"}, - expectedFlags: []string{"--all", "--term-width", "100", "-s", "name", "-a", "-l", "--"}, - expectedPaths: []string{"dir1", "-a", "dir2", "--fake-flag"}, + expectedFlags: []string{"--all", "--term-width", "100", "-s", "name", "--"}, + expectedPaths: []string{"dir1", "-a", "-a", "-l", "dir2", "--", "--fake-flag"}, }, } From 2766c4dbec1ad16252c8301f3572f0def561e206 Mon Sep 17 00:00:00 2001 From: Equationzhao Date: Fri, 14 Aug 2026 19:49:19 +0800 Subject: [PATCH 2/2] fix: clone args before deleting --no-config DeleteFunc mutates the shared os.Args backing array. Clone the prefix first so the delimiter suffix is never read from a mutated slice. Co-authored-by: Claude Co-authored-by: Codex --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 5bc1258..71faf1e 100644 --- a/main.go +++ b/main.go @@ -70,7 +70,7 @@ func preprocessArgs() { } else { // contains -no-config // remove it before the cli.G starts - os.Args = append(slices.DeleteFunc(argsBeforeDelimiter, hasNoConfig), os.Args[len(argsBeforeDelimiter):]...) + os.Args = append(slices.DeleteFunc(slices.Clone(argsBeforeDelimiter), hasNoConfig), os.Args[len(argsBeforeDelimiter):]...) } }