Repository navigation
fix: refresh repository identity after SSH translation - #274
Conversation
|
Request to merge this urgently Quite a common usecase for users with multiple github accounts |
There was a problem hiding this comment.
Pull request overview
This PR fixes incorrect remote host/owner/repo identity caching when SSH hostname aliases are translated, ensuring host-based filtering works against the post-translation canonical hostname (e.g., github.com).
Changes:
- Refactors SSH URL translation into
translateRemotesand refreshesRemote{Host,Owner,Repo}after translation. - Preserves fetch URL metadata precedence, falling back to push URL metadata only when fetch repo info cannot be derived.
- Adds unit tests covering SSH alias translation and the fetch-to-push fallback behavior.
Show a summary per file
| File | Description |
|---|---|
| pkg/repository/repository.go | Translates remotes via a helper and refreshes cached remote identity fields after SSH alias resolution, with fetch-first fallback semantics. |
| pkg/repository/repository_test.go | Adds tests validating remote identity refresh after translation and fallback to push URL when fetch repo info is invalid. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
|
@williammartin any update? |
|
Hey @benekuehn, sorry, let me take a look today. I also just stumbled across your comment on github/gh-stack#45 |
3db916e to
7ab1165
Compare
Refresh cached remote metadata after translating SSH host aliases. Preserve go-gh's fetch-then-push fallback when extracting repository identity. Relates to github/gh-stack#45 and github/gh-stack#337.
Drop a remote's parse-time identity when neither its fetch URL nor its last push URL names a repository, matching gh's TranslateRemotes. Replace the translateRemotes unit tests with Current() scenarios for every acceptance criterion in cli#310, using the test binary as a fake `ssh -G` so SSH host aliases are exercised on all platforms. Co-authored-by: Copilot App <[email protected]>
Test Current() through the shared fake ssh instead of translateRemotes, and isolate tests from GH_HOST. Co-authored-by: Copilot App <[email protected]>
7ab1165 to
125442e
Compare
There was a problem hiding this comment.
🟢 Approval recommended
The focused identity refresh preserves the intended precedence and fallback behavior, with targeted regression coverage and no unresolved issues.
0 open findings
1 resolved since last review
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
Co-authored-by: Copilot App <[email protected]>
Co-authored-by: Copilot App <[email protected]>
|
@benekuehn I made some more changes here so I've requested a review from @babakks. Hopefully we can get this released and then into a |
| } | ||
| } | ||
| for _, rem := range remotes { | ||
| rem.resolveIdentity() |
There was a problem hiding this comment.
This is just a cleanup that allows the Remotes/parseRemotes and the Translate to share the same
|
|
||
| // Translate rewrites the remote's URLs and re-derives its repository identity | ||
| // from the result. | ||
| func (r *Remote) Translate(translate func(*url.URL) *url.URL) { |
There was a problem hiding this comment.
Moved translation here rather than keeping it in Current because the pattern exhibited a bit of https://refactoring.guru/smells/feature-envy
babakks
left a comment
There was a problem hiding this comment.
LGTM. Just one question/callout.
| func (r *Remote) resolveIdentity() { | ||
| r.Host, r.Owner, r.Repo = "", "", "" | ||
| for _, u := range []*url.URL{r.FetchURL, r.PushURL} { | ||
| if u == nil { | ||
| continue | ||
| } | ||
| if host, owner, repo, err := RepoInfoFromURL(u); err == nil { | ||
| r.Host, r.Owner, r.Repo = host, owner, repo | ||
| return | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
question: I know this is meant to simplify the old switch-statement on URL type, but I'm just a bit wondering about any potential edge cases, because it's not a 100% refactor in terms of behaviour (looking at the early return in the loop).
In this old switch-statement, any of Host, Owner, or Repo values that were not already populated from the fetch URL, would have a chance to be assigned from the push URL. So, a configuration like this (which is not actually correct, but technically possible):
fetchurl=https:///owner/repo
pushurl=https://some.host/owner/repo
would result in a different state with this PR's changes: some.host is not picked up as opposed to the current implementation.
I know it's super unlikely for a user to have such a weird config, so I think we're fine shipping the changes. Just wanted to call out in case you think otherwise.
There was a problem hiding this comment.
I don't think your specific example is right because https:///owner/repo will return an error from RepoInfoFromURL since there is no hostname.
https://a.host/owner/.git would probably be an issue. However, I think this is so weird, and would also diverge from gh that I'm not worried about.
Thanks for your diligence!
|
@benekuehn merged, released, merged into Hopefully works for you. Thanks for your help. |

Fixes #310
Description
Some people point a git remote at an SSH host alias from their SSH config, like
[email protected]:cli/go-gh.git.repository.Current()already runsssh -Gto translate the alias to its real host. However, it kept theHost,OwnerandRepoit parsed from the untranslated URL.So the remote looked like it pointed at
github.com-work. That isn't a known host, andCurrent()failed. Extensions likegh stackhit this in github/gh-stack#45 and github/gh-stack#337.This PR refreshes those fields after translation. It also makes
Current()pick a URL the wayghdoes when push URLs are configured.ghreads only the last push URL, so earlier push URLs are never used. The acceptance criteria on #310 list each case.How did you test this change?
I created a Go extension with
gh extension create, which uses go-ghv2.16.1. It adds agithub.com-workalias to the SSH config and runs the extension in a repository whose remote uses that alias. Then it points the extension at this branch with areplacedirective, rebuilds, and runs it again.It ran with a temporary
HOME.sshonPATHthere is a wrapper that runs/usr/bin/ssh -F "$HOME/.ssh/config", because realsshfinds the home directory through the user database, not$HOME. The recording was built from 1117f89. The only later commit, 393ceb0, moves code without changing behaviour.Key points
ghso extensions andghagree on the current repository. It is kind of breaking, but not so much that I really care. Let someone complain.gh.Notes for reviewers
Start with
Current()inpkg/repository/repository.goandRemoteSet.Translateininternal/git/remote.go. Then readpkg/repository/repository_test.go. It has oneCurrent()test per acceptance criterion on #310.The first commit is the original fix by @benekuehn. The later commits align push URL handling with
gh, rewrite the tests, and move identity resolution ontogit.Remote.Authorship and follow-up
Who wrote this:
Who answers review comments: