Go 52 lines
//go:build windows
package selfupdate
import (
"os"
"path/filepath"
)
// swap replaces target with newPath. Windows cannot rename a file onto
// a running executable, but it can rename the running executable out of
// the way first: move target -> target.old, then move new -> target.
// target.old is removed best-effort; if Windows holds it open, the file
// remains and the caller should print a one-line hint.
func swap(newPath, target string) error {
info, err := os.Stat(target)
if err != nil {
return err
}
dir := filepath.Dir(target)
tmp, err := os.CreateTemp(dir, ".eeco-new-*")
if err != nil {
return err
}
tmpName := tmp.Name()
if err := tmp.Close(); err != nil {
os.Remove(tmpName)
return err
}
if err := copyFile(newPath, tmpName); err != nil {
os.Remove(tmpName)
return err
}
if err := os.Chmod(tmpName, info.Mode()); err != nil {
os.Remove(tmpName)
return err
}
old := target + ".old"
_ = os.Remove(old)
if err := os.Rename(target, old); err != nil {
os.Remove(tmpName)
return err
}
if err := os.Rename(tmpName, target); err != nil {
_ = os.Rename(old, target)
os.Remove(tmpName)
return err
}
_ = os.Remove(old)
return nil
}