mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-08-24 10:13:41 -05:00
Fix pull requests silently disappearing until lazygit is restarted (#5921)
A lazygit instance that has been open for a while sometimes stops showing pull requests, and keeps not showing them until you quit and restart it. The cause is that we resolve the GitHub token once per process and then keep using that stale answer. We get the token from go-gh, which reads gh's `hosts.yml` on the first call and caches it for the lifetime of the process. gh rewrites that file whenever the active account changes, and keeps the active account's token either in the file or in the system keyring, depending on the account. Once the file changes under us, our snapshot no longer describes reality: either we keep sending a token for an account that is no longer active, or — if the snapshot was taken while a keyring-backed account was active — we find no token at all, drop the remote, and show no pull requests. Nothing is reported to the user, so it looks like PR fetching is simply broken. Switching accounts with `gh auth switch` is the easiest way to trigger this, but it isn't limited to multi-account setups: a single account hits the same thing whenever its token is rotated, or moved between the keyring and `hosts.yml` by a fresh `gh auth login`. So ask gh itself for the token on every refresh, with `gh auth token --hostname <host>`, which resolves it afresh from whichever of the environment, the keyring, or the config file currently holds it. Not passing `--secure-storage` (which go-gh does internally) also means it stops mattering which of the two the active account uses. go-gh's lookup stays as a fallback for setups without the gh binary, where it still picks up `GH_TOKEN` and friends; when gh is present it checks those variables itself. To reproduce on master, with two gh accounts, one with its token in the keyring and one in `hosts.yml`: start lazygit while the keyring-backed account is active, then run `gh auth switch` to the other one. The pull request information disappears on the next refresh and doesn't come back until lazygit is restarted. Running `gh auth token --secure-storage --hostname github.com` by hand at that point prints `no oauth token found for github.com`.
This commit is contained in:
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -160,9 +162,51 @@ func fetchPullRequestsQuery(branches []string, owner string, repo string) (strin
|
||||
return queryString, variables
|
||||
}
|
||||
|
||||
// GetAuthToken returns the token to authenticate against the given host with,
|
||||
// or an empty string if there is none.
|
||||
//
|
||||
// The token has to come from gh itself rather than from an in-process lookup
|
||||
// with go-gh: that reads gh's config file once per process and answers from
|
||||
// that snapshot ever after, whereas gh rewrites the file whenever the active
|
||||
// account changes, and keeps the active account's token either there or in the
|
||||
// system keyring. Under a long-running lazygit the snapshot therefore drifts
|
||||
// out of date, leaving us with a token for an account that is no longer active,
|
||||
// or with no token at all.
|
||||
func (self *GitHubCommands) GetAuthToken(host string) string {
|
||||
token, _ := auth.TokenForHost(host)
|
||||
return token
|
||||
ghExe := ghExecutable()
|
||||
if ghExe == "" {
|
||||
// Without gh installed, the environment variables and config file that
|
||||
// gh would have consulted are still worth a look.
|
||||
token, _ := auth.TokenFromEnvOrConfig(host)
|
||||
return token
|
||||
}
|
||||
|
||||
cmdArgs := []string{ghExe, "auth", "token", "--hostname", host}
|
||||
output, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
|
||||
if err != nil {
|
||||
// Not being logged in to this host is a normal state rather than
|
||||
// something to report; the runner logs gh's stderr for the rest.
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.TrimSpace(output)
|
||||
}
|
||||
|
||||
// ghExecutable returns the path of the gh binary, or an empty string if it
|
||||
// isn't installed.
|
||||
func ghExecutable() string {
|
||||
if ghExe := os.Getenv("GH_PATH"); ghExe != "" {
|
||||
return ghExe
|
||||
}
|
||||
|
||||
// A gh found in the current directory rather than on PATH comes back as
|
||||
// exec.ErrDot, which we treat as not having found one at all.
|
||||
ghExe, err := exec.LookPath("gh")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return ghExe
|
||||
}
|
||||
|
||||
// FetchRecentPRs fetches recent pull requests using GraphQL. serviceInfo
|
||||
|
||||
Reference in New Issue
Block a user