glab mr for and glab mr create --related-issue never return when the issue's branch cannot be created
Checklist
- I'm using the latest version of the extension: glab built from
mainat a6fd50ce. The code is the same in v1.124.0, the latest release. - Operating system and version: Linux 6.1.0-53-amd64 x86_64
- Gitlab.com or self-managed instance? Both: the loop is in glab and does not depend on the instance.
- GitLab version (if self-managed): not applicable, see Environment.
- I have performed
glab auth statusto check for authentication issues: not applicable, the failures below are the API's answers. - Run the command in debug mode: not needed, the output below shows each request.
Summary
glab mr for <issue> and glab mr create --related-issue <issue> without --source-branch create a branch named after the issue. When that first branch creation fails, both commands try numbered names (<name>-1, <name>-2, ...) until one succeeds, whatever the error was. A failure that is not about an existing branch (no permission to push, an invalid name, a target branch that does not exist, a network error) therefore repeats forever. The numbered names also skip the sanitization of the first name, so for an issue whose branch already exists and whose title contains characters such as :, ~ or ?, every numbered name is invalid and the loop never ends either.
Ctrl+C does not stop it: glab turns SIGINT and SIGTERM into a cancelled command context, but neither the loop nor its requests look at that context, so neither signal ends it. In the runs below, only SIGKILL did.
Environment
- OS: Linux 6.1.0-53-amd64 x86_64
- SHELL: /bin/bash
- TERM: none, the commands ran without a terminal
- GLAB: built from
mainat a6fd50ce (glab DEV)
Other: no GitLab instance. The commands ran against a local stand-in for the API routes they call (get the project and the issue, create a branch, create a merge request), answering as GitLab does: 403 Forbidden for a user who cannot push, and 400 with Branch already exists or Branch name is invalid.
Steps to reproduce
- In a project you cannot push to, run
glab mr for <issue-iid>, orglab mr create --related-issue <issue-iid> --title "x" --yes, without--source-branch. - Or, in your own project, create an issue titled
Fix: crash in ~/.config?, create the branch<iid>-fix-crash-in-config-yourself, and run either command for that issue.
What is the current bug behavior?
The command never returns and sends a new branch creation request for each number. Against the local stand-in, each run below was sent SIGINT 15 seconds after it started and SIGKILL 5 seconds later:
| Command | Answer to branch creation | Requests sent | Ended by |
|---|---|---|---|
glab mr for 7 |
403 | 1567 | SIGKILL |
glab mr create --related-issue 7 |
403 | 1682 | SIGKILL |
glab mr for 7 (title Fix: crash in ~/.config?) |
400, the branch exists, then 400, the name is invalid | 887 | SIGKILL |
glab mr create --related-issue 7 (same title) |
same | 797 | SIGKILL |
glab mr for also prints the error of every retry to stdout, and prints <nil> once a numbered name is created. No merge request is created in any of the four runs.
What is the expected correct behavior?
A numbered name is tried only when the branch, or a branch below it (<name>/...), already exists, it is built from the sanitized name (7-fix-crash-in-config--1), the attempts stop after a bounded number, and any other error ends the command with the API's message.
Relevant logs and/or screenshots
The first three branch creation requests of glab mr for 7 for the title Fix: crash in ~/.config?, when 7-fix-crash-in-config- exists, as the stand-in recorded them (method, path and the branch parameter):
POST /api/v4/projects/OWNER%2FREPO/repository/branches 7-fix-crash-in-config-
POST /api/v4/projects/OWNER%2FREPO/repository/branches 7-fix:-crash-in-~/.config?-1
POST /api/v4/projects/OWNER%2FREPO/repository/branches 7-fix:-crash-in-~/.config?-2and the first two lines of its stdout:
POST http://127.0.0.1:18745/api/v4/projects/OWNER%2FREPO/repository/branches: 400 {message: Branch name is invalid}
POST http://127.0.0.1:18745/api/v4/projects/OWNER%2FREPO/repository/branches: 400 {message: Branch name is invalid}Possible fixes
The loops are in mr_for.go#L84-97 and mr_create.go#L546-552. The first name goes through utils.ReplaceNonAlphaNumericChars (mr_for.go#L77), the numbered names through strings.ReplaceAll(title, " ", "-") (mr_for.go#L88). GitLab answers an existing branch with 400 Branch already exists (branches.rb#L240, validate_new_service.rb#L13), or with 400 Failed to create branch '<name>': Branch name conflicts with existing branch hierarchy. when a branch <name>/... exists (create_service.rb#L89-94), and an invalid name with the same status (validate_new_service.rb#L10), so the message is what tells them apart. Retrying only on those two answers, numbering the sanitized name, capping the attempts and passing the command's context to the requests would fix both commands; I have a merge request ready for it.