X Tutup
Skip to content

Commit 98f1f5e

Browse files
committed
Use absolute path when configuring gh as git credential
This keeps git operations working even when PATH is modified, e.g. `brew update` will work even though Homebrew runs the command explicitly without `/usr/local/bin` in PATH. Additionally, this inserts a blank value for `credential.*.helper` to instruct git to ignore previously configured credential helpers, i.e. those that might have been set up in system configuration files. We do this because otherwise, git will store the credential obtained from gh in every other credential helper in the chain, which we want to avoid. Before: git config --global credential.https://github.com.helper '!gh auth git-credential' After: git config --global credential.https://github.com.helper '' git config --global --add credential.https://github.com.helper '!/path/to/gh auth git-credential'
1 parent 3444d00 commit 98f1f5e

File tree

7 files changed

+70
-9
lines changed

7 files changed

+70
-9
lines changed

pkg/cmd/auth/login/login.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ type LoginOptions struct {
2323
Config func() (config.Config, error)
2424
HttpClient func() (*http.Client, error)
2525

26+
MainExecutable string
27+
2628
Interactive bool
2729

2830
Hostname string
@@ -36,6 +38,8 @@ func NewCmdLogin(f *cmdutil.Factory, runF func(*LoginOptions) error) *cobra.Comm
3638
IO: f.IOStreams,
3739
Config: f.Config,
3840
HttpClient: f.HttpClient,
41+
42+
MainExecutable: f.Executable,
3943
}
4044

4145
var tokenStdin bool
@@ -189,6 +193,7 @@ func loginRun(opts *LoginOptions) error {
189193
Interactive: opts.Interactive,
190194
Web: opts.Web,
191195
Scopes: opts.Scopes,
196+
Executable: opts.MainExecutable,
192197
})
193198
}
194199

pkg/cmd/auth/refresh/refresh.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ type RefreshOptions struct {
1919
IO *iostreams.IOStreams
2020
Config func() (config.Config, error)
2121

22+
MainExecutable string
23+
2224
Hostname string
2325
Scopes []string
2426
AuthFlow func(config.Config, *iostreams.IOStreams, string, []string) error
@@ -34,6 +36,7 @@ func NewCmdRefresh(f *cmdutil.Factory, runF func(*RefreshOptions) error) *cobra.
3436
_, err := authflow.AuthFlowWithConfig(cfg, io, hostname, "", scopes)
3537
return err
3638
},
39+
MainExecutable: f.Executable,
3740
}
3841

3942
cmd := &cobra.Command{

pkg/cmd/auth/shared/git_credential.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
)
1616

