mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-18 13:41:42 +02:00
The repository's config.example.yaml ships the release version this code was cut from (upstream bumps it in the release commit: v1.7.21's config.example says v1.7.21), while the live config.yaml is operator data that no merge ever touches. So after an update the header badge - and the ?v= on every static asset - kept saying the version the installation was born with while the tree already ran newer code. An update, an adoption, or a rollback now keeps the two together: after the merge the update reads the version the new code carries (config.example.yaml) and writes it into the live config's version field - that one line, temp file plus atomic rename, everything else byte-for-byte preserved, and only when the value actually differs. The job log records the change (the page shows the step), and the state file records both numbers so a rollback puts the old version back with the old code; a number hand-edited after the update is the operator's and is left alone. The side effect worth naming: the version bump also changes the ?v= query on static assets, so a browser stops serving the pre-update JavaScript. Trees without a live config or without a shipped version are left untouched - a source checkout has nothing to sync, and nothing is ever guessed. internal/config gains the two focused helpers (FileVersion / WriteVersion, line level like the tarball upgrade path always did); tests pin the replace, the insert-when-absent, byte preservation, the mode, the malformed-value refusal, and the update/adopt/rollback/leave-alone behaviours end to end.
105 lines
3.5 KiB
Go
105 lines
3.5 KiB
Go
package update
|
|
|
|
import (
|
|
"cyberstrike-ai/internal/config"
|
|
"fmt"
|
|
"path/filepath"
|
|
)
|
|
|
|
// The release version travels with the code: config.example.yaml ships in the repository
|
|
// and upstream bumps its version field in the release commit, but the live config.yaml is
|
|
// the operator's file and no merge ever touches it. So without the steps below an updated
|
|
// installation keeps showing the version it was born with - the header badge and the
|
|
// static-asset cache buster would both say v-old while the tree runs v-new. The fix is
|
|
// deliberately small: read the version the new code carries, write it into the live
|
|
// config's version line (byte-for-byte otherwise untouched, atomic rename), and let a
|
|
// rollback put the old number back with the old code.
|
|
|
|
// liveConfigFile names the operator's configuration inside an install tree: the
|
|
// conventional config.yaml, then config.yml. Empty when neither exists (a source
|
|
// checkout, where there is nothing to sync).
|
|
func liveConfigFile(root string) string {
|
|
for _, name := range []string{"config.yaml", "config.yml"} {
|
|
path := filepath.Join(root, name)
|
|
if fileExists(path) {
|
|
return path
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// installVersion is the release version the code sitting in root carries, read from the
|
|
// shipped config.example.yaml; empty when the tree does not say.
|
|
func installVersion(root string) string {
|
|
version, err := config.FileVersion(filepath.Join(root, "config.example.yaml"))
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return version
|
|
}
|
|
|
|
// syncVersionToConfig writes the code's release version into the live configuration and
|
|
// returns (before, after, error) - both empty when there was nothing to do. A tree
|
|
// without a live config, or without a shipped version, is left alone; a current value
|
|
// that already matches is not rewritten.
|
|
func syncVersionToConfig(root string) (before, after string, err error) {
|
|
path := liveConfigFile(root)
|
|
if path == "" {
|
|
return "", "", nil
|
|
}
|
|
target := installVersion(root)
|
|
if target == "" {
|
|
return "", "", nil
|
|
}
|
|
current, err := config.FileVersion(path)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
if current == target {
|
|
return "", "", nil
|
|
}
|
|
changed, err := config.WriteVersion(path, target)
|
|
if err != nil {
|
|
return current, "", err
|
|
}
|
|
if !changed {
|
|
return "", "", nil
|
|
}
|
|
return current, target, nil
|
|
}
|
|
|
|
// versionStep renders the sync outcome as one progress line; no lines for "nothing to
|
|
// do", a warning when the write failed (the update itself still stands).
|
|
func versionStep(step func(string, string), before, after string, err error) {
|
|
if err != nil {
|
|
step("version", "版本号写入 config.yaml 失败(不影响本次更新):"+err.Error())
|
|
return
|
|
}
|
|
if after == "" {
|
|
return
|
|
}
|
|
if before == "" {
|
|
step("version", fmt.Sprintf("版本号随新代码写入 config.yaml:%s(重启后页头生效)", after))
|
|
return
|
|
}
|
|
step("version", fmt.Sprintf("版本号已随新代码更新:%s → %s(config.yaml,重启后页头生效)", before, after))
|
|
}
|
|
|
|
// restoreVersionAfterRollback puts the old number back with the old code - but only while
|
|
// the live config still carries exactly what the update wrote: a version someone edited by
|
|
// hand after the update belongs to them, not to the rollback.
|
|
func restoreVersionAfterRollback(root string, st State) {
|
|
if st.VersionAfter == "" {
|
|
return
|
|
}
|
|
path := liveConfigFile(root)
|
|
if path == "" {
|
|
return
|
|
}
|
|
current, err := config.FileVersion(path)
|
|
if err != nil || current != st.VersionAfter {
|
|
return
|
|
}
|
|
_, _ = config.WriteVersion(path, st.VersionBefore)
|
|
}
|