Teach gh cs create to use current repo as default - #6596
Conversation
|
Hi! Thanks for the pull request. Please ensure that this change is linked to an issue by mentioning an issue number in the description of the pull request. If this pull request would close the issue, please put the word 'Fixes' before the issue number somewhere in the pull request body. If this is a tiny change like fixing a typo, feel free to ignore this message. |
cmbrose
left a comment
There was a problem hiding this comment.
I like the idea of this change, but have some concerns with pain points that it creates. If we can address those I think the implementation idea looks fine
| currentRepo, err := a.baseRepo() | ||
| if err != nil { | ||
| return err | ||
| } |
There was a problem hiding this comment.
I tried this out and had a couple pain points:
- If you're not in any repo, then the command just fails - I would have expected it to fall back on existing behavior
- If you are in a repo, it seems like you can only choose that repo, or a related remote - I expected that it would use the default text, but allow you to clear it out and use any repo
There was a problem hiding this comment.
Thanks for taking a look, I completely agree with you about both of these issues. I'll take a look at improving the PR in line with your suggestions.
There was a problem hiding this comment.
If you're not in any repo, then the command just fails - I would have expected it to fall back on existing behavior
I've fixed this 👍
If you are in a repo, it seems like you can only choose that repo, or a related remote - I expected that it would use the default text, but allow you to clear it out and use any repo
I've experimented and this is actually a generic prompt used by all commands (e.g. try running gh pr list) the first time that you run any command in a checkout with multiple remotes. If you choose one then you can proceed.
I followed the style of `pr list` here by threading through the baseRepo from the root command.
In this case we simply shouldn't provide a default repository name.
fcae4d2 to
365ca81
Compare
| if currentRepo, err := a.baseRepo(); err == nil { | ||
| defaultRepo = ghrepo.FullName(currentRepo) | ||
| } |
There was a problem hiding this comment.
I see two UX issues here both stemming from using SmartBaseRepoFunc to determine the base repo:
SmartBaseRepoFuncoutputs error messages to stderr which seems like something we would want to silence in this case since we are only using the resulting value if there is no error. I would have to double check but I believe all other use cases ofSmartBaseRepoFuncwe exit on error so that the user can act on the error message from that function.- The
SmartBaseRepoFunchas cases where it will return a repo that has not actually been created on GitHub. Having that set as the default and being selected would likely cause an error, right? Having the default value cause an error seems like something we would want to avoid.
Having said that, I think number 1 is a much more glaring issue that will cause confusion.
A different approach that we can take is to use the Remotes() factory method which will return a list of git remotes, and from there use the ResolvedRemote() method and use the as the default value. The ResolvedRemote() method will return the remote that was selected with the repo set-default function. This approach would solve both issues as there is no output from either of those methods and by using the remote that the user has already selected as the default we know that it exists on GitHub. This is the approach we use in repo set-default command.
There was a problem hiding this comment.
Thanks for the review, great point. I've changed the implementation as you suggest.
This avoids outputting to stderr and matches the canonical approach in `gh repo set-default`.
| remotes, err := a.remotes() | ||
| if err != nil { | ||
| return err | ||
| } |
There was a problem hiding this comment.
If there is an error retrieving the remotes do we actually want to error out here? Seems like we could continue executing the command without specifying a default.
There was a problem hiding this comment.
Following this logic, where we don't care if there is an error, I am wondering if we should implement something like the executable interface for remotes:
type remoter interface {
Remote() clicontext.Remotes
}Where implementation of the Remote() function will return an empty list of remotes if there was ever an error retrieving the git remotes.
There was a problem hiding this comment.
If there is an error retrieving the remotes do we actually want to error out here? Seems like we could continue executing the command without specifying a default.
Yes, that makes sense, I've made that change.
I am wondering if we should implement something like the executable interface for remotes
I had a look at this and wasn't sure about the readability of the result. Factory already has a field Remotes and adding a method Remote with a similar signature felt awkward. Perhaps TryRemotes to keep the plural form (as it returns a list) but to make it intuitive that it cannot fail. The Try prefix is more of a .NET-ism, I haven't seen something similar used widely in Go, only Must to indicate a function panics on failure.
These are only used to suggest a default, so erroring out is a bit over the top.
0c5b969 to
be3fdaa
Compare
samcoe
left a comment
There was a problem hiding this comment.
LGTM, thanks for making the requested changes!
| remotes, _ := a.remotes() | ||
| if remotes != nil { | ||
| defaultRemote, _ := remotes.ResolvedRemote() | ||
| if defaultRemote != nil { | ||
| defaultRepo = ghrepo.FullName(defaultRemote) | ||
| } | ||
| } |
There was a problem hiding this comment.
Small style nit:
| remotes, _ := a.remotes() | |
| if remotes != nil { | |
| defaultRemote, _ := remotes.ResolvedRemote() | |
| if defaultRemote != nil { | |
| defaultRepo = ghrepo.FullName(defaultRemote) | |
| } | |
| } | |
| if remotes, err := a.remotes(); err != nil { | |
| if defaultRemote, err := remotes.ResolvedRemote(); err != nil { | |
| defaultRepo = ghrepo.FullName(defaultRemote) | |
| } | |
| } |
There was a problem hiding this comment.
We're ignoring the errors deliberately here, so I think you meant if remotes, _ := a.remotes(); remotes != nil { right (and the same for the second line)? I've made that change.
Thanks for the feedback to make this a much better change! |


I followed the style of
pr listhere by threading through the baseRepo from the root command.That being said, the 'cs' hierarchy of commands is implemented a little differently from the others, presumably due to
its history as a extension so I am very open to feedback on how this should be implemented.
I appreciate that the tests are failing, but please consider this PR as a suggested implementation. If it looks reasonable I will get right on the tests.