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 }