Skip to content

Commit 6dff762

Browse files
corylanouclaude
andauthored
Improve error message when flags are positioned after positional arguments (#736)
Co-authored-by: Claude <[email protected]>
1 parent eba7ccc commit 6dff762

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

cmd/litestream/replicate.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
_ "net/http/pprof"
1111
"os"
1212
"os/exec"
13+
"strings"
1314

1415
"github.com/mattn/go-shellwords"
1516
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -79,6 +80,10 @@ func (c *ReplicateCommand) ParseFlags(_ context.Context, args []string) (err err
7980

8081
dbConfig := &DBConfig{Path: fs.Arg(0)}
8182
for _, u := range fs.Args()[1:] {
83+
// Check if this looks like a flag that was placed after positional arguments
84+
if strings.HasPrefix(u, "-") {
85+
return fmt.Errorf("flag %q must be positioned before DB_PATH and REPLICA_URL arguments", u)
86+
}
8287
syncInterval := litestream.DefaultSyncInterval
8388
dbConfig.Replicas = append(dbConfig.Replicas, &ReplicaConfig{
8489
URL: u,

cmd/litestream/replicate_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package main_test
2+
3+
import (
4+
"context"
5+
"strings"
6+
"testing"
7+
8+
main "github.com/benbjohnson/litestream/cmd/litestream"
9+
)
10+
11+
func TestReplicateCommand_ParseFlags_FlagPositioning(t *testing.T) {
12+
t.Run("ExecFlagAfterPositionalArgs", func(t *testing.T) {
13+
cmd := main.NewReplicateCommand()
14+
15+
// Test the scenario from issue #245: -exec flag after positional arguments
16+
args := []string{"test.db", "s3://bucket/test.db", "-exec", "echo test"}
17+
18+
err := cmd.ParseFlags(context.Background(), args)
19+
if err == nil {
20+
t.Fatal("expected error when -exec flag is positioned after positional arguments")
21+
}
22+
23+
expectedError := `flag "-exec" must be positioned before DB_PATH and REPLICA_URL arguments`
24+
if !strings.Contains(err.Error(), expectedError) {
25+
t.Errorf("expected error message to contain %q, got %q", expectedError, err.Error())
26+
}
27+
})
28+
29+
t.Run("ExecFlagBeforePositionalArgs", func(t *testing.T) {
30+
cmd := main.NewReplicateCommand()
31+
32+
// Test the correct usage: -exec flag before positional arguments
33+
args := []string{"-exec", "echo test", "test.db", "s3://bucket/test.db"}
34+
35+
err := cmd.ParseFlags(context.Background(), args)
36+
if err != nil {
37+
t.Fatalf("unexpected error when -exec flag is positioned correctly: %v", err)
38+
}
39+
40+
// Verify the exec command was set correctly
41+
if cmd.Config.Exec != "echo test" {
42+
t.Errorf("expected exec command to be %q, got %q", "echo test", cmd.Config.Exec)
43+
}
44+
})
45+
46+
t.Run("ConfigFlagAfterPositionalArgs", func(t *testing.T) {
47+
cmd := main.NewReplicateCommand()
48+
49+
// Test other flags after positional arguments
50+
args := []string{"test.db", "s3://bucket/test.db", "-config", "/path/to/config"}
51+
52+
err := cmd.ParseFlags(context.Background(), args)
53+
if err == nil {
54+
t.Fatal("expected error when -config flag is positioned after positional arguments")
55+
}
56+
57+
expectedError := `flag "-config" must be positioned before DB_PATH and REPLICA_URL arguments`
58+
if !strings.Contains(err.Error(), expectedError) {
59+
t.Errorf("expected error message to contain %q, got %q", expectedError, err.Error())
60+
}
61+
})
62+
63+
t.Run("MultipleFlags", func(t *testing.T) {
64+
cmd := main.NewReplicateCommand()
65+
66+
// Test multiple flags in correct position
67+
args := []string{"-exec", "echo test", "-no-expand-env", "test.db", "s3://bucket/test.db"}
68+
69+
err := cmd.ParseFlags(context.Background(), args)
70+
if err != nil {
71+
t.Fatalf("unexpected error with multiple flags positioned correctly: %v", err)
72+
}
73+
74+
// Verify the exec command was set correctly
75+
if cmd.Config.Exec != "echo test" {
76+
t.Errorf("expected exec command to be %q, got %q", "echo test", cmd.Config.Exec)
77+
}
78+
})
79+
80+
t.Run("OnlyDatabasePathProvided", func(t *testing.T) {
81+
cmd := main.NewReplicateCommand()
82+
83+
// Test with only database path (should error but for different reason)
84+
args := []string{"test.db"}
85+
86+
err := cmd.ParseFlags(context.Background(), args)
87+
if err == nil {
88+
t.Fatal("expected error when only database path is provided without replica URL")
89+
}
90+
91+
// Should get the "must specify at least one replica URL" error, not the flag positioning error
92+
expectedError := "must specify at least one replica URL"
93+
if !strings.Contains(err.Error(), expectedError) {
94+
t.Errorf("expected error message to contain %q, got %q", expectedError, err.Error())
95+
}
96+
})
97+
}

0 commit comments

Comments
 (0)