glab api -f/-F flags fail to serialize array parameters with hyphens, uppercase, or digits

Summary

The glab api command's -F flag has array detection logic (stringArrayRegexPattern in internal/commands/api/http.go) that only matches bracket-enclosed values where each element consists of lowercase letters and underscores ([[:lower:]_]). This causes array-type API parameters to be sent as strings when values contain hyphens, uppercase letters, digits, or other common characters.

Steps to reproduce

# Attempt to set project topics
glab api -X PUT projects/:id -F "topics=[my-topic, GitLab]"
# Expected: {"topics":["my-topic","GitLab"]}
# Actual:   {"topics":"[my-topic, GitLab]"} (string, not array)

# Even single values fail with -f
glab api -X PUT projects/:id -f "topics=nomograph"
# Expected: {"topics":["nomograph"]}
# Actual:   {"topics":"nomograph"} (string, not array)

What is the current bug behavior?

The stringArrayRegexPattern regex (^\[\s*([[:lower:]_]+(\s*,\s*[[:lower:]_]+)*)?\s*\]$) rejects:

  • Hyphens (my-topic)
  • Uppercase (GitLab)
  • Digits (topic2)
  • Quoted strings ("my topic")

Values that don't match the regex are serialized as JSON strings instead of arrays.

What is the expected correct behavior?

Array parameters should be correctly serialized to JSON arrays for any valid string values, not just [a-z_] tokens.

Relevant logs and/or screenshots

The existing test case in http_test.go only covers the happy path:

params: map[string]any{"scopes": "[api, read_api]"},
want: expects{body: `{"scopes":["api","read_api"]}`},

No test cases exist for values containing hyphens, uppercase, digits, quoted strings, or empty arrays.

Environment

Affects all versions with the current regex. Verified against main branch source.

Possible fixes

Option A (minimal): Broaden the regex character class to accept common characters in API values.

Option B (robust): If a -F value is valid JSON (starts with [ or {), parse with json.Unmarshal and use the parsed value directly. This would also fix #7467 (complex GraphQL variables).

Proposed test coverage

The following cases should be added to Test_httpRequest in http_test.go to prevent regression and validate the fix:

{
    name: "POST with hyphenated array elements",
    args: args{
        host:   "gitlab.com",
        method: http.MethodPost,
        p:      "projects",
        params: map[string]any{"topics": "[my-topic, knowledge-graph]"},
        headers: []string{},
    },
    want: expects{
        method: http.MethodPost,
        u:      "https://gitlab.com/api/v4/projects",
        body:   `{"topics":["my-topic","knowledge-graph"]}`,
    },
},
{
    name: "POST with uppercase array elements",
    args: args{
        host:   "gitlab.com",
        method: http.MethodPost,
        p:      "projects",
        params: map[string]any{"topics": "[GitLab, CI]"},
        headers: []string{},
    },
    want: expects{
        method: http.MethodPost,
        u:      "https://gitlab.com/api/v4/projects",
        body:   `{"topics":["GitLab","CI"]}`,
    },
},
{
    name: "POST with digits in array elements",
    args: args{
        host:   "gitlab.com",
        method: http.MethodPost,
        p:      "projects",
        params: map[string]any{"topics": "[topic1, v2]"},
        headers: []string{},
    },
    want: expects{
        method: http.MethodPost,
        u:      "https://gitlab.com/api/v4/projects",
        body:   `{"topics":["topic1","v2"]}`,
    },
},
{
    name: "POST with single array element",
    args: args{
        host:   "gitlab.com",
        method: http.MethodPost,
        p:      "projects",
        params: map[string]any{"topics": "[nomograph]"},
        headers: []string{},
    },
    want: expects{
        method: http.MethodPost,
        u:      "https://gitlab.com/api/v4/projects",
        body:   `{"topics":["nomograph"]}`,
    },
},
{
    name: "POST with empty array",
    args: args{
        host:   "gitlab.com",
        method: http.MethodPost,
        p:      "projects",
        params: map[string]any{"topics": "[]"},
        headers: []string{},
    },
    want: expects{
        method: http.MethodPost,
        u:      "https://gitlab.com/api/v4/projects",
        body:   `{"topics":[]}`,
    },
},
{
    name: "POST with quoted array elements containing spaces",
    args: args{
        host:   "gitlab.com",
        method: http.MethodPost,
        p:      "projects",
        params: map[string]any{"topics": `["My Topic", "CI/CD"]`},
        headers: []string{},
    },
    want: expects{
        method: http.MethodPost,
        u:      "https://gitlab.com/api/v4/projects",
        body:   `{"topics":["My Topic","CI/CD"]}`,
    },
},
  • #7467 - Same root cause affecting GraphQL variables