Bug fixes, config generator
This commit is contained in:
parent
5d2ca40d04
commit
cdf92c8a54
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package cmd
|
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"`
|
OwnerOnly bool `yaml:"ownerOnly" json:"ownerOnly"`
|
||||||
File string `yaml:"file" json:"file"`
|
}
|
||||||
Load struct {
|
|
||||||
OwnerOnly bool `yaml:"ownerOnly" json:"ownerOnly"`
|
type cacheConfig struct {
|
||||||
} `yaml:"load" json:"load"`
|
Ttl time.Duration `yaml:"ttl,omitempty" json:"ttl,omitempty"`
|
||||||
Clear struct {
|
File string `yaml:"file,omitempty" json:"file,omitempty"`
|
||||||
ClearAliases bool `yaml:"clearAliases" json:"clearAliases"`
|
Load loadConfig `yaml:"load" json:"load"`
|
||||||
} `yaml:"clear" json:"clear"`
|
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{
|
||||||
|
Loading…
Reference in New Issue
Block a user