Skip to content
Open
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
90 changes: 60 additions & 30 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,37 +308,39 @@ func init() {

//-1表示不覆盖配置文件中的MaxRetryTimes参数
global.MaxRetryTimes = -1
for idx, arg := range os.Args {
if arg == "--profile" && len(os.Args) > idx+1 && os.Args[idx+1] != "" {
global.Profile = os.Args[idx+1]
}
if arg == "--public-key" && len(os.Args) > idx+1 && os.Args[idx+1] != "" {
global.PublicKey = os.Args[idx+1]
}
if arg == "--private-key" && len(os.Args) > idx+1 && os.Args[idx+1] != "" {
global.PrivateKey = os.Args[idx+1]
}
if arg == "--base-url" && len(os.Args) > idx+1 && os.Args[idx+1] != "" {
global.BaseURL = os.Args[idx+1]
}
if arg == "--channel-key" && len(os.Args) > idx+1 && os.Args[idx+1] != "" {
global.ChannelKey = os.Args[idx+1]
}
if arg == "--timeout-sec" && len(os.Args) > idx+1 && os.Args[idx+1] != "" {
sec, err := strconv.Atoi(os.Args[idx+1])
if err != nil {
fmt.Printf("parse timeout-sec failed: %v\n", err)
} else {
global.Timeout = sec
}
// 启动期预扫描:在 cobra 解析前把连接类参数落到 global.*,供 InitClientRuntime 用。
// 每个 flag 都识别 `--flag value` 与 `--flag=value` 两形式(scanFlagValue)。
// --profile 额外识别短选项 -p:漏识别会让 ConfigIns 指向错误 profile,
// OAuth 刷新可能把别人的 Bearer 重放到当前请求(见 platform/client.go 注释)。
if v, ok := scanFlagValue(os.Args, "--profile", "-p"); ok {
global.Profile = v
}
if v, ok := scanFlagValue(os.Args, "--public-key"); ok {
global.PublicKey = v
}
if v, ok := scanFlagValue(os.Args, "--private-key"); ok {
global.PrivateKey = v
}
if v, ok := scanFlagValue(os.Args, "--base-url"); ok {
global.BaseURL = v
}
if v, ok := scanFlagValue(os.Args, "--channel-key"); ok {
global.ChannelKey = v
}
if v, ok := scanFlagValue(os.Args, "--timeout-sec"); ok {
sec, err := strconv.Atoi(v)
if err != nil {
fmt.Printf("parse timeout-sec failed: %v\n", err)
} else {
global.Timeout = sec
}
if arg == "--max-retry-times" && len(os.Args) > idx+1 && os.Args[idx+1] != "" {
times, err := strconv.Atoi(os.Args[idx+1])
if err != nil {
fmt.Printf("parse max-retry-times failed: %v\n", err)
} else {
global.MaxRetryTimes = times
}
}
if v, ok := scanFlagValue(os.Args, "--max-retry-times"); ok {
times, err := strconv.Atoi(v)
if err != nil {
fmt.Printf("parse max-retry-times failed: %v\n", err)
} else {
global.MaxRetryTimes = times
}
}
if sec, found, err := parseWaitTimeoutSec(os.Args); found {
Expand All @@ -351,6 +353,34 @@ func init() {
cobra.EnableCommandSorting = false
}

// scanFlagValue finds the value of any of names in args, recognizing both the
// space form (`--profile foo`) and the equals form (`--profile=foo`). Multiple
// names allow aliases, e.g. scanFlagValue(args, "--profile", "-p"). The first
// (leftmost) hit wins; an empty value (`--profile=` or a trailing `--profile`
// with no following arg) counts as no hit and scanning continues.
//
// This is the startup pre-scan that must run before cobra parses flags, because
// connection-class params (base-url/channel-key/profile/keys) must be settled
// before InitClientRuntime builds sdk.Config. Exact-comparison-only scans missed
// the equals form — the same #119 bug parseWaitTimeoutSec already fixed for
// --wait-timeout-sec. Attached/combined shorthand (`-pfoo`, `-dpfoo`) is out of
// scope: hand-parsing combined shorthand is error-prone and its residual risk
// matches today's behavior (the pre-scan never recognized `-p` at all).
func scanFlagValue(args []string, names ...string) (string, bool) {
for i, arg := range args {
for _, name := range names {
if arg == name {
if i+1 < len(args) && args[i+1] != "" {
return args[i+1], true
}
} else if v, ok := strings.CutPrefix(arg, name+"="); ok && v != "" {
return v, true
}
}
}
return "", false
}

// parseWaitTimeoutSec scans args for --wait-timeout-sec in either
// `--wait-timeout-sec N` or `--wait-timeout-sec=N` form. found=true when
// the flag is present with a value; err is set when that value isn't an int.
Expand Down
99 changes: 99 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,102 @@ func TestParseWaitTimeoutSec(t *testing.T) {
})
}
}

// TestScanFlagValue covers the generalized startup pre-scan helper. Same #119
// bug as parseWaitTimeoutSec (equals form silently ignored), now for the
// connection-class flags. The critical -pfoo case pins the R3 boundary:
// attached/combined shorthand is intentionally NOT recognized.
func TestScanFlagValue(t *testing.T) {
tests := []struct {
name string
args []string
names []string
wantVal string
wantFound bool
}{
{
name: "long space form",
args: []string{"ucloud", "config", "--profile", "foo"},
names: []string{"--profile", "-p"},
wantVal: "foo",
wantFound: true,
},
{
name: "long equals form",
args: []string{"ucloud", "config", "--profile=foo"},
names: []string{"--profile", "-p"},
wantVal: "foo",
wantFound: true,
},
{
name: "short space form",
args: []string{"ucloud", "config", "-p", "foo"},
names: []string{"--profile", "-p"},
wantVal: "foo",
wantFound: true,
},
{
name: "short equals form",
args: []string{"ucloud", "config", "-p=foo"},
names: []string{"--profile", "-p"},
wantVal: "foo",
wantFound: true,
},
{
// R3 boundary: attached shorthand is out of scope, must NOT match.
name: "attached shorthand not recognized",
args: []string{"ucloud", "config", "-pfoo"},
names: []string{"--profile", "-p"},
wantVal: "",
wantFound: false,
},
{
name: "empty equals is no hit",
args: []string{"ucloud", "config", "--profile="},
names: []string{"--profile", "-p"},
wantVal: "",
wantFound: false,
},
{
name: "trailing flag no value",
args: []string{"ucloud", "config", "--profile"},
names: []string{"--profile", "-p"},
wantVal: "",
wantFound: false,
},
{
name: "absent",
args: []string{"ucloud", "config"},
names: []string{"--profile", "-p"},
wantVal: "",
wantFound: false,
},
{
name: "base-url equals form single name",
args: []string{"ucloud", "uhost", "list", "--base-url=http://x/"},
names: []string{"--base-url"},
wantVal: "http://x/",
wantFound: true,
},
{
// leftmost hit wins (pre-scan only needs one early value, no override)
name: "leftmost wins",
args: []string{"ucloud", "--profile", "a", "--profile", "b"},
names: []string{"--profile", "-p"},
wantVal: "a",
wantFound: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
val, found := scanFlagValue(tt.args, tt.names...)
if val != tt.wantVal {
t.Errorf("val = %q, want %q", val, tt.wantVal)
}
if found != tt.wantFound {
t.Errorf("found = %v, want %v", found, tt.wantFound)
}
})
}
}
Loading