From d6833a9ea04fca8a3ec0051aeaf05754a473ff86 Mon Sep 17 00:00:00 2001 From: Ryan D McGuire Date: Mon, 15 Jan 2024 15:39:35 -0500 Subject: [PATCH] Refactor gitlab to generic remotes --- cmd/alias_add.go | 10 +-- cmd/alias_delete.go | 4 +- cmd/project.go | 8 +- cmd/project_show.go | 4 +- cmd/util_fzf.go | 46 +++++------ internal/projects/cache.go | 7 +- internal/projects/cache_aliases.go | 6 +- internal/projects/cache_projects.go | 8 +- internal/projects/fuzz.go | 6 +- internal/projects/projects.go | 14 ++-- internal/projects/projects_alias.go | 10 +-- internal/projects/projects_fs.go | 12 +-- internal/projects/projects_git.go | 6 +- internal/remotes/load/load.go | 20 +++++ internal/remotes/projects/projects.go | 72 +++++++++++++++++ .../projects_git.go} | 2 +- .../projects_net.go} | 2 +- internal/remotes/remote/remote.go | 13 +++ internal/remotes/remote/remote_opts.go | 8 ++ internal/remotes/remotes.go | 81 ++----------------- internal/remotes/remotes_load.go | 41 ++++------ 21 files changed, 208 insertions(+), 172 deletions(-) create mode 100644 internal/remotes/load/load.go create mode 100644 internal/remotes/projects/projects.go rename internal/remotes/{remotes_git.go => projects/projects_git.go} (98%) rename internal/remotes/{remotes_net.go => projects/projects_net.go} (98%) create mode 100644 internal/remotes/remote/remote.go create mode 100644 internal/remotes/remote/remote_opts.go diff --git a/cmd/alias_add.go b/cmd/alias_add.go index a99281b..170cbc2 100644 --- a/cmd/alias_add.go +++ b/cmd/alias_add.go @@ -7,8 +7,8 @@ import ( "github.com/pterm/pterm" "github.com/spf13/cobra" "github.com/spf13/viper" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + cacheProjects "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" ) var aliasAddCmd = &cobra.Command{ @@ -21,7 +21,7 @@ var aliasAddCmd = &cobra.Command{ } func runAddAliasCmd(cmd *cobra.Command, args []string) { - var project *remotes.Project + var project *projects.Project // Check by flag if projectID := viper.GetInt("alias.add.projectid"); projectID > 0 { @@ -76,12 +76,12 @@ func addNewAliases(projectID int) { } } -func promptAliasesForProject(p *remotes.Project) []string { +func promptAliasesForProject(p *projects.Project) []string { aliases := cache.GetProjectAliases(p) if len(aliases) > 0 { plog.Info("Adding aliases to project", plog.Args( "project", p.String(), - "existingAliases", projects.ProjectAliasesString(aliases), + "existingAliases", cacheProjects.ProjectAliasesString(aliases), )) } else { pterm.Info.Printfln("Adding aliases to %s", p.Name) diff --git a/cmd/alias_delete.go b/cmd/alias_delete.go index 4cf1f59..a9a5baf 100644 --- a/cmd/alias_delete.go +++ b/cmd/alias_delete.go @@ -6,7 +6,7 @@ import ( "github.com/pterm/pterm" "github.com/spf13/cobra" "github.com/spf13/viper" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" ) var aliasDeleteCmd = &cobra.Command{ @@ -19,7 +19,7 @@ var aliasDeleteCmd = &cobra.Command{ } func runDeleteAliasCmd(cmd *cobra.Command, args []string) { - var project *remotes.Project + var project *projects.Project var err error fzfOpts := &fzfProjectOpts{ diff --git a/cmd/project.go b/cmd/project.go index c5f54ab..e467cc2 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -3,8 +3,8 @@ package cmd import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + cacheProjects "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" ) var projectCmd = &cobra.Command{ @@ -19,7 +19,7 @@ var projectCmd = &cobra.Command{ // Run: projectGoCmdRun, } -func getProject(args []string) *remotes.Project { +func getProject(args []string) *projects.Project { gitlabs := viper.GetStringSlice("project.gitlabs") fzfOpts := &fzfProjectOpts{ Ctx: rootCmd.Context(), @@ -33,7 +33,7 @@ func getProject(args []string) *remotes.Project { } else { plog.Debug("Houston, we have a project", plog.Args( "project", project.String(), - "aliases", projects.ProjectAliasesString( + "aliases", cacheProjects.ProjectAliasesString( cache.GetProjectAliases(project)), )) } diff --git a/cmd/project_show.go b/cmd/project_show.go index 7a9608b..3ec71e8 100644 --- a/cmd/project_show.go +++ b/cmd/project_show.go @@ -7,7 +7,7 @@ import ( "github.com/pterm/pterm" "github.com/spf13/cobra" "github.com/spf13/viper" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" ) var projectShowCmd = &cobra.Command{ @@ -20,7 +20,7 @@ var projectShowCmd = &cobra.Command{ } func projectShowCmdRun(cmd *cobra.Command, args []string) { - var project *remotes.Project + var project *projects.Project var inCwd bool gitlabs := viper.GetStringSlice("project.gitlabs") diff --git a/cmd/util_fzf.go b/cmd/util_fzf.go index f33c8d9..5893cfb 100644 --- a/cmd/util_fzf.go +++ b/cmd/util_fzf.go @@ -4,8 +4,8 @@ import ( "context" fzf "github.com/ktr0731/go-fuzzyfinder" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + cacheProjects "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/projects" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" "golang.org/x/exp/slices" ) @@ -18,8 +18,8 @@ type fzfProjectOpts struct { // This will try to find a project by alias if a search term // is given, otherwise will fuzzy find by project -func fzfFindProject(opts *fzfProjectOpts) *remotes.Project { - var project *remotes.Project +func fzfFindProject(opts *fzfProjectOpts) *projects.Project { + var project *projects.Project if opts.Search != "" { project = fzfSearchProjectAliases(opts) @@ -37,8 +37,8 @@ func fzfFindProject(opts *fzfProjectOpts) *remotes.Project { // If . is given as a project, will open project from the // current working directory. Otherwise, will attempt to fuzzy-find // a project given a search term if provided -func fzfCwdOrSearchProjectAliases(opts *fzfProjectOpts) *remotes.Project { - var project *remotes.Project +func fzfCwdOrSearchProjectAliases(opts *fzfProjectOpts) *projects.Project { + var project *projects.Project if opts.Search == "." { project, _ = cache.GetProjectFromCwd() } else { @@ -49,9 +49,9 @@ func fzfCwdOrSearchProjectAliases(opts *fzfProjectOpts) *remotes.Project { // This will fuzzy search only aliases, preferring an exact // match if one is given -func fzfSearchProjectAliases(opts *fzfProjectOpts) *remotes.Project { - var project *remotes.Project - var alias *projects.ProjectAlias +func fzfSearchProjectAliases(opts *fzfProjectOpts) *projects.Project { + var project *projects.Project + var alias *cacheProjects.ProjectAlias if alias = cache.GetAliasByName(opts.Search, opts.Gitlabs...); alias != nil { project = cache.GetProjectByAlias(alias) plog.Info("Perfect alias match... flawless") @@ -75,8 +75,8 @@ func fzfSearchProjectAliases(opts *fzfProjectOpts) *remotes.Project { // a single one. Replaced by fzfProjectFromAliases in fzfSearchProjectAliases // as merging is preferred, but can be used if it's ever desirable to // return a single alias from all aliases -func fzfAliasFromAliases(opts *fzfProjectOpts, aliases []*projects.ProjectAlias) *projects.ProjectAlias { - var alias *projects.ProjectAlias +func fzfAliasFromAliases(opts *fzfProjectOpts, aliases []*cacheProjects.ProjectAlias) *cacheProjects.ProjectAlias { + var alias *cacheProjects.ProjectAlias i, err := fzf.Find( aliases, func(i int) string { @@ -95,8 +95,8 @@ func fzfAliasFromAliases(opts *fzfProjectOpts, aliases []*projects.ProjectAlias) // Given a list of aliases, merge them together and use the resulting // list of projects to return a project -func fzfProjectFromAliases(opts *fzfProjectOpts, aliases []*projects.ProjectAlias) ( - *remotes.Project, error) { +func fzfProjectFromAliases(opts *fzfProjectOpts, aliases []*cacheProjects.ProjectAlias) ( + *projects.Project, error) { mergedProjects := projectsFromAliases(aliases) if len(mergedProjects) == 1 { return mergedProjects[0], nil @@ -104,8 +104,8 @@ func fzfProjectFromAliases(opts *fzfProjectOpts, aliases []*projects.ProjectAlia return fzfProjectFromProjects(opts, mergedProjects) } -func projectsFromAliases(aliases []*projects.ProjectAlias) []*remotes.Project { - projects := make([]*remotes.Project, 0) +func projectsFromAliases(aliases []*cacheProjects.ProjectAlias) []*projects.Project { + projects := make([]*projects.Project, 0) ALIASES: for _, a := range aliases { @@ -123,8 +123,8 @@ ALIASES: // If opts.MustHaveAlias, will only allow selection of projects // that have at least one alias defined -func fzfProject(opts *fzfProjectOpts) (*remotes.Project, error) { - var searchableProjects []*remotes.Project +func fzfProject(opts *fzfProjectOpts) (*projects.Project, error) { + var searchableProjects []*projects.Project if opts.MustHaveAlias { searchableProjects = cache.GetProjectsWithAliases() } else { @@ -136,8 +136,8 @@ func fzfProject(opts *fzfProjectOpts) (*remotes.Project, error) { } // Takes a list of projects and performs a fuzzyfind -func fzfProjectFromProjects(opts *fzfProjectOpts, projects []*remotes.Project) ( - *remotes.Project, error) { +func fzfProjectFromProjects(opts *fzfProjectOpts, projects []*projects.Project) ( + *projects.Project, error) { i, err := fzf.Find(projects, func(i int) string { // Display the project along with its aliases @@ -162,16 +162,16 @@ func fzfPreviewWindow(i, w, h int) string { return cache.ProjectString(p) } -func filterProjectsWithGitlabs(projects []*remotes.Project, gitlabs ...string) []*remotes.Project { - filteredProjects := make([]*remotes.Project, 0, len(projects)) +func filterProjectsWithGitlabs(gitProjects []*projects.Project, gitlabs ...string) []*projects.Project { + filteredProjects := make([]*projects.Project, 0, len(gitProjects)) if len(gitlabs) > 0 { - for _, p := range projects { + for _, p := range gitProjects { if slices.Contains(gitlabs, p.Remote) { filteredProjects = append(filteredProjects, p) } } } else { - filteredProjects = projects + filteredProjects = gitProjects } return filteredProjects } diff --git a/internal/projects/cache.go b/internal/projects/cache.go index 96ecced..ee6ddd6 100644 --- a/internal/projects/cache.go +++ b/internal/projects/cache.go @@ -9,11 +9,12 @@ import ( "github.com/pterm/pterm" "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/config" "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" "gopkg.in/yaml.v3" ) type Cache struct { - Projects []*remotes.Project + Projects []*projects.Project Aliases []*ProjectAlias Updated time.Time config *config.Config @@ -124,7 +125,7 @@ func (c *Cache) Read() error { // project aliase cache. Writes to disk. func (c *Cache) clear(clearAliases bool) { c.log.Info("Clearing project cache") - c.Projects = make([]*remotes.Project, 0) + c.Projects = make([]*projects.Project, 0) if clearAliases { c.log.Info("Clearing project alias cache") c.Aliases = make([]*ProjectAlias, 0) @@ -201,7 +202,7 @@ func NewProjectCache(opts *CacheOpts) (*Cache, error) { } cache := &Cache{ - Projects: make([]*remotes.Project, 0), + Projects: make([]*projects.Project, 0), Aliases: make([]*ProjectAlias, 0), config: opts.Config, file: opts.Path, diff --git a/internal/projects/cache_aliases.go b/internal/projects/cache_aliases.go index f69f788..d044f30 100644 --- a/internal/projects/cache_aliases.go +++ b/internal/projects/cache_aliases.go @@ -3,7 +3,7 @@ package projects import ( "errors" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" "golang.org/x/exp/slices" ) @@ -42,8 +42,8 @@ func (c *Cache) AddAlias(alias string, projectID int, remote string) error { return c.addAlias(alias, projectID, remote) } -func (c *Cache) GetProjectsWithAliases() []*remotes.Project { - projectList := make([]*remotes.Project, 0) +func (c *Cache) GetProjectsWithAliases() []*projects.Project { + projectList := make([]*projects.Project, 0) projectsFound := make([]int, 0) for _, a := range c.Aliases { if !slices.Contains(projectsFound, a.ProjectID) { diff --git a/internal/projects/cache_projects.go b/internal/projects/cache_projects.go index ea74e07..a4a25ef 100644 --- a/internal/projects/cache_projects.go +++ b/internal/projects/cache_projects.go @@ -4,15 +4,15 @@ import ( "strings" "github.com/pterm/pterm" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" "golang.org/x/exp/slices" ) -func (c *Cache) AddProjects(projects ...*remotes.Project) { +func (c *Cache) AddProjects(gitProjects ...*projects.Project) { c.contentLock.Lock() defer c.contentLock.Unlock() - for _, p := range projects { - var existing *remotes.Project + for _, p := range gitProjects { + var existing *projects.Project sameID := c.GetProjectsByID(p.ID) // If there is only one by ID, we don't diff --git a/internal/projects/fuzz.go b/internal/projects/fuzz.go index 8f8dc7d..d794e84 100644 --- a/internal/projects/fuzz.go +++ b/internal/projects/fuzz.go @@ -4,7 +4,7 @@ import ( "strings" "github.com/lithammer/fuzzysearch/fuzzy" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" ) // Performs a fuzzy find on the input string, returning the closest @@ -39,8 +39,8 @@ func (c *Cache) FuzzyFindAlias(name string) []*ProjectAlias { // Returns all matching projects by fuzzy find term // Matches NameWithNamespace and Aliases -func (c *Cache) FuzzyFindProjects(search string) []*remotes.Project { - projects := make([]*remotes.Project, 0, len(c.Projects)) +func (c *Cache) FuzzyFindProjects(search string) []*projects.Project { + projects := make([]*projects.Project, 0, len(c.Projects)) for _, p := range c.Projects { if fuzzy.MatchFold(search, p.NameWithNamespace) { projects = append(projects, p) diff --git a/internal/projects/projects.go b/internal/projects/projects.go index 0b7af2e..30e4df5 100644 --- a/internal/projects/projects.go +++ b/internal/projects/projects.go @@ -4,11 +4,11 @@ import ( "strings" "github.com/pterm/pterm" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" "golang.org/x/exp/slices" ) -func (c *Cache) ProjectString(p *remotes.Project) string { +func (c *Cache) ProjectString(p *projects.Project) string { info := strings.Builder{} info.WriteString(pterm.LightGreen("\n--------------\n")) @@ -42,7 +42,7 @@ func (c *Cache) ProjectStrings(prefix string) []string { return slices.Clip(projects) } -func (c *Cache) GetProjectByPath(path string) *remotes.Project { +func (c *Cache) GetProjectByPath(path string) *projects.Project { for _, p := range c.Projects { if p.PathWithNamespace == path { return p @@ -51,7 +51,7 @@ func (c *Cache) GetProjectByPath(path string) *remotes.Project { return nil } -func (c *Cache) GetProjectByRemoteAndId(remote string, id int) *remotes.Project { +func (c *Cache) GetProjectByRemoteAndId(remote string, id int) *projects.Project { for _, p := range c.Projects { if p.ID == id && p.Remote == remote { return p @@ -60,7 +60,7 @@ func (c *Cache) GetProjectByRemoteAndId(remote string, id int) *remotes.Project return nil } -func (c *Cache) GetProjectByID(id int) *remotes.Project { +func (c *Cache) GetProjectByID(id int) *projects.Project { for _, p := range c.Projects { if p.ID == id { return p @@ -72,8 +72,8 @@ func (c *Cache) GetProjectByID(id int) *remotes.Project { // Plural form of GetProjectByID // Since multiple remotes may have the same project ID, // this will return all matching -func (c *Cache) GetProjectsByID(id int) []*remotes.Project { - projects := make([]*remotes.Project, 0) +func (c *Cache) GetProjectsByID(id int) []*projects.Project { + projects := make([]*projects.Project, 0) for _, p := range c.Projects { if p.ID == id { projects = append(projects, p) diff --git a/internal/projects/projects_alias.go b/internal/projects/projects_alias.go index 7e32f34..648e6b6 100644 --- a/internal/projects/projects_alias.go +++ b/internal/projects/projects_alias.go @@ -7,7 +7,7 @@ import ( "text/tabwriter" "github.com/pterm/pterm" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" "golang.org/x/exp/slices" ) @@ -17,7 +17,7 @@ type ProjectAlias struct { Remote string } -func (c *Cache) GetProjectAliasStrings(project *remotes.Project) []string { +func (c *Cache) GetProjectAliasStrings(project *projects.Project) []string { aliases := c.GetProjectAliases(project) strings := make([]string, len(aliases)) for i, a := range c.GetProjectAliases(project) { @@ -26,7 +26,7 @@ func (c *Cache) GetProjectAliasStrings(project *remotes.Project) []string { return strings } -func (c *Cache) GetProjectStringWithAliases(project *remotes.Project) string { +func (c *Cache) GetProjectStringWithAliases(project *projects.Project) string { aliases := c.GetProjectAliasStrings(project) return fmt.Sprintf("%s (%s) -> %s", project.Name, @@ -83,7 +83,7 @@ func (c *Cache) GetAliasByName(name string, gitlabs ...string) *ProjectAlias { return nil } -func (c *Cache) GetProjectByAlias(alias *ProjectAlias) *remotes.Project { +func (c *Cache) GetProjectByAlias(alias *ProjectAlias) *projects.Project { if alias == nil { return nil } @@ -95,7 +95,7 @@ func (c *Cache) GetProjectByAlias(alias *ProjectAlias) *remotes.Project { return nil } -func (c *Cache) GetProjectAliases(project *remotes.Project) []*ProjectAlias { +func (c *Cache) GetProjectAliases(project *projects.Project) []*ProjectAlias { aliases := make([]*ProjectAlias, 0) for _, alias := range c.Aliases { if alias.ProjectID == project.ID { diff --git a/internal/projects/projects_fs.go b/internal/projects/projects_fs.go index 96a4cf8..a6ebf24 100644 --- a/internal/projects/projects_fs.go +++ b/internal/projects/projects_fs.go @@ -6,10 +6,10 @@ import ( "path/filepath" "strings" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" ) -func (c *Cache) GoTo(project *remotes.Project) { +func (c *Cache) GoTo(project *projects.Project) { pPath := c.GetProjectPath(project) c.log.Debug("Going to project", c.log.Args( @@ -25,8 +25,8 @@ func (c *Cache) GoTo(project *remotes.Project) { os.Chdir(filepath.Dir(pPath)) } -func (c *Cache) GetProjectFromCwd() (*remotes.Project, error) { - var project *remotes.Project +func (c *Cache) GetProjectFromCwd() (*projects.Project, error) { + var project *projects.Project cwd, err := os.Getwd() if err != nil { @@ -46,7 +46,7 @@ func (c *Cache) GetProjectFromCwd() (*remotes.Project, error) { return project, nil } -func (c *Cache) IsProjectCloned(p *remotes.Project) bool { +func (c *Cache) IsProjectCloned(p *projects.Project) bool { _, err := os.Stat(c.GetProjectPath(p) + "/.git") if err == nil { return true @@ -63,6 +63,6 @@ func (c *Cache) PrepProjectPath(path string) { } } -func (c *Cache) GetProjectPath(p *remotes.Project) string { +func (c *Cache) GetProjectPath(p *projects.Project) string { return c.path + "/" + p.SanitizedPath() } diff --git a/internal/projects/projects_git.go b/internal/projects/projects_git.go index 7628589..fd87b9a 100644 --- a/internal/projects/projects_git.go +++ b/internal/projects/projects_git.go @@ -5,7 +5,7 @@ import ( "time" git "github.com/go-git/go-git/v5" - "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" ) const gitCloneTimeoutSecs = 30 @@ -13,7 +13,7 @@ const gitCloneTimeoutSecs = 30 // Will either read in the current repo, preparing a report // on its current state, or will clone the project if it has not // already been cloned in its path -func (c *Cache) OpenProject(ctx context.Context, project *remotes.Project) *git.Repository { +func (c *Cache) OpenProject(ctx context.Context, project *projects.Project) *git.Repository { path := c.GetProjectPath(project) cloneCtx, cncl := context.WithDeadline(ctx, time.Now().Add(gitCloneTimeoutSecs*time.Second)) defer cncl() @@ -31,7 +31,7 @@ func (c *Cache) OpenProject(ctx context.Context, project *remotes.Project) *git. // Check to make sure we can connect before we time out // shouldn't be necessary, but go-git does not properly // honor its context - if err := project.CheckHost(remotes.GitlabProtoSSH); err != nil { + if err := project.CheckHost(projects.GitlabProtoSSH); err != nil { c.log.Fatal("Git remote unreachable, giving up", c.log.Args("error", err)) } diff --git a/internal/remotes/load/load.go b/internal/remotes/load/load.go new file mode 100644 index 0000000..5a9be7d --- /dev/null +++ b/internal/remotes/load/load.go @@ -0,0 +1,20 @@ +package load + +import ( + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" +) + +type ProgressInfo struct { + ProgressChan chan Progress + ProjectsChan chan []*projects.Project + ErrorChan chan error + DoneChan chan interface{} + NumProjects int +} + +type Progress struct { + Page int + Pages int + Projects int + TotalProjects int +} diff --git a/internal/remotes/projects/projects.go b/internal/remotes/projects/projects.go new file mode 100644 index 0000000..d48628f --- /dev/null +++ b/internal/remotes/projects/projects.go @@ -0,0 +1,72 @@ +package projects + +import ( + "fmt" + "strings" + "time" + + "github.com/go-git/go-git/v5" +) + +type Project struct { + ID int + Description string + SSHURLToRepo string + HTTPURLToRepo string + WebURL string + Name string + NameWithNamespace string + Path string + PathWithNamespace string + AvatarURL string + LastActivityAt time.Time + Readme string + Remote string + Owner string + Languages *ProjectLanguages + gitRepo *git.Repository +} + +type ProjectLanguages []*ProjectLanguage + +type ProjectLanguage struct { + Name string + Percentage float32 +} + +func (p *Project) String() string { + var projectString string + if p != nil { + projectString = fmt.Sprintf("%s (%s)", p.Path, p.PathWithNamespace) + } + return projectString +} + +func (p *Project) GetLanguage() *ProjectLanguage { + if p.Languages == nil { + return nil + } + + var lang *ProjectLanguage + var maxPcnt float32 + for _, p := range *p.Languages { + if p.Percentage > maxPcnt { + lang = p + } + maxPcnt = p.Percentage + } + + return lang +} + +func (p *Project) SanitizedPath() string { + return strings.Trim(p.PathWithNamespace, " '\"%<>|`") +} + +func (p *Project) SetRepo(r *git.Repository) { + p.gitRepo = r +} + +func (p *Project) GetRepo() *git.Repository { + return p.gitRepo +} diff --git a/internal/remotes/remotes_git.go b/internal/remotes/projects/projects_git.go similarity index 98% rename from internal/remotes/remotes_git.go rename to internal/remotes/projects/projects_git.go index 0fa6ea7..611c709 100644 --- a/internal/remotes/remotes_git.go +++ b/internal/remotes/projects/projects_git.go @@ -1,4 +1,4 @@ -package remotes +package projects import "github.com/pterm/pterm" diff --git a/internal/remotes/remotes_net.go b/internal/remotes/projects/projects_net.go similarity index 98% rename from internal/remotes/remotes_net.go rename to internal/remotes/projects/projects_net.go index 5ef94ed..686bb4d 100644 --- a/internal/remotes/remotes_net.go +++ b/internal/remotes/projects/projects_net.go @@ -1,4 +1,4 @@ -package remotes +package projects import ( "errors" diff --git a/internal/remotes/remote/remote.go b/internal/remotes/remote/remote.go new file mode 100644 index 0000000..1497469 --- /dev/null +++ b/internal/remotes/remote/remote.go @@ -0,0 +1,13 @@ +package remote + +import "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/load" + +// Any remote needs to be able to return +// the number of projects the user has access to and also +// stream all projects along with updates to channels +// provided by *load.ProgressInfo +type Remote interface { + Name() string + GetNumProjects(*RemoteQueryOpts) int + StreamProjects(*RemoteQueryOpts) *load.ProgressInfo +} diff --git a/internal/remotes/remote/remote_opts.go b/internal/remotes/remote/remote_opts.go new file mode 100644 index 0000000..3b10ecf --- /dev/null +++ b/internal/remotes/remote/remote_opts.go @@ -0,0 +1,8 @@ +package remote + +import "context" + +type RemoteQueryOpts struct { + Ctx context.Context + OwnerOnly bool +} diff --git a/internal/remotes/remotes.go b/internal/remotes/remotes.go index 16f7201..666fd56 100644 --- a/internal/remotes/remotes.go +++ b/internal/remotes/remotes.go @@ -1,13 +1,9 @@ package remotes import ( - "fmt" - "strings" - "time" - - "github.com/go-git/go-git/v5" "github.com/pterm/pterm" "github.com/xanzy/go-gitlab" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" ) // Will determine number of total projects, @@ -19,32 +15,6 @@ const ( projectsPerGoroutine = 200 ) -type Project struct { - ID int - Description string - SSHURLToRepo string - HTTPURLToRepo string - WebURL string - Name string - NameWithNamespace string - Path string - PathWithNamespace string - AvatarURL string - LastActivityAt time.Time - Readme string - Remote string - Owner string - Languages *ProjectLanguages - gitRepo *git.Repository -} - -type ProjectLanguages []*ProjectLanguage - -type ProjectLanguage struct { - Name string - Percentage float32 -} - type User struct { ID int Username string @@ -57,43 +27,6 @@ func (c *Client) Api() *gitlab.Client { return c.apiClient } -func (p *Project) String() string { - var projectString string - if p != nil { - projectString = fmt.Sprintf("%s (%s)", p.Path, p.PathWithNamespace) - } - return projectString -} - -func (p *Project) GetLanguage() *ProjectLanguage { - if p.Languages == nil { - return nil - } - - var lang *ProjectLanguage - var maxPcnt float32 - for _, p := range *p.Languages { - if p.Percentage > maxPcnt { - lang = p - } - maxPcnt = p.Percentage - } - - return lang -} - -func (p *Project) SanitizedPath() string { - return strings.Trim(p.PathWithNamespace, " '\"%<>|`") -} - -func (p *Project) SetRepo(r *git.Repository) { - p.gitRepo = r -} - -func (p *Project) GetRepo() *git.Repository { - return p.gitRepo -} - func (c *Client) GetTotalProjects(opts *gitlab.ListProjectsOptions) int { reqOpts := *opts reqOpts.ListOptions = gitlab.ListOptions{ @@ -112,8 +45,8 @@ func (c *Client) GetTotalProjects(opts *gitlab.ListProjectsOptions) int { // Returns a list of projects along with the next page and an error // if there was an error func (c *Client) ListProjects(opts *gitlab.ListProjectsOptions) ( - []*Project, *gitlab.Response, error) { - pList := make([]*Project, 0) + []*projects.Project, *gitlab.Response, error) { + pList := make([]*projects.Project, 0) projects, resp, err := c.apiClient.Projects.ListProjects( opts, gitlab.WithContext(c.Ctx), @@ -126,19 +59,19 @@ func (c *Client) ListProjects(opts *gitlab.ListProjectsOptions) ( // A nil return indicates an API error or GitLab doesn't know what // language the project uses. -func (c *Client) GetProjectLanguages(project *gitlab.Project) *ProjectLanguages { +func (c *Client) GetProjectLanguages(project *gitlab.Project) *projects.ProjectLanguages { l, _, e := c.apiClient.Projects.GetProjectLanguages(project.ID, gitlab.WithContext(c.Ctx)) if e != nil { pterm.Error.Printfln("Failed requesting project languages: %s", e.Error()) return nil } - var pLangs ProjectLanguages - pLangs = make([]*ProjectLanguage, len(*l)) + var pLangs projects.ProjectLanguages + pLangs = make([]*projects.ProjectLanguage, len(*l)) var i int for name, pcnt := range *l { - pLangs[i] = &ProjectLanguage{ + pLangs[i] = &projects.ProjectLanguage{ Name: name, Percentage: pcnt, } diff --git a/internal/remotes/remotes_load.go b/internal/remotes/remotes_load.go index 193cf47..6c08155 100644 --- a/internal/remotes/remotes_load.go +++ b/internal/remotes/remotes_load.go @@ -1,26 +1,14 @@ package remotes import ( + "fmt" "sync" "github.com/xanzy/go-gitlab" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/load" + "gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/remotes/projects" ) -type ProgressInfo struct { - ProgressChan chan Progress - ProjectsChan chan []*Project - ErrorChan chan error - DoneChan chan interface{} - NumProjects int -} - -type Progress struct { - Page int - Pages int - Projects int - TotalProjects int -} - var DefaultListOpts = &gitlab.ListProjectsOptions{ ListOptions: gitlab.ListOptions{ PerPage: projectsPerPage, @@ -33,13 +21,14 @@ var DefaultListOpts = &gitlab.ListProjectsOptions{ // channels that stream progress info and then finally the full // list of projects on separate channels. If ownerOnly=true, only // projects for which you are an owner will be loaded -func (c *Client) StreamProjects(ownerOnly bool, projects int) *ProgressInfo { - pi := &ProgressInfo{ - ProgressChan: make(chan Progress), - ProjectsChan: make(chan []*Project), +func (c *Client) StreamProjects(ownerOnly bool, numProjects int) *load.ProgressInfo { + fmt.Println(numProjects) + pi := &load.ProgressInfo{ + ProgressChan: make(chan load.Progress), + ProjectsChan: make(chan []*projects.Project), ErrorChan: make(chan error), DoneChan: make(chan interface{}), - NumProjects: projects, + NumProjects: numProjects, } go c.streamProjects(pi, ownerOnly) @@ -47,7 +36,7 @@ func (c *Client) StreamProjects(ownerOnly bool, projects int) *ProgressInfo { return pi } -func (c *Client) streamProjects(pi *ProgressInfo, ownerOnly bool) { +func (c *Client) streamProjects(pi *load.ProgressInfo, ownerOnly bool) { defer close(pi.ProgressChan) defer close(pi.ProjectsChan) @@ -75,7 +64,7 @@ func (c *Client) streamProjects(pi *ProgressInfo, ownerOnly bool) { } pi.ProjectsChan <- projects - pi.ProgressChan <- Progress{ + pi.ProgressChan <- load.Progress{ Page: resp.CurrentPage, Pages: resp.TotalPages, Projects: len(projects), @@ -99,16 +88,16 @@ func (c *Client) streamProjects(pi *ProgressInfo, ownerOnly bool) { pi.DoneChan <- nil } -func (c *Client) handleProjects(projects []*gitlab.Project) []*Project { +func (c *Client) handleProjects(gitProjects []*gitlab.Project) []*projects.Project { // Opportunity to perform any filtering or additional lookups // on a per-project basis - pList := make([]*Project, 0, len(projects)) - for _, project := range projects { + pList := make([]*projects.Project, 0, len(gitProjects)) + for _, project := range gitProjects { var owner string if project.Owner != nil { owner = project.Owner.Email } - p := &Project{ + p := &projects.Project{ ID: project.ID, Description: project.Description, SSHURLToRepo: project.SSHURLToRepo,