Merge pull request 'Remove all legacy config' (#3) from remove-legacy-support into main

Reviewed-on: 50W/git-project-manager#3
This commit is contained in:
Ryan McGuire 2024-01-17 21:57:56 +00:00
commit 4b2b251136
13 changed files with 101 additions and 142 deletions

View File

@ -58,32 +58,32 @@ cache:
## TODO ## TODO
- [ ] Make generic, this is any git remote, not just GitLab - [ ] Make generic, this is any git remote, not just GitLab
- [ ] Add Gitea support - [x] Add Gitea support
- [ ] Add GitHub support - [ ] Add GitHub support
- [ ] Rename --gitlab flag to --remote - [x] Remove separate gitlab/gitea logic and keys, stop supporting legacy keys
- [ ] Add option to select individual remote for `gpm cache load` - [x] Rename --gitlab flag to --remote
- [ ] Option to prune missing after the load is complete
- [ ] Add flag for clone timeout for large repos - [ ] 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] Use multi-gitlab by default in config init
- [x] Update docs for multi-gitlab - [x] Update docs for multi-gitlab
- [x] Remove all references to old keys - [x] Remove all references to old keys
- [x] Add auto-completion helper for --gitlab flag - [x] Add auto-completion helper for --gitlab flag
- [x] Fix initial setup requiring project path, and set https:// as default for gitlab host - [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 - [x] Add config setters and getters
- [ ] Add TTL check to cache load, and add -f / --force flag to re-build regardless - [x] For each project loaded, check if it is the same, and do nothing
- [ ] For each project loaded, check if it is the same, and do nothing
- [x] prevents clobbering cache on a partial update - [x] prevents clobbering cache on a partial update
- [x] track already loaded projects to diff after load - [x] track already loaded projects to diff after load
- [ ] should prune missing after the load is complete
- [x] Add open command - [x] Add open command
- [x] config should exist for editor (vim, code, etc..) - [x] config should exist for editor (vim, code, etc..)
- [ ] Update README for shell completion, aliases, usage
- [x] Add fzf to `plist` / `gpm projects list` - [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` - [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] Merge aliases together for same project when selecting
- [x] If after merging there is only one project, go there by default - [x] If after merging there is only one project, go there by default

View File

@ -70,36 +70,43 @@ func writeConfigFile(c *config.Config, path string) {
} }
func promptConfigSettings(c *config.Config) *config.Config { 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 // Just pick the first if we have one, considering this
// is meant to be a first-time use tool // is meant to be a first-time use tool
if len(c.Gitlabs) > 0 { if len(c.Remotes) > 0 {
gitlabConfig = &c.Gitlabs[0] gitRemote = &c.Remotes[0]
} else { } else {
gitlabConfig = &config.DefaultConfig.Gitlabs[0] gitRemote = &config.DefaultConfig.Remotes[0]
c.Gitlabs = append(c.Gitlabs, *gitlabConfig) c.Remotes = append(c.Remotes, *gitRemote)
} }
if host, err := pterm.DefaultInteractiveTextInput. if host, err := pterm.DefaultInteractiveTextInput.
WithDefaultValue(gitlabConfig.Host). WithDefaultValue(gitRemote.Host).
WithDefaultText("Enter gitlab URL"). WithDefaultText("Enter remote URL").
Show(); err == nil { Show(); err == nil {
gitlabConfig.Host = host gitRemote.Host = host
} }
if name, err := pterm.DefaultInteractiveTextInput. if name, err := pterm.DefaultInteractiveTextInput.
WithDefaultValue(gitlabConfig.Name). WithDefaultValue(gitRemote.Name).
WithDefaultText("Enter gitlab name (e.g. My Private GitLab)"). WithDefaultText("Enter remote name (e.g. My Private remote)").
Show(); err == nil { Show(); err == nil {
gitlabConfig.Name = name gitRemote.Name = name
} }
if token, err := pterm.DefaultInteractiveTextInput. if token, err := pterm.DefaultInteractiveTextInput.
WithMask("*"). WithMask("*").
WithDefaultValue(gitlabConfig.Token). WithDefaultValue(gitRemote.Token).
WithDefaultText("Enter gitlab Token"). WithDefaultText("Enter remote Token").
Show(); err == nil { 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. if proto, err := pterm.DefaultInteractiveSelect.
@ -107,9 +114,9 @@ func promptConfigSettings(c *config.Config) *config.Config {
WithDefaultText("Git Clone Protocol"). WithDefaultText("Git Clone Protocol").
Show(); err == nil { Show(); err == nil {
if proto == "ssh" { if proto == "ssh" {
gitlabConfig.CloneProto = info.CloneProtoSSH gitRemote.CloneProto = info.CloneProtoSSH
} else { } else {
gitlabConfig.CloneProto = info.CloneProtoHTTP gitRemote.CloneProto = info.CloneProtoHTTP
} }
} }

View File

@ -21,7 +21,9 @@ func runConfigShowCmd(cmd *cobra.Command, args []string) {
showSensitive, _ := cmd.Flags().GetBool("sensitive") showSensitive, _ := cmd.Flags().GetBool("sensitive")
if !showSensitive { if !showSensitive {
plog.Info("Sensitive fields hidden, do not use unreviewed as config") 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 { } else {
plog.Warn("Displaying sensitive fields!") plog.Warn("Displaying sensitive fields!")
} }

View File

@ -10,8 +10,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/config" "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 var conf config.Config
@ -111,7 +109,6 @@ func initConfig() {
} }
checkConfigPerms(viper.ConfigFileUsed()) // Abort on world-readable config checkConfigPerms(viper.ConfigFileUsed()) // Abort on world-readable config
setConfigFields() // Final chance to update config struct
plog.Debug("Configuration loaded", plog.Args("conf", conf)) plog.Debug("Configuration loaded", plog.Args("conf", conf))
} }
@ -133,17 +130,6 @@ func getPtermLogLevel(level string) pterm.LogLevel {
return pLevel 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 // Don't allow world-readable configuration
func checkConfigPerms(file string) { func checkConfigPerms(file string) {
stat, err := os.Stat(file) stat, err := os.Stat(file)

View File

@ -57,7 +57,7 @@ func getRemotes(cmd *cobra.Command) *remotes.Remotes {
for _, r := range conf.Remotes { for _, r := range conf.Remotes {
// Create a copy, set context // Create a copy, set context
gitRemoteInfo := r gitRemoteInfo := r
gitRemoteInfo.Ctx = cmd.Context() gitRemoteInfo.SetContext(cmd.Context())
var gitRemote remote.Remote var gitRemote remote.Remote
var err error var err error
switch r.Type { switch r.Type {

View File

@ -35,7 +35,7 @@ func (c *Cache) LoadRemotes(gitRemotes ...string) {
)) ))
opts := &remote.RemoteQueryOpts{ opts := &remote.RemoteQueryOpts{
Ctx: r.GetInfo().Ctx, Ctx: r.GetInfo().Context(),
OwnerOnly: c.config.Cache.Load.OwnerOnly, OwnerOnly: c.config.Cache.Load.OwnerOnly,
} }
@ -76,8 +76,8 @@ func (c *Cache) ReceiveRemoteStream(remote remote.Remote, wg *sync.WaitGroup, pB
c.AddProjects(p...) c.AddProjects(p...)
case e := <-progressInfo.ErrorChan: case e := <-progressInfo.ErrorChan:
c.log.Error("Fetch projects error", c.log.Args("error", e, "remote", remote.GetInfo().Name)) c.log.Error("Fetch projects error", c.log.Args("error", e, "remote", remote.GetInfo().Name))
case <-remote.GetInfo().Ctx.Done(): case <-remote.GetInfo().Context().Done():
c.log.Warn("LoadProjects cancelled", c.log.Args("reason", remote.GetInfo().Ctx.Err())) c.log.Warn("LoadProjects cancelled", c.log.Args("reason", remote.GetInfo().Context().Err()))
return return
case <-progressInfo.DoneChan: case <-progressInfo.DoneChan:
return return

View File

@ -9,10 +9,6 @@ import (
type Config struct { type Config struct {
// Named keys above maintained for backwards compatibility // Named keys above maintained for backwards compatibility
// Ideally only Gitlabs is used // 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 Remotes []info.RemoteInfo
LogLevel string `yaml:"logLevel" json:"logLevel" enum:"info,warn,debug,error"` LogLevel string `yaml:"logLevel" json:"logLevel" enum:"info,warn,debug,error"`
ProjectPath string `yaml:"projectPath" json:"projectPath"` ProjectPath string `yaml:"projectPath" json:"projectPath"`
@ -23,20 +19,6 @@ type Config struct {
Editor editorConfig `yaml:"editor" json:"editor"` 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 { type editorConfig struct {
DisplayName string `yaml:"displanName,omitempty" json:"displanName,omitempty"` DisplayName string `yaml:"displanName,omitempty" json:"displanName,omitempty"`
Binary string `yaml:"binary,omitempty" json:"binary,omitempty"` Binary string `yaml:"binary,omitempty" json:"binary,omitempty"`
@ -63,11 +45,12 @@ type cacheConfig struct {
var DefaultConfig = Config{ var DefaultConfig = Config{
LogLevel: "warn", LogLevel: "warn",
ProjectPath: "~/work/projects", ProjectPath: "~/work/projects",
Gitlabs: []GitlabConfig{{ Remotes: []info.RemoteInfo{{
Host: "https://gitlab.com", Host: "https://gitlab.com",
Token: "yourtokenhere", Token: "yourtokenhere",
CloneProto: info.CloneProtoSSH, CloneProto: info.CloneProtoSSH,
Name: "GitLab", Name: "GitLab",
Type: "gitlab",
}}, }},
Cache: cacheConfig{ Cache: cacheConfig{
Ttl: 168 * time.Hour, Ttl: 168 * time.Hour,

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"code.gitea.io/sdk/gitea" "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" "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/info"
) )
@ -13,36 +12,12 @@ type GiteaRemote struct {
api *gitea.Client 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 { func (r *GiteaRemote) GetInfo() *info.RemoteInfo {
return r.info return r.info
} }
func (r *GiteaRemote) GetType() string { func (r *GiteaRemote) GetType() string {
return r.info.Type return r.info.Type.String()
} }
func (r *GiteaRemote) String() string { func (r *GiteaRemote) String() string {
@ -52,7 +27,7 @@ func (r *GiteaRemote) String() string {
func NewGiteaRemote(remoteInfo *info.RemoteInfo) (*GiteaRemote, error) { func NewGiteaRemote(remoteInfo *info.RemoteInfo) (*GiteaRemote, error) {
client, err := gitea.NewClient(remoteInfo.Host, client, err := gitea.NewClient(remoteInfo.Host,
gitea.SetContext(remoteInfo.Ctx), gitea.SetContext(remoteInfo.Context()),
gitea.SetToken(remoteInfo.Token), gitea.SetToken(remoteInfo.Token),
) )

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"github.com/xanzy/go-gitlab" "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" "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/info"
) )
@ -13,46 +12,12 @@ type GitlabRemote struct {
api *gitlab.Client 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 { func (r *GitlabRemote) GetInfo() *info.RemoteInfo {
return r.info return r.info
} }
func (r *GitlabRemote) GetType() string { func (r *GitlabRemote) GetType() string {
return r.info.Type return r.info.Type.String()
} }
func (r *GitlabRemote) String() string { func (r *GitlabRemote) String() string {

View File

@ -120,6 +120,6 @@ func (r *GitlabRemote) GetProjectLanguages(project *gitlab.Project) *projects.Pr
func (r *GitlabRemote) GetDefaultRequestOptions() []gitlab.RequestOptionFunc { func (r *GitlabRemote) GetDefaultRequestOptions() []gitlab.RequestOptionFunc {
requestOpts := make([]gitlab.RequestOptionFunc, 1) requestOpts := make([]gitlab.RequestOptionFunc, 1)
requestOpts[0] = gitlab.WithContext(r.GetInfo().Ctx) requestOpts[0] = gitlab.WithContext(r.GetInfo().Context())
return requestOpts return requestOpts
} }

View File

@ -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 // We're done when we have it all or our context is done
// or we've hit our total pages // 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 break
} else if opts.Page == endPage { } else if opts.Page == endPage {
break break

View File

@ -12,13 +12,58 @@ const (
DefaultCloneProto CloneProto = CloneProtoSSH 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 // Globally shared info for all remote types
// Stub package to prevent import cycle // Stub package to prevent import cycle
type RemoteInfo struct { type RemoteInfo struct {
Ctx context.Context // Base context for all API calls Host string // Host as URL with protocol (e.g. https://gitlab.com)
Host string // Host as URL with protocol (e.g. https://gitlab.com) Name string // Human-friendly name for remote
Name string // Human-friendly name for remote Token string // API token for remote
Token string // API token for remote Type RemoteType // Remote type (e.g. gitlab, gitea)
Type string // Remote type (e.g. gitlab, gitea) CloneProto CloneProto // CloneProto (ssh or http) determines what url to use for git clone
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
} }

View File

@ -6,7 +6,6 @@ import (
"net/url" "net/url"
"time" "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/info"
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/load" "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 // stream all projects along with updates to channels
// provided by *load.ProgressInfo // provided by *load.ProgressInfo
type Remote interface { 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 String() string // String info for remote
GetInfo() *info.RemoteInfo // Returns basic RemoteInfo struct GetInfo() *info.RemoteInfo // Returns basic RemoteInfo struct
GetType() string // Returns the remote type (e.g. GitLab, Gitea) GetType() string // Returns the remote type (e.g. GitLab, Gitea)