1717
type GitCredentialFlow struct {
18+
Executable string
19+
1820
shouldSetup bool
1921
helper string
2022
scopes []string
@@ -50,13 +52,26 @@ func (flow *GitCredentialFlow) ShouldSetup() bool {
5052
}
5153

5254
func (flow *GitCredentialFlow) Setup(hostname, username, authToken string) error {
53-
return GitCredentialSetup(hostname, username, authToken, flow.helper)
55+
return flow.gitCredentialSetup(hostname, username, authToken)
5456
}
5557

56-
func GitCredentialSetup(hostname, username, password, helper string) error {
57-
if helper == "" {
58+
func (flow *GitCredentialFlow) gitCredentialSetup(hostname, username, password string) error {
59+
if flow.helper == "" {
60+
// first use a blank value to indicate to git we want to sever the chain of credential helpers
61+
preConfigureCmd, err := git.GitCommand("config", "--global", gitCredentialHelperKey(hostname), "")
62+
if err != nil {
63+
return err
64+
}
65+
if err = run.PrepareCmd(preConfigureCmd).Run(); err != nil {
66+
return err
67+
}
68+
5869
// use GitHub CLI as a credential helper (for this host only)
59-
configureCmd, err := git.GitCommand("config", "--global", gitCredentialHelperKey(hostname), "!gh auth git-credential")
70+
configureCmd, err := git.GitCommand(
71+
"config", "--global", "--add",
72+
gitCredentialHelperKey(hostname),
73+
fmt.Sprintf("!%s auth git-credential", shellQuote(flow.Executable)),
74+
)
6075
if err != nil {
6176
return err
6277
}
@@ -124,3 +139,10 @@ func isOurCredentialHelper(cmd string) bool {
124139

125140
return strings.TrimSuffix(filepath.Base(args[0]), ".exe") == "gh"
126141
}
142+
143+
func shellQuote(s string) string {
144+
if strings.ContainsAny(s, " $") {
145+
return "'" + s + "'"
146+
}
147+
return s
148+
}

pkg/cmd/auth/shared/git_credential_test.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,42 @@ func TestGitCredentialSetup_configureExisting(t *testing.T) {
1212
cs.Register(`git credential reject`, 0, "")
1313
cs.Register(`git credential approve`, 0, "")
1414

15-
if err := GitCredentialSetup("example.com", "monalisa", "PASSWD", "osxkeychain"); err != nil {
15+
f := GitCredentialFlow{
16+
Executable: "gh",
17+
helper: "osxkeychain",
18+
}
19+
20+
if err := f.gitCredentialSetup("example.com", "monalisa", "PASSWD"); err != nil {
1621
t.Errorf("GitCredentialSetup() error = %v", err)
1722
}
1823
}
1924

2025
func TestGitCredentialSetup_setOurs(t *testing.T) {
2126
cs, restoreRun := run.Stub()
2227
defer restoreRun(t)
23-
cs.Register(`git config --global credential\.https://example\.com\.helper`, 0, "", func(args []string) {
24-
if val := args[len(args)-1]; val != "!gh auth git-credential" {
28+
cs.Register(`git config --global credential\.`, 0, "", func(args []string) {
29+
if key := args[len(args)-2]; key != "credential.https://example.com.helper" {
30+
t.Errorf("git config key was %q", key)
31+
}
32+
if val := args[len(args)-1]; val != "" {
2533
t.Errorf("global credential helper configured to %q", val)
2634
}
2735
})
36+
cs.Register(`git config --global --add credential\.`, 0, "", func(args []string) {
37+
if key := args[len(args)-2]; key != "credential.https://example.com.helper" {
38+
t.Errorf("git config key was %q", key)
39+
}
40+
if val := args[len(args)-1]; val != "!/path/to/gh auth git-credential" {
41+
t.Errorf("global credential helper configured to %q", val)
42+
}
43+
})
44+
45+
f := GitCredentialFlow{
46+
Executable: "/path/to/gh",
47+
helper: "",
48+
}
2849

29-
if err := GitCredentialSetup("example.com", "monalisa", "PASSWD", ""); err != nil {
50+
if err := f.gitCredentialSetup("example.com", "monalisa", "PASSWD"); err != nil {
3051
t.Errorf("GitCredentialSetup() error = %v", err)
3152
}
3253
}

pkg/cmd/auth/shared/login_flow.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type LoginOptions struct {
2828
Interactive bool
2929
Web bool
3030
Scopes []string
31+
Executable string
3132

3233
sshContext sshContext
3334
}
@@ -56,7 +57,7 @@ func Login(opts *LoginOptions) error {
5657

5758
var additionalScopes []string
5859

59-
credentialFlow := &GitCredentialFlow{}
60+
credentialFlow := &GitCredentialFlow{Executable: opts.Executable}
6061
if opts.Interactive && gitProtocol == "https" {
6162
if err := credentialFlow.Prompt(hostname); err != nil {
6263
return err

pkg/cmd/factory/default.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ func New(appVersion string) *cmdutil.Factory {
4444
}
4545
remotesFunc := rr.Resolver(hostOverride)
4646

47+
ghExecutable := "gh"
48+
if exe, err := os.Executable(); err == nil {
49+
ghExecutable = exe
50+
}
51+
4752
return &cmdutil.Factory{
4853
IOStreams: io,
4954
Config: configFunc,
@@ -70,5 +75,6 @@ func New(appVersion string) *cmdutil.Factory {
7075
}
7176
return currentBranch, nil
7277
},
78+
Executable: ghExecutable,
7379
}
7480
}

pkg/cmdutil/factory.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@ type Factory struct {
1616
Remotes func() (context.Remotes, error)
1717
Config func() (config.Config, error)
1818
Branch func() (string, error)
19+
20+
// Executable is the path to the currently invoked gh binary
21+
Executable string
1922
}

0 commit comments

Comments
 (0)
X Tutup