Repository navigation
feat(zsh): pick the gh account per repo from the SSH host alias - #137
Conversation
The gh wrapper reads the origin remote's SSH host alias and runs gh with the matching account's token (GH_HOST_ACCOUNTS), so personal repos use iamgerwin without switching the global active account. An explicit GH_TOKEN always wins.
Mocked gh and throwaway repositories cover the mapped SSH alias (scp and ssh:// forms), unmapped remotes, running outside a repository, an explicit GH_TOKEN, a mapped account without a token, and that the token stays out of the calling shell.
README explains GH_HOST_ACCOUNTS and the GH_TOKEN override; CHANGELOG records the wrapper under Unreleased.
Review: mergeability and securityVerdict: mergeable after fixes. CI is green, there are no conflicts with Should-fix1. 2. 3. xtrace prints the token (security, credential exposure) 4. Falling back to the active account is silent (security, identity) Suggested
|
- Work under setopt nounset (default-expand GH_TOKEN and the map lookup). - Skip the mapping when -R/--repo or GH_REPO targets another repository, so those calls keep the active account. - Disable xtrace/verbose inside the function so the token is never traced. - Warn on stderr when a mapped account has no token instead of silently using the active account.
… warning Each new case fails against the previous wrapper. Also drops an unused import.
README covers -R/GH_REPO, gh auth switch inside mapped repos, the missing-token warning and keeping personal aliases in custom.zsh. CHANGELOG reflects the hardened behavior; learnings/dump.md records why credential-injecting wrappers must survive caller shell options.
Review round 1 addressedPushed
All 6 new tests fail against the previous wrapper and pass with the fix. Not changed: the ShellCheck CI gap for zsh files (outside this PR's scope). |
Review round 2: mergeability and security (head
|
- Stop the -R/--repo scan at `--` and skip values of text-taking flags, so `--body "--repo"` or `--title "-Rebase"` keep the mapped account. - Fail closed (error, exit 1) when a mapped account has no token instead of running as the active account. - Guard the map's type, so an unset or scalar GH_HOST_ACCOUNTS no longer breaks every gh call, and match host aliases case-insensitively. - Ship an empty map in functions.zsh; the personal mapping moves to custom.zsh, which loads last.
Adds cases for text flag values and `--`, an unset or scalar map, a mixed-case host, the empty default map, the custom.zsh mapping, and the fail-closed exit. Tests now set their own mapping. Each new case fails against the previous head.
README moves the mapping to custom.zsh (with ~/.zshrc.local for aliases kept out of git), and documents fail-closed behavior, flag-value handling, case-insensitive hosts and the origin trust model. CHANGELOG and learnings/dump.md reflect the second review round.
Review round 2 addressedPushed
Correction to round 2: All new cases fail against |
Summary
ghwrapper that reads theoriginremote's SSH host alias and runsghwith the mapped account's token, so personal clones (github.com-iamgerwin) useiamgerwinwhile work repos keep the active account. The global active account never changes.nounset, never traces the token,-R/--repo/GH_REPOkeep the active account while text flag values and--do not trigger that, a mapped account without a token fails closed, an unset or scalar map cannot crashgh, and host aliases match case-insensitively.functions.zshships an emptyGH_HOST_ACCOUNTS; the personal mapping lives incustom.zsh(or~/.zshrc.localto keep it out of git); one pair per~/.ssh/configalias. Handles scp-style andssh://remotes.GH_TOKENoverride; CHANGELOG records it under Unreleased.Evidence
ghin a clone of a personal repo ran as the active work account; private personal repos returned "not found" unless the user rangh auth switchglobally.After:
gh api userreturnsiamgerwinin agithub.com-iamgerwinclone, and the active account in a work repo and outside any repo (checked locally on macOS zsh).bash scripts/check.shpasses locally on macOS (44 Python tests, including 16 intests/gh_wrapper_test.py, plus shell, skill and doc checks); CI green on Linux and macOS.learnings/dump.mdrecords the credential-wrapper lesson from the review.ghand throwaway repositories: mapped alias (scp andssh://), unmapped remote, outside a repo, explicitGH_TOKEN, mapped account without a token (fails closed), no token leaking into the calling shell,setopt nounset,setopt xtrace(token never traced, caller options restored), and-R/--repo/GH_REPOskipping the mapping. The new cases fail against the first version.Merge Danger
Door: two-way. Revert the commit or remove the function;
command ghis untouched.Blast Radius: every interactive zsh
ghcall. Unmapped hosts behave exactly as before; scripts callingcommand ghor running in bash are unaffected.