diff options
| author | Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> | 2019-01-04 03:24:46 +0300 |
|---|---|---|
| committer | Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com> | 2019-01-04 03:24:46 +0300 |
| commit | b4547c56f3c28f0d03ee9437ec93c0e6fb6e0331 (patch) | |
| tree | 0458a9afbabdb34938e287ba8c3bec5edb948f38 /app | |
| parent | remove unnecessary code (diff) | |
| download | gitbatch-b4547c56f3c28f0d03ee9437ec93c0e6fb6e0331.tar.gz | |
huge refactor, package layour re-organized
Diffstat (limited to 'app')
| -rw-r--r-- | app/app.go | 111 | ||||
| -rw-r--r-- | app/config.go | 123 | ||||
| -rw-r--r-- | app/files.go | 92 | ||||
| -rw-r--r-- | app/quick.go | 50 |
4 files changed, 376 insertions, 0 deletions
diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000..b8eebc8 --- /dev/null +++ b/app/app.go @@ -0,0 +1,111 @@ +package app + +import ( + "os" + + "github.com/isacikgoz/gitbatch/gui" + log "github.com/sirupsen/logrus" +) + +// The App struct is responsible to hold app-wide related entities. Currently +// it has only the gui.Gui pointer for interface entity. +type App struct { + Gui *gui.Gui + Config *SetupConfig +} + +// SetupConfig is an assembler data to initiate a setup +type SetupConfig struct { + Directories []string + LogLevel string + Depth int + QuickMode bool + Mode string +} + +// Setup will handle pre-required operations. It is designed to be a wrapper for +// main method right now. +func Setup(setupConfig *SetupConfig) (*App, error) { + // initiate the app and give it initial values + app := &App{} + if len(setupConfig.Directories) <= 0 { + d, _ := os.Getwd() + setupConfig.Directories = []string{d} + } + + appConfig, err := overrideDefaults(setupConfig) + if err != nil { + return nil, err + } + + setLogLevel(appConfig.LogLevel) + directories := generateDirectories(appConfig.Directories, appConfig.Depth) + + if appConfig.QuickMode { + x := appConfig.Mode == "fetch" + y := appConfig.Mode == "pull" + if x == y { + log.Error("Unrecognized quick mode: " + appConfig.Mode) + os.Exit(1) + } + quick(directories, appConfig.Depth, appConfig.Mode) + os.Exit(0) + } + + // create a gui.Gui struct and set it as App's gui + app.Gui, err = gui.NewGui(appConfig.Mode, directories) + if err != nil { + // the error types and handling is not considered yet + return nil, err + } + // hopefull everything went smooth as butter + log.Trace("App configuration completed") + return app, nil +} + +// Close function will handle if any cleanup is required. e.g. closing streams +// or cleaning temproray files so on and so forth +func (app *App) Close() error { + return nil +} + +// set the level of logging it is fatal by default +func setLogLevel(logLevel string) { + switch logLevel { + case "trace": + log.SetLevel(log.TraceLevel) + case "debug": + log.SetLevel(log.DebugLevel) + case "info": + log.SetLevel(log.InfoLevel) + case "warn": + log.SetLevel(log.WarnLevel) + case "error": + log.SetLevel(log.ErrorLevel) + default: + log.SetLevel(log.FatalLevel) + } + log.WithFields(log.Fields{ + "level": logLevel, + }).Trace("logging level has been set") +} + +func overrideDefaults(setupConfig *SetupConfig) (appConfig *SetupConfig, err error) { + appConfig, err = LoadConfiguration() + if len(setupConfig.Directories) > 0 { + appConfig.Directories = setupConfig.Directories + } + if len(setupConfig.LogLevel) > 0 { + appConfig.LogLevel = setupConfig.LogLevel + } + if setupConfig.Depth > 0 { + appConfig.Depth = setupConfig.Depth + } + if setupConfig.QuickMode { + appConfig.QuickMode = setupConfig.QuickMode + } + if len(setupConfig.Mode) > 0 { + appConfig.Mode = setupConfig.Mode + } + return appConfig, err +} diff --git a/app/config.go b/app/config.go new file mode 100644 index 0000000..57c2e6e --- /dev/null +++ b/app/config.go @@ -0,0 +1,123 @@ +package app + +import ( + "os" + "path/filepath" + "runtime" + + log "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +// config file stuff +var ( + configFileName = "config" + configFileExt = ".yml" + configType = "yaml" + appName = "gitbatch" + + configurationDirectory = filepath.Join(osConfigDirectory(), appName) + configFileAbsPath = filepath.Join(configurationDirectory, configFileName) +) + +// configuration items +var ( + modeKey = "mode" + modeKeyDefault = "fetch" + pathsKey = "paths" + pathsKeyDefault = []string{"."} + logLevelKey = "loglevel" + logLevelKeyDefault = "error" + qucikKey = "quick" + qucikKeyDefault = false + recursionKey = "recursion" + recursionKeyDefault = 1 +) + +// LoadConfiguration returns a Config struct is filled +func LoadConfiguration() (*SetupConfig, error) { + if err := initializeConfigurationManager(); err != nil { + return nil, err + } + if err := setDefaults(); err != nil { + return nil, err + } + if err := readConfiguration(); err != nil { + return nil, err + } + var directories []string + if len(viper.GetStringSlice(pathsKey)) <= 0 { + d, _ := os.Getwd() + directories = []string{d} + } else { + directories = viper.GetStringSlice(pathsKey) + } + config := &SetupConfig{ + Directories: directories, + LogLevel: viper.GetString(logLevelKey), + Depth: viper.GetInt(recursionKey), + QuickMode: viper.GetBool(qucikKey), + Mode: viper.GetString(modeKey), + } + return config, nil +} + +// set default configuration parameters +func setDefaults() error { + viper.SetDefault(logLevelKey, logLevelKeyDefault) + viper.SetDefault(qucikKey, qucikKeyDefault) + viper.SetDefault(recursionKey, recursionKeyDefault) + viper.SetDefault(modeKey, modeKeyDefault) + // viper.SetDefault(pathsKey, pathsKeyDefault) + return nil +} + +// read configuration from file +func readConfiguration() error { + err := viper.ReadInConfig() // Find and read the config file + if err != nil { // Handle errors reading the config file + // if file does not exist, simply create one + if _, err := os.Stat(configFileAbsPath + configFileExt); os.IsNotExist(err) { + os.MkdirAll(configurationDirectory, 0755) + os.Create(configFileAbsPath + configFileExt) + } else { + return err + } + // let's write defaults + if err := viper.WriteConfig(); err != nil { + return err + } + } + return nil +} + +// write configuration to a file +func writeConfiguration() error { + err := viper.WriteConfig() + return err +} + +// initialize the configuration manager +func initializeConfigurationManager() error { + // config viper + viper.AddConfigPath(configurationDirectory) + viper.SetConfigName(configFileName) + viper.SetConfigType(configType) + + return nil +} + +// returns OS dependent config directory +func osConfigDirectory() (osConfigDirectory string) { + switch osname := runtime.GOOS; osname { + case "windows": + osConfigDirectory = os.Getenv("APPDATA") + case "darwin": + osConfigDirectory = os.Getenv("HOME") + "/Library/Application Support" + case "linux": + osConfigDirectory = os.Getenv("HOME") + "/.config" + default: + log.Warn("Operating system couldn't be recognized") + } + return osConfigDirectory +} diff --git a/app/files.go b/app/files.go new file mode 100644 index 0000000..1ec05cd --- /dev/null +++ b/app/files.go @@ -0,0 +1,92 @@ +package app + +import ( + "io/ioutil" + "os" + "path/filepath" + + log "github.com/sirupsen/logrus" +) + +// generateDirectories returns poosible git repositories to pipe into git pkg's +// load function +func generateDirectories(dirs []string, depth int) []string { + gitDirs := make([]string, 0) + for i := 0; i <= depth; i++ { + nonrepos, repos := walkRecursive(dirs, gitDirs) + dirs = nonrepos + gitDirs = repos + } + return gitDirs +} + +// returns given values, first search directories and second stands for possible +// git repositories. Call this func from a "for i := 0; i<depth; i++" loop +func walkRecursive(search, appendant []string) ([]string, []string) { + max := len(search) + for i := 0; i < max; i++ { + if i >= len(search) { + continue + } + // find possible repositories and remaining ones, b slice is possible ones + a, b, err := seperateDirectories(search[i]) + if err != nil { + log.WithFields(log.Fields{ + "directory": search[i], + }).WithError(err).Trace("Can't read directory") + continue + } + // since we started to search let's get rid of it and remove from search + // array + search[i] = search[len(search)-1] + search = search[:len(search)-1] + // lets append what we have found to continue recursion + search = append(search, a...) + appendant = append(appendant, b...) + } + return search, appendant +} + +// seperateDirectories is to find all the files in given path. This method +// does not check if the given file is a valid git repositories +func seperateDirectories(directory string) ([]string, []string, error) { + dirs := make([]string, 0) + gitDirs := make([]string, 0) + files, err := ioutil.ReadDir(directory) + // can we read the directory? + if err != nil { + log.WithFields(log.Fields{ + "directory": directory, + }).Trace("Can't read directory") + return nil, nil, nil + } + for _, f := range files { + repo := directory + string(os.PathSeparator) + f.Name() + file, err := os.Open(repo) + // if we cannot open it, simply continue to iteration and don't consider + if err != nil { + log.WithFields(log.Fields{ + "file": file, + "directory": repo, + }).WithError(err).Trace("Failed to open file in the directory") + file.Close() + continue + } + dir, err := filepath.Abs(file.Name()) + if err != nil { + file.Close() + continue + } + // with this approach, we ignore submodule or sub repositoreis in a git repository + ff, err := os.Open(dir + string(os.PathSeparator) + ".git") + if err != nil { + dirs = append(dirs, dir) + } else { + gitDirs = append(gitDirs, dir) + } + ff.Close() + file.Close() + + } + return dirs, gitDirs, nil +} diff --git a/app/quick.go b/app/quick.go new file mode 100644 index 0000000..005c033 --- /dev/null +++ b/app/quick.go @@ -0,0 +1,50 @@ +package app + +import ( + "fmt" + "sync" + "time" + + "github.com/isacikgoz/gitbatch/core/command" + "github.com/isacikgoz/gitbatch/core/git" +) + +func quick(directories []string, depth int, mode string) { + var wg sync.WaitGroup + start := time.Now() + for _, dir := range directories { + wg.Add(1) + go func(d string, mode string) { + defer wg.Done() + err := operate(d, mode) + if err != nil { + fmt.Printf("%s: %s\n", d, err.Error()) + } else { + fmt.Printf("%s: successful\n", d) + } + }(dir, mode) + } + wg.Wait() + elapsed := time.Since(start) + fmt.Printf("%d repositories finished in: %s\n", len(directories), elapsed) +} + +func operate(directory, mode string) error { + r, err := git.FastInitializeRepo(directory) + if err != nil { + return err + } + switch mode { + case "fetch": + return command.Fetch(r, command.FetchOptions{ + RemoteName: "origin", + Progress: true, + }) + case "pull": + return command.Pull(r, command.PullOptions{ + RemoteName: "origin", + Progress: true, + }) + } + return nil +} |
