6
0
mirror of https://github.com/grdl/git-get.git synced 2026-02-09 02:14:16 +00:00

Make the URL scheme default to ssh instead of https and make it configurable

This commit is contained in:
Grzegorz Dlugoszewski
2020-09-02 03:14:01 +02:00
parent ccbee82f0b
commit 0d797d625a
5 changed files with 84 additions and 57 deletions

View File

@@ -27,6 +27,7 @@ var cmd = &cobra.Command{
func init() { func init() {
cmd.PersistentFlags().StringP(cfg.KeyBranch, "b", "", "Branch (or tag) to checkout after cloning.") cmd.PersistentFlags().StringP(cfg.KeyBranch, "b", "", "Branch (or tag) to checkout after cloning.")
cmd.PersistentFlags().StringP(cfg.KeyDefaultHost, "t", cfg.Defaults[cfg.KeyDefaultHost], "Host to use when <REPO> doesn't have a specified host.") cmd.PersistentFlags().StringP(cfg.KeyDefaultHost, "t", cfg.Defaults[cfg.KeyDefaultHost], "Host to use when <REPO> doesn't have a specified host.")
cmd.PersistentFlags().StringP(cfg.KeyDefaultScheme, "c", cfg.Defaults[cfg.KeyDefaultScheme], "Scheme to use when <REPO> doesn't have a specified scheme.")
cmd.PersistentFlags().StringP(cfg.KeyDump, "d", "", "Path to a dump file listing repos to clone. Ignored when <REPO> argument is used.") cmd.PersistentFlags().StringP(cfg.KeyDump, "d", "", "Path to a dump file listing repos to clone. Ignored when <REPO> argument is used.")
cmd.PersistentFlags().BoolP(cfg.KeySkipHost, "s", false, "Don't create a directory for host.") cmd.PersistentFlags().BoolP(cfg.KeySkipHost, "s", false, "Don't create a directory for host.")
cmd.PersistentFlags().StringP(cfg.KeyReposRoot, "r", cfg.Defaults[cfg.KeyReposRoot], "Path to repos root where repositories are cloned.") cmd.PersistentFlags().StringP(cfg.KeyReposRoot, "r", cfg.Defaults[cfg.KeyReposRoot], "Path to repos root where repositories are cloned.")
@@ -35,6 +36,7 @@ func init() {
viper.BindPFlag(cfg.KeyBranch, cmd.PersistentFlags().Lookup(cfg.KeyBranch)) viper.BindPFlag(cfg.KeyBranch, cmd.PersistentFlags().Lookup(cfg.KeyBranch))
viper.BindPFlag(cfg.KeyDefaultHost, cmd.PersistentFlags().Lookup(cfg.KeyDefaultHost)) viper.BindPFlag(cfg.KeyDefaultHost, cmd.PersistentFlags().Lookup(cfg.KeyDefaultHost))
viper.BindPFlag(cfg.KeyDefaultScheme, cmd.PersistentFlags().Lookup(cfg.KeyDefaultScheme))
viper.BindPFlag(cfg.KeyDump, cmd.PersistentFlags().Lookup(cfg.KeyDump)) viper.BindPFlag(cfg.KeyDump, cmd.PersistentFlags().Lookup(cfg.KeyDump))
viper.BindPFlag(cfg.KeyReposRoot, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot)) viper.BindPFlag(cfg.KeyReposRoot, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot))
viper.BindPFlag(cfg.KeySkipHost, cmd.PersistentFlags().Lookup(cfg.KeySkipHost)) viper.BindPFlag(cfg.KeySkipHost, cmd.PersistentFlags().Lookup(cfg.KeySkipHost))
@@ -51,12 +53,13 @@ func run(cmd *cobra.Command, args []string) error {
cfg.Expand(cfg.KeyReposRoot) cfg.Expand(cfg.KeyReposRoot)
config := &pkg.GetCfg{ config := &pkg.GetCfg{
Branch: viper.GetString(cfg.KeyBranch), Branch: viper.GetString(cfg.KeyBranch),
DefHost: viper.GetString(cfg.KeyDefaultHost), DefHost: viper.GetString(cfg.KeyDefaultHost),
Dump: viper.GetString(cfg.KeyDump), DefScheme: viper.GetString(cfg.KeyDefaultScheme),
SkipHost: viper.GetBool(cfg.KeySkipHost), Dump: viper.GetString(cfg.KeyDump),
Root: viper.GetString(cfg.KeyReposRoot), SkipHost: viper.GetBool(cfg.KeySkipHost),
URL: url, Root: viper.GetString(cfg.KeyReposRoot),
URL: url,
} }
return pkg.Get(config) return pkg.Get(config)
} }

View File

@@ -17,21 +17,22 @@ const GitgetPrefix = "gitget"
// CLI flag keys. // CLI flag keys.
var ( var (
KeyBranch = "branch" KeyBranch = "branch"
KeyDump = "dump" KeyDump = "dump"
KeyDefaultHost = "host" KeyDefaultHost = "host"
KeyFetch = "fetch" KeyFetch = "fetch"
KeyOutput = "out" KeyOutput = "out"
KeySkipHost = "skip-host" KeyDefaultScheme = "scheme"
KeyReposRoot = "root" KeySkipHost = "skip-host"
KeyReposRoot = "root"
) )
// Defaults is a map of default values for config keys. // Defaults is a map of default values for config keys.
var Defaults = map[string]string{ var Defaults = map[string]string{
KeyDefaultHost: "github.com", KeyDefaultHost: "github.com",
KeyOutput: OutTree, KeyOutput: OutTree,
KeyReposRoot: fmt.Sprintf("~%c%s", filepath.Separator, "repositories"), KeyReposRoot: fmt.Sprintf("~%c%s", filepath.Separator, "repositories"),
// KeySkipHost: "false", KeyDefaultScheme: "ssh",
} }
// Values for the --out flag. // Values for the --out flag.

View File

@@ -8,12 +8,13 @@ import (
// GetCfg provides configuration for the Get command. // GetCfg provides configuration for the Get command.
type GetCfg struct { type GetCfg struct {
Branch string Branch string
DefHost string DefHost string
Dump string DefScheme string
Root string Dump string
SkipHost bool Root string
URL string SkipHost bool
URL string
} }
// Get executes the "git get" command. // Get executes the "git get" command.
@@ -33,7 +34,7 @@ func Get(c *GetCfg) error {
} }
func cloneSingleRepo(c *GetCfg) error { func cloneSingleRepo(c *GetCfg) error {
url, err := ParseURL(c.URL, c.DefHost) url, err := ParseURL(c.URL, c.DefHost, c.DefScheme)
if err != nil { if err != nil {
return err return err
} }
@@ -56,7 +57,7 @@ func cloneDumpFile(c *GetCfg) error {
} }
for _, line := range parsedLines { for _, line := range parsedLines {
url, err := ParseURL(line.rawurl, c.DefHost) url, err := ParseURL(line.rawurl, c.DefHost, c.DefScheme)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -17,8 +17,9 @@ var errEmptyURLPath = errors.New("parsed URL path is empty")
var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`)
// ParseURL parses given rawURL string into a URL. // ParseURL parses given rawURL string into a URL.
// The defaultHost argument defines the host to use (eg, github.com) in case parsed URL has an empty host. // When the parsed URL has an empty host, use the defaultHost.
func ParseURL(rawURL string, defaultHost string) (url *urlpkg.URL, err error) { // When the parsed URL has an empty scheme, use the defaultScheme.
func ParseURL(rawURL string, defaultHost string, defaultScheme string) (url *urlpkg.URL, err error) {
// If rawURL matches the SCP-like syntax, convert it into a standard ssh Path. // If rawURL matches the SCP-like syntax, convert it into a standard ssh Path.
// eg, git@github.com:user/repo => ssh://git@github.com/user/repo // eg, git@github.com:user/repo => ssh://git@github.com/user/repo
if m := scpSyntax.FindStringSubmatch(rawURL); m != nil { if m := scpSyntax.FindStringSubmatch(rawURL); m != nil {
@@ -26,7 +27,7 @@ func ParseURL(rawURL string, defaultHost string) (url *urlpkg.URL, err error) {
Scheme: "ssh", Scheme: "ssh",
User: urlpkg.User(m[1]), User: urlpkg.User(m[1]),
Host: m[2], Host: m[2],
Path: m[3], Path: path.Join("/", m[3]),
} }
} else { } else {
url, err = urlpkg.Parse(rawURL) url, err = urlpkg.Parse(rawURL)
@@ -43,14 +44,21 @@ func ParseURL(rawURL string, defaultHost string) (url *urlpkg.URL, err error) {
url.Scheme = "ssh" url.Scheme = "ssh"
} }
// Default to "git" user when using ssh and no user is provided
if url.Scheme == "ssh" && url.User == nil {
url.User = urlpkg.User("git")
}
// Default to configured defaultHost when host is empty // Default to configured defaultHost when host is empty
if url.Host == "" { if url.Host == "" {
url.Host = defaultHost url.Host = defaultHost
// Add a leading slash to path when host is missing. It's needed to correctly compare urlpkg.URL structs.
url.Path = path.Join("/", url.Path)
}
// Default to configured defaultScheme when scheme is empty
if url.Scheme == "" {
url.Scheme = defaultScheme
}
// Default to "git" user when using ssh and no user is provided
if url.Scheme == "ssh" && url.User == nil {
url.User = urlpkg.User("git")
} }
// Don't use host when scheme is file://. The fragment detected as url.Host should be a first directory of url.Path // Don't use host when scheme is file://. The fragment detected as url.Host should be a first directory of url.Path
@@ -59,11 +67,6 @@ func ParseURL(rawURL string, defaultHost string) (url *urlpkg.URL, err error) {
url.Host = "" url.Host = ""
} }
// Default to https when scheme is empty
if url.Scheme == "" {
url.Scheme = "https"
}
return url, nil return url, nil
} }

View File

@@ -3,6 +3,8 @@ package pkg
import ( import (
"git-get/pkg/cfg" "git-get/pkg/cfg"
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
// Following URLs are considered valid according to https://git-scm.com/docs/git-clone#_git_urls: // Following URLs are considered valid according to https://git-scm.com/docs/git-clone#_git_urls:
@@ -50,16 +52,11 @@ func TestURLParse(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
url, err := ParseURL(test.in, cfg.Defaults[cfg.KeyDefaultHost]) url, err := ParseURL(test.in, cfg.Defaults[cfg.KeyDefaultHost], cfg.Defaults[cfg.KeyDefaultScheme])
if err != nil { assert.NoError(t, err)
t.Fatalf("got error: %+v", err)
}
got := URLToPath(*url, false) got := URLToPath(*url, false)
assert.Equal(t, test.want, got)
if got != test.want {
t.Errorf("wrong result for %q; expected %q; got %q", test.in, test.want, got)
}
} }
} }
func TestURLParseSkipHost(t *testing.T) { func TestURLParseSkipHost(t *testing.T) {
@@ -95,16 +92,39 @@ func TestURLParseSkipHost(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
url, err := ParseURL(test.in, cfg.Defaults[cfg.KeyDefaultHost]) url, err := ParseURL(test.in, cfg.Defaults[cfg.KeyDefaultHost], cfg.Defaults[cfg.KeyDefaultScheme])
if err != nil { assert.NoError(t, err)
t.Fatalf("got error: %+v", err)
}
got := URLToPath(*url, true) got := URLToPath(*url, true)
assert.Equal(t, test.want, got)
}
}
if got != test.want { func TestDefaultScheme(t *testing.T) {
t.Errorf("wrong result for %q; expected %q; got %q", test.in, test.want, got) tests := []struct {
} in string
scheme string
want string
}{
{"grdl/git-get", "ssh", "ssh://git@github.com/grdl/git-get"},
{"grdl/git-get", "https", "https://github.com/grdl/git-get"},
{"https://github.com/grdl/git-get", "ssh", "https://github.com/grdl/git-get"},
{"https://github.com/grdl/git-get", "https", "https://github.com/grdl/git-get"},
{"ssh://github.com/grdl/git-get", "ssh", "ssh://git@github.com/grdl/git-get"},
{"ssh://github.com/grdl/git-get", "https", "ssh://git@github.com/grdl/git-get"},
{"git+ssh://github.com/grdl/git-get", "https", "ssh://git@github.com/grdl/git-get"},
{"git@github.com:grdl/git-get", "ssh", "ssh://git@github.com/grdl/git-get"},
{"git@github.com:grdl/git-get", "https", "ssh://git@github.com/grdl/git-get"},
}
for _, test := range tests {
url, err := ParseURL(test.in, cfg.Defaults[cfg.KeyDefaultHost], test.scheme)
assert.NoError(t, err)
want, err := url.Parse(test.want)
assert.NoError(t, err)
assert.Equal(t, url, want)
} }
} }
@@ -119,9 +139,8 @@ func TestInvalidURLParse(t *testing.T) {
} }
for _, test := range invalidURLs { for _, test := range invalidURLs {
got, err := ParseURL(test, cfg.Defaults[cfg.KeyDefaultHost]) _, err := ParseURL(test, cfg.Defaults[cfg.KeyDefaultHost], cfg.Defaults[cfg.KeyDefaultScheme])
if err == nil {
t.Errorf("expected error; got %q", got) assert.Error(t, err)
}
} }
} }