docs: explain update locking and interrupted rollback recovery

This commit is contained in:
Sycun committed 2026-10-09 11:10:24 +08:00
1 parent 98de085912
commit a63070dbd7
4 files changed
+24 -8

No files matched your search

+2 -2
View File
@@ -99,7 +99,7 @@ mirror are all just this one setting; empty means the official repository. An in
from a Release archive (no `.git`) can be connected from the same page (no configuration needed - the from a Release archive (no `.git`) can be connected from the same page (no configuration needed - the
default official repository applies): the preview lists the files the target would replace and the default official repository applies): the preview lists the files the target would replace and the
operator content that is kept, replaced files are backed up under operator content that is kept, replaced files are backed up under
`.update-backup/<timestamp>/overwritten/`, and confirming turns the directory into a normal `.update-backup/<unique-backup>/overwritten/`, and confirming turns the directory into a normal
installation with one-click updates. installation with one-click updates.
`upgrade.sh` still works and is now a thin shell over that implementation, with two paths decided by `upgrade.sh` still works and is now a thin shell over that implementation, with two paths decided by
@@ -136,7 +136,7 @@ quick upgrades without compatibility risk; in production still back up first. Py
The one-click update leaves its own rollback point: `./cyberstrike-ai -update-rollback`, or the button The one-click update leaves its own rollback point: `./cyberstrike-ai -update-rollback`, or the button
on the page, goes back to the commit before that update and puts `cyberstrike-ai.prev` back. It runs on the page, goes back to the commit before that update and puts `cyberstrike-ai.prev` back. It runs
only while HEAD is still the commit that update wrote, so work done afterwards is not discarded. only while HEAD is still the commit that update wrote, so work done afterwards is not discarded.
Content that was put aside also survives under `.update-backup/<timestamp>/`. Content that was put aside also survives under `.update-backup/<unique-backup>/`.
Beyond that, roll back together: Beyond that, roll back together:
+11 -2
View File
@@ -86,7 +86,7 @@ An installation that started as an unpacked Release archive has no `.git`; it ca
the same page (the default official repository works without any configuration): the preview fetches in a throwaway repository and first lists the same page (the default official repository works without any configuration): the preview fetches in a throwaway repository and first lists
**the local files the target would replace** and **the operator content that will be kept**; on **the local files the target would replace** and **the operator content that will be kept**; on
confirm the directory becomes a git work tree (`git init`, origin added, the target branch checked confirm the directory becomes a git work tree (`git init`, origin added, the target branch checked
out), every replaced file is kept under `.update-backup/<timestamp>/overwritten/`, operator content out), every replaced file is kept under `.update-backup/<unique-backup>/overwritten/`, operator content
is stashed and restored as usual, and the binary is rebuilt. From then on it is a normal is stashed and restored as usual, and the binary is rebuilt. From then on it is a normal
installation with one-click update and rollback - but note there is no git history before the installation with one-click update and rollback - but note there is no git history before the
connection, so the connection itself has no earlier commit to roll back to; the first real update connection, so the connection itself has no earlier commit to roll back to; the first real update
@@ -119,11 +119,15 @@ else -> 400). Other failures (`merge_failed`, `build_failed`, `swap_failed`) car
compiler's own output; a failed build leaves the previous binary in place while the source has already compiler's own output; a failed build leaves the previous binary in place while the source has already
moved and the state file is written, so it stays rollback-able. moved and the state file is written, so it stays rollback-able.
Check, update, adoption, rollback and restart share an operating-system file lock in the install
directory (`.update-lock`). Wait for the other operation when `update_busy` is reported. The system
releases the lock when its process exits; do not delete the lock file to bypass it.
### How operator content survives ### How operator content survives
`roles/ skills/ tools/ agents/ knowledge_base/ data/ log/ venv/ config.yaml .env` belong to the `roles/ skills/ tools/ agents/ knowledge_base/ data/ log/ venv/ config.yaml .env` belong to the
**operator** (`update.Protected` is the judge). Before the merge, only those of those paths the **operator** (`update.Protected` is the judge). Before the merge, only those of those paths the
update actually writes to are copied into `.update-backup/<timestamp>/` and removed from the tree - update actually writes to are copied into `.update-backup/<unique-backup>/` and removed from the tree -
your extra directories that upstream never touched are not disturbed - and they are put back your extra directories that upstream never touched are not disturbed - and they are put back
afterwards, which is what "update the code, keep my work" means. The result names every file in afterwards, which is what "update the code, keep my work" means. The result names every file in
`keptContent` instead of quietly dropping it: where both you and upstream changed the same file, the `keptContent` instead of quietly dropping it: where both you and upstream changed the same file, the
@@ -141,6 +145,11 @@ not the one the record pairs with that commit (`binary_changed`), **HEAD has mov
(`moved_since_update` - a rollback undoes the update, not the work done after it), or source is (`moved_since_update` - a rollback undoes the update, not the work done after it), or source is
modified locally (`local_source_edits`). modified locally (`local_source_edits`).
Rollback writes `.update-rollback-pending.json` before changing source or binaries. Run rollback
again after an interruption to finish recovery; the console reports `rollbackPending` and pauses new
updates. Keep the recovery record, backup directory and `.prev` file. If recorded binary hashes do not
match the files on disk, recovery refuses to overwrite them.
### The two restart cases ### The two restart cases
The process only stands down (graceful `Shutdown`, then exit 0) when the request explicitly says The process only stands down (graceful `Shutdown`, then exit 0) when the request explicitly says
+2 -2
View File
@@ -196,7 +196,7 @@ SQLite 热备份时最好先停止服务,或至少复制 `*.db`、`*.db-wal`
更新源是 `config.yaml` `update` 段里的**一个仓库地址**(`repo`),也可以直接在控制台「一键更新」页填写保存: 更新源是 `config.yaml` `update` 段里的**一个仓库地址**(`repo`),也可以直接在控制台「一键更新」页填写保存:
想跟随官方仓库、自己的二开、别人的二开或镜像,改的都是这一处;不写就是官方仓库。**用 Release 包解压安装、 想跟随官方仓库、自己的二开、别人的二开或镜像,改的都是这一处;不写就是官方仓库。**用 Release 包解压安装、
目录里没有 git 的**,在同一页点「预览并接入」(不配置也能用默认的官方仓库):预览先列出会被目标版本替换的 目录里没有 git 的**,在同一页点「预览并接入」(不配置也能用默认的官方仓库):预览先列出会被目标版本替换的
文件与会保留的运维者内容,被替换的都会备份在 `.update-backup/<时间戳>/overwritten/`,确认后目录成为可一键 文件与会保留的运维者内容,被替换的都会备份在 `.update-backup/<唯一备份目录>/overwritten/`,确认后目录成为可一键
更新的正常安装。 更新的正常安装。
`upgrade.sh` 仍然可用,它现在只是这条实现的薄壳,并按安装形态分两条路: `upgrade.sh` 仍然可用,它现在只是这条实现的薄壳,并按安装形态分两条路:
@@ -227,7 +227,7 @@ Python 依赖方面:`venv/` 由 `run.sh` 自动创建和管理,保留它就
一键更新自带回滚点:`./cyberstrike-ai -update-rollback` 或页面上的回滚按钮,会撤到那次更新前的提交并把 一键更新自带回滚点:`./cyberstrike-ai -update-rollback` 或页面上的回滚按钮,会撤到那次更新前的提交并把
`cyberstrike-ai.prev` 换回原位;它只在"HEAD 仍是那次更新写下的提交"时才执行,之后的工作不会被顺手抹掉。 `cyberstrike-ai.prev` 换回原位;它只在"HEAD 仍是那次更新写下的提交"时才执行,之后的工作不会被顺手抹掉。
被暂存过的内容也留在 `.update-backup/<时间戳>/` 里。 被暂存过的内容也留在 `.update-backup/<唯一备份目录>/` 里。
除此之外,回滚时同时恢复: 除此之外,回滚时同时恢复:
+9 -2
View File
@@ -86,7 +86,7 @@ update:
一开始用 Release 包解压安装、目录里没有 `.git` 的,可在同一页执行「预览并接入」(不配置也能用默认的官方仓库): 一开始用 Release 包解压安装、目录里没有 `.git` 的,可在同一页执行「预览并接入」(不配置也能用默认的官方仓库):
预览在临时仓库里 fetch,先列出**会被目标版本替换的本机文件**与**会保留的运维者内容**;确认后目录接入 预览在临时仓库里 fetch,先列出**会被目标版本替换的本机文件**与**会保留的运维者内容**;确认后目录接入
成为 git 工作树(`git init` + 添加 origin + 落地目标分支),被替换的文件全部留底在 成为 git 工作树(`git init` + 添加 origin + 落地目标分支),被替换的文件全部留底在
`.update-backup/<时间戳>/overwritten/`,运维者内容照旧先暂存再放回,随后重编译二进制。 `.update-backup/<唯一备份目录>/overwritten/`,运维者内容照旧先暂存再放回,随后重编译二进制。
接入后它就是正常安装,一键更新与回滚都可用了;注意接入前没有 git 历史,因此「接入」本身没有可回滚的 接入后它就是正常安装,一键更新与回滚都可用了;注意接入前没有 git 历史,因此「接入」本身没有可回滚的
上一提交,第一个回滚点由接入后的第一次更新写下。 上一提交,第一个回滚点由接入后的第一次更新写下。
@@ -114,11 +114,14 @@ update:
`no_binary` → 409,其余 → 400)。其他失败(`merge_failed`、`build_failed`、`swap_failed`)带着 git/go 的原始输出; `no_binary` → 409,其余 → 400)。其他失败(`merge_failed`、`build_failed`、`swap_failed`)带着 git/go 的原始输出;
编译失败时二进制保持原版本,而源码已移动、状态文件已写好,因此仍然可以回滚。 编译失败时二进制保持原版本,而源码已移动、状态文件已写好,因此仍然可以回滚。
检查、更新、接入、回滚和重启共用安装目录的系统文件锁(`.update-lock`)。遇到
`update_busy` 时等待另一操作完成;进程退出后锁由系统释放,不要删除锁文件来绕过互斥。
### 运维者的内容如何被保留 ### 运维者的内容如何被保留
`roles/ skills/ tools/ agents/ knowledge_base/ data/ log/ venv/ config.yaml .env` 是**运维者的内容**, `roles/ skills/ tools/ agents/ knowledge_base/ data/ log/ venv/ config.yaml .env` 是**运维者的内容**,
判定在 `update.Protected`。合并前,只有本次更新**确实会写到**的那些路径被复制到 判定在 `update.Protected`。合并前,只有本次更新**确实会写到**的那些路径被复制到
`.update-backup/<时间戳>/` 并从工作树移开(你多出来的、上游没动的目录连碰都不碰),合并后再原样放回, `.update-backup/<唯一备份目录>/` 并从工作树移开(你多出来的、上游没动的目录连碰都不碰),合并后再原样放回,
于是"更新代码、留下我的工作"成立。结果里的 `keptContent` 逐个点名被保留的文件——绝不静默丢弃: 于是"更新代码、留下我的工作"成立。结果里的 `keptContent` 逐个点名被保留的文件——绝不静默丢弃:
当上游与你在同一个文件上都有改动,落地的是你的版本,而这件事写在结果里。 当上游与你在同一个文件上都有改动,落地的是你的版本,而这件事写在结果里。
**回滚遵循同一条规则**:`reset --hard` 会把受保护文件上现存的本地改动先暂存、重置后放回(更新前改的 **回滚遵循同一条规则**:`reset --hard` 会把受保护文件上现存的本地改动先暂存、重置后放回(更新前改的
@@ -134,6 +137,10 @@ update:
**HEAD 自那次更新之后又动过**(`moved_since_update`:回滚只该撤到更新前,不该顺手抹掉之后的工作)、 **HEAD 自那次更新之后又动过**(`moved_since_update`:回滚只该撤到更新前,不该顺手抹掉之后的工作)、
以及存在本地源码改动(`local_source_edits`)。 以及存在本地源码改动(`local_source_edits`)。
回滚开始前会写入 `.update-rollback-pending.json`。中断后再次执行回滚会继续恢复;页面的
`rollbackPending` 提示会暂停新更新。保留恢复记录、备份目录和 `.prev`,不要手动删除它们。
如果记录中的二进制哈希无法与现场对应,会拒绝覆盖并保留现场。
### 重启的两种情形 ### 重启的两种情形
进程只有在请求显式带 `restart: true`、且启动时装配了重启钩子时才会优雅退出(`Shutdown` 后退出码 0)。 进程只有在请求显式带 `restart: true`、且启动时装配了重启钩子时才会优雅退出(`Shutdown` 后退出码 0)。