chore: better upgrade

This commit is contained in:
Larvan2 2023-04-02 15:16:42 +08:00
parent 526ac3906d
commit affc453b6e
3 changed files with 47 additions and 32 deletions

View file

@ -38,25 +38,29 @@ func restart(w http.ResponseWriter, r *http.Request) {
// The background context is used because the underlying functions wrap it // The background context is used because the underlying functions wrap it
// with timeout and shut down the server, which handles current request. It // with timeout and shut down the server, which handles current request. It
// also should be done in a separate goroutine for the same reason. // also should be done in a separate goroutine for the same reason.
go func() { go runRestart(execPath)
if runtime.GOOS == "windows" { }
cmd := exec.Command(execPath, os.Args[1:]...)
log.Infoln("restarting: %q %q", execPath, os.Args[1:])
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
log.Fatalln("restarting: %s", err)
}
os.Exit(0) func runRestart(execPath string) {
} var err error
if runtime.GOOS == "windows" {
cmd := exec.Command(execPath, os.Args[1:]...)
log.Infoln("restarting: %q %q", execPath, os.Args[1:]) log.Infoln("restarting: %q %q", execPath, os.Args[1:])
err = syscall.Exec(execPath, os.Args, os.Environ()) cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil { if err != nil {
log.Fatalln("restarting: %s", err) log.Fatalln("restarting: %s", err)
} }
}()
os.Exit(0)
}
log.Infoln("restarting: %q %q", execPath, os.Args[1:])
err = syscall.Exec(execPath, os.Args, os.Environ())
if err != nil {
log.Fatalln("restarting: %s", err)
}
} }

View file

@ -1,12 +1,15 @@
package route package route
import ( import (
"fmt"
"net/http" "net/http"
"os"
"github.com/Dreamacro/clash/hub/updater" "github.com/Dreamacro/clash/hub/updater"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/render"
) )
func upgradeRouter() http.Handler { func upgradeRouter() http.Handler {
@ -18,11 +21,24 @@ func upgradeRouter() http.Handler {
func upgrade(w http.ResponseWriter, r *http.Request) { func upgrade(w http.ResponseWriter, r *http.Request) {
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108 // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108
log.Infoln("start update") log.Infoln("start update")
err := updater.Update() execPath, err := os.Executable()
if err != nil { if err != nil {
log.Errorln("err:%s", err) render.Status(r, http.StatusInternalServerError)
render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err)))
return return
} }
restart(w, r) err = updater.Update(execPath)
if err != nil {
render.Status(r, http.StatusInternalServerError)
render.JSON(w, r, newError(fmt.Sprintf("Upgrade: %s", err)))
return
}
render.JSON(w, r, render.M{"status": "ok"})
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
go runRestart(execPath)
} }

View file

@ -50,12 +50,12 @@ type updateError struct {
} }
func (e *updateError) Error() string { func (e *updateError) Error() string {
return fmt.Sprintf("error: %s", e.Message) return fmt.Sprintf("update error: %s", e.Message)
} }
// Update performs the auto-updater. It returns an error if the updater failed. // Update performs the auto-updater. It returns an error if the updater failed.
// If firstRun is true, it assumes the configuration file doesn't exist. // If firstRun is true, it assumes the configuration file doesn't exist.
func Update() (err error) { func Update(execPath string) (err error) {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
@ -63,7 +63,6 @@ func Update() (err error) {
goarch = runtime.GOARCH goarch = runtime.GOARCH
latestVersion, err = getLatestVersion() latestVersion, err = getLatestVersion()
if err != nil { if err != nil {
err := &updateError{Message: err.Error()}
return err return err
} }
@ -84,11 +83,6 @@ func Update() (err error) {
} }
}() }()
execPath, err := os.Executable()
if err != nil {
return fmt.Errorf("getting executable path: %w", err)
}
workDir = filepath.Dir(execPath) workDir = filepath.Dir(execPath)
err = prepare(execPath) err = prepare(execPath)
@ -110,7 +104,7 @@ func Update() (err error) {
err = backup() err = backup()
if err != nil { if err != nil {
return fmt.Errorf("replacing: %w", err) return fmt.Errorf("backuping: %w", err)
} }
err = replace() err = replace()
@ -186,9 +180,9 @@ func unpack() error {
return nil return nil
} }
// backup makes a backup of the current configuration and supporting files. // backup makes a backup of the current executable file
func backup() (err error) { func backup() (err error) {
log.Infoln("updater: backing up current ExecFile:%s", currentExeName) log.Infoln("updater: backing up current ExecFile:%s to %s", currentExeName, backupExeName)
_ = os.Mkdir(backupDir, 0o755) _ = os.Mkdir(backupDir, 0o755)
err = os.Rename(currentExeName, backupExeName) err = os.Rename(currentExeName, backupExeName)
@ -199,12 +193,11 @@ func backup() (err error) {
return nil return nil
} }
// replace moves the current executable with the updated one and also copies the // replace moves the current executable with the updated one
// supporting files.
func replace() error { func replace() error {
var err error var err error
log.Infoln("copying: %s to %s", updateExeName, currentExeName) log.Infoln("replacing: %s to %s", updateExeName, currentExeName)
if goos == "windows" { if goos == "windows" {
// rename fails with "File in use" error // rename fails with "File in use" error
err = copyFile(updateExeName, currentExeName) err = copyFile(updateExeName, currentExeName)
@ -214,7 +207,9 @@ func replace() error {
if err != nil { if err != nil {
return err return err
} }
log.Infoln("updater: renamed: %s to %s", updateExeName, currentExeName) log.Infoln("updater: renamed: %s to %s", updateExeName, currentExeName)
return nil return nil
} }