summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorIbrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>2019-01-04 03:24:46 +0300
committerIbrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>2019-01-04 03:24:46 +0300
commitb4547c56f3c28f0d03ee9437ec93c0e6fb6e0331 (patch)
tree0458a9afbabdb34938e287ba8c3bec5edb948f38 /app
parentremove unnecessary code (diff)
downloadgitbatch-b4547c56f3c28f0d03ee9437ec93c0e6fb6e0331.tar.gz
huge refactor, package layour re-organized
Diffstat (limited to 'app')
-rw-r--r--app/app.go111
-rw-r--r--app/config.go123
-rw-r--r--app/files.go92
-rw-r--r--app/quick.go50
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
+}