mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-19 05:57:39 +02:00
139 lines
4.4 KiB
Go
139 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"cyberstrike-ai/internal/config"
|
|
"cyberstrike-ai/internal/update"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// The same one-click update the console page drives, for the two cases a page cannot
|
|
// serve: the process is not running at all, and somebody is ssh'd in with a keyboard.
|
|
// Keeping it in the binary rather than only in a shell script means one implementation of
|
|
// "fetch, fast-forward, rebuild, swap" instead of two that can disagree. The update
|
|
// source is read from the same config.yaml field the page saves to, so the keyboard and
|
|
// the button can never update from different repositories.
|
|
func updateCommandIfNeeded(configPath string, check, apply, rollback bool) (bool, int) {
|
|
if !check && !apply && !rollback {
|
|
return false, 0
|
|
}
|
|
root := filepath.Dir(configPath)
|
|
repo, err := config.LoadUpdateRepo(configPath)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "读取更新源失败: %v\n", err)
|
|
return true, 1
|
|
}
|
|
opts := update.Options{Root: root, Repo: repo}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Minute)
|
|
defer cancel()
|
|
|
|
source := strings.TrimSpace(repo)
|
|
if source == "" {
|
|
source = update.DefaultRepoURL + "(官方仓库默认)"
|
|
}
|
|
fmt.Printf("安装目录:%s\n", root)
|
|
fmt.Printf("更新源:%s\n", source)
|
|
switch {
|
|
case check:
|
|
snap, err := update.Check(ctx, opts)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "检查更新失败: %v\n", err)
|
|
return true, 1
|
|
}
|
|
printSnapshot(snap)
|
|
if !snap.Installed || snap.CheckError != "" {
|
|
return true, 1
|
|
}
|
|
return true, 0
|
|
case apply:
|
|
res, err := update.Apply(ctx, opts, func(s update.Step) {
|
|
fmt.Printf("[%s] %s\n", s.Phase, s.Message)
|
|
})
|
|
if res != nil {
|
|
printResult(res)
|
|
}
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "更新未完成: %v\n", err)
|
|
return true, 1
|
|
}
|
|
fmt.Println("源码与二进制已就位;重启服务后新代码生效。")
|
|
return true, 0
|
|
case rollback:
|
|
res, err := update.Rollback(ctx, opts)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "回滚未完成: %v\n", err)
|
|
return true, 1
|
|
}
|
|
printResult(res)
|
|
return true, 0
|
|
}
|
|
return true, 0
|
|
}
|
|
|
|
func printSnapshot(s *update.Snapshot) {
|
|
if !s.Installed {
|
|
fmt.Printf("这个目录不是 git 工作树(%s),无法自动更新\n", s.Root)
|
|
return
|
|
}
|
|
target := s.TargetBranch
|
|
if target == "" {
|
|
target = "?"
|
|
}
|
|
fmt.Printf("本机分支 %s / 提交 %s (%s),更新源默认分支 %s\n", s.Branch, s.Commit, s.Subject, target)
|
|
if s.CheckError != "" {
|
|
fmt.Printf("检查失败:%s\n", s.CheckError)
|
|
return
|
|
}
|
|
fmt.Printf("更新源最新 %s (%s),落后 %d 个提交,本地领先 %d 个提交\n", s.RemoteCommit, s.RemoteSubject, s.Behind, s.Ahead)
|
|
for _, c := range s.Incoming {
|
|
fmt.Printf(" ○ %s %s\n", c.Commit, c.Subject)
|
|
}
|
|
if s.Behind > len(s.Incoming) {
|
|
fmt.Printf(" …(共 %d 个,只列出 %d 个)\n", s.Behind, len(s.Incoming))
|
|
}
|
|
if len(s.BlockingChanges) > 0 {
|
|
fmt.Printf("本机改过 %d 个源码文件,更新会拒绝执行:\n", len(s.BlockingChanges))
|
|
for _, c := range s.BlockingChanges {
|
|
fmt.Printf(" ! %s (%s)\n", c.Path, c.Status)
|
|
}
|
|
}
|
|
if !s.CanBuild {
|
|
fmt.Println("本机没有 Go 工具链:更新只搬源码,不会重编译")
|
|
}
|
|
if s.BuildPending {
|
|
fmt.Println("上一次更新留下了未完成的编译(源码已就位、二进制还是旧的):装好工具链后重跑更新即可补上")
|
|
}
|
|
if s.HasRollback {
|
|
fmt.Printf("可回滚到 %s\n", s.RollbackTo)
|
|
}
|
|
}
|
|
|
|
func printResult(r *update.Result) {
|
|
moved := r.Commits > 0 || r.ToCommit != r.FromCommit
|
|
if !moved && !r.BinaryBuilt {
|
|
fmt.Println("已经是最新,没有改动")
|
|
return
|
|
}
|
|
if moved {
|
|
fmt.Printf("%s → %s(%d 个提交,涉及 %d 个文件,用时 %s)\n", r.FromCommit, r.ToCommit, r.Commits, r.FilesTouched, r.Duration)
|
|
} else {
|
|
// commits == 0 but a binary was built: a retry that finished a build the previous
|
|
// attempt could not. Saying "已经是最新,没有改动" here would bury the one thing
|
|
// that did happen.
|
|
fmt.Printf("源码已经是最新(%s),本次补上了二进制\n", r.ToCommit)
|
|
}
|
|
if len(r.KeptContent) > 0 {
|
|
fmt.Printf("保留了 %d 个本机内容文件(roles/skills/tools 等,没有被上游覆盖):\n", len(r.KeptContent))
|
|
for _, p := range r.KeptContent {
|
|
fmt.Printf(" = %s\n", p)
|
|
}
|
|
}
|
|
if r.BinaryBuilt {
|
|
fmt.Printf("二进制已换新:%s(旧版本留在 %s 以便回滚)\n", r.BinaryPath, r.PrevBinary)
|
|
}
|
|
}
|