From 4b389fca1c7092f669b4c9606e8d256f7d921f16 Mon Sep 17 00:00:00 2001 From: Ryan D McGuire Date: Wed, 17 Jan 2024 16:56:41 -0500 Subject: [PATCH] Remove all legacy config --- README.md | 26 +++++------ cmd/config_generate.go | 39 +++++++++------- cmd/config_show.go | 4 +- cmd/root.go | 14 ------ cmd/util_init.go | 2 +- internal/cache/cache_load.go | 6 +-- internal/config/config.go | 21 +-------- internal/remotes/gitea/gitea.go | 29 +----------- internal/remotes/gitlab/gitlab.go | 37 +-------------- internal/remotes/gitlab/gitlab_api.go | 2 +- internal/remotes/gitlab/gitlab_strean.go | 2 +- internal/remotes/info/info.go | 57 +++++++++++++++++++++--- internal/remotes/remote/remote.go | 4 -- 13 files changed, 101 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index 946bdc3..736766f 100644 --- a/README.md +++ b/README.md @@ -58,32 +58,32 @@ cache: ## TODO - [ ] Make generic, this is any git remote, not just GitLab - - [ ] Add Gitea support + - [x] Add Gitea support - [ ] Add GitHub support - - [ ] Rename --gitlab flag to --remote -- [ ] Add option to select individual remote for `gpm cache load` + - [x] Remove separate gitlab/gitea logic and keys, stop supporting legacy keys + - [x] Rename --gitlab flag to --remote +- [ ] Option to prune missing after the load is complete - [ ] Add flag for clone timeout for large repos +- [ ] Fix NPE when cache is reset or project for whatever reason leaves an orphaned alias +- [ ] Add TTL check to cache load, and add -f / --force flag to re-build regardless +- [ ] Update README for shell completion, aliases, usage +- [ ] Make a Makefile +- [ ] Add git repo status to project go (up-to-date, pending commits, etc..) +- [ ] Build pipeline, and link to gitlab registry for a release binary +- [ ] Brew package and GitHub +- [x] Add option to select individual remote for `gpm cache load` - [x] Use multi-gitlab by default in config init - [x] Update docs for multi-gitlab - [x] Remove all references to old keys - [x] Add auto-completion helper for --gitlab flag - [x] Fix initial setup requiring project path, and set https:// as default for gitlab host -- [ ] Fix NPE when cache is reset or project for whatever reason leaves an orphaned alias - [x] 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 +- [x] For each project loaded, check if it is the same, and do nothing - [x] prevents clobbering cache on a partial update - [x] track already loaded projects to diff after load - - [ ] should prune missing after the load is complete - [x] Add open command - [x] config should exist for editor (vim, code, etc..) -- [ ] Update README for shell completion, aliases, usage - [x] Add fzf to `plist` / `gpm projects list` -- [ ] Make a Makefile -- [ ] Add git repo status to project go (up-to-date, pending commits, etc..) - [x] Update `gpm project show` with pterm box like `gpm project list` -- [ ] Build pipeline, and link to gitlab registry for a release binary -- [ ] Brew package and GitHub - [x] Merge aliases together for same project when selecting - [x] If after merging there is only one project, go there by default - diff --git a/cmd/config_generate.go b/cmd/config_generate.go index c90984c..53321f5 100644 --- a/cmd/config_generate.go +++ b/cmd/config_generate.go @@ -70,36 +70,43 @@ func writeConfigFile(c *config.Config, path string) { } func promptConfigSettings(c *config.Config) *config.Config { - var gitlabConfig *config.GitlabConfig + var gitRemote *info.RemoteInfo // Just pick the first if we have one, considering this // is meant to be a first-time use tool - if len(c.Gitlabs) > 0 { - gitlabConfig = &c.Gitlabs[0] + if len(c.Remotes) > 0 { + gitRemote = &c.Remotes[0] } else { - gitlabConfig = &config.DefaultConfig.Gitlabs[0] - c.Gitlabs = append(c.Gitlabs, *gitlabConfig) + gitRemote = &config.DefaultConfig.Remotes[0] + c.Remotes = append(c.Remotes, *gitRemote) } if host, err := pterm.DefaultInteractiveTextInput. - WithDefaultValue(gitlabConfig.Host). - WithDefaultText("Enter gitlab URL"). + WithDefaultValue(gitRemote.Host). + WithDefaultText("Enter remote URL"). Show(); err == nil { - gitlabConfig.Host = host + gitRemote.Host = host } if name, err := pterm.DefaultInteractiveTextInput. - WithDefaultValue(gitlabConfig.Name). - WithDefaultText("Enter gitlab name (e.g. My Private GitLab)"). + WithDefaultValue(gitRemote.Name). + WithDefaultText("Enter remote name (e.g. My Private remote)"). Show(); err == nil { - gitlabConfig.Name = name + gitRemote.Name = name } if token, err := pterm.DefaultInteractiveTextInput. WithMask("*"). - WithDefaultValue(gitlabConfig.Token). - WithDefaultText("Enter gitlab Token"). + WithDefaultValue(gitRemote.Token). + WithDefaultText("Enter remote Token"). Show(); err == nil { - gitlabConfig.Token = token + gitRemote.Token = token + } + + if remoteType, err := pterm.DefaultInteractiveSelect. + WithOptions(info.RemoteTypesAll.Strings()). + WithDefaultText("Git Clone Protocol"). + Show(); err == nil { + gitRemote.Type = info.GetRemoteTypeFromString(remoteType) } if proto, err := pterm.DefaultInteractiveSelect. @@ -107,9 +114,9 @@ func promptConfigSettings(c *config.Config) *config.Config { WithDefaultText("Git Clone Protocol"). Show(); err == nil { if proto == "ssh" { - gitlabConfig.CloneProto = info.CloneProtoSSH + gitRemote.CloneProto = info.CloneProtoSSH } else { - gitlabConfig.CloneProto = info.CloneProtoHTTP + gitRemote.CloneProto = info.CloneProtoHTTP } } diff --git a/cmd/config_show.go b/cmd/config_show.go index 976865e..7648367 100644 --- a/cmd/config_show.go +++ b/cmd/config_show.go @@ -21,7 +21,9 @@ func runConfigShowCmd(cmd *cobra.Command, args []string) { showSensitive, _ := cmd.Flags().GetBool("sensitive") if !showSensitive { plog.Info("Sensitive fields hidden, do not use unreviewed as config") - c.GitlabToken = strings.Repeat("*", len(c.GitlabToken)) + for _, r := range c.Remotes { + r.Token = strings.Repeat("*", len(r.Token)) + } } else { plog.Warn("Displaying sensitive fields!") } diff --git a/cmd/root.go b/cmd/root.go index 58c53a8..52617eb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -10,8 +10,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/config" - gitearemote "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/gitea" - gitlabremote "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/gitlab" ) var conf config.Config @@ -111,7 +109,6 @@ func initConfig() { } checkConfigPerms(viper.ConfigFileUsed()) // Abort on world-readable config - setConfigFields() // Final chance to update config struct plog.Debug("Configuration loaded", plog.Args("conf", conf)) } @@ -133,17 +130,6 @@ func getPtermLogLevel(level string) pterm.LogLevel { return pLevel } -// Do any post-processing of configuration here -func setConfigFields() { - // Load Gitlabs - glRemotes := gitlabremote.GitlabRemote{} - conf.Remotes = append(conf.Remotes, glRemotes.GetInfos(conf)...) - - // Load Giteas - giteaRemotes := gitearemote.GiteaRemote{} - conf.Remotes = append(conf.Remotes, giteaRemotes.GetInfos(conf)...) -} - // Don't allow world-readable configuration func checkConfigPerms(file string) { stat, err := os.Stat(file) diff --git a/cmd/util_init.go b/cmd/util_init.go index b116b8c..3f5625d 100644 --- a/cmd/util_init.go +++ b/cmd/util_init.go @@ -57,7 +57,7 @@ func getRemotes(cmd *cobra.Command) *remotes.Remotes { for _, r := range conf.Remotes { // Create a copy, set context gitRemoteInfo := r - gitRemoteInfo.Ctx = cmd.Context() + gitRemoteInfo.SetContext(cmd.Context()) var gitRemote remote.Remote var err error switch r.Type { diff --git a/internal/cache/cache_load.go b/internal/cache/cache_load.go index 4265ca0..e25cc63 100644 --- a/internal/cache/cache_load.go +++ b/internal/cache/cache_load.go @@ -35,7 +35,7 @@ func (c *Cache) LoadRemotes(gitRemotes ...string) { )) opts := &remote.RemoteQueryOpts{ - Ctx: r.GetInfo().Ctx, + Ctx: r.GetInfo().Context(), OwnerOnly: c.config.Cache.Load.OwnerOnly, } @@ -76,8 +76,8 @@ func (c *Cache) ReceiveRemoteStream(remote remote.Remote, wg *sync.WaitGroup, pB c.AddProjects(p...) case e := <-progressInfo.ErrorChan: c.log.Error("Fetch projects error", c.log.Args("error", e, "remote", remote.GetInfo().Name)) - case <-remote.GetInfo().Ctx.Done(): - c.log.Warn("LoadProjects cancelled", c.log.Args("reason", remote.GetInfo().Ctx.Err())) + case <-remote.GetInfo().Context().Done(): + c.log.Warn("LoadProjects cancelled", c.log.Args("reason", remote.GetInfo().Context().Err())) return case <-progressInfo.DoneChan: return diff --git a/internal/config/config.go b/internal/config/config.go index 86d1a9e..e5de3d5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -9,10 +9,6 @@ import ( type Config struct { // Named keys above maintained for backwards compatibility // Ideally only Gitlabs is used - GitlabHost string `yaml:"gitlabHost,omitempty" json:"gitlabHost,omitempty"` - GitlabToken string `yaml:"gitlabToken,omitempty" json:"gitlabToken,omitempty"` - Gitlabs []GitlabConfig `yaml:"gitlabs" json:"gitlabs"` - Giteas []GiteaConfig `yaml:"giteas" json:"giteas"` Remotes []info.RemoteInfo LogLevel string `yaml:"logLevel" json:"logLevel" enum:"info,warn,debug,error"` ProjectPath string `yaml:"projectPath" json:"projectPath"` @@ -23,20 +19,6 @@ type Config struct { Editor editorConfig `yaml:"editor" json:"editor"` } -type GiteaConfig struct { - Host string `yaml:"host" json:"host"` - Name string `yaml:"name" json:"name"` - Token string `yaml:"token" json:"token"` - CloneProto info.CloneProto `yaml:"cloneProto" json:"cloneProto"` -} - -type GitlabConfig struct { - Host string `yaml:"host" json:"host"` - Name string `yaml:"name" json:"name"` - Token string `yaml:"token" json:"token"` - CloneProto info.CloneProto `yaml:"cloneProto" json:"cloneProto"` -} - type editorConfig struct { DisplayName string `yaml:"displanName,omitempty" json:"displanName,omitempty"` Binary string `yaml:"binary,omitempty" json:"binary,omitempty"` @@ -63,11 +45,12 @@ type cacheConfig struct { var DefaultConfig = Config{ LogLevel: "warn", ProjectPath: "~/work/projects", - Gitlabs: []GitlabConfig{{ + Remotes: []info.RemoteInfo{{ Host: "https://gitlab.com", Token: "yourtokenhere", CloneProto: info.CloneProtoSSH, Name: "GitLab", + Type: "gitlab", }}, Cache: cacheConfig{ Ttl: 168 * time.Hour, diff --git a/internal/remotes/gitea/gitea.go b/internal/remotes/gitea/gitea.go index 91a04c6..336cc59 100644 --- a/internal/remotes/gitea/gitea.go +++ b/internal/remotes/gitea/gitea.go @@ -4,7 +4,6 @@ import ( "fmt" "code.gitea.io/sdk/gitea" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/config" "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/info" ) @@ -13,36 +12,12 @@ type GiteaRemote struct { api *gitea.Client } -func (r *GiteaRemote) GetInfos(conf config.Config) []info.RemoteInfo { - // Prepare infos - infos := make([]info.RemoteInfo, len(conf.Giteas)) - for i, g := range conf.Giteas { - // Set Defaults - proto := info.CloneProtoSSH - if g.CloneProto == info.CloneProtoHTTP { - proto = info.CloneProtoHTTP - } - if g.Name == "" { - g.Name = g.Host - } - - infos[i] = info.RemoteInfo{ - Host: g.Host, - Name: g.Name, - Type: "gitea", - Token: g.Token, - CloneProto: proto, - } - } - return infos -} - func (r *GiteaRemote) GetInfo() *info.RemoteInfo { return r.info } func (r *GiteaRemote) GetType() string { - return r.info.Type + return r.info.Type.String() } func (r *GiteaRemote) String() string { @@ -52,7 +27,7 @@ func (r *GiteaRemote) String() string { func NewGiteaRemote(remoteInfo *info.RemoteInfo) (*GiteaRemote, error) { client, err := gitea.NewClient(remoteInfo.Host, - gitea.SetContext(remoteInfo.Ctx), + gitea.SetContext(remoteInfo.Context()), gitea.SetToken(remoteInfo.Token), ) diff --git a/internal/remotes/gitlab/gitlab.go b/internal/remotes/gitlab/gitlab.go index 2e1f57f..c56960d 100644 --- a/internal/remotes/gitlab/gitlab.go +++ b/internal/remotes/gitlab/gitlab.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/xanzy/go-gitlab" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/config" "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/info" ) @@ -13,46 +12,12 @@ type GitlabRemote struct { api *gitlab.Client } -func (r *GitlabRemote) GetInfos(conf config.Config) []info.RemoteInfo { - // Support legacy fields - if conf.GitlabHost != "" && conf.GitlabToken != "" { - conf.Gitlabs = append(conf.Gitlabs, config.GitlabConfig{ - Host: conf.GitlabHost, - Name: conf.GitlabHost, - Token: conf.GitlabToken, - CloneProto: info.CloneProtoSSH, - }) - } - - // Prepare infos - infos := make([]info.RemoteInfo, len(conf.Gitlabs)) - for i, g := range conf.Gitlabs { - // Set Defaults - proto := info.CloneProtoSSH - if g.CloneProto == info.CloneProtoHTTP { - proto = info.CloneProtoHTTP - } - if g.Name == "" { - g.Name = g.Host - } - - infos[i] = info.RemoteInfo{ - Host: g.Host, - Name: g.Name, - Type: "gitlab", - Token: g.Token, - CloneProto: proto, - } - } - return infos -} - func (r *GitlabRemote) GetInfo() *info.RemoteInfo { return r.info } func (r *GitlabRemote) GetType() string { - return r.info.Type + return r.info.Type.String() } func (r *GitlabRemote) String() string { diff --git a/internal/remotes/gitlab/gitlab_api.go b/internal/remotes/gitlab/gitlab_api.go index 632f8dd..9630661 100644 --- a/internal/remotes/gitlab/gitlab_api.go +++ b/internal/remotes/gitlab/gitlab_api.go @@ -120,6 +120,6 @@ func (r *GitlabRemote) GetProjectLanguages(project *gitlab.Project) *projects.Pr func (r *GitlabRemote) GetDefaultRequestOptions() []gitlab.RequestOptionFunc { requestOpts := make([]gitlab.RequestOptionFunc, 1) - requestOpts[0] = gitlab.WithContext(r.GetInfo().Ctx) + requestOpts[0] = gitlab.WithContext(r.GetInfo().Context()) return requestOpts } diff --git a/internal/remotes/gitlab/gitlab_strean.go b/internal/remotes/gitlab/gitlab_strean.go index 43b730d..f1c78f8 100644 --- a/internal/remotes/gitlab/gitlab_strean.go +++ b/internal/remotes/gitlab/gitlab_strean.go @@ -54,7 +54,7 @@ func (r *GitlabRemote) StreamProjects(pi *load.ProgressInfo, opts *remote.Remote // We're done when we have it all or our context is done // or we've hit our total pages - if r.info.Ctx.Err() != nil || resp.NextPage == 0 { + if r.info.Context().Err() != nil || resp.NextPage == 0 { break } else if opts.Page == endPage { break diff --git a/internal/remotes/info/info.go b/internal/remotes/info/info.go index 760ee29..70c86b8 100644 --- a/internal/remotes/info/info.go +++ b/internal/remotes/info/info.go @@ -12,13 +12,58 @@ const ( DefaultCloneProto CloneProto = CloneProtoSSH ) +type RemoteType string +type RemoteTypes []RemoteType + +// Register remote types here and also add a case +// to func GetRemoteTypeFromString +var ( + RemoteTypeGitlab RemoteType = "gitlab" + RemoteTypeGitea RemoteType = "gitea" + RemoteTypesAll RemoteTypes = []RemoteType{ + RemoteTypeGitea, + RemoteTypeGitlab, + } +) + +func GetRemoteTypeFromString(remoteType string) RemoteType { + var rt RemoteType + switch remoteType { + case RemoteTypeGitea.String(): + rt = RemoteTypeGitea + case RemoteTypeGitlab.String(): + rt = RemoteTypeGitlab + } + return rt +} + +func (rt *RemoteTypes) Strings() []string { + rtStrings := make([]string, len(*rt)) + for i, t := range *rt { + rtStrings[i] = string(t) + } + return rtStrings +} + +func (rt *RemoteType) String() string { + return string(*rt) +} + // Globally shared info for all remote types // Stub package to prevent import cycle type RemoteInfo struct { - Ctx context.Context // Base context for all API calls - Host string // Host as URL with protocol (e.g. https://gitlab.com) - Name string // Human-friendly name for remote - Token string // API token for remote - Type string // Remote type (e.g. gitlab, gitea) - CloneProto CloneProto // CloneProto (ssh or http) determines what url to use for git clone + Host string // Host as URL with protocol (e.g. https://gitlab.com) + Name string // Human-friendly name for remote + Token string // API token for remote + Type RemoteType // Remote type (e.g. gitlab, gitea) + CloneProto CloneProto // CloneProto (ssh or http) determines what url to use for git clone + ctx context.Context +} + +func (ri *RemoteInfo) SetContext(ctx context.Context) { + ri.ctx = ctx +} + +func (ri *RemoteInfo) Context() context.Context { + return ri.ctx } diff --git a/internal/remotes/remote/remote.go b/internal/remotes/remote/remote.go index 8b14061..c7c33e1 100644 --- a/internal/remotes/remote/remote.go +++ b/internal/remotes/remote/remote.go @@ -6,7 +6,6 @@ import ( "net/url" "time" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/config" "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/info" "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/load" ) @@ -18,9 +17,6 @@ const defNetDialTimeoutSecs = 3 // stream all projects along with updates to channels // provided by *load.ProgressInfo type Remote interface { - // Helper to process configs. - // Realistically, conf.Gitlabs and conf.Giteas should be conf.Remotes - GetInfos(config.Config) []info.RemoteInfo String() string // String info for remote GetInfo() *info.RemoteInfo // Returns basic RemoteInfo struct GetType() string // Returns the remote type (e.g. GitLab, Gitea)