Bug fixes, config generator
This commit is contained in:
		
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							@@ -3,6 +3,16 @@
 | 
				
			|||||||
## TODO
 | 
					## TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Fix NPE when cache is reset or project for whatever reason leaves an orphaned alias
 | 
					- Fix NPE when cache is reset or project for whatever reason leaves an orphaned alias
 | 
				
			||||||
 | 
					- Add config setters and getters
 | 
				
			||||||
 | 
					- Add TTL check to cache load, and add -f / --force flag to re-build regardless
 | 
				
			||||||
 | 
					- For each project loaded, check if it is the same, and do nothing
 | 
				
			||||||
 | 
						- prevents clobbering cache on a partial update
 | 
				
			||||||
 | 
						- track loaded projects in []*Project to diff after load
 | 
				
			||||||
 | 
						- should prune missing after the load is complete
 | 
				
			||||||
 | 
					- Add open command
 | 
				
			||||||
 | 
						- config should exist for editor (vim, code, etc..)
 | 
				
			||||||
 | 
					- Update README for shell completion, aliases, usage
 | 
				
			||||||
 | 
					- Make a Makefile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Purpose
 | 
					## Purpose
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,3 +17,9 @@ var aliasCmd = &cobra.Command{
 | 
				
			|||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	rootCmd.AddCommand(aliasCmd)
 | 
						rootCmd.AddCommand(aliasCmd)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func mustHaveAliases(cmd *cobra.Command, args []string) {
 | 
				
			||||||
 | 
						if len(cache.Aliases) == 0 {
 | 
				
			||||||
 | 
							plog.Fatal("No aliases set, nothing to " + cmd.Name())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ var aliasDeleteCmd = &cobra.Command{
 | 
				
			|||||||
	Aliases: []string{"rm", "del", "d"},
 | 
						Aliases: []string{"rm", "del", "d"},
 | 
				
			||||||
	Short:   "Delete a project alias",
 | 
						Short:   "Delete a project alias",
 | 
				
			||||||
	Long:    aliasDeleteCmdLong,
 | 
						Long:    aliasDeleteCmdLong,
 | 
				
			||||||
 | 
						PreRun:  mustHaveAliases,
 | 
				
			||||||
	Run:     runDeleteAliasCmd,
 | 
						Run:     runDeleteAliasCmd,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ var aliasListCmd = &cobra.Command{
 | 
				
			|||||||
	Use:     "list",
 | 
						Use:     "list",
 | 
				
			||||||
	Aliases: []string{"dump", "show", "ls", "ll", "l"},
 | 
						Aliases: []string{"dump", "show", "ls", "ll", "l"},
 | 
				
			||||||
	Short:   "List Aliases",
 | 
						Short:   "List Aliases",
 | 
				
			||||||
 | 
						PreRun:  mustHaveAliases,
 | 
				
			||||||
	Long:    aliasListCmdLong,
 | 
						Long:    aliasListCmdLong,
 | 
				
			||||||
	Run:     runListAliasCmd,
 | 
						Run:     runListAliasCmd,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ var cache *projects.Cache
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var cacheCmd = &cobra.Command{
 | 
					var cacheCmd = &cobra.Command{
 | 
				
			||||||
	Use:               "cache",
 | 
						Use:               "cache",
 | 
				
			||||||
 | 
						Aliases:           []string{"a", "ln"},
 | 
				
			||||||
	Short:             "Manage GitLab project cache",
 | 
						Short:             "Manage GitLab project cache",
 | 
				
			||||||
	Long:              cacheCmdLong,
 | 
						Long:              cacheCmdLong,
 | 
				
			||||||
	PersistentPreRun:  runCacheCmd,
 | 
						PersistentPreRun:  runCacheCmd,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								cmd/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								cmd/config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					package cmd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var configCmd = &cobra.Command{
 | 
				
			||||||
 | 
						Use:     "config",
 | 
				
			||||||
 | 
						Short:   "GitLab Project Manager Configuration",
 | 
				
			||||||
 | 
						Aliases: []string{"conf"},
 | 
				
			||||||
 | 
						Long:    configCmdLong,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						rootCmd.AddCommand(configCmd)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								cmd/config_generate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								cmd/config_generate.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					package cmd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
						"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/config"
 | 
				
			||||||
 | 
						"gopkg.in/yaml.v3"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var configGenerateCmd = &cobra.Command{
 | 
				
			||||||
 | 
						Use:     "generate",
 | 
				
			||||||
 | 
						Short:   "Generate a default configuration",
 | 
				
			||||||
 | 
						Aliases: []string{"gen", "new", "default"},
 | 
				
			||||||
 | 
						Long:    configGenCmdLong,
 | 
				
			||||||
 | 
						Run:     runConfigGenerateCmd,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runConfigGenerateCmd(cmd *cobra.Command, args []string) {
 | 
				
			||||||
 | 
						plog.Info("Suggest running with > " + defConfigPath)
 | 
				
			||||||
 | 
						c, _ := yaml.Marshal(config.DefaultConfig)
 | 
				
			||||||
 | 
						fmt.Print(string(c))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						configCmd.AddCommand(configGenerateCmd)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -46,6 +46,7 @@ func getProject(args []string) *gitlab.Project {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func initProjectCmd(cmd *cobra.Command, args []string) {
 | 
					func initProjectCmd(cmd *cobra.Command, args []string) {
 | 
				
			||||||
	initProjectCache(cmd, args)
 | 
						initProjectCache(cmd, args)
 | 
				
			||||||
 | 
						mustHaveProjects(cmd, args)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func postProjectCmd(cmd *cobra.Command, args []string) {
 | 
					func postProjectCmd(cmd *cobra.Command, args []string) {
 | 
				
			||||||
@@ -55,3 +56,9 @@ func postProjectCmd(cmd *cobra.Command, args []string) {
 | 
				
			|||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	rootCmd.AddCommand(projectCmd)
 | 
						rootCmd.AddCommand(projectCmd)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func mustHaveProjects(cmd *cobra.Command, args []string) {
 | 
				
			||||||
 | 
						if len(cache.Aliases) == 0 {
 | 
				
			||||||
 | 
							plog.Fatal("No projects to " + cmd.Name() + ", try running cache load")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								cmd/root.go
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								cmd/root.go
									
									
									
									
									
								
							@@ -38,7 +38,7 @@ func Execute() {
 | 
				
			|||||||
	err := rootCmd.ExecuteContext(ctx)
 | 
						err := rootCmd.ExecuteContext(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		plog.Error("Failed to execute command", plog.Args("err", err))
 | 
							pterm.Error.Printfln(pterm.LightYellow("Command failed, " + err.Error()))
 | 
				
			||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -48,12 +48,12 @@ func init() {
 | 
				
			|||||||
	cobra.OnInitialize(initConfig)
 | 
						cobra.OnInitialize(initConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rootCmd.PersistentFlags().String("config", "",
 | 
						rootCmd.PersistentFlags().String("config", "",
 | 
				
			||||||
		"config file (default is $HOME/.gitlab-project-manager.yaml)")
 | 
							"config file (default is "+defConfigPath+")")
 | 
				
			||||||
	rootCmd.PersistentFlags().String("gitlabHost", defGitlabHost,
 | 
						rootCmd.PersistentFlags().String("gitlabHost", defGitlabHost,
 | 
				
			||||||
		"GitLab Hostname (e.g. gitlab.com)")
 | 
							"GitLab Hostname (e.g. gitlab.com)")
 | 
				
			||||||
	rootCmd.PersistentFlags().String("gitlabToken", "",
 | 
						rootCmd.PersistentFlags().String("gitlabToken", "",
 | 
				
			||||||
		"GitLab Tokenname (e.g. gitlab.com)")
 | 
							"GitLab Tokenname (e.g. gitlab.com)")
 | 
				
			||||||
	rootCmd.PersistentFlags().String("projectPath", defProjectsPath,
 | 
						rootCmd.PersistentFlags().String("projectPath", "",
 | 
				
			||||||
		"Sets a path for local clones of projects")
 | 
							"Sets a path for local clones of projects")
 | 
				
			||||||
	rootCmd.PersistentFlags().String("logLevel", defLogLevel,
 | 
						rootCmd.PersistentFlags().String("logLevel", defLogLevel,
 | 
				
			||||||
		"Default log level -- info, warn, error, debug")
 | 
							"Default log level -- info, warn, error, debug")
 | 
				
			||||||
@@ -73,16 +73,14 @@ func initConfig() {
 | 
				
			|||||||
		cobra.CheckErr(err)
 | 
							cobra.CheckErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Search config in home directory with name ".gitlab-project-manager" (without extension).
 | 
							// Search config in home directory with name ".gitlab-project-manager" (without extension).
 | 
				
			||||||
		viper.AddConfigPath(home)
 | 
							viper.AddConfigPath(home + "/.config")
 | 
				
			||||||
		viper.SetConfigType("yaml")
 | 
							viper.SetConfigType("yaml")
 | 
				
			||||||
		viper.SetConfigName(".gitlab-project-manager")
 | 
							viper.SetConfigName("gitlab-project-manager")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	viper.AutomaticEnv()
 | 
						viper.AutomaticEnv()
 | 
				
			||||||
	viper.ReadInConfig()
 | 
						viper.ReadInConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	checkConfigPerms(viper.ConfigFileUsed()) // Abort on world-readable config
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Configure pretty logger
 | 
						// Configure pretty logger
 | 
				
			||||||
	plog = pterm.DefaultLogger.
 | 
						plog = pterm.DefaultLogger.
 | 
				
			||||||
		WithLevel(getPtermLogLevel(viper.GetString("logLevel"))).
 | 
							WithLevel(getPtermLogLevel(viper.GetString("logLevel"))).
 | 
				
			||||||
@@ -97,6 +95,18 @@ func initConfig() {
 | 
				
			|||||||
		plog.Error("Failed loading config", plog.Args("err", err))
 | 
							plog.Error("Failed loading config", plog.Args("err", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(os.Args) > 0 && strings.HasPrefix(os.Args[1], "conf") {
 | 
				
			||||||
 | 
							plog.Debug("Permitting missing config for config sub-command")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						} else if conf.ProjectPath == "" {
 | 
				
			||||||
 | 
							plog.Fatal("Minimal configuration missing, must have projectPath", plog.Args(
 | 
				
			||||||
 | 
								"do",
 | 
				
			||||||
 | 
								"Try running `gitlab-project-manager config default > "+defConfigPath,
 | 
				
			||||||
 | 
							))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						checkConfigPerms(viper.ConfigFileUsed()) // Abort on world-readable config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	plog.Debug("Configuration loaded", plog.Args("conf", conf))
 | 
						plog.Debug("Configuration loaded", plog.Args("conf", conf))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,8 +2,8 @@ package cmd
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	defGitlabHost = "gitlab.sweetwater.com"
 | 
						defGitlabHost = "gitlab.sweetwater.com"
 | 
				
			||||||
	defProjectsPath = "~/work/projects"
 | 
					 | 
				
			||||||
	defLogLevel   = "info"
 | 
						defLogLevel   = "info"
 | 
				
			||||||
 | 
						defConfigPath = "~/.config/gitlab-project-manager.yaml"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const aliasCmdLong = `Manages project aliases, with options for
 | 
					const aliasCmdLong = `Manages project aliases, with options for
 | 
				
			||||||
@@ -43,3 +43,9 @@ uses fuzzy find to locate the project`
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const projShowCmdLong = `Shows detail for a particular project
 | 
					const projShowCmdLong = `Shows detail for a particular project
 | 
				
			||||||
Will always fuzzy find`
 | 
					Will always fuzzy find`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const configCmdLong = `Commands for managing configuration, particulary
 | 
				
			||||||
 | 
					useful for seeding a new config file`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const configGenCmdLong = `Produces yaml to stdout that can be used
 | 
				
			||||||
 | 
					to seed the configuration file`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,13 +13,28 @@ type Config struct {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type cacheConfig struct {
 | 
					type loadConfig struct {
 | 
				
			||||||
	Ttl  time.Duration `yaml:"ttl" json:"ttl"`
 | 
					 | 
				
			||||||
	File string        `yaml:"file" json:"file"`
 | 
					 | 
				
			||||||
	Load struct {
 | 
					 | 
				
			||||||
	OwnerOnly bool `yaml:"ownerOnly" json:"ownerOnly"`
 | 
						OwnerOnly bool `yaml:"ownerOnly" json:"ownerOnly"`
 | 
				
			||||||
	} `yaml:"load" json:"load"`
 | 
					}
 | 
				
			||||||
	Clear struct {
 | 
					
 | 
				
			||||||
		ClearAliases bool `yaml:"clearAliases" json:"clearAliases"`
 | 
					type cacheConfig struct {
 | 
				
			||||||
	} `yaml:"clear" json:"clear"`
 | 
						Ttl   time.Duration `yaml:"ttl,omitempty" json:"ttl,omitempty"`
 | 
				
			||||||
 | 
						File  string        `yaml:"file,omitempty" json:"file,omitempty"`
 | 
				
			||||||
 | 
						Load  loadConfig    `yaml:"load" json:"load"`
 | 
				
			||||||
 | 
						Clear struct {
 | 
				
			||||||
 | 
							ClearAliases bool `yaml:"clearAliases,omitempty" json:"clearAliases,omitempty"`
 | 
				
			||||||
 | 
						} `yaml:"clear,omitempty" json:"clear,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var DefaultConfig = Config{
 | 
				
			||||||
 | 
						GitlabHost:  "https://gitlab.com",
 | 
				
			||||||
 | 
						GitlabToken: "yourtokenhere",
 | 
				
			||||||
 | 
						LogLevel:    "warn",
 | 
				
			||||||
 | 
						ProjectPath: "~/work/projects",
 | 
				
			||||||
 | 
						Cache: cacheConfig{
 | 
				
			||||||
 | 
							Ttl: 168 * time.Hour,
 | 
				
			||||||
 | 
							Load: loadConfig{
 | 
				
			||||||
 | 
								OwnerOnly: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -129,16 +129,17 @@ func (c *Client) streamProjects(pi *ProgressInfo, ownerOnly bool) {
 | 
				
			|||||||
	for {
 | 
						for {
 | 
				
			||||||
		projects, resp, err := c.ListProjects(listOpts)
 | 
							projects, resp, err := c.ListProjects(listOpts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								pi.ErrorChan <- err
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// We're done when we have it all or our context is done
 | 
							// We're done when we have it all or our context is done
 | 
				
			||||||
		if c.Ctx.Err() != nil || resp.NextPage == 0 {
 | 
							if c.Ctx.Err() != nil || resp.NextPage == 0 {
 | 
				
			||||||
			pi.DoneChan <- nil
 | 
								pi.DoneChan <- nil
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			pi.ErrorChan <- err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		numProjects += len(projects)
 | 
							numProjects += len(projects)
 | 
				
			||||||
		pi.ProjectsChan <- projects
 | 
							pi.ProjectsChan <- projects
 | 
				
			||||||
		pi.ProgressChan <- Progress{
 | 
							pi.ProgressChan <- Progress{
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user