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  }