Repository navigation
Add one-command account selection - #14920
Draft
williammartin wants to merge 3 commits into
Draft
williammartin wants to merge 3 commits into
williammartin wants to merge 3 commits into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 965836ef-19ac-4473-ba1f-e326ce0f106b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 965836ef-19ac-4473-ba1f-e326ce0f106b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2 of 6 tasks
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved authentication regressions can crash existing git clients, replace exact-host credentials with another host’s credentials, and apply account overrides to hosts the commands do not target.
7 open issues
Handle a nil authentication provider without panicking · New Preserve exact-host authentication when no account override is set · New Recognize resource URLs in nested issue commands · New Keep bare repository names on the default host · New Resolve organization and user scopes to the default host · New Select the first supported GitHub remote for skill publishing · New Resolve the default repository host before selecting an account · New
What changed in this PR
This draft prototype adds --account LOGIN to select a saved account for one gh invocation without changing the saved default. It connects account selection to API requests and authenticated Git subprocesses; SSH account routing is deliberately unsupported at this layer.
Changes:
- Add command-level account selection and distinguish effective authentication from the persisted default.
- Forward selection to Git credential helpers and introduce resolver-based HTTP authentication.
- Add regression tests and an isolated macOS offline replay scenario.
| File | Description |
|---|---|
| script/multiaccount-review/README.md | Document replay usage, isolation, and limitations. |
| script/multiaccount-review/01-account.sh | Add an offline account-selection replay. |
| pkg/cmd/skills/publish/publish_test.go | Supply authentication callbacks in publish fixtures. |
| pkg/cmd/root/root.go | Register the account flag and selection hooks. |
| pkg/cmd/root/help.go | Hide the inherited account flag in auth help. |
| pkg/cmd/root/account.go | Validate account selection and determine its host. |
| pkg/cmd/root/account_test.go | Test invocation-local selection and host handling. |
| pkg/cmd/root/account_git_test.go | Test HTTPS selection and SSH refusal. |
| pkg/cmd/repo/fork/fork_test.go | Update Git authentication fixtures. |
| pkg/cmd/repo/create/create_test.go | Update Git authentication fixtures. |
| pkg/cmd/repo/clone/clone_test.go | Update Git authentication fixtures. |
| pkg/cmd/pr/merge/merge_test.go | Update Git authentication fixtures. |
| pkg/cmd/pr/create/create_test.go | Update Git authentication fixtures. |
| pkg/cmd/pr/checkout/checkout_test.go | Update Git authentication fixtures. |
| pkg/cmd/issue/develop/develop_test.go | Update Git authentication fixtures. |
| pkg/cmd/gist/clone/clone_test.go | Update Git authentication fixtures. |
| pkg/cmd/factory/git_authentication_test.go | Test deferred selection and unchanged ordinary SSH behavior. |
| pkg/cmd/factory/default.go | Wire API resolvers and Git authentication context. |
| pkg/cmd/extension/mocks.go | Mock authenticated remote-version lookup. |
| pkg/cmd/extension/git.go | Add authenticated ls-remote support. |
| pkg/cmd/extension/git_test.go | Test authentication failure propagation. |
| pkg/cmd/extension/extension.go | Use authenticated extension-version lookup. |
| pkg/cmd/auth/token/token.go | Resolve the persisted default username. |
| pkg/cmd/auth/switch/switch.go | Use persisted account state when switching. |
| pkg/cmd/auth/status/status.go | Report the configured default account. |
| pkg/cmd/auth/refresh/refresh.go | Use the configured username during refresh. |
| pkg/cmd/auth/logout/logout.go | Compare persisted defaults around logout. |
| pkg/cmd/auth/gitcredential/helper.go | Delegate credential selection to authentication config. |
| pkg/cmd/auth/gitcredential/helper_test.go | Adapt the credential-resolution test double. |
| pkg/cmd/auth/gitcredential/gist_production_test.go | Test gist selection with production configuration. |
| pkg/cmd/auth/gitcredential/gist_identity_test.go | Add credential-test isolation helpers. |
| pkg/cmd/auth/auth.go | Add a hidden helper that refuses SSH routing. |
| pkg/cmd/api/api.go | Use effective-account token resolution. |
| pkg/cmd/api/api_test.go | Update authentication configuration fixtures. |
| internal/identity/credentials.go | Add account-specific and API token resolvers. |
| internal/identity/credentials_test.go | Test gateway selection and request authentication boundaries. |
| internal/gh/gh.go | Extend the authentication configuration contract. |
| internal/config/test.go | Clear inherited account-selection variables in tests. |
| internal/config/refreshable_test.go | Check persisted account activation state. |
| internal/config/config.go | Implement effective account and Git credential selection. |
| internal/config/auth_config_test.go | Update unknown-user expectations. |
| internal/config/active_account_test.go | Test selection precedence and default preservation. |
| git/client.go | Apply invocation-local authentication to Git subprocesses. |
| git/client_test.go | Supply authentication callbacks in existing tests. |
| git/authenticated_command_test.go | Test deferred authentication and local-command independence. |
| api/http_client.go | Add error-aware token resolvers and clone authenticated requests. |
| api/auth_resolver_test.go | Test resolver failures and conflicting token sources. |
Review effort: Balanced
Comment on lines
+174
to
+177
| auth, err := c.Authentication() | ||
| if err != nil { | ||
| return nil, err | ||
| } |
Comment on lines
+23
to
+25
| if canonical, ok := auth.HostForAPIHost(host); ok { | ||
| host = canonical | ||
| } |
| // such as env var, command-line flags, repository remotes, and more. I'd honestly be a bit shocked if this logic is correct in all cases. | ||
| repositoryArgument := strings.Contains(cmd.Use, "<repository>") || | ||
| parent == "repo" && (cmd.Name() == "create" || cmd.Name() == "sync") | ||
| resourceURL := cmd.Name() == "api" || parent == "pr" || parent == "issue" || parent == "gist" |
Comment on lines
+117
to
+119
| if cmd.Name() == "create" || cmd.Name() == "clone" { | ||
| return host, nil | ||
| } |
| return host, nil | ||
| } | ||
| } | ||
| repositoryContext := parent == "repo" && strings.Contains(cmd.Use, "repository") |
Comment on lines
+150
to
+152
| remotes, err := client.Remotes(context.Background()) | ||
| if err == nil && len(remotes) > 0 && remotes[0].FetchURL != nil { | ||
| return remotes[0].FetchURL.Host, nil |
| if repositoryContext { | ||
| remotes, err := f.Remotes() | ||
| if err == nil && len(remotes) > 0 { | ||
| return remotes[0].RepoHost(), nil |
This comment was marked as spam.
This comment was marked as spam.
This was referenced Nov 11, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Description
Switching the saved active account changes later commands too, which makes parallel work with different accounts awkward. This PR adds
--account LOGINfor one command without changing the saved default.Layer 1 of an eight-part prototype review stack. This is a way to review the design and implementation in smaller pieces, not a proposal to merge the current stack as-is.
How did you test this change?
I used both saved accounts for API requests and saw the default remain unchanged after the override. I also cloned a private repository over HTTPS and saw SSH account routing refused at this layer.
One-command account selection: real SSH refusal and private HTTPS clone
multiaccount-layer-1-02-One-command-account-selection-real-SSH-refusal-and-private-HTTPS-clone.mp4
One command, another real account
multiaccount-layer-1-03-One-command-another-real-account.mp4
Refuse an unsaved account
multiaccount-layer-1-04-Refuse-an-unsaved-account.mp4
Key points
The authentication configuration resolves the effective account. Child Git processes receive invocation-local selection and credential-helper settings, not a persisted account switch. Plain Git commands do not acquire an authentication dependency.
SSH routing is deliberately unsupported here. The next layer adds identities with declared SSH aliases.
Notes for reviewers
Start with the distinction between the effective account and the saved default, then follow selection into API requests and authenticated Git subprocesses. Explicit request headers and cross-host redirect rules still belong to the shared API authentication transport.
The recordings use the product code at this layer, not a later stack endpoint. The included offline replay scripts are supplementary and do not reproduce the live-account recordings.
Offline replay on macOS
Build this layer, then run its synthetic scenario with an absolute executable path. This is a replay instruction, not a claim that the command below was used to make the videos.
Authorship and follow-up
Who wrote this:
Who answers review comments:
Acceptance Criteria
Prototype behaviour under review:
Given both accounts are saved for the host.
When I pass
--account wilmartin_microsoftto a supported command.Then that invocation uses the requested account without changing the saved default.
DO NOT MERGE