github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/platform/runtime/setup/implementations/alternative/runtime.go (about)

     1  package alternative
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"sort"
     7  
     8  	"github.com/ActiveState/cli/internal/errs"
     9  	"github.com/ActiveState/cli/internal/fileutils"
    10  	"github.com/ActiveState/cli/internal/locale"
    11  	"github.com/ActiveState/cli/internal/multilog"
    12  	"github.com/ActiveState/cli/pkg/buildplan"
    13  	"github.com/ActiveState/cli/pkg/platform/runtime/store"
    14  	"github.com/go-openapi/strfmt"
    15  	"github.com/thoas/go-funk"
    16  )
    17  
    18  type Setup struct {
    19  	store *store.Store
    20  }
    21  
    22  func NewSetup(store *store.Store) *Setup {
    23  	return &Setup{store: store}
    24  }
    25  
    26  func (s *Setup) DeleteOutdatedArtifacts(changeset *buildplan.ArtifactChangeset, storedArtifacted, alreadyInstalled store.StoredArtifactMap) error {
    27  	if changeset == nil {
    28  		return nil
    29  	}
    30  
    31  	del := map[strfmt.UUID]struct{}{}
    32  	for _, upd := range changeset.Updated {
    33  		del[upd.From.ArtifactID] = struct{}{}
    34  	}
    35  	for _, rem := range changeset.Removed {
    36  		del[rem.ArtifactID] = struct{}{}
    37  	}
    38  
    39  	// sort files and dirs in keep for faster look-up
    40  	for _, artf := range alreadyInstalled {
    41  		sort.Strings(artf.Dirs)
    42  		sort.Strings(artf.Files)
    43  	}
    44  
    45  	for _, artf := range storedArtifacted {
    46  		if _, deleteMe := del[artf.ArtifactID]; !deleteMe {
    47  			continue
    48  		}
    49  
    50  		for _, file := range artf.Files {
    51  			if !fileutils.TargetExists(file) {
    52  				continue // don't care it's already deleted (might have been deleted by another artifact that supplied the same file)
    53  			}
    54  			if artifactsContainFile(file, alreadyInstalled) {
    55  				continue
    56  			}
    57  			if err := os.Remove(file); err != nil {
    58  				return locale.WrapError(err, "err_rm_artf", "Could not remove old package file at {{.V0}}.", file)
    59  			}
    60  		}
    61  
    62  		dirs := artf.Dirs
    63  		sort.Slice(dirs, func(i, j int) bool {
    64  			return dirs[i] > dirs[j]
    65  		})
    66  
    67  		for _, dir := range dirs {
    68  			if !fileutils.DirExists(dir) {
    69  				continue
    70  			}
    71  
    72  			deleteOk, err := dirCanBeDeleted(dir, alreadyInstalled)
    73  			if err != nil {
    74  				multilog.Error("Could not determine if directory %s could be deleted: %v", dir, err)
    75  				continue
    76  			}
    77  			if !deleteOk {
    78  				continue
    79  			}
    80  
    81  			err = os.RemoveAll(dir)
    82  			if err != nil {
    83  				return locale.WrapError(err, "err_rm_artf_dir", "Could not remove empty artifact directory at {{.V0}}", dir)
    84  			}
    85  		}
    86  
    87  		if err := s.store.DeleteArtifactStore(artf.ArtifactID); err != nil {
    88  			return errs.Wrap(err, "Could not delete artifact store")
    89  		}
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // dirCanBeDeleted checks if the given directory is empty - ignoring files and sub-directories that
    96  // are not in the cache.
    97  func dirCanBeDeleted(dir string, cache map[strfmt.UUID]store.StoredArtifact) (bool, error) {
    98  	if artifactsContainDir(dir, cache) {
    99  		return false, nil
   100  	}
   101  
   102  	entries, err := os.ReadDir(dir)
   103  	if err != nil {
   104  		return false, errs.Wrap(err, "Could not read directory.")
   105  	}
   106  	for _, entry := range entries {
   107  		if entry.IsDir() {
   108  			if artifactsContainDir(filepath.Join(dir, entry.Name()), cache) {
   109  				return false, nil
   110  			}
   111  		} else {
   112  			if artifactsContainFile(filepath.Join(dir, entry.Name()), cache) {
   113  				return false, nil
   114  			}
   115  		}
   116  	}
   117  	return true, nil
   118  }
   119  
   120  func sortedStringSliceContains(slice []string, x string) bool {
   121  	i := sort.SearchStrings(slice, x)
   122  	return i != len(slice) && slice[i] == x
   123  }
   124  
   125  func artifactsContainDir(dir string, artifactCache map[strfmt.UUID]store.StoredArtifact) bool {
   126  	for _, v := range artifactCache {
   127  		if funk.Contains(v.Dirs, dir) {
   128  			return true
   129  		}
   130  	}
   131  	return false
   132  }
   133  
   134  func artifactsContainFile(file string, artifactCache map[strfmt.UUID]store.StoredArtifact) bool {
   135  	for _, v := range artifactCache {
   136  		if sortedStringSliceContains(v.Files, file) {
   137  			return true
   138  		}
   139  	}
   140  	return false
   141  }
   142  
   143  func (s *Setup) ResolveArtifactName(a strfmt.UUID) string {
   144  	return locale.T("alternative_unknown_pkg_name")
   145  }