Skip to content

feat: auto-switch accounts based on git config github.account - #12628

Closed
yuvrajangadsingh wants to merge 5 commits into
cli:trunkfrom
yuvrajangadsingh:feature/auto-switch-account-git-config
Closed

yuvrajangadsingh wants to merge 5 commits into
cli:trunkfrom
yuvrajangadsingh:feature/auto-switch-account-git-config

Conversation

@yuvrajangadsingh

@yuvrajangadsingh yuvrajangadsingh commented Feb 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Auto-switch gh accounts based on git config github.account, enabling seamless multi-account workflows without manual gh auth switch.

Developers who use git's includeIf for directory-based identity already get automatic user.name/user.email switching. This extends that pattern to gh CLI authentication.

How it works

ActiveUser() in internal/config/config.go now checks git config github.account before falling back to the stored active user:

  1. Read git config github.account
  2. If set and the account is authenticated with gh, use it
  3. Otherwise, fall back to existing behavior
# ~/.gitconfig
[includeIf "gitdir:~/work/"]
    path = ~/.gitconfig-work

# ~/.gitconfig-work
[user]
    email = [email protected]
[github]
    account = work-username
cd ~/work/project && gh pr list    # uses work-username automatically
cd ~/personal/project && gh pr list # uses personal-username automatically

Changes

internal/config/config.go (+22 lines)

  • Added gitConfigAccount() - reads git config github.account
  • Modified ActiveUser() - checks git config first, validates account is authenticated, falls back to stored user

internal/config/config_test.go (+62 lines)

  • TestActiveUserGitConfigOverride with 4 test cases:
    • git config account matches authenticated user -> uses it
    • git config account not authenticated -> falls back to stored user
    • no git config set -> falls back to stored user
    • no git config and no stored user -> returns error

Testing

  • go test ./internal/config/ - all tests pass (existing + new)
  • go build ./cmd/gh/ - full binary builds clean
  • Manual end-to-end test with includeIf + two authenticated accounts - auto-switches correctly by directory

Fixes #12459

When git config "github.account" is set (e.g. via includeIf for
directory-based identity), use that account if it's authenticated
with gh. Falls back to the stored active user if not set or the
account isn't authenticated.

This enables seamless multi-account workflows where users already
use git's conditional includes for switching user.name/user.email.

Fixes cli#12459
Test cases:
- git config account matches authenticated user -> uses it
- git config account not authenticated -> falls back to stored user
- no git config set -> falls back to stored user
- no git config and no stored user -> returns error
@yuvrajangadsingh
yuvrajangadsingh force-pushed the feature/auto-switch-account-git-config branch from 2a6fe00 to a3d2ceb Compare February 6, 2026 19:18
@yuvrajangadsingh
yuvrajangadsingh marked this pull request as ready for review February 6, 2026 19:37
@yuvrajangadsingh
yuvrajangadsingh requested a review from a team as a code owner February 6, 2026 19:37
@cliAutomation cliAutomation added the external pull request originating outside of the CLI core team label Feb 6, 2026
@BagToad

BagToad commented Feb 15, 2026

Copy link
Copy Markdown
Member

Thank you for your contribution! Unfortunately, this pull request is not linked to a help wanted issue, so we are unable to accept it at this time.

We encourage community contributions on issues labeled help wanted Contributions welcome . If you'd like to contribute, please find an open help wanted Contributions welcome issue and link your PR to it.

If you believe this was done in error, please let us know.

@BagToad BagToad closed this Feb 15, 2026
BIGPHOR

This comment was marked as spam.

rexovasgroup-open-source Bot added a commit to rexovasgroup/ghx that referenced this pull request May 29, 2026
* chore(deps): bump github.com/gdamore/tcell/v2 from 2.13.9 to 2.13.10

Bumps [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell) from 2.13.9 to 2.13.10.
- [Release notes](https://github.com/gdamore/tcell/releases)
- [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv3.md)
- [Commits](gdamore/tcell@v2.13.9...v2.13.10)

---
updated-dependencies:
- dependency-name: github.com/gdamore/tcell/v2
  dependency-version: 2.13.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

* chore(deps): bump golangci/golangci-lint-action from 9.2.0 to 9.2.1

Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 9.2.0 to 9.2.1.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](golangci/golangci-lint-action@1e7e51e...82606bf)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-version: 9.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

* fix: use separate http client for non-github hosts

