mirror of
https://github.com/ayflying/p2p.git
synced 2026-03-05 01:39:23 +00:00
增加更新重启逻辑
This commit is contained in:
25
build/update/hack/config.yaml
Normal file
25
build/update/hack/config.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
# CLI tool, only in development environment.
|
||||
# https://goframe.org/docs/cli
|
||||
gfcli:
|
||||
build:
|
||||
name: "update"
|
||||
arch: "amd64"
|
||||
system: "windows"
|
||||
mod: "none"
|
||||
# packSrc: "resource,manifest"
|
||||
version: "v1.0.0"
|
||||
# output: "./bin/"
|
||||
# extra: -trimpath -ldflags="-s -w -H=windowsgui"
|
||||
extra: -trimpath -ldflags="-s -w"
|
||||
# cgo: true
|
||||
dumpEnv: true
|
||||
gen:
|
||||
dao:
|
||||
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
descriptionTag: true
|
||||
|
||||
docker:
|
||||
build: "-a amd64 -s linux -p temp -ew"
|
||||
tagPrefixes:
|
||||
- my.image.pub/my-app
|
||||
20
build/update/hack/hack-cli.mk
Normal file
20
build/update/hack/hack-cli.mk
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
# Install/Update to the latest CLI tool.
|
||||
.PHONY: cli
|
||||
cli:
|
||||
@set -e; \
|
||||
wget -O gf \
|
||||
https://github.com/gogf/gf/releases/latest/download/gf_$(shell go env GOOS)_$(shell go env GOARCH) && \
|
||||
chmod +x gf && \
|
||||
./gf install -y && \
|
||||
rm ./gf
|
||||
|
||||
|
||||
# Check and install CLI tool.
|
||||
.PHONY: cli.install
|
||||
cli.install:
|
||||
@set -e; \
|
||||
gf -v > /dev/null 2>&1 || if [[ "$?" -ne "0" ]]; then \
|
||||
echo "GoFame CLI is not installed, start proceeding auto installation..."; \
|
||||
make cli; \
|
||||
fi;
|
||||
75
build/update/hack/hack.mk
Normal file
75
build/update/hack/hack.mk
Normal file
@@ -0,0 +1,75 @@
|
||||
.DEFAULT_GOAL := build
|
||||
|
||||
# Update GoFrame and its CLI to latest stable version.
|
||||
.PHONY: up
|
||||
up: cli.install
|
||||
@gf up -a
|
||||
|
||||
# Build binary using configuration from hack/config.yaml.
|
||||
.PHONY: build
|
||||
build: cli.install
|
||||
@gf build -ew
|
||||
|
||||
# Parse api and generate controller/sdk.
|
||||
.PHONY: ctrl
|
||||
ctrl: cli.install
|
||||
@gf gen ctrl
|
||||
|
||||
# Generate Go files for DAO/DO/Entity.
|
||||
.PHONY: dao
|
||||
dao: cli.install
|
||||
@gf gen dao
|
||||
|
||||
# Parse current project go files and generate enums go file.
|
||||
.PHONY: enums
|
||||
enums: cli.install
|
||||
@gf gen enums
|
||||
|
||||
# Generate Go files for Service.
|
||||
.PHONY: service
|
||||
service: cli.install
|
||||
@gf gen service
|
||||
|
||||
|
||||
# Build docker image.
|
||||
.PHONY: image
|
||||
image: cli.install
|
||||
$(eval _TAG = $(shell git rev-parse --short HEAD))
|
||||
ifneq (, $(shell git status --porcelain 2>/dev/null))
|
||||
$(eval _TAG = $(_TAG).dirty)
|
||||
endif
|
||||
$(eval _TAG = $(if ${TAG}, ${TAG}, $(_TAG)))
|
||||
$(eval _PUSH = $(if ${PUSH}, ${PUSH}, ))
|
||||
@gf docker ${_PUSH} -tn $(DOCKER_NAME):${_TAG};
|
||||
|
||||
|
||||
# Build docker image and automatically push to docker repo.
|
||||
.PHONY: image.push
|
||||
image.push: cli.install
|
||||
@make image PUSH=-p;
|
||||
|
||||
|
||||
# Deploy image and yaml to current kubectl environment.
|
||||
.PHONY: deploy
|
||||
deploy: cli.install
|
||||
$(eval _TAG = $(if ${TAG}, ${TAG}, develop))
|
||||
|
||||
@set -e; \
|
||||
mkdir -p $(ROOT_DIR)/temp/kustomize;\
|
||||
cd $(ROOT_DIR)/manifest/deploy/kustomize/overlays/${_ENV};\
|
||||
kustomize build > $(ROOT_DIR)/temp/kustomize.yaml;\
|
||||
kubectl apply -f $(ROOT_DIR)/temp/kustomize.yaml; \
|
||||
if [ $(DEPLOY_NAME) != "" ]; then \
|
||||
kubectl patch -n $(NAMESPACE) deployment/$(DEPLOY_NAME) -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"$(shell date +%s)\"}}}}}"; \
|
||||
fi;
|
||||
|
||||
|
||||
# Parsing protobuf files and generating go files.
|
||||
.PHONY: pb
|
||||
pb: cli.install
|
||||
@gf gen pb
|
||||
|
||||
# Generate protobuf files for database tables.
|
||||
.PHONY: pbentity
|
||||
pbentity: cli.install
|
||||
@gf gen pbentity
|
||||
183
build/update/main.go
Normal file
183
build/update/main.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
updateServer = "https://your-server.com/update"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 3 {
|
||||
fmt.Println("用法: updater <主程序路径> <当前版本> <更新服务器URL>")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
mainExePath := os.Args[1]
|
||||
currentVersion := os.Args[2]
|
||||
updateServer = os.Args[3]
|
||||
|
||||
fmt.Println("开始更新程序...")
|
||||
|
||||
// 1. 等待主程序完全退出
|
||||
if err := waitForMainExit(mainExePath); err != nil {
|
||||
fmt.Printf("等待主程序退出失败: %v\n", err)
|
||||
pauseAndExit()
|
||||
}
|
||||
|
||||
// 2. 获取最新版本信息
|
||||
updateInfo, err := getLatestVersionInfo(currentVersion)
|
||||
if err != nil {
|
||||
fmt.Printf("获取更新信息失败: %v\n", err)
|
||||
pauseAndExit()
|
||||
}
|
||||
|
||||
// 3. 下载新版本
|
||||
newExePath, err := downloadNewVersion(updateInfo.DownloadURL)
|
||||
if err != nil {
|
||||
fmt.Printf("下载更新失败: %v\n", err)
|
||||
pauseAndExit()
|
||||
}
|
||||
|
||||
// 4. 替换主程序
|
||||
if err := replaceMainExe(mainExePath, newExePath); err != nil {
|
||||
fmt.Printf("替换程序失败: %v\n", err)
|
||||
pauseAndExit()
|
||||
}
|
||||
|
||||
// 5. 重启主程序
|
||||
if err := restartMainProgram(mainExePath); err != nil {
|
||||
fmt.Printf("重启程序失败: %v\n", err)
|
||||
pauseAndExit()
|
||||
}
|
||||
|
||||
fmt.Println("更新完成")
|
||||
}
|
||||
|
||||
// 等待主程序退出
|
||||
func waitForMainExit(mainPath string) error {
|
||||
// 获取主程序文件名
|
||||
mainName := filepath.Base(mainPath)
|
||||
|
||||
for {
|
||||
// 检查是否还有同名进程在运行
|
||||
if !isProcessRunning(mainName) {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// 检查进程是否在运行
|
||||
func isProcessRunning(name string) bool {
|
||||
// Windows下通过tasklist命令检查进程
|
||||
cmd := exec.Command("tasklist", "/FI", fmt.Sprintf("IMAGENAME eq %s", name))
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 获取最新版本信息
|
||||
func getLatestVersionInfo(currentVersion string) (UpdateInfo, error) {
|
||||
var info UpdateInfo
|
||||
resp, err := http.Get(fmt.Sprintf("%s/latest?current=%s", updateServer, currentVersion))
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// 下载新版本
|
||||
func downloadNewVersion(url string) (string, error) {
|
||||
tempFile := filepath.Join(os.TempDir(), "main_new.exe")
|
||||
out, err := os.Create(tempFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 下载并计算哈希值
|
||||
hash := sha256.New()
|
||||
writer := io.MultiWriter(out, hash)
|
||||
|
||||
if _, err := io.Copy(writer, resp.Body); err != nil {
|
||||
os.Remove(tempFile)
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 这里可以添加哈希校验逻辑
|
||||
|
||||
return tempFile, nil
|
||||
}
|
||||
|
||||
// 替换主程序文件
|
||||
func replaceMainExe(oldPath, newPath string) error {
|
||||
// 备份旧版本
|
||||
backupPath := oldPath + ".bak"
|
||||
if err := os.Remove(backupPath); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Rename(oldPath, backupPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 移动新版本到目标路径
|
||||
if err := os.Rename(newPath, oldPath); err != nil {
|
||||
// 替换失败,恢复备份
|
||||
os.Rename(backupPath, oldPath)
|
||||
return err
|
||||
}
|
||||
|
||||
// 替换成功,删除备份
|
||||
os.Remove(backupPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 重启主程序
|
||||
func restartMainProgram(mainPath string) error {
|
||||
cmd := exec.Command(mainPath)
|
||||
// 在后台启动主程序
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: false}
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
// 暂停并退出(给用户看错误信息)
|
||||
func pauseAndExit() {
|
||||
fmt.Println("按任意键退出...")
|
||||
var input string
|
||||
fmt.Scanln(&input)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 更新信息结构体
|
||||
type UpdateInfo struct {
|
||||
Version string `json:"version"`
|
||||
DownloadURL string `json:"download_url"`
|
||||
SHA256 string `json:"sha256"`
|
||||
}
|
||||
Reference in New Issue
Block a user