4

I'm trying to run the equivalent of git clone in Go but I want to exit out of the command if prompted for input from stdin (ex: authentication if an ssh key hasn't been set up in the remote repository). Is there a way to do this? Right now it will simply block on input.

  • Have you tried to pass `nil` to Stdin? https://golang.org/pkg/os/exec/#Cmd Also pls check examples below with CommandContext and StdinPipe. – Eugene Lisitsky Sep 27 '17 at 07:10
  • @EugeneLisitsky yeah passing nil doesn't do anything. golang.org/pkg/os/exec/#Cmd.StdinPipe seems like closing the stdinPipe should keep the command from blocking I still get prompted for a password. –  Sep 27 '17 at 07:23
  • Please post your solution as an answer not as an edit to the question see [tour] – Petter Friberg Sep 27 '17 at 19:53

3 Answers3

3

UPDATE: special git solution.

Since v 2.3 git supports environment setting GIT_TERMINAL_PROMPT=0 which says not to ask for credentials but fail.

func main() {
    cmd := exec.Command("git", "clone", "https://github.com/some/non-existing-repo")
    os.Setenv("GIT_SSH_COMMAND", "ssh -oBatchMode=yes")  // 
    cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=1", "GIT_SSH_COMMAND='ssh -oBatchMode=yes'")

    cmd.Stdout = os.Stdout
    err := cmd.Run()
    if err != nil {
        log.Println("Error: ", err)
    }
} 
Eugene Lisitsky
  • 12,113
  • 5
  • 38
  • 59
  • This works well, the only problem is that `Read()` still gets called even if there's no prompt to input so it will technically abort the git clone op even if there's no prompt for password –  Sep 27 '17 at 15:50
  • I almost wrote this answer, but I double checked the docs first. This will indeed not work. I was not able to come up with a solution that works, but I didn't try very hard either... (Why is this marked accepted if it doesn't work?) – Milo Christiansen Sep 27 '17 at 16:01
  • I checked - yes `git` tries to read stdin even it does not need. So we need a way to distinguish such situations. – Eugene Lisitsky Sep 27 '17 at 16:04
  • @MiloChristiansen I thought it did work so I gave it the accepted answer –  Sep 27 '17 at 17:33
  • @EugeneLisitsky The particular comment you posted is only valid for https cloning. However, there was also a useful comment about ssh in the post. In order to disable stdin you can set `os.Setenv("GIT_SSH_COMMAND", "ssh -oBatchMode=yes")` before running the clone. Do you want to update your answer and I'll give you the accepted answer? –  Sep 27 '17 at 17:56
  • @sreya I've update solution. However for me both https and git works the same. Could you pls check? Could you provide your test repo url? – Eugene Lisitsky Sep 27 '17 at 22:19
  • @EugeneLisitsky unfortunately the repo I'm testing against is private. The documentation seems to confirm that `GIT_TERMINAL_PROMPT` is only valid for https, i've updated my answer to reflect this. –  Sep 28 '17 at 23:24
  • If you use a native go-lang solution then ssh is possible with the added bonus of not relying on the git binary – Clive Makamara Sep 29 '17 at 06:50
2

How about a natively golang solution for using git instead. This will mean that your program won't require git and you won't have issues with stdin and the git binary. Checkout this example from https://godoc.org/gopkg.in/src-d/go-git.v4. A plus is that the documentation is really good, this is from the examples page of the project. It also supports the ssh protocol. Here is a full comparison of git vs this project.

import (
    "fmt"
    "os"

    "gopkg.in/src-d/go-git.v4"
    . "gopkg.in/src-d/go-git.v4/_examples"
)

// Basic example of how to clone a repository using clone options.
func main() {
    url := "https://github.com/some/non-existing-repo"
    directory := "/foo/bar"

    // Clone the given repository to the given directory
    Info("git clone %s %s --recursive", url, directory)

    r, err := git.PlainClone(directory, false, &git.CloneOptions{
        URL:               url,
        RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
    })

    CheckIfError(err)  

}
Clive Makamara
  • 2,845
  • 16
  • 31
1

SSH Solution:

The accepted answer will only work with http clones. If you want a way to do this with ssh the following will work:

//disables stdin prompts for username/password. If passwordless-ssh isn't configured we want to fail
os.Setenv("GIT_SSH_COMMAND", "ssh -oBatchMode=yes") 
cmd := exec.Command(name, args...)
return cmd.CombinedOutput()

man git entry for GIT_SSH_COMMAND

If either of these environment variables is set then git fetch and git push will use the specified command instead of ssh when they need to connect to a remote system. The command will be given exactly two or four arguments: the username@host (or just host) from the URL and the shell command to execute on that remote system, optionally preceded by -p (literally) and the port from the URL when it specifies something other than the default SSH port.

$GIT_SSH_COMMAND takes precedence over $GIT_SSH, and is interpreted by the shell, which allows additional arguments to be included. $GIT_SSH on the other hand must be just the path to a program (which can be a wrapper shell script, if additional arguments are needed).

Usually it is easier to configure any desired options through your personal .ssh/config file. Please consult your ssh documentation for further details.

man git entry for GIT_TERMINAL_PROMPT

If this environment variable is set to 0, git will not prompt on the terminal (e.g., when asking for HTTP authentication).