glab uses the last HostName for an SSH host alias, while ssh uses the first
Checklist
- I'm using the latest version of the extension (Run
glab --version)- Extension version:
glab 1.124.0 (a6fd50cec), built frommainat a6fd50ce.internal/git/ssh_config.go, where the cause is, is the same in the v1.124.0 release.
- Extension version:
- Operating system and version: Debian 13 (trixie), x86_64, with OpenSSH 10.0p2
- Gitlab.com or self-managed instance? Not applicable: glab picks the host from the SSH configuration before it sends any request, so it happens with either.
- GitLab version (if self-managed): not applicable, for the same reason. The reproduction uses host names under the
.invalidTLD. - I have performed
glab auth statusto check for authentication issues: not applicable, the problem is which host glab picks, not its credentials. - Run the command in debug mode (like
GLAB_DEBUG=true glab mr list) and attach any useful output:GLAB_DEBUG=trueadds nothing to the output below.
Summary
When a git remote uses an SSH host alias that is not itself a configured glab host, such as git@gl:group/project.git, glab reads the SSH configuration to find the real host name. If the configuration sets HostName for the alias more than once, glab keeps the last value, while ssh keeps the first. So a second Host block for the same alias further down ~/.ssh/config, or a Host block for it in /etc/ssh/ssh_config, decides the host for glab but not for ssh and git. glab then reports that no remote points to a known GitLab host, or sends its requests to another instance than the one git push reaches.
ssh_config(5) says "Unless noted otherwise, for each parameter, the first obtained value will be used", and lists the user's file before the system-wide one in the order ssh reads them (ssh_config.5 at OpenSSH 10.0p2).
Environment
- OS: Linux 6.12 x86_64 (a Debian 13 container)
- SHELL: /bin/bash (GNU bash 5.2.37)
- TERM: dumb
- GLAB: glab 1.124.0 (a6fd50ce)
Other: OpenSSH 10.0p2 from Debian 13. glab ran in a scratch repository with a cleared environment (PATH, HOME, GLAB_CONFIG_DIR, GLAB_SEND_TELEMETRY=false, GLAB_CHECK_UPDATE=false, NO_COLOR=1 and GLAB_NO_PROMPT=true), so no GITLAB_* variable was set. Its configuration held only the hosts named below, each with a placeholder token.
Steps to reproduce
-
Put this in
~/.ssh/config:Host gl HostName gitlab.invalid -
Add this at the end of
/etc/ssh/ssh_config:Host gl HostName system.invalid -
Configure glab for
gitlab.invalidonly (one entry underhostsin itsconfig.yml), and use a repository whose only remote isgit@gl:group/project.git. -
Run
ssh -G gl | grep '^hostname ', thenglab mr listin the repository.
The same happens within one file, with nothing added to /etc/ssh/ssh_config, when glab is configured for both gitlab.invalid and other.invalid:
Host gl
HostName gitlab.invalid
Host gl gh
HostName other.invalidWhat is the current bug behavior?
ssh -G gl prints hostname gitlab.invalid, but glab maps gl to system.invalid and, since that host is not configured, stops with (its line wrapping removed):
none of the git remotes configured for this repository point to a known GitLab host. Please use `glab auth login` to authenticate and configure a new host for glab.
Configured remotes: system.invalid.With the single-file configuration, ssh -G gl again prints hostname gitlab.invalid (and ssh -G gh prints hostname other.invalid), but glab sends the request for the gl remote to the other host:
Get "https://other.invalid/api/v4/projects/group%2Fproject/merge_requests?page=1&per_page=30&state=opened": dial tcp: lookup other.invalid [...]: no such host.What is the expected correct behavior?
glab resolves gl to gitlab.invalid, the host ssh and git connect to, and sends its requests there.
Relevant logs and/or screenshots
Configurations of the same shapes, given to sshParser.read in a table-driven test that compares the whole alias map, fail on main with:
expected: git.SSHAliasMap{"gl":"first.example.com"}
actual : git.SSHAliasMap{"gl":"second.example.com"}
expected: git.SSHAliasMap{"gh":"shared.example.com", "gl":"gl.example.com"}
actual : git.SSHAliasMap{"gh":"shared.example.com", "gl":"shared.example.com"}
expected: git.SSHAliasMap{"gl":"user.example.com", "sys":"only-system.example.com"}
actual : git.SSHAliasMap{"gl":"system.example.com", "sys":"only-system.example.com"}The three cases are two Host gl blocks in one file, gl listed again beside another alias (Host gl gh), and gl set in both the user and the system-wide file.
Possible fixes
Each HostName line stores its value for the patterns of the current Host line, replacing whatever was stored for them before: https://gitlab.com/gitlab-org/cli/-/blob/a6fd50cecb4bbe630dfb03a621cbe2cedfef4aef/internal/git/ssh_config.go#L84-92
Storing a value only when the alias has none yet would match ssh, because ParseSSHConfig already reads ~/.ssh/config before /etc/ssh_config and /etc/ssh/ssh_config, and reads an Include where it appears: https://gitlab.com/gitlab-org/cli/-/blob/a6fd50cecb4bbe630dfb03a621cbe2cedfef4aef/internal/git/ssh_config.go#L139-159
I have a merge request ready for it.