diff --git a/.gitignore b/.gitignore index daf913b..7739ec7 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,10 @@ _testmain.go *.exe *.test *.prof + +# dep management files +glide.lock +vendor + +# don't accidentally commit a build of main +boilr diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..fb44600 --- /dev/null +++ b/glide.yaml @@ -0,0 +1,9 @@ +package: github.com/tmrts/boilr +import: +#- package: github.com/src-d/go-git +- package: github.com/spf13/cobra +- package: github.com/docker/go-units +- package: github.com/olekukonko/tablewriter +- package: github.com/fatih/color +- package: gopkg.in/src-d/go-git.v4 +- package: github.com/src-d/crypto/ssh/knownhosts diff --git a/pkg/cmd/download.go b/pkg/cmd/download.go index d642d73..840ef25 100644 --- a/pkg/cmd/download.go +++ b/pkg/cmd/download.go @@ -5,6 +5,7 @@ import ( "os" cli "github.com/spf13/cobra" + //"github.com/src-d/go-git/plumbing/transport/ssh" "github.com/tmrts/boilr/pkg/boilr" "github.com/tmrts/boilr/pkg/host" @@ -21,7 +22,7 @@ var Download = &cli.Command{ // FIXME Half-Updates leave messy templates Run: func(c *cli.Command, args []string) { MustValidateArgs(args, []validate.Argument{ - {"template-repo", validate.UnixPath}, + {"template-repo", validate.RepoURL}, {"template-tag", validate.AlphanumericExt}, }) @@ -50,7 +51,9 @@ var Download = &cli.Command{ // TODO(tmrts): allow fetching other branches than 'master' if err := git.Clone(targetDir, git.CloneOptions{ - URL: host.URL(templateURL), + URL: templateURL, + Auth: host.AuthMethodForURL(templateURL), + Progress: os.Stdout, }); err != nil { exit.Error(fmt.Errorf("download: %s", err)) } diff --git a/pkg/host/github.go b/pkg/host/github.go deleted file mode 100644 index ff1fdd4..0000000 --- a/pkg/host/github.go +++ /dev/null @@ -1,49 +0,0 @@ -package host - -import ( - "regexp" - "strings" -) - -const ( - githubURL = "https://github.com" - githubStorageURL = "https://codeload.github.com" -) - -// ZipURL returns the URL of the zip archive given a github repository URL. -func ZipURL(repo string) string { - var version = "master" - - repo = strings.TrimSuffix(strings.TrimPrefix(repo, "/"), "/") - - zipRegex := regexp.MustCompile(`zip/(\S+)$`) - if zipRegex.MatchString(repo) { - return repo - } - - // FIXME(tmrts): this check could also identify a port number, but since - // we only support github I don't believe using it as a version modifier - // is a problem. Perhaps we should reconsider? - if strings.Contains(repo, ":") { - parts := strings.SplitAfter(repo, ":") - - repo = parts[0] - version = parts[1] - - repo = strings.TrimSuffix(repo, ":") - } - - urlTokens := []string{githubStorageURL, repo, "zip", version} - - return strings.Join(urlTokens, "/") -} - -// URL returns the normalized URL of a GitHub repository. -func URL(repo string) string { - githubRegex := regexp.MustCompile(githubURL + `/(\S+)$`) - if githubRegex.MatchString(repo) { - return repo - } - - return strings.Join([]string{githubURL, repo}, "/") -} diff --git a/pkg/host/host.go b/pkg/host/host.go new file mode 100644 index 0000000..817f6af --- /dev/null +++ b/pkg/host/host.go @@ -0,0 +1,83 @@ +package host + +import ( + "bufio" + "fmt" + "os" + "regexp" + "strings" + + "github.com/howeyc/gopass" + "gopkg.in/src-d/go-git.v4/plumbing/transport/http" + + "gopkg.in/src-d/go-git.v4/plumbing/transport" + "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh" +) + +var ( + scpLikeUrlRegExp = regexp.MustCompile("^(ssh://)?(?:(?P[^@]+)(?:@))?(?P[^:|/]+)(?::|/)?(?P.+)$") + isHttpSchemeRegExp = regexp.MustCompile("^(http|https)://") +) + +func IsRepoURL(url string) bool { + return isHttpSchemeRegExp.MatchString(url) || scpLikeUrlRegExp.MatchString(url) +} + +func IsSSH(url string) bool { + if !isHttpSchemeRegExp.MatchString(url) && scpLikeUrlRegExp.MatchString(url) { + return true + } + + return false +} + +func AuthMethodForURL(url string) transport.AuthMethod { + + if IsSSH(url) { + // 1:scheme, 2:user, 3:host, 4:path + m := scpLikeUrlRegExp.FindStringSubmatch(url) + a, _ := ssh.NewSSHAgentAuth(m[2]) + return a + } + + ba := http.NewBasicAuth("", "") + ba.CredentialsProvider = promptForCredentials + + return ba +} + +func promptForCredentials() (string, string) { + var u = "" + var p = "" + + uname, err := promptForUsername() + + if err == nil { + u = uname + } + + pbytes, err := gopass.GetPasswdPrompt("password: ", true, os.Stdin, os.Stdout) + + if err == nil { + p = string(pbytes) + } + + return u, p +} + +func promptForUsername() (string, error) { + consolereader := bufio.NewReader(os.Stdin) + + fmt.Print("username: ") + response, err := consolereader.ReadString('\n') + + if err != nil { + return "", err + } + + if len(response) < 1 { + return promptForUsername() + } + + return strings.TrimSuffix(response, "\n"), nil +} diff --git a/pkg/util/validate/string.go b/pkg/util/validate/string.go index f1dd0f1..18afc17 100644 --- a/pkg/util/validate/string.go +++ b/pkg/util/validate/string.go @@ -2,12 +2,17 @@ package validate import ( "reflect" + "regexp" "runtime" "strings" "github.com/tmrts/boilr/pkg/util/validate/pattern" ) +var ( + repoUrlRegExp = regexp.MustCompile("^(ssh://|https://)?(?:(?P[^@]+)(?:@))?(?P[^:|/]+)(?::|/)?(?P.+)$") +) + // String is the validation function used for checking whether a string is valid or not. type String func(string) bool @@ -43,3 +48,29 @@ func Alphanumeric(s string) bool { func AlphanumericExt(s string) bool { return pattern.AlphanumericExt.MatchString(s) } + +// RepoURL validates whether a string is a valid repo url. +func RepoURL(s string) bool { + isurl := false + if repoUrlRegExp.MatchString(s) { + isurl = true + m := repoUrlRegExp.FindStringSubmatch(s) + + //bad scheme + if m[1] == "http://" { + isurl = false + } + + //blank host + if len(strings.TrimSpace(m[3])) < 1 { + isurl = false + } + + //blank path + if len(strings.TrimSpace(m[4])) < 1 { + isurl = false + } + } + + return isurl +}