6
0
mirror of https://github.com/grdl/git-get.git synced 2026-02-06 03:32:58 +00:00

Remove gogit and major refactoring (#2)

* Fix typo in readme

* Reimplement all git methods without go-git

* Rename repo pkg to git, add gitconfig methods

* Improve tests for configuration reading

* Rename package file to io and move RepoFinder there

* Refactor printers

- Remove smart printer
- Decouple printers from git repos with interfaces
- Update printer functions
- Remove unnecessary flags
- Add better remote URL detection

* Update readme and go.mod

* Add author to git commit in tests

Otherwise tests will fail in CI.

* Install git before running tests and don't use cgo

* Add better error message, revert installing git

* Ensure commit message is in quotes

* Set up git config before running tests
This commit is contained in:
Grzegorz Dlugoszewski
2020-06-24 23:54:44 +02:00
committed by GitHub
parent 2ef739ea49
commit 8c132cdafa
26 changed files with 1452 additions and 1648 deletions

View File

@@ -1,3 +1,5 @@
// Package cfg provides common configuration to all commands.
// It contains config key names, default values and provides methods to read values from global gitconfig file.
package cfg
import (
@@ -5,8 +7,6 @@ import (
"path"
"strings"
"github.com/go-git/go-git/v5/config"
plumbing "github.com/go-git/go-git/v5/plumbing/format/config"
"github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
)
@@ -17,29 +17,25 @@ const GitgetPrefix = "gitget"
// CLI flag keys and their default values.
const (
KeyBranch = "branch"
DefBranch = "master"
KeyDump = "dump"
KeyDefaultHost = "host"
DefDefaultHost = "github.com"
KeyFetch = "fetch"
KeyOutput = "out"
DefOutput = OutTree
KeyPrivateKey = "privateKey"
DefPrivateKey = "id_rsa"
KeyReposRoot = "root"
DefReposRoot = "repositories"
)
// Values for the --out flag.
const (
OutDump = "dump"
OutFlat = "flat"
OutSmart = "smart"
OutTree = "tree"
OutDump = "dump"
OutFlat = "flat"
OutTree = "tree"
)
// AllowedOut are allowed values for the --out flag.
var AllowedOut = []string{OutDump, OutFlat, OutSmart, OutTree}
var AllowedOut = []string{OutDump, OutFlat, OutTree}
// Version metadata set by ldflags during the build.
var (
@@ -58,79 +54,39 @@ func Version() string {
return fmt.Sprintf("%s - revision %s built at %s", version, commit[:6], date)
}
// gitconfig provides methods for looking up configiration values inside .gitconfig file
type gitconfig struct {
*config.Config
// Gitconfig represents gitconfig file
type Gitconfig interface {
Get(key string) string
}
// Init initializes viper config registry. Values are looked up in the following order: cli flag, env variable, gitconfig file, default value
// Init initializes viper config registry. Values are looked up in the following order: cli flag, env variable, gitconfig file, default value.
// Viper doesn't support gitconfig file format so it can't find missing values there automatically. They need to be specified in setMissingValues func.
//
// Because it reads the cli flags it needs to be called after the cmd.Execute().
func Init() {
func Init(cfg Gitconfig) {
viper.SetEnvPrefix(strings.ToUpper(GitgetPrefix))
viper.AutomaticEnv()
cfg := loadGitconfig()
setMissingValues(cfg)
}
// loadGitconfig loads configuration from a gitconfig file.
// We ignore errors when gitconfig file can't be found, opened or parsed. In those cases viper will provide default config values.
func loadGitconfig() *gitconfig {
// TODO: load system scope
cfg, _ := config.LoadConfig(config.GlobalScope)
return &gitconfig{
Config: cfg,
}
}
// setMissingValues checks if config values are provided by flags or env vars. If not, it tries loading them from gitconfig file.
// If that fails, the default values are used.
func setMissingValues(cfg *gitconfig) {
func setMissingValues(cfg Gitconfig) {
if isUnsetOrEmpty(KeyReposRoot) {
viper.Set(KeyReposRoot, cfg.get(KeyReposRoot, path.Join(home(), DefReposRoot)))
viper.Set(KeyReposRoot, getOrDef(cfg, KeyReposRoot, path.Join(home(), DefReposRoot)))
}
if isUnsetOrEmpty(KeyDefaultHost) {
viper.Set(KeyDefaultHost, cfg.get(KeyDefaultHost, DefDefaultHost))
}
if isUnsetOrEmpty(KeyPrivateKey) {
viper.Set(KeyPrivateKey, cfg.get(KeyPrivateKey, path.Join(home(), ".ssh", DefPrivateKey)))
viper.Set(KeyDefaultHost, getOrDef(cfg, KeyDefaultHost, DefDefaultHost))
}
}
// get looks up the value for a given key in gitconfig file.
// It returns the default value when gitconfig is missing, or it doesn't contain a gitget section,
// or if the section is empty, or if it doesn't contain a valid value for the key.
func (c *gitconfig) get(key string, def string) string {
if c == nil || c.Config == nil {
return def
func getOrDef(cfg Gitconfig, key string, def string) string {
if val := cfg.Get(key); val != "" {
return val
}
gitget := c.findGitconfigSection(GitgetPrefix)
if gitget == nil {
return def
}
opt := gitget.Option(key)
if strings.TrimSpace(opt) == "" {
return def
}
return opt
}
func (c *gitconfig) findGitconfigSection(name string) *plumbing.Section {
for _, s := range c.Raw.Sections {
if strings.ToLower(s.Name) == strings.ToLower(name) {
return s
}
}
return nil
return def
}
// home returns path to a home directory or empty string if can't be found.

View File

@@ -3,163 +3,115 @@ package cfg
import (
"fmt"
"os"
"path"
"strings"
"testing"
"github.com/go-git/go-git/v5/config"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
envDefaultHost = strings.ToUpper(fmt.Sprintf("%s_%s", GitgetPrefix, KeyDefaultHost))
envReposRoot = strings.ToUpper(fmt.Sprintf("%s_%s", GitgetPrefix, KeyReposRoot))
envVarName = strings.ToUpper(fmt.Sprintf("%s_%s", GitgetPrefix, KeyDefaultHost))
fromGitconfig = "value.from.gitconfig"
fromEnv = "value.from.env"
fromFlag = "value.from.flag"
)
func newConfigWithFullGitconfig() *gitconfig {
cfg := config.NewConfig()
gitget := cfg.Raw.Section(GitgetPrefix)
gitget.AddOption(KeyReposRoot, "file.root")
gitget.AddOption(KeyDefaultHost, "file.host")
return &gitconfig{
Config: cfg,
}
}
func newConfigWithEmptyGitgetSection() *gitconfig {
cfg := config.NewConfig()
_ = cfg.Raw.Section(GitgetPrefix)
return &gitconfig{
Config: cfg,
}
}
func newConfigWithEmptyValues() *gitconfig {
cfg := config.NewConfig()
gitget := cfg.Raw.Section(GitgetPrefix)
gitget.AddOption(KeyReposRoot, "")
gitget.AddOption(KeyDefaultHost, " ")
return &gitconfig{
Config: cfg,
}
}
func newConfigWithoutGitgetSection() *gitconfig {
cfg := config.NewConfig()
return &gitconfig{
Config: cfg,
}
}
func newConfigWithEmptyGitconfig() *gitconfig {
return &gitconfig{
Config: nil,
}
}
func newConfigWithEnvVars() *gitconfig {
_ = os.Setenv(envDefaultHost, "env.host")
_ = os.Setenv(envReposRoot, "env.root")
return &gitconfig{
Config: nil,
}
}
func newConfigWithGitconfigAndEnvVars() *gitconfig {
cfg := config.NewConfig()
gitget := cfg.Raw.Section(GitgetPrefix)
gitget.AddOption(KeyReposRoot, "file.root")
gitget.AddOption(KeyDefaultHost, "file.host")
_ = os.Setenv(envDefaultHost, "env.host")
_ = os.Setenv(envReposRoot, "env.root")
return &gitconfig{
Config: cfg,
}
}
func newConfigWithEmptySectionAndEnvVars() *gitconfig {
cfg := config.NewConfig()
_ = cfg.Raw.Section(GitgetPrefix)
_ = os.Setenv(envDefaultHost, "env.host")
_ = os.Setenv(envReposRoot, "env.root")
return &gitconfig{
Config: cfg,
}
}
func newConfigWithMixed() *gitconfig {
cfg := config.NewConfig()
gitget := cfg.Raw.Section(GitgetPrefix)
gitget.AddOption(KeyReposRoot, "file.root")
gitget.AddOption(KeyDefaultHost, "file.host")
_ = os.Setenv(envDefaultHost, "env.host")
return &gitconfig{
Config: cfg,
}
}
func TestConfig(t *testing.T) {
defReposRoot := path.Join(home(), DefReposRoot)
var tests = []struct {
makeConfig func() *gitconfig
wantReposRoot string
wantDefaultHost string
tests := []struct {
name string
configMaker func(*testing.T)
key string
want string
}{
{newConfigWithFullGitconfig, "file.root", "file.host"},
{newConfigWithoutGitgetSection, defReposRoot, DefDefaultHost},
{newConfigWithEmptyGitconfig, defReposRoot, DefDefaultHost},
{newConfigWithEnvVars, "env.root", "env.host"},
{newConfigWithGitconfigAndEnvVars, "env.root", "env.host"},
{newConfigWithEmptySectionAndEnvVars, "env.root", "env.host"},
{newConfigWithEmptyGitgetSection, defReposRoot, DefDefaultHost},
{newConfigWithEmptyValues, defReposRoot, DefDefaultHost},
{newConfigWithMixed, "file.root", "env.host"},
{
name: "no config",
configMaker: testConfigEmpty,
key: KeyDefaultHost,
want: DefDefaultHost,
},
{
name: "value only in gitconfig",
configMaker: testConfigOnlyInGitconfig,
key: KeyDefaultHost,
want: fromGitconfig,
},
{
name: "value only in env var",
configMaker: testConfigOnlyInEnvVar,
key: KeyDefaultHost,
want: fromEnv,
},
{
name: "value in gitconfig and env var",
configMaker: testConfigInGitconfigAndEnvVar,
key: KeyDefaultHost,
want: fromEnv,
},
{
name: "value in flag",
configMaker: testConfigInFlag,
key: KeyDefaultHost,
want: fromFlag,
},
}
for _, test := range tests {
viper.SetEnvPrefix(strings.ToUpper(GitgetPrefix))
viper.AutomaticEnv()
t.Run(test.name, func(t *testing.T) {
test.configMaker(t)
cfg := test.makeConfig()
setMissingValues(cfg)
got := viper.GetString(test.key)
if got != test.want {
t.Errorf("expected %q; got %q", test.want, got)
}
if viper.GetString(KeyDefaultHost) != test.wantDefaultHost {
t.Errorf("Wrong %s value, got: %s; want: %s", KeyDefaultHost, viper.GetString(KeyDefaultHost), test.wantDefaultHost)
}
if viper.GetString(KeyReposRoot) != test.wantReposRoot {
t.Errorf("Wrong %s value, got: %s; want: %s", KeyReposRoot, viper.GetString(KeyReposRoot), test.wantReposRoot)
}
// Unset env variables and reset viper registry after each test
viper.Reset()
err := os.Unsetenv(envDefaultHost)
checkFatal(t, err)
err = os.Unsetenv(envReposRoot)
checkFatal(t, err)
// Clear env variables and reset viper registry after each test so they impact other tests.
os.Clearenv()
viper.Reset()
})
}
}
func checkFatal(t *testing.T, err error) {
if err != nil {
t.Fatalf("%+v", err)
}
type gitconfigEmpty struct{}
func (c *gitconfigEmpty) Get(key string) string {
return ""
}
type gitconfigValid struct{}
func (c *gitconfigValid) Get(key string) string {
return fromGitconfig
}
func testConfigEmpty(t *testing.T) {
Init(&gitconfigEmpty{})
}
func testConfigOnlyInGitconfig(t *testing.T) {
Init(&gitconfigValid{})
}
func testConfigOnlyInEnvVar(t *testing.T) {
os.Setenv(envVarName, fromEnv)
Init(&gitconfigEmpty{})
}
func testConfigInGitconfigAndEnvVar(t *testing.T) {
os.Setenv(envVarName, fromEnv)
Init(&gitconfigValid{})
}
func testConfigInFlag(t *testing.T) {
os.Setenv(envVarName, fromEnv)
cmd := cobra.Command{}
cmd.PersistentFlags().String(KeyDefaultHost, DefDefaultHost, "")
viper.BindPFlag(KeyDefaultHost, cmd.PersistentFlags().Lookup(KeyDefaultHost))
cmd.SetArgs([]string{"--" + KeyDefaultHost, fromFlag})
cmd.Execute()
Init(&gitconfigValid{})
}