Commit d329a542 authored by wei.xuan's avatar wei.xuan

feat:support self update

parent 7a35afd0
......@@ -28,7 +28,7 @@ cc_win := /home/xuan/musl-cross/i686-w64-mingw32/bin/i686-w64-mingw32-gcc
cxx_win := /home/xuan/musl-cross/i686-w64-mingw32/bin/i686-w64-mingw32-g++
buildlocal:
CGO_ENABLE=0 go build -trimpath -ldflags '-w -s' -o majora-cli
CGO_ENABLE=0 go build -trimpath -ldflags "-w -s -X main.Version=v0.0.1" -o majora-cli
releaselocal:
goreleaser build --rm-dist --snapshot --single-target
......
......@@ -14,6 +14,7 @@ import (
"virjar.com/majora-go/client"
"virjar.com/majora-go/common"
"virjar.com/majora-go/logger"
"virjar.com/majora-go/updater"
)
var (
......@@ -24,10 +25,11 @@ var (
dnsServer string
disableDNS bool
localAddr string
disableUpdate bool
)
var (
version = "dev"
Version string
date = "unknown"
)
......@@ -40,6 +42,7 @@ func init() {
flag.StringVar(&dnsServer, "dnsServer", common.DNSServer, "custom dns server")
flag.BoolVar(&disableDNS, "disableDns", false, "disable default dns server")
flag.StringVar(&localAddr, "localIp", "", "bind local ip")
flag.BoolVar(&disableUpdate, "disableUpdate", false, "disable self update")
flag.Parse()
}
......@@ -65,10 +68,23 @@ func initPprof() {
}
}
func update() {
logger.Debug().Msgf("check update %v ...", disableUpdate)
if disableUpdate {
return
}
if _, err := updater.Update("majora", Version); err != nil {
logger.Error().Msgf("check update error %+s", err.Error())
}
}
func main() {
initPprof()
logger.SetLogLevel(logLevel)
logger.Info().Msgf("current version %s, build at %s", version, date)
logger.Info().Msgf("current Version %s, build at %s", Version, date)
update()
logger.Info().Msgf("hostinfo os:%s, arch:%s", runtime.GOOS, runtime.GOARCH)
client.NewClient(client.WithNatServerAddr(natServer),
client.WithLocalIP(localAddr),
......
package common
const (
DNSServer = "114.114.114.114:53"
)
package common
const (
DNSServer = "114.114.114.114:53"
)
const (
UpdateServer = "http://172.16.28.131:8888"
Latest = "latest.txt"
UpdateBinaryPath = "/version/"
// VersionTpl majora-v0.0.1-linux-arm64 name-version-os-arch
VersionTpl = "%s-%s-%s-%s"
)
const (
DefaultMode = 0755
)
......@@ -3,6 +3,7 @@ module virjar.com/majora-go
go 1.17
require (
github.com/blang/semver/v4 v4.0.0
github.com/google/uuid v1.3.0
github.com/rs/zerolog v1.25.0
)
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
......
File added
package updater
import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"runtime"
"strings"
"github.com/blang/semver/v4"
"virjar.com/majora-go/common"
"virjar.com/majora-go/logger"
)
// getBinary 下载二进制
func getBinary(name, os, arch, version string) (io.ReadCloser, error) {
fileName := fmt.Sprintf(common.VersionTpl, name, version, os, arch)
if os == "windows" {
fileName += ".exe"
}
url := common.UpdateServer + common.UpdateBinaryPath + fileName
logger.Debug().Msgf("getBinary url %s, filename %s", url, fileName)
body, err := http.DefaultClient.Get(url)
if err != nil {
return nil, err
}
return body.Body, nil
}
func downloadFile(name, os, arch, version string, f *os.File) error {
binary, err := getBinary(name, os, arch, version)
if err != nil {
return err
}
defer func(binary io.ReadCloser) {
_ = binary.Close()
}(binary)
body, err := ioutil.ReadAll(binary)
if err != nil {
return err
}
cnt, err := f.Write(body)
if err != nil {
return err
}
if cnt != len(body) {
return errors.New("write to file is not enough")
}
return nil
}
func rename(src, dst string) error {
if err := os.Chmod(src, common.DefaultMode); err != nil {
return err
}
if err := os.Rename(src, dst); err != nil {
return err
}
logger.Debug().Msgf("rename %s to %s", src, dst)
return nil
}
func restart() {
cmd := exec.Command(os.Args[0], os.Args[1:]...) //nolint:gosec
cmd.Env = os.Environ()
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
logger.Debug().Msgf("restart ... %+v", cmd)
if err := cmd.Run(); err != nil {
os.Exit(1)
}
os.Exit(0)
}
func UpdateCore(name, latestVer, targetFile string) (bool, error) {
tf, err := ioutil.TempFile("", "")
if err != nil {
logger.Debug().Msgf("UpdateCore TempFile with error %s", err.Error())
return false, err
}
if err := downloadFile(name, runtime.GOOS, runtime.GOARCH, latestVer, tf); err != nil {
logger.Debug().Msgf("UpdateCore downloadFile with error %s", err.Error())
return false, err
}
if err := tf.Close(); err != nil {
logger.Debug().Msgf("UpdateCore tf close with error %s", err.Error())
return false, err
}
if err := rename(tf.Name(), targetFile); err != nil {
return false, err
}
restart()
return true, nil
}
func getLatestVersion() (string, error) {
url := common.UpdateServer + common.UpdateBinaryPath + common.Latest
logger.Debug().Msgf("getLatestVersion from %s", url)
resp, err := http.DefaultClient.Get(url)
if err != nil || resp == nil {
return "", err
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
if resp.StatusCode != http.StatusOK {
logger.Error().Msgf("get latest version with error %s->%d", url, resp.StatusCode)
return "", nil
}
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return strings.ReplaceAll(string(bytes), "\n", ""), nil
}
func needUpdate(curVer string) (bool, string) {
latestVer, err := getLatestVersion()
if err != nil {
logger.Error().Msgf("getLatestVersion with error %s", err.Error())
return false, ""
}
if curVer[0] == 'v' || curVer[0] == 'V' {
curVer = curVer[1:]
}
latest := latestVer
if latestVer[0] == 'v' || latestVer[0] == 'V' {
latest = latest[1:]
}
cur, _ := semver.Make(curVer)
next, _ := semver.Make(latest)
logger.Debug().Msgf("curVer %s->cur %s, latestVer %s->next %s", curVer, cur, latest, next)
return cur.Compare(next) < 0, latestVer
}
// Update update to the latest version
func Update(name, curVer string) (bool, error) {
// get current exec path
executable, err := os.Executable()
if err != nil {
return false, err
}
logger.Debug().Msgf("current exec info %s", executable)
// check has new version
needUpdate, latestVer := needUpdate(curVer)
logger.Debug().Msgf("curVer %s, latestVer %s, need update %v", curVer, latestVer, needUpdate)
if !needUpdate {
logger.Debug().Msgf("no need update ...")
return false, nil
}
updateStatus, err := UpdateCore(name, latestVer, executable)
if err != nil {
logger.Error().Msgf("update to latest version with error %s", err)
return false, err
}
logger.Info().Msgf("update from %s to %s with success %v", curVer, latestVer, updateStatus)
return updateStatus, nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment