github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/cmd/state-installer/installer_windows.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 "time" 9 10 "github.com/ActiveState/cli/internal/errs" 11 "github.com/ActiveState/cli/internal/fileutils" 12 "github.com/ActiveState/cli/internal/installation" 13 "github.com/ActiveState/cli/internal/logging" 14 "github.com/ActiveState/cli/internal/subshell" 15 "github.com/ActiveState/cli/internal/subshell/sscommon" 16 ) 17 18 func InstallSystemFiles(_, _, _ string) error { 19 return nil 20 } 21 22 // PrepareBinTargets will move aside any targets in the bin dir that we would otherwise overwrite. 23 // This guards us from file in use errors as well as false positives by security software 24 func (i *Installer) PrepareBinTargets() error { 25 sourceBinPath := filepath.Join(i.payloadPath, "bin") 26 targetBinPath := filepath.Join(i.path, "bin") 27 28 // Clean up exectuables from potentially corrupted installed 29 err := removeOldExecutables(i.path) 30 if err != nil { 31 return errs.Wrap(err, "Could not remove executables at: %s", i.path) 32 } 33 34 // Clean up exectuables from old install 35 err = removeOldExecutables(targetBinPath) 36 if err != nil { 37 return errs.Wrap(err, "Could not remove executables at: %s", i.path) 38 } 39 40 // Move aside conflicting executables in target 41 if fileutils.DirExists(sourceBinPath) { 42 files, err := os.ReadDir(sourceBinPath) 43 if err != nil { 44 return errs.Wrap(err, "Could not read target dir") 45 } 46 47 for _, file := range files { 48 if file.IsDir() { 49 continue 50 } 51 52 // Move executables aside 53 targetFile := filepath.Join(targetBinPath, file.Name()) 54 if fileutils.TargetExists(targetFile) { 55 logging.Debug("Moving aside conflicting file: %s", targetFile) 56 renamedFile := filepath.Join(targetBinPath, fmt.Sprintf("%s-%d.old", file.Name(), time.Now().Unix())) 57 if err := os.Rename(targetFile, renamedFile); err != nil { 58 return errs.Wrap(err, "Could not move executable aside prior to install: %s to %s", targetFile, renamedFile) 59 } 60 // Make an attempt to remove the file. This has a decent chance of failing if we're updating. 61 // That's just a limitation on Windows and worse case scenario we'll clean it up the next update attempt. 62 os.Remove(renamedFile) 63 } 64 } 65 } 66 67 return nil 68 } 69 70 func (i *Installer) sanitizeInstallPath() error { 71 if !fileutils.DirExists(i.path) { 72 return nil 73 } 74 75 files, err := os.ReadDir(i.path) 76 if err != nil { 77 return errs.Wrap(err, "Could not installation directory: %s", i.path) 78 } 79 80 for _, file := range files { 81 fname := strings.ToLower(file.Name()) 82 targetFile := filepath.Join(i.path, file.Name()) 83 if isStateExecutable(fname) { 84 renamedFile := filepath.Join(i.path, fmt.Sprintf("%s-%d.old", fname, time.Now().Unix())) 85 if err := os.Rename(targetFile, renamedFile); err != nil { 86 return errs.Wrap(err, "Could not rename corrupted executable: %s to %s", targetFile, renamedFile) 87 } 88 // This will likely fail but we try anyways 89 os.Remove(renamedFile) 90 } 91 } 92 93 installContext, err := installation.GetContext() 94 if err != nil { 95 return errs.Wrap(err, "Could not get initial installation context") 96 } 97 98 // Since we are repairing a corrupted install we need to also remove the old 99 // PATH entry. The new PATH entry will be added later in the install/update process. 100 // This is only an issue on Windows as on other platforms we can simply rewrite 101 // the PATH entry. 102 s := subshell.New(i.cfg) 103 if err := s.CleanUserEnv(i.cfg, sscommon.InstallID, !installContext.InstalledAsAdmin); err != nil { 104 return errs.Wrap(err, "Failed to State Tool installation PATH") 105 } 106 107 return nil 108 } 109 110 func removeOldExecutables(dir string) error { 111 if !fileutils.TargetExists(dir) { 112 return nil 113 } 114 115 files, err := os.ReadDir(dir) 116 if err != nil { 117 return errs.Wrap(err, "Could not read installer dir") 118 } 119 120 for _, file := range files { 121 if file.IsDir() { 122 continue 123 } 124 125 if strings.HasSuffix(file.Name(), ".old") { 126 logging.Debug("Deleting old file: %s", file.Name()) 127 oldFile := filepath.Join(dir, file.Name()) 128 if err := os.Remove(oldFile); err != nil { 129 logging.Debug("Failed to remove old executable: %s. Error: %s", oldFile, errs.JoinMessage(err)) 130 } 131 } 132 } 133 134 return nil 135 }