github.com/taubyte/tau-cli@v0.1.13-0.20240326000942-487f0d57edfc/cli/commands/resources/project/delete.go (about) 1 package project 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "strings" 8 9 "github.com/pterm/pterm" 10 "github.com/taubyte/go-project-schema/libraries" 11 "github.com/taubyte/go-project-schema/project" 12 "github.com/taubyte/go-project-schema/website" 13 "github.com/taubyte/tau-cli/cli/common" 14 "github.com/taubyte/tau-cli/flags" 15 "github.com/taubyte/tau-cli/i18n" 16 projectI18n "github.com/taubyte/tau-cli/i18n/project" 17 repositoryI18n "github.com/taubyte/tau-cli/i18n/repository" 18 loginLib "github.com/taubyte/tau-cli/lib/login" 19 projectLib "github.com/taubyte/tau-cli/lib/project" 20 "github.com/taubyte/tau-cli/prompts" 21 authClient "github.com/taubyte/tau-cli/singletons/auth_client" 22 "github.com/taubyte/tau-cli/singletons/config" 23 client "github.com/taubyte/tau/clients/http/auth" 24 authCommon "github.com/taubyte/tau/clients/http/auth/git/common" 25 "github.com/urfave/cli/v2" 26 ) 27 28 func (link) Delete() common.Command { 29 return common.Create( 30 &cli.Command{ 31 Action: _delete, 32 Flags: []cli.Flag{ 33 flags.Yes, 34 }, 35 }, 36 ) 37 } 38 39 func _delete(ctx *cli.Context) error { 40 profile, err := loginLib.GetSelectedProfile() 41 if err != nil { 42 return err 43 } 44 45 project, schema, err := selectDeletion(ctx) 46 if err != nil { 47 return err 48 } 49 50 repos, err := project.Repositories() 51 if err != nil { 52 return err 53 } 54 55 repoNames := []string{repos.Code.Fullname, repos.Configuration.Fullname} 56 57 msg := formatDeleteConfirm(project.Name, repoNames) 58 if prompts.ConfirmPrompt(ctx, msg) { 59 libraries, websites, err := resources(schema) 60 if err != nil { 61 // use i18n 62 return err 63 } 64 65 if resources := resourceNames(websites, libraries); len(resources) > 0 { 66 repoNames = append(repoNames, 67 prompts.MultiSelect(ctx, 68 prompts.MultiSelectConfig{ 69 Field: "resource", 70 Prompt: "Select additional resources to unregister", 71 Options: resources, 72 }, 73 )..., 74 ) 75 } 76 77 if _, err = project.Delete(); err != nil { 78 return projectI18n.ErrorDeleteProject(project.Name, err) 79 } 80 81 prj, err := config.Projects().Get(project.Name) 82 if err == nil { 83 if err = os.RemoveAll(prj.Location); err == nil { 84 pterm.Success.Println("Removed", prj.Location) 85 } 86 } 87 88 auth, err := authClient.Load() 89 if err != nil { 90 return err 91 } 92 93 if err = unregister(auth, repoNames); err != nil { 94 return repositoryI18n.ErrorUnregisterRepositories(err) 95 } 96 97 projectI18n.RemovedProject(project.Name, profile.Network) 98 99 repoNames = prompts.MultiSelect(ctx, prompts.MultiSelectConfig{ 100 Field: "github", 101 Prompt: "Remove from github?", 102 Options: repoNames, 103 }) 104 105 token := profile.Token 106 userName := profile.GitUsername 107 for _, name := range repoNames { 108 name = strings.TrimPrefix(name, userName+"/") 109 if _err := removeFromGithub(ctx.Context, token, userName, name); _err != nil { 110 if err != nil { 111 err = fmt.Errorf("%s:%w", err, _err) 112 } else { 113 err = _err 114 } 115 } 116 } 117 if err != nil { 118 if errors.Is(err, repositoryI18n.ErrorAdminRights) { 119 pterm.Info.Println("Delete repositories manually") 120 return nil 121 } 122 123 return err 124 } 125 } 126 127 return nil 128 } 129 130 func selectDeletion(ctx *cli.Context) (*client.Project, project.Project, error) { 131 projects, err := projectLib.ListResources() 132 if err != nil { 133 return nil, nil, err 134 } 135 136 // TODO: Avoid making a list and map by adding Prompt: that will parse Name() or String() methods 137 projectMap := make(map[string]*client.Project, len(projects)) 138 projectList := make([]string, len(projects)) 139 for idx, project := range projects { 140 projectList[idx] = project.Name 141 projectMap[project.Name] = project 142 } 143 144 projectName := prompts.GetOrAskForSelection(ctx, "name", "Project:", projectList) 145 project, ok := projectMap[projectName] 146 if !ok { 147 return nil, nil, i18n.ErrorDoesNotExist("project", projectName) 148 } 149 150 config, err := config.Projects().Get(projectName) 151 if err != nil { 152 return nil, nil, err 153 } 154 155 schema, err := config.Interface() 156 if err != nil { 157 return nil, nil, err 158 } 159 160 return project, schema, nil 161 } 162 163 func resources(schema project.Project) ([]libraries.Library, []website.Website, error) { 164 applications := schema.Get().Applications() 165 166 libs := make([]libraries.Library, 0) 167 _, libNames := schema.Get().Libraries("") 168 for _, name := range libNames { 169 lib, err := schema.Library(name, "") 170 if err != nil { 171 return nil, nil, err 172 } 173 libs = append(libs, lib) 174 } 175 176 webs := make([]website.Website, 0) 177 _, webNames := schema.Get().Websites("") 178 for _, name := range webNames { 179 web, err := schema.Website(name, "") 180 if err != nil { 181 return nil, nil, err 182 } 183 184 webs = append(webs, web) 185 } 186 187 for _, app := range applications { 188 libNames, _ = schema.Get().Libraries(app) 189 for _, name := range libNames { 190 lib, err := schema.Library(name, app) 191 if err != nil { 192 return nil, nil, err 193 } 194 195 libs = append(libs, lib) 196 } 197 198 webNames, _ = schema.Get().Websites(app) 199 for _, name := range webNames { 200 web, err := schema.Website(name, app) 201 if err != nil { 202 return nil, nil, err 203 } 204 205 webs = append(webs, web) 206 } 207 } 208 209 return libs, webs, nil 210 } 211 212 func resourceNames(websites []website.Website, libraries []libraries.Library) []string { 213 names := make([]string, 0, len(websites)+len(libraries)) 214 for _, website := range websites { 215 _, _, name := website.Get().Git() 216 names = append(names, name) 217 } 218 219 for _, library := range libraries { 220 _, _, name := library.Get().Git() 221 names = append(names, name) 222 } 223 224 return names 225 } 226 227 func formatDeleteConfirm(project string, repos []string) string { 228 formattedMessage := fmt.Sprintf( 229 "Removing project `%s` will unregister the following repositories from auth:", 230 pterm.FgCyan.Sprint(project), 231 ) 232 233 for _, repo := range repos { 234 formattedMessage += fmt.Sprintf("\n%s", cyanBullet(repo)) 235 } 236 237 formattedMessage += "\nProceed?" 238 return formattedMessage 239 } 240 241 func cyanBullet(name string) string { 242 return fmt.Sprintf(" \u2022 %s", pterm.FgCyan.Sprint(name)) 243 } 244 245 func unregister(auth *client.Client, names []string) error { 246 repos := make([]authCommon.Repository, len(names)) 247 for idx, name := range names { 248 repo, err := auth.GetRepositoryByName(name) 249 if err != nil { 250 return err 251 } 252 253 repos[idx] = repo 254 } 255 256 var err error 257 for _, repo := range repos { 258 if _err := auth.UnregisterRepository(repo.Get().ID()); _err != nil { 259 if err != nil { 260 err = fmt.Errorf("%s:%w", err, _err) 261 } else { 262 err = _err 263 } 264 } 265 } 266 267 return err 268 }