X Tutup
Skip to content

Commit d731cb9

Browse files
committed
Fix determining current process location
1 parent 55c3064 commit d731cb9

File tree

1 file changed

+40
-16
lines changed

1 file changed

+40
-16
lines changed

pkg/cmd/factory/default.go

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"net/http"
77
"os"
88
"path/filepath"
9-
"strings"
109

1110
"github.com/cli/cli/v2/api"
1211
"github.com/cli/cli/v2/context"
@@ -16,14 +15,13 @@ import (
1615
"github.com/cli/cli/v2/pkg/cmd/extension"
1716
"github.com/cli/cli/v2/pkg/cmdutil"
1817
"github.com/cli/cli/v2/pkg/iostreams"
19-
"github.com/cli/safeexec"
2018
)
2119

2220
func New(appVersion string) *cmdutil.Factory {
2321
f := &cmdutil.Factory{
24-
Config: configFunc(), // No factory dependencies
25-
Branch: branchFunc(), // No factory dependencies
26-
Executable: executable(), // No factory dependencies
22+
Config: configFunc(), // No factory dependencies
23+
Branch: branchFunc(), // No factory dependencies
24+
Executable: executable("gh"), // No factory dependencies
2725

2826
ExtensionManager: extension.NewManager(),
2927
}
@@ -116,24 +114,50 @@ func browserLauncher(f *cmdutil.Factory) string {
116114
return os.Getenv("BROWSER")
117115
}
118116

119-
func executable() string {
120-
exe, _ := os.Executable()
117+
// Finds the location of the executable for the current process as it's found in PATH, respecting symlinks.
118+
// If the process couldn't determine its location, return fallbackName. If the executable wasn't found in
119+
// PATH, return the absolute location to the program.
120+
//
121+
// The idea is that the result of this function is callable in the future and refers to the same
122+
// installation of gh, even across upgrades. This is needed primarily for Homebrew, which installs software
123+
// under a location such as `/usr/local/Cellar/gh/1.13.1/bin/gh` and symlinks it from `/usr/local/bin/gh`.
124+
// When the version is upgraded, Homebrew will often delete older versions, but keep the symlink. Because of
125+
// this, we want to refer to the `gh` binary as `/usr/local/bin/gh` and not as its internal Homebrew
126+
// location.
127+
//
128+
// None of this would be needed if we could just refer to GitHub CLI as `gh`, i.e. without using an absolute
129+
// path. However, for some reason Homebrew does not include `/usr/local/bin` in PATH when it invokes git
130+
// commands to update its taps. If `gh` (no path) is being used as git credential helper, as set up by `gh
131+
// auth login`, running `brew update` will print out authentication errors as git is unable to locate
132+
// Homebrew-installed `gh`.
133+
func executable(fallbackName string) string {
134+
exe, err := os.Executable()
135+
if err != nil {
136+
return fallbackName
137+
}
121138

139+
base := filepath.Base(exe)
122140
path := os.Getenv("PATH")
123141
for _, dir := range filepath.SplitList(path) {
124-
if strings.HasSuffix(dir, "gh") {
125-
if dir == exe {
126-
return dir
127-
}
128-
if symlink, _ := os.Readlink(dir); symlink == exe {
129-
return dir
142+
p, err := filepath.Abs(filepath.Join(dir, base))
143+
if err != nil {
144+
continue
145+
}
146+
f, err := os.Stat(p)
147+
if err != nil {
148+
continue
149+
}
150+
151+
if p == exe {
152+
return p
153+
} else if f.Mode()&os.ModeSymlink != 0 {
154+
if t, err := os.Readlink(p); err == nil && t == exe {
155+
return p
130156
}
131157
}
132158
}
133159

134-
gh, _ := safeexec.LookPath("gh")
135-
136-
return gh
160+
return exe
137161
}
138162

139163
func configFunc() func() (config.Config, error) {

0 commit comments

Comments
 (0)
X Tutup