Skip to content

Commit

Permalink
Removes another dep that uses osext
Browse files Browse the repository at this point in the history
  • Loading branch information
sanbornm committed Jul 13, 2023
1 parent 93e4995 commit 4aec645
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 13 deletions.
6 changes: 1 addition & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,4 @@ module github.com/sanbornm/go-selfupdate

go 1.15

require (
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/kr/binarydist v0.1.0
gopkg.in/inconshreveable/go-update.v0 v0.0.0-20150814200126-d8b0b1d421aa
)
require github.com/kr/binarydist v0.1.0
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kr/binarydist v0.1.0 h1:6kAoLA9FMMnNGSehX0s1PdjbEaACznAv/W219j2uvyo=
github.com/kr/binarydist v0.1.0/go.mod h1:DY7S//GCoz1BCd0B0EVrinCKAZN3pXe+MDaIZbXQVgM=
gopkg.in/inconshreveable/go-update.v0 v0.0.0-20150814200126-d8b0b1d421aa h1:drvf2JoUL1fz3ttkGNkw+rf3kZa2//7XkYGpSO4NHNA=
gopkg.in/inconshreveable/go-update.v0 v0.0.0-20150814200126-d8b0b1d421aa/go.mod h1:tuNm0ntQ7IH9VSA39XxzLMpee5c2DwgIbjD4x3ydo8Y=
8 changes: 8 additions & 0 deletions selfupdate/hide_noop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build !windows
// +build !windows

package selfupdate

func hideFile(path string) error {
return nil
}
19 changes: 19 additions & 0 deletions selfupdate/hide_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package selfupdate

import (
"syscall"
"unsafe"
)

func hideFile(path string) error {
kernel32 := syscall.NewLazyDLL("kernel32.dll")
setFileAttributes := kernel32.NewProc("SetFileAttributesW")

r1, _, err := setFileAttributes.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), 2)

if r1 == 0 {
return err
} else {
return nil
}
}
90 changes: 86 additions & 4 deletions selfupdate/selfupdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"time"

"github.com/kr/binarydist"
"gopkg.in/inconshreveable/go-update.v0"
)

const (
Expand All @@ -30,7 +29,6 @@ const (
var (
ErrHashMismatch = errors.New("new file hash mismatch after patch")

up = update.New()
defaultHTTPRequester = HTTPRequester{}
)

Expand Down Expand Up @@ -75,6 +73,28 @@ func (u *Updater) getExecRelativeDir(dir string) string {
return path
}

func canUpdate() (err error) {
// get the directory the file exists in
path, err := os.Executable()
if err != nil {
return
}

fileDir := filepath.Dir(path)
fileName := filepath.Base(path)

// attempt to open a file in the file's directory
newPath := filepath.Join(fileDir, fmt.Sprintf(".%s.new", fileName))
fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
if err != nil {
return
}
fp.Close()

_ = os.Remove(newPath)
return
}

// BackgroundRun starts the update check and apply cycle.
func (u *Updater) BackgroundRun() error {
if err := os.MkdirAll(u.getExecRelativeDir(u.Dir), 0755); err != nil {
Expand All @@ -84,7 +104,7 @@ func (u *Updater) BackgroundRun() error {
// check to see if we want to check for updates based on version
// and last update time
if u.WantUpdate() {
if err := up.CanUpdate(); err != nil {
if err := canUpdate(); err != nil {
// fail
return err
}
Expand Down Expand Up @@ -210,7 +230,7 @@ func (u *Updater) Update() error {
// it can't be renamed if a handle to the file is still open
old.Close()

err, errRecover := up.FromStream(bytes.NewBuffer(bin))
err, errRecover := fromStream(bytes.NewBuffer(bin))
if errRecover != nil {
return fmt.Errorf("update and recovery errors: %q %q", err, errRecover)
}
Expand All @@ -226,6 +246,68 @@ func (u *Updater) Update() error {
return nil
}

func fromStream(updateWith io.Reader) (err error, errRecover error) {
updatePath, err := os.Executable()
if err != nil {
return
}

var newBytes []byte
newBytes, err = ioutil.ReadAll(updateWith)
if err != nil {
return
}

// get the directory the executable exists in
updateDir := filepath.Dir(updatePath)
filename := filepath.Base(updatePath)

// Copy the contents of of newbinary to a the new executable file
newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename))
fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
if err != nil {
return
}
defer fp.Close()
_, err = io.Copy(fp, bytes.NewReader(newBytes))

// if we don't call fp.Close(), windows won't let us move the new executable
// because the file will still be "in use"
fp.Close()

// this is where we'll move the executable to so that we can swap in the updated replacement
oldPath := filepath.Join(updateDir, fmt.Sprintf(".%s.old", filename))

// delete any existing old exec file - this is necessary on Windows for two reasons:
// 1. after a successful update, Windows can't remove the .old file because the process is still running
// 2. windows rename operations fail if the destination file already exists
_ = os.Remove(oldPath)

// move the existing executable to a new file in the same directory
err = os.Rename(updatePath, oldPath)
if err != nil {
return
}

// move the new exectuable in to become the new program
err = os.Rename(newPath, updatePath)

if err != nil {
// copy unsuccessful
errRecover = os.Rename(oldPath, updatePath)
} else {
// copy successful, remove the old binary
errRemove := os.Remove(oldPath)

// windows has trouble with removing old binaries, so hide it instead
if errRemove != nil {
_ = hideFile(oldPath)
}
}

return
}

// fetchInfo fetches the update JSON manifest at u.ApiURL/appname/platform.json
// and updates u.Info.
func (u *Updater) fetchInfo() error {
Expand Down

0 comments on commit 4aec645

Please sign in to comment.