github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/gits/features/disable_features.go (about) 1 package features 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "github.com/jenkins-x/jx-logging/pkg/log" 9 10 "github.com/olli-ai/jx/v2/pkg/gits" 11 "github.com/olli-ai/jx/v2/pkg/util" 12 "github.com/pkg/errors" 13 ) 14 15 // DisableFeaturesForOrg iterates over all the repositories in org (except those that match excludes) and disables issue 16 // trackers, projects and wikis if they are not in use. 17 // 18 // Issue trackers are not in use if they have no open or closed issues 19 // Projects are not in use if there are no open projects 20 // Wikis are not in use if the provider returns that the wiki is not enabled 21 // 22 // Note that the requirement for issues is no issues at all so that we don't close issue trackers that have historic info 23 // 24 // If includes is not empty only those that match an include will be operated on. If dryRun is true, the operations to 25 // be done will printed and but nothing done. If batchMode is false, then each change will be prompted. 26 func DisableFeaturesForOrg(org string, includes []string, excludes []string, dryRun bool, batchMode bool, provider gits.GitProvider, handles util.IOFileHandles) error { 27 28 type closeTypes struct { 29 *gits.GitRepository 30 closeIssues bool 31 closeProject bool 32 closeWiki bool 33 keepIssues bool 34 keepProject bool 35 keepWiki bool 36 } 37 38 repos, err := provider.ListRepositories(org) 39 if err != nil { 40 return errors.Wrapf(err, "listing repositories in %s", org) 41 } 42 sort.Slice(repos, func(i, j int) bool { 43 return repos[i].Name < repos[j].Name 44 }) 45 // dedupe 46 dedupedRepos := make([]*gits.GitRepository, 0) 47 previous := "" 48 for _, r := range repos { 49 if r.Name != previous { 50 dedupedRepos = append(dedupedRepos, r) 51 } 52 previous = r.Name 53 } 54 ct := make([]closeTypes, 0) 55 56 log.Logger().Infof("Analysing repositories\n") 57 58 for _, repo := range dedupedRepos { 59 c := closeTypes{ 60 GitRepository: repo, 61 closeIssues: false, 62 closeProject: false, 63 closeWiki: false, 64 } 65 if c.Archived { 66 // Ignore archived repos 67 continue 68 } 69 if !util.Contains(excludes, fmt.Sprintf("%s/%s", repo.Organisation, repo.Name)) && (len(includes) == 0 || util.Contains(includes, fmt.Sprintf("%s/%s", repo.Organisation, repo.Name))) { 70 issues := "" 71 if repo.HasIssues { 72 openIssues, err := provider.SearchIssues(repo.Organisation, repo.Name, "open") 73 if err != nil { 74 return errors.Wrapf(err, "finding open issues in %s/%s", repo.Organisation, repo.Name) 75 } 76 closedIssues, err := provider.SearchIssues(repo.Organisation, repo.Name, "closed") 77 if err != nil { 78 return errors.Wrapf(err, "finding open issues in %s/%s", repo.Organisation, repo.Name) 79 } 80 open := len(openIssues) 81 all := len(openIssues) + len(closedIssues) 82 stat := fmt.Sprintf("%d/%d", open, all) 83 if open > 0 { 84 stat = util.ColorInfo(stat) 85 } 86 if all == 0 { 87 c.closeIssues = true 88 } else { 89 c.keepIssues = true 90 } 91 issues = fmt.Sprintf("%s issues are open", stat) 92 } else { 93 issues = "Disabled" 94 } 95 projects := "" 96 if repo.HasProjects { 97 allProjects, err := provider.GetProjects(repo.Organisation, repo.Name) 98 if err != nil { 99 return errors.Wrapf(err, "getting projects for %s/%s", repo.Organisation, repo.Name) 100 } 101 open := 0 102 for _, p := range allProjects { 103 if p.State == gits.ProjectOpen { 104 open++ 105 } 106 } 107 stat := fmt.Sprintf("%d/%d", open, len(allProjects)) 108 if open > 0 { 109 stat = util.ColorInfo(stat) 110 c.keepProject = true 111 } else { 112 c.closeProject = true 113 } 114 projects = fmt.Sprintf("%s open projects", stat) 115 } else { 116 projects = "Disabled" 117 } 118 wikis := "" 119 if repo.HasWiki { 120 enabled, err := provider.IsWikiEnabled(repo.Organisation, repo.Name) 121 if err != nil { 122 return errors.Wrapf(err, "checking if wiki for %s/%s is enabled", repo.Organisation, repo.Name) 123 } 124 if enabled { 125 wikis = util.ColorInfo("In use") 126 c.keepWiki = true 127 } else { 128 wikis = "Not in use" 129 c.closeWiki = true 130 } 131 } else { 132 wikis = "Disabled" 133 } 134 log.Logger().Infof(` 135 %s 136 Issues: %s 137 Projects: %s 138 Wiki Pages: %s 139 `, util.ColorBold(fmt.Sprintf("%s/%s", repo.Organisation, repo.Name)), issues, projects, wikis) 140 ct = append(ct, c) 141 } 142 } 143 log.Logger().Infof("\n\n Analysis complete\n") 144 for _, c := range ct { 145 toClose := make([]string, 0) 146 toKeep := make([]string, 0) 147 var wiki, issues, project *bool 148 disabled := false 149 if c.closeProject { 150 toClose = append(toClose, util.ColorWarning("project")) 151 project = &disabled 152 } 153 if c.keepProject { 154 toKeep = append(toKeep, util.ColorInfo("project")) 155 } 156 if c.closeWiki { 157 toClose = append(toClose, util.ColorWarning("wiki")) 158 wiki = &disabled 159 } 160 if c.keepWiki { 161 toKeep = append(toKeep, util.ColorInfo("wiki")) 162 } 163 if c.closeIssues { 164 toClose = append(toClose, util.ColorWarning("issues")) 165 issues = &disabled 166 } 167 if c.keepIssues { 168 toKeep = append(toKeep, util.ColorInfo("issues")) 169 } 170 if len(toClose) > 0 || len(toKeep) > 0 { 171 if dryRun { 172 log.Logger().Infof("Would disable %s on %s", strings.Join(toClose, ", "), util.ColorInfo(fmt.Sprintf("%s/%s", c.Organisation, c.Name))) 173 } else { 174 175 if !batchMode { 176 if answer, err := util.Confirm(fmt.Sprintf("Are you sure you want to disable %s on %s", strings.Join(toClose, ","), util.ColorInfo(fmt.Sprintf("%s/%s", c.Organisation, c.Name))), true, "", handles); err != nil { 177 return err 178 } else if !answer { 179 continue 180 } 181 } 182 logStr := "" 183 if len(toClose) > 0 { 184 logStr = fmt.Sprintf("Disabling %s", strings.Join(toClose, ", ")) 185 } 186 if len(toKeep) > 0 { 187 if len(logStr) > 0 { 188 logStr += "; " 189 } 190 logStr += fmt.Sprintf("Keeping %s", strings.Join(toKeep, ", ")) 191 } 192 if len(logStr) > 0 { 193 log.Logger().Infof("%s: %s", util.ColorInfo(fmt.Sprintf("%s/%s", c.Organisation, c.Name)), logStr) 194 } 195 _, err = provider.ConfigureFeatures(c.Organisation, c.Name, issues, project, wiki) 196 if err != nil { 197 return errors.Wrapf(err, "disabling %s on %s/%s", strings.Join(toClose, ", "), c.Organisation, c.Name) 198 } 199 } 200 } 201 } 202 return nil 203 }