fix(api): URL-encode magic placeholder substitutions
Summary
Placeholders like :branch and :namespace (for nested subgroups) can contain /, which GitLab's API requires encoded as %2F. The composite placeholders :fullpath and :namespace/:repo were already encoded; this MR extends the same treatment to the simple placeholders so a branch like feature/foo substitutes as feature%2Ffoo instead of breaking the request path.
Closes #8352 (closed).
What changed
In fillPlaceholders (internal/commands/api/api.go), wrap the returns for :branch, :group, :namespace, :repo, :user, and :username in url.PathEscape.
Two of these are correctness fixes:
:branch— branch names commonly contain/(e.g.feature/foo,release/1.2); leaving them unencoded produces 404s on endpoints like/projects/:id/protected_branches/:branch.:namespace— for projects in nested subgroups,RepoNamespace()returns the path between the group and repo (e.g.security/cli), which also needs encoding.
The other three (:group, :repo, :user/:username) are defensive — GitLab restricts these identifiers to characters that don't need encoding, so url.PathEscape is a no-op today, but applying it uniformly avoids future surprises and matches the principle: if glab filled the value, glab encodes it. The user never typed :branch's value, so the user can't be expected to encode it.
Test plan
- Added a
Test_fillPlaceholderscase asserting:branchwith valuefeature/foosubstitutes asfeature%2Ffoo. - Existing placeholder tests still pass.
- Full
internal/commands/api/...test suite passes (81% coverage). - Manual: in a repo on a branch named
test/whateverwith branch protection,glab api /projects/:id/protected_branches/:branchreturns the protection rule instead of 404.