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_fillPlaceholders case asserting :branch with value feature/foo substitutes as feature%2Ffoo.
  • Existing placeholder tests still pass.
  • Full internal/commands/api/... test suite passes (81% coverage).
  • Manual: in a repo on a branch named test/whatever with branch protection, glab api /projects/:id/protected_branches/:branch returns the protection rule instead of 404.
Edited by Jay McCure

Merge request reports

Loading