Skip to content

Commit

Permalink
Merge pull request cli#3075 from cli/credential-helper-absolute
Browse files Browse the repository at this point in the history
Use absolute path when configuring gh as git credential
  • Loading branch information
mislav authored Mar 4, 2021
2 parents e96d974 + cfbfb57 commit aa5cf6c
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 13 deletions.
5 changes: 1 addition & 4 deletions cmd/gh/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,7 @@ func mainRun() exitCode {

newRelease := <-updateMessageChan
if newRelease != nil {
isHomebrew := false
if ghExe, err := os.Executable(); err == nil {
isHomebrew = isUnderHomebrew(ghExe)
}
isHomebrew := isUnderHomebrew(cmdFactory.Executable)
if isHomebrew && isRecentRelease(newRelease.PublishedAt) {
// do not notify Homebrew users before the version bump had a chance to get merged into homebrew-core
return exitOK
Expand Down
5 changes: 5 additions & 0 deletions pkg/cmd/auth/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type LoginOptions struct {
Config func() (config.Config, error)
HttpClient func() (*http.Client, error)

MainExecutable string

Interactive bool

Hostname string
Expand All @@ -36,6 +38,8 @@ func NewCmdLogin(f *cmdutil.Factory, runF func(*LoginOptions) error) *cobra.Comm
IO: f.IOStreams,
Config: f.Config,
HttpClient: f.HttpClient,

MainExecutable: f.Executable,
}

var tokenStdin bool
Expand Down Expand Up @@ -189,6 +193,7 @@ func loginRun(opts *LoginOptions) error {
Interactive: opts.Interactive,
Web: opts.Web,
Scopes: opts.Scopes,
Executable: opts.MainExecutable,
})
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/cmd/auth/refresh/refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type RefreshOptions struct {
IO *iostreams.IOStreams
Config func() (config.Config, error)

MainExecutable string

Hostname string
Scopes []string
AuthFlow func(config.Config, *iostreams.IOStreams, string, []string) error
Expand All @@ -34,6 +36,7 @@ func NewCmdRefresh(f *cmdutil.Factory, runF func(*RefreshOptions) error) *cobra.
_, err := authflow.AuthFlowWithConfig(cfg, io, hostname, "", scopes)
return err
},
MainExecutable: f.Executable,
}

cmd := &cobra.Command{
Expand Down
30 changes: 26 additions & 4 deletions pkg/cmd/auth/shared/git_credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
)

type GitCredentialFlow struct {
Executable string

shouldSetup bool
helper string
scopes []string
Expand Down Expand Up @@ -50,13 +52,26 @@ func (flow *GitCredentialFlow) ShouldSetup() bool {
}

func (flow *GitCredentialFlow) Setup(hostname, username, authToken string) error {
return GitCredentialSetup(hostname, username, authToken, flow.helper)
return flow.gitCredentialSetup(hostname, username, authToken)
}

func GitCredentialSetup(hostname, username, password, helper string) error {
if helper == "" {
func (flow *GitCredentialFlow) gitCredentialSetup(hostname, username, password string) error {
if flow.helper == "" {
// first use a blank value to indicate to git we want to sever the chain of credential helpers
preConfigureCmd, err := git.GitCommand("config", "--global", gitCredentialHelperKey(hostname), "")
if err != nil {
return err
}
if err = run.PrepareCmd(preConfigureCmd).Run(); err != nil {
return err
}

// use GitHub CLI as a credential helper (for this host only)
configureCmd, err := git.GitCommand("config", "--global", gitCredentialHelperKey(hostname), "!gh auth git-credential")
configureCmd, err := git.GitCommand(
"config", "--global", "--add",
gitCredentialHelperKey(hostname),
fmt.Sprintf("!%s auth git-credential", shellQuote(flow.Executable)),
)
if err != nil {
return err
}
Expand Down Expand Up @@ -124,3 +139,10 @@ func isOurCredentialHelper(cmd string) bool {

return strings.TrimSuffix(filepath.Base(args[0]), ".exe") == "gh"
}

func shellQuote(s string) string {
if strings.ContainsAny(s, " $") {
return "'" + s + "'"
}
return s
}
29 changes: 25 additions & 4 deletions pkg/cmd/auth/shared/git_credential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,42 @@ func TestGitCredentialSetup_configureExisting(t *testing.T) {
cs.Register(`git credential reject`, 0, "")
cs.Register(`git credential approve`, 0, "")

if err := GitCredentialSetup("example.com", "monalisa", "PASSWD", "osxkeychain"); err != nil {
f := GitCredentialFlow{
Executable: "gh",
helper: "osxkeychain",
}

if err := f.gitCredentialSetup("example.com", "monalisa", "PASSWD"); err != nil {
t.Errorf("GitCredentialSetup() error = %v", err)
}
}

func TestGitCredentialSetup_setOurs(t *testing.T) {
cs, restoreRun := run.Stub()
defer restoreRun(t)
cs.Register(`git config --global credential\.https://example\.com\.helper`, 0, "", func(args []string) {
if val := args[len(args)-1]; val != "!gh auth git-credential" {
cs.Register(`git config --global credential\.`, 0, "", func(args []string) {
if key := args[len(args)-2]; key != "credential.https://example.com.helper" {
t.Errorf("git config key was %q", key)
}
if val := args[len(args)-1]; val != "" {
t.Errorf("global credential helper configured to %q", val)
}
})
cs.Register(`git config --global --add credential\.`, 0, "", func(args []string) {
if key := args[len(args)-2]; key != "credential.https://example.com.helper" {
t.Errorf("git config key was %q", key)
}
if val := args[len(args)-1]; val != "!/path/to/gh auth git-credential" {
t.Errorf("global credential helper configured to %q", val)
}
})

f := GitCredentialFlow{
Executable: "/path/to/gh",
helper: "",
}

if err := GitCredentialSetup("example.com", "monalisa", "PASSWD", ""); err != nil {
if err := f.gitCredentialSetup("example.com", "monalisa", "PASSWD"); err != nil {
t.Errorf("GitCredentialSetup() error = %v", err)
}
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/auth/shared/login_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type LoginOptions struct {
Interactive bool
Web bool
Scopes []string
Executable string

sshContext sshContext
}
Expand Down Expand Up @@ -56,7 +57,7 @@ func Login(opts *LoginOptions) error {

var additionalScopes []string

credentialFlow := &GitCredentialFlow{}
credentialFlow := &GitCredentialFlow{Executable: opts.Executable}
if opts.Interactive && gitProtocol == "https" {
if err := credentialFlow.Prompt(hostname); err != nil {
return err
Expand Down
6 changes: 6 additions & 0 deletions pkg/cmd/factory/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ func New(appVersion string) *cmdutil.Factory {
}
remotesFunc := rr.Resolver(hostOverride)

ghExecutable := "gh"
if exe, err := os.Executable(); err == nil {
ghExecutable = exe
}

return &cmdutil.Factory{
IOStreams: io,
Config: configFunc,
Expand All @@ -70,5 +75,6 @@ func New(appVersion string) *cmdutil.Factory {
}
return currentBranch, nil
},
Executable: ghExecutable,
}
}
3 changes: 3 additions & 0 deletions pkg/cmdutil/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ type Factory struct {
Remotes func() (context.Remotes, error)
Config func() (config.Config, error)
Branch func() (string, error)

// Executable is the path to the currently invoked gh binary
Executable string
}

0 comments on commit aa5cf6c

Please sign in to comment.