Signed-off-by: Babak K. Shandiz <[email protected]>

Co-authored-by: Kynan Ware <[email protected]>

* test(attestation): align integration tests with new external HTTP client

Commit e6dfcd3 ("fix: use separate http client for non-github hosts")
renamed verification.SigstoreConfig.HttpClient to ExternalHttpClient,
added an external *http.Client argument to api.NewLiveClient, and added
the corresponding Factory.ExternalHttpClient field. It updated the
non-tagged unit tests but missed every //go:build integration file in
pkg/cmd/attestation/..., which broke trunk CI (build (ubuntu-latest)
and build (macos-latest) in the Unit and Integration Tests workflow):

- pkg/cmd/attestation/verification and pkg/cmd/attestation/verify
  failed to build (unknown field HttpClient; not enough arguments in
  call to api.NewLiveClient).
- TestNewInspectCmd_PrintOutputJSONFormat panicked because the
  cmdutil.Factory literal it builds did not set ExternalHttpClient,
  so the new f.ExternalHttpClient() call in inspect.go dereferenced
  a nil func value.

Rename the field at the integration-test call sites, pass
http.DefaultClient as the new external client to api.NewLiveClient,
and populate ExternalHttpClient on the inspect test factory. No
production code changes.

Co-authored-by: Copilot <[email protected]>

* feat: auto-select authenticated account based on repo context

Three-tier resolution in ActiveUser(), first authenticated match wins:

1. GH_USER env var — explicit override for scripts/CI
2. git config github.account — works with git includeIf per-directory identity
3. Remote origin owner → account_rules mapping in gh config — zero
   per-repo setup, just map orgs to accounts once

Falls back to stored active user if nothing matches (existing behavior).

Superior to cli#12628 which only supports tier 2. Our account_rules
approach works regardless of where a repo is cloned — it reads the org
from the remote URL itself, no directory conventions or gitconfig needed.

Refs: cli#12459, cli#13529

* Add auto-sync pipeline, versioning, and Pull App config

- GitHub Actions workflow: on upstream sync, rebase rexovas branch,
  run tests, cross-compile (darwin/linux × arm64/amd64), create release
- Auto-creates issues on rebase conflicts or upstream touching patched files
- Pull App config: auto-sync main from upstream cli/cli trunk
- GHX_VERSION file for release tagging (v{upstream}-ghx.{major}.{minor})
- Gitignore internal docs from public repo

* Add pre-commit hooks for public repo safety (#1)

- Blocks internal/sensitive files (GHX_*.md, ROADMAP, INTERNAL, .env, credentials)
- Runs Go tests automatically when config files are modified
- Runs golangci-lint on changed Go files (if available)
- Hooks stored in .githooks/, activated via core.hooksPath

* Brand version output as "gh xtreme" for ghx builds (#2)

When the version string contains "-ghx.", display "gh xtreme" instead
of "gh version" and point the changelog URL to rexovasgroup/gh.
Non-ghx builds are unaffected.

* feat: add gh ruleset create, update, and delete commands (#3)

Adds the write side of ruleset management that upstream has left
unimplemented since 2021 (cli#3528).

Commands:
  gh ruleset create <name> [flags]   — create with flags or --from-json
  gh ruleset update <id> [flags]     — GET-modify-PUT merge, or --from-json
  gh ruleset delete <id> [--yes]     — with confirmation prompt

PR rule shortcuts: --required-approvals, --dismiss-stale-reviews,
--require-code-owner-review, --require-last-push-approval merge into
a single pull_request rule.

Supports both repo and org rulesets via --org flag.
Tested live: create → view → update → delete cycle on rexovasgroup/gh.

Refs: cli#3528, cli#8019

* Add make ghx target and bump version to 1.1 (#4)

make ghx reads GHX_VERSION and upstream tag automatically, builds
and installs to /usr/local/bin/gh. No manual version strings needed.

* Update refs for repo rename to ghx, install as both gh and ghx (#5)

- make ghx now installs binary to both /usr/local/bin/gh and /usr/local/bin/ghx
- Updated changelog URL to rexovasgroup/ghx
- Updated CI release target from rexovas to main
- Updated install instructions to use get.rexov.as

* Add 'Replace stock gh' install option to release notes (#6)

* Add post-release dispatch hook (#7)

* Add post-release dispatch hook to CI workflow

Fires a repository_dispatch after each release so downstream
systems can pick up new binaries automatically.

* Add install script for ghx via get.rexov.as

Platform-detecting installer with checksum verification,
--replace-gh flag, and interactive prompt for existing gh users.

* Use GitHub App token for post-release dispatch (#9)

Replace plain token with actions/create-github-app-token to generate
short-lived installation tokens from the rexovas-artifact-relay app.

* feat: add gh repo environment list, create, and delete (#8)

* feat: add gh repo environment list, create, and delete commands

Adds environment management that upstream has left unimplemented
since 2021 (cli#5149).

Commands:
  gh repo environment list              — table output with name, branch policy, rules
  gh repo environment create <name>     — with --wait-timer, --reviewer, --deployment-branch-policy, --from-json
  gh repo environment delete <name>     — with --yes confirmation prompt

Uses the Environments REST API (PUT upsert for create/update).
Tested live: create → list → delete cycle on rexovasgroup/ghx.

Bumps GHX_VERSION to 1.2.

Refs: cli#5149

* fix: gofmt formatting

* Fix app token hash, trigger on upstream branch (#10)

- Pin create-github-app-token to v3.2.0 (correct hash)
- Pull App now syncs cli/cli:trunk to `upstream` branch (not main)
- Workflow triggers on push to `upstream`, rebases main onto it
- Prevents Pull App hardreset from destroying ghx patches on main

* Use app token for force-push to bypass branch protection (#11)

Generate token before push step, set remote URL to use it.
Same token reused for post-release dispatch.

* Use client-id instead of deprecated app-id for app token (#12)

* Scope app token to current repo, use client-id (#13)

* Split sync and build into separate workflows (#14)

Sync workflow: rebases main onto upstream, creates PR with auto-merge.
Build workflow: triggers on push to main, compiles, releases, dispatches.

Avoids force-pushing main, works with branch protection naturally.

* Rename main to trunk, use fixed sync branch (#15)

- Workflows now target trunk (default branch) instead of main
- Sync workflow pushes to fixed `sync` branch instead of timestamped
- Build workflow triggers on push to trunk
- Matches cli/cli convention of using trunk as primary branch

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Babak K. Shandiz <[email protected]>
Co-authored-by: Kynan Ware <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Markus Olsson <[email protected]>
Co-authored-by: David E. Lovas <[email protected]>
Co-authored-by: David Lovas <[email protected]>
rexovasgroup-open-source Bot added a commit to rexovasgroup/ghx that referenced this pull request May 29, 2026
* chore(deps): bump github.com/gdamore/tcell/v2 from 2.13.9 to 2.13.10

Bumps [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell) from 2.13.9 to 2.13.10.
- [Release notes](https://github.com/gdamore/tcell/releases)
- [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv3.md)
- [Commits](gdamore/tcell@v2.13.9...v2.13.10)

---
updated-dependencies:
- dependency-name: github.com/gdamore/tcell/v2
  dependency-version: 2.13.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

* chore(deps): bump golangci/golangci-lint-action from 9.2.0 to 9.2.1

Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 9.2.0 to 9.2.1.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](golangci/golangci-lint-action@1e7e51e...82606bf)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-version: 9.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

* fix: use separate http client for non-github hosts

Signed-off-by: Babak K. Shandiz <[email protected]>

Co-authored-by: Kynan Ware <[email protected]>

* test(attestation): align integration tests with new external HTTP client

Commit e6dfcd3 ("fix: use separate http client for non-github hosts")
renamed verification.SigstoreConfig.HttpClient to ExternalHttpClient,
added an external *http.Client argument to api.NewLiveClient, and added
the corresponding Factory.ExternalHttpClient field. It updated the
non-tagged unit tests but missed every //go:build integration file in
pkg/cmd/attestation/..., which broke trunk CI (build (ubuntu-latest)
and build (macos-latest) in the Unit and Integration Tests workflow):

- pkg/cmd/attestation/verification and pkg/cmd/attestation/verify
  failed to build (unknown field HttpClient; not enough arguments in
  call to api.NewLiveClient).
- TestNewInspectCmd_PrintOutputJSONFormat panicked because the
  cmdutil.Factory literal it builds did not set ExternalHttpClient,
  so the new f.ExternalHttpClient() call in inspect.go dereferenced
  a nil func value.

Rename the field at the integration-test call sites, pass
http.DefaultClient as the new external client to api.NewLiveClient,
and populate ExternalHttpClient on the inspect test factory. No
production code changes.

Co-authored-by: Copilot <[email protected]>

* feat: auto-select authenticated account based on repo context

Three-tier resolution in ActiveUser(), first authenticated match wins:

1. GH_USER env var — explicit override for scripts/CI
2. git config github.account — works with git includeIf per-directory identity
3. Remote origin owner → account_rules mapping in gh config — zero
   per-repo setup, just map orgs to accounts once

Falls back to stored active user if nothing matches (existing behavior).

Superior to cli#12628 which only supports tier 2. Our account_rules
approach works regardless of where a repo is cloned — it reads the org
from the remote URL itself, no directory conventions or gitconfig needed.

Refs: cli#12459, cli#13529

* Add auto-sync pipeline, versioning, and Pull App config

- GitHub Actions workflow: on upstream sync, rebase rexovas branch,
  run tests, cross-compile (darwin/linux × arm64/amd64), create release
- Auto-creates issues on rebase conflicts or upstream touching patched files
- Pull App config: auto-sync main from upstream cli/cli trunk
- GHX_VERSION file for release tagging (v{upstream}-ghx.{major}.{minor})
- Gitignore internal docs from public repo

* Add pre-commit hooks for public repo safety (#1)

- Blocks internal/sensitive files (GHX_*.md, ROADMAP, INTERNAL, .env, credentials)
- Runs Go tests automatically when config files are modified
- Runs golangci-lint on changed Go files (if available)
- Hooks stored in .githooks/, activated via core.hooksPath

* Brand version output as "gh xtreme" for ghx builds (#2)

When the version string contains "-ghx.", display "gh xtreme" instead
of "gh version" and point the changelog URL to rexovasgroup/gh.
Non-ghx builds are unaffected.

* feat: add gh ruleset create, update, and delete commands (#3)

Adds the write side of ruleset management that upstream has left
unimplemented since 2021 (cli#3528).

Commands:
  gh ruleset create <name> [flags]   — create with flags or --from-json
  gh ruleset update <id> [flags]     — GET-modify-PUT merge, or --from-json
  gh ruleset delete <id> [--yes]     — with confirmation prompt

PR rule shortcuts: --required-approvals, --dismiss-stale-reviews,
--require-code-owner-review, --require-last-push-approval merge into
a single pull_request rule.

Supports both repo and org rulesets via --org flag.
Tested live: create → view → update → delete cycle on rexovasgroup/gh.

Refs: cli#3528, cli#8019

* Add make ghx target and bump version to 1.1 (#4)

make ghx reads GHX_VERSION and upstream tag automatically, builds
and installs to /usr/local/bin/gh. No manual version strings needed.

* Update refs for repo rename to ghx, install as both gh and ghx (#5)

- make ghx now installs binary to both /usr/local/bin/gh and /usr/local/bin/ghx
- Updated changelog URL to rexovasgroup/ghx
- Updated CI release target from rexovas to main
- Updated install instructions to use get.rexov.as

* Add 'Replace stock gh' install option to release notes (#6)

* Add post-release dispatch hook (#7)

* Add post-release dispatch hook to CI workflow

Fires a repository_dispatch after each release so downstream
systems can pick up new binaries automatically.

* Add install script for ghx via get.rexov.as

Platform-detecting installer with checksum verification,
--replace-gh flag, and interactive prompt for existing gh users.

* Use GitHub App token for post-release dispatch (#9)

Replace plain token with actions/create-github-app-token to generate
short-lived installation tokens from the rexovas-artifact-relay app.

* feat: add gh repo environment list, create, and delete (#8)

* feat: add gh repo environment list, create, and delete commands

Adds environment management that upstream has left unimplemented
since 2021 (cli#5149).

Commands:
  gh repo environment list              — table output with name, branch policy, rules
  gh repo environment create <name>     — with --wait-timer, --reviewer, --deployment-branch-policy, --from-json
  gh repo environment delete <name>     — with --yes confirmation prompt

Uses the Environments REST API (PUT upsert for create/update).
Tested live: create → list → delete cycle on rexovasgroup/ghx.

Bumps GHX_VERSION to 1.2.

Refs: cli#5149

* fix: gofmt formatting

* Fix app token hash, trigger on upstream branch (#10)

- Pin create-github-app-token to v3.2.0 (correct hash)
- Pull App now syncs cli/cli:trunk to `upstream` branch (not main)
- Workflow triggers on push to `upstream`, rebases main onto it
- Prevents Pull App hardreset from destroying ghx patches on main

* Use app token for force-push to bypass branch protection (#11)

Generate token before push step, set remote URL to use it.
Same token reused for post-release dispatch.

* Use client-id instead of deprecated app-id for app token (#12)

* Scope app token to current repo, use client-id (#13)

* Split sync and build into separate workflows (#14)

Sync workflow: rebases main onto upstream, creates PR with auto-merge.
Build workflow: triggers on push to main, compiles, releases, dispatches.

Avoids force-pushing main, works with branch protection naturally.

* Rename main to trunk, use fixed sync branch (#15)

- Workflows now target trunk (default branch) instead of main
- Sync workflow pushes to fixed `sync` branch instead of timestamped
- Build workflow triggers on push to trunk
- Matches cli/cli convention of using trunk as primary branch

* Use app bot identity for verified commits (#17)

GitHub auto-verifies commits when the author matches the app
token used to push.

* Fix release targeting upstream repo instead of fork (#18)

Add --repo flag to gh release commands so they target
rexovasgroup/ghx instead of the upstream cli/cli.

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Babak K. Shandiz <[email protected]>
Co-authored-by: Kynan Ware <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Markus Olsson <[email protected]>
Co-authored-by: David E. Lovas <[email protected]>
Co-authored-by: David Lovas <[email protected]>
rexovasgroup-open-source Bot pushed a commit to rexovasgroup/ghx that referenced this pull request May 30, 2026
Three-tier resolution in ActiveUser(), first authenticated match wins:

1. GH_USER env var — explicit override for scripts/CI
2. git config github.account — works with git includeIf per-directory identity
3. Remote origin owner → account_rules mapping in gh config — zero
   per-repo setup, just map orgs to accounts once

Falls back to stored active user if nothing matches (existing behavior).

Superior to cli#12628 which only supports tier 2. Our account_rules
approach works regardless of where a repo is cloned — it reads the org
from the remote URL itself, no directory conventions or gitconfig needed.

Refs: cli#12459, cli#13529
rexovasgroup-open-source Bot added a commit to rexovasgroup/ghx that referenced this pull request May 30, 2026
Three-tier resolution in ActiveUser(), first authenticated match wins:

1. GH_USER env var — explicit override for scripts/CI
2. git config github.account — works with git includeIf per-directory identity
3. Remote origin owner → account_rules mapping in gh config — zero
   per-repo setup, just map orgs to accounts once

Falls back to stored active user if nothing matches (existing behavior).

Superior to cli#12628 which only supports tier 2. Our account_rules
approach works regardless of where a repo is cloned — it reads the org
from the remote URL itself, no directory conventions or gitconfig needed.

Refs: cli#12459, cli#13529
rexovasgroup-open-source Bot added a commit to rexovasgroup/ghx that referenced this pull request May 30, 2026
Three-tier resolution in ActiveUser(), first authenticated match wins:

1. GH_USER env var — explicit override for scripts/CI
2. git config github.account — works with git includeIf per-directory identity
3. Remote origin owner → account_rules mapping in gh config — zero
   per-repo setup, just map orgs to accounts once

Falls back to stored active user if nothing matches (existing behavior).

Superior to cli#12628 which only supports tier 2. Our account_rules
approach works regardless of where a repo is cloned — it reads the org
from the remote URL itself, no directory conventions or gitconfig needed.

Refs: cli#12459, cli#13529
rexovasgroup-open-source Bot added a commit to rexovasgroup/ghx that referenced this pull request May 30, 2026
Three-tier resolution in ActiveUser(), first authenticated match wins:

1. GH_USER env var — explicit override for scripts/CI
2. git config github.account — works with git includeIf per-directory identity
3. Remote origin owner → account_rules mapping in gh config — zero
   per-repo setup, just map orgs to accounts once

Falls back to stored active user if nothing matches (existing behavior).

Superior to cli#12628 which only supports tier 2. Our account_rules
approach works regardless of where a repo is cloned — it reads the org
from the remote URL itself, no directory conventions or gitconfig needed.

Refs: cli#12459, cli#13529
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external pull request originating outside of the CLI core team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Auto-switch accounts based on git config

4 participants