feat(api): support custom placeholder values

Description

Adds -V, --placeholder <name>=<value> to glab api, so you can define your own :name placeholders alongside the built-in ones (:fullpath, :branch, and so on).

Why: values like branch names often contain /, which has to be percent-encoded in an API path. Until now you had to encode them yourself before interpolating. Custom placeholders reuse the same encoding the built-in placeholders got in d914d050:

  • In the endpoint, the value is percent-encoded (-V target=feat/x turns branches/:target into branches/feat%2Fx).
  • In --field values, the value is sent unchanged, matching the built-ins. --raw-field values are left alone, as they are for built-ins.

How it works:

  • validate parses each flag into a name/value map and returns a FlagError for a missing =, a malformed name (must start with a letter, then letters, digits, _ or -, and not end with -), a repeated name, or a name that clashes with a built-in placeholder (point 2 of the issue).
  • When custom placeholders are defined, one combined regexp is built per invocation from the built-in and custom names, sorted longest first, so :repo-name is not swallowed by :repo. Everything expands in a single pass, so a custom value containing :repo is not expanded again. Without --placeholder, the existing package-level regexp is used unchanged.
  • Long and Example are updated, and docs/source/api/_index.md is regenerated with make gen-docs.

Example:

glab api projects/:fullpath/repository/branches/:target --placeholder target=feature/login

Closes #8303 (closed)

How has this been tested?

  • New table tests drive the command end to end (runAPIArgvRecording and runAPIArgvGuarded):
    • Test_apiRun_customPlaceholders: encoding in the endpoint, raw value in --field, untouched --raw-field, several placeholders, a name extending a built-in one, no re-expansion of values, undefined names sent verbatim, and an empty value.
    • Test_apiRun_customPlaceholderErrors: missing =, empty, digit-first, trailing-hyphen and slash names, clash with a built-in, and a duplicate name. All of them fail before any request is sent.
  • go test ./internal/commands/api/... ./internal/commands/... ./cmd/glab/... (api package plus its reverse deps, including skills/bundled)
  • golangci-lint run ./internal/commands/api/... (0 issues)
  • go run ./scripts/comment-overlap (no findings in the touched files)
  • make gen-docs (the tree is clean afterwards)

Merge request reports

Loading
Loading