mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-05-05 03:23:49 +08:00
update self update
This commit is contained in:
parent
76d00a57ff
commit
079bd9d6ed
@ -57,4 +57,5 @@ nfpms:
|
|||||||
file_name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
|
file_name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
|
||||||
formats:
|
formats:
|
||||||
- deb
|
- deb
|
||||||
- rpm
|
- rpm
|
||||||
|
maintainer: Mrs4s
|
100
global/fs.go
100
global/fs.go
@ -1,28 +1,20 @@
|
|||||||
package global
|
package global
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/bzip2"
|
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kardianos/osext"
|
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -160,95 +152,3 @@ func ReadAddrFile(path string) []*net.TCPAddr {
|
|||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteCounter 写入量计算实例
|
|
||||||
type WriteCounter struct {
|
|
||||||
Total uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write 方法将写入的byte长度追加至写入的总长度Total中
|
|
||||||
func (wc *WriteCounter) Write(p []byte) (int, error) {
|
|
||||||
n := len(p)
|
|
||||||
wc.Total += uint64(n)
|
|
||||||
wc.PrintProgress()
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintProgress 方法将打印当前的总写入量
|
|
||||||
func (wc *WriteCounter) PrintProgress() {
|
|
||||||
fmt.Printf("\r%s", strings.Repeat(" ", 35))
|
|
||||||
fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateFromStream copy form getlantern/go-update
|
|
||||||
func UpdateFromStream(updateWith io.Reader) (err error, errRecover error) {
|
|
||||||
updatePath, err := osext.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var newBytes []byte
|
|
||||||
// no patch to apply, go on through
|
|
||||||
var fileHeader []byte
|
|
||||||
bufBytes := bufio.NewReader(updateWith)
|
|
||||||
fileHeader, err = bufBytes.Peek(2)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// The content is always bzip2 compressed except when running test, in
|
|
||||||
// which case is not prefixed with the magic byte sequence for sure.
|
|
||||||
if bytes.Equal([]byte{0x42, 0x5a}, fileHeader) {
|
|
||||||
// Identifying bzip2 files.
|
|
||||||
updateWith = bzip2.NewReader(bufBytes)
|
|
||||||
} else {
|
|
||||||
updateWith = io.Reader(bufBytes)
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
// We won't log this error, because it's always going to happen.
|
|
||||||
defer func() { _ = fp.Close() }()
|
|
||||||
if _, err = io.Copy(fp, bytes.NewReader(newBytes)); err != nil {
|
|
||||||
log.Errorf("Unable to copy data: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we don't call fp.Close(), windows won't let us move the new executable
|
|
||||||
// because the file will still be "in use"
|
|
||||||
if err := fp.Close(); err != nil {
|
|
||||||
log.Errorf("Unable to close file: %v\n", err)
|
|
||||||
}
|
|
||||||
// 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 executable 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
|
|
||||||
_ = os.Remove(oldPath)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
96
global/update/update.go
Normal file
96
global/update/update.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package update
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
|
"github.com/kardianos/osext"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WriteCounter 写入量计算实例
|
||||||
|
type WriteCounter struct {
|
||||||
|
Total uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write 方法将写入的byte长度追加至写入的总长度Total中
|
||||||
|
func (wc *WriteCounter) Write(p []byte) (int, error) {
|
||||||
|
n := len(p)
|
||||||
|
wc.Total += uint64(n)
|
||||||
|
wc.PrintProgress()
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintProgress 方法将打印当前的总写入量
|
||||||
|
func (wc *WriteCounter) PrintProgress() {
|
||||||
|
fmt.Printf("\r%s", strings.Repeat(" ", 35))
|
||||||
|
fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFromStream copy form getlantern/go-update
|
||||||
|
func UpdateFromStream(updateWith io.Reader) (err error, errRecover error) {
|
||||||
|
updatePath, err := osext.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var newBytes []byte
|
||||||
|
// no patch to apply, go on through
|
||||||
|
bufBytes := bufio.NewReader(updateWith)
|
||||||
|
updateWith = io.Reader(bufBytes)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// We won't log this error, because it's always going to happen.
|
||||||
|
defer func() { _ = fp.Close() }()
|
||||||
|
if _, err = io.Copy(fp, bytes.NewReader(newBytes)); err != nil {
|
||||||
|
log.Errorf("Unable to copy data: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we don't call fp.Close(), windows won't let us move the new executable
|
||||||
|
// because the file will still be "in use"
|
||||||
|
if err := fp.Close(); err != nil {
|
||||||
|
log.Errorf("Unable to close file: %v\n", err)
|
||||||
|
}
|
||||||
|
// 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 executable 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
|
||||||
|
_ = os.Remove(oldPath)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
50
global/update/update_others.go
Normal file
50
global/update/update_others.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package update
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Update(url string) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("更新失败: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
wc := WriteCounter{}
|
||||||
|
data, err := io.ReadAll(io.TeeReader(resp.Body, &wc))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("更新失败: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gr, err := gzip.NewReader(bytes.NewReader(data))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("更新失败: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tr := tar.NewReader(gr)
|
||||||
|
for {
|
||||||
|
header, err := tr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if header.Name == "go-cqhttp" {
|
||||||
|
err, _ := UpdateFromStream(tr)
|
||||||
|
fmt.Println()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("更新失败!", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("更新完成!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
global/update/update_windows.go
Normal file
35
global/update/update_windows.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package update
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Update(url string) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("更新失败: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
wc := WriteCounter{}
|
||||||
|
rsp, _ := io.ReadAll(io.TeeReader(resp.Body, &wc))
|
||||||
|
reader, _ := zip.NewReader(bytes.NewReader(rsp), resp.ContentLength)
|
||||||
|
file, err := reader.Open("go-cqhttp.exe")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("更新失败!", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err, _ = UpdateFromStream(file)
|
||||||
|
fmt.Println()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("更新失败!", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("更新完成!")
|
||||||
|
}
|
38
main.go
38
main.go
@ -9,9 +9,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
@ -25,6 +23,7 @@ import (
|
|||||||
"github.com/Mrs4s/go-cqhttp/coolq"
|
"github.com/Mrs4s/go-cqhttp/coolq"
|
||||||
"github.com/Mrs4s/go-cqhttp/global"
|
"github.com/Mrs4s/go-cqhttp/global"
|
||||||
"github.com/Mrs4s/go-cqhttp/global/terminal"
|
"github.com/Mrs4s/go-cqhttp/global/terminal"
|
||||||
|
"github.com/Mrs4s/go-cqhttp/global/update"
|
||||||
"github.com/Mrs4s/go-cqhttp/server"
|
"github.com/Mrs4s/go-cqhttp/server"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/binary"
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
@ -488,8 +487,9 @@ func selfUpdate(imageURL string) {
|
|||||||
log.Info("当前最新版本为 ", version)
|
log.Info("当前最新版本为 ", version)
|
||||||
log.Warn("是否更新(y/N): ")
|
log.Warn("是否更新(y/N): ")
|
||||||
r := strings.TrimSpace(readLine())
|
r := strings.TrimSpace(readLine())
|
||||||
|
if r != "y" && r != "Y" {
|
||||||
doUpdate := func() {
|
log.Warn("已取消更新!")
|
||||||
|
} else {
|
||||||
log.Info("正在更新,请稍等...")
|
log.Info("正在更新,请稍等...")
|
||||||
url := fmt.Sprintf(
|
url := fmt.Sprintf(
|
||||||
"%v/Mrs4s/go-cqhttp/releases/download/%v/go-cqhttp-%v-%v-%v",
|
"%v/Mrs4s/go-cqhttp/releases/download/%v/go-cqhttp-%v-%v-%v",
|
||||||
@ -499,34 +499,14 @@ func selfUpdate(imageURL string) {
|
|||||||
}
|
}
|
||||||
return "https://github.com"
|
return "https://github.com"
|
||||||
}(),
|
}(),
|
||||||
version,
|
version, version, runtime.GOOS, runtime.GOARCH,
|
||||||
version,
|
|
||||||
runtime.GOOS,
|
|
||||||
runtime.GOARCH,
|
|
||||||
)
|
)
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
url += ".exe"
|
url += ".zip"
|
||||||
|
} else {
|
||||||
|
url += ".tar.gz"
|
||||||
}
|
}
|
||||||
resp, err := http.Get(url)
|
update.Update(url)
|
||||||
if err != nil {
|
|
||||||
log.Error("更新失败: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() { _ = resp.Body.Close() }()
|
|
||||||
wc := global.WriteCounter{}
|
|
||||||
err, _ = global.UpdateFromStream(io.TeeReader(resp.Body, &wc))
|
|
||||||
fmt.Println()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("更新失败!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("更新完成!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == "y" || r == "Y" {
|
|
||||||
doUpdate()
|
|
||||||
} else {
|
|
||||||
log.Warn("已取消更新!")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Info("当前版本已经是最新版本!")
|
log.Info("当前版本已经是最新版本!")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user