fix: a rollback keeps a recreated role, and an owed build is finished first

Three ways the installation could still end up with its source, its binary and
the operator's content describing different things.

Rollback copied aside only *tracked* protected files, so a role upstream had
deleted and the operator had recreated at the same path was untracked - and
`git reset --hard` wrote the old commit's copy over it. The stash now also
covers untracked protected paths the target commit writes to, and names them in
keptContent like any other file it saved.

The pending-build marker said only "a compile is owed". An update that found
new commits therefore merged them and built the *new* tree, leaving `.prev`
holding a binary for a commit the state file no longer named - a rollback then
paired old source with the original binary. And a marker left behind after a
successful swap rebuilt and overwrote that same `.prev`. The marker now names
its target commit and the hashes on both sides of the swap, the state records
which commit the live binary came from, an owed build is finished before the
tree moves again, and a retry that finds the swap already done clears the
marker instead of repeating it. Rollback verifies that pairing before touching
anything and refuses (binary_changed) rather than reset the source beside a
binary it cannot account for; an update whose build failed never swapped one,
so its rollback moves the source only and no longer demands a `.prev`.

The version pair is recorded before the config is written, so a crash in
between still leaves a rollback that can put the old number back.
This commit is contained in:
Sycun committed 2026-10-08 22:46:17 +08:00
1 parent e8df681d9b
commit b645914e1f
10 files changed
+910 -104

No files matched your search

+6 -3
View File
@@ -132,9 +132,12 @@ version that lands is yours, and the result says so.
### Rollback
`./cyberstrike-ai -update-rollback`, or the button on the page: `git reset --hard` back to the commit
the update came from, the binary kept as `cyberstrike-ai.prev` is put back, and `.update-state.json`
is removed. It refuses when there is no update record (`no_state`), no kept binary (`no_binary`), the
recorded commit is not in this repository (`bad_state`), **HEAD has moved since that update**
the update came from, the binary kept as `cyberstrike-ai.prev` is put back when that update swapped
one, and `.update-state.json` is removed. An update whose build failed never swapped a binary, so its
rollback moves the source only - the binary on disk already belongs to the commit it returns to. It
refuses when there is no update record (`no_state`), a swap is owed but no `cyberstrike-ai.prev` is
left (`no_binary`), a recorded commit is not in this repository (`bad_state`), the binary on disk is
not the one the record pairs with that commit (`binary_changed`), **HEAD has moved since that update**
(`moved_since_update` - a rollback undoes the update, not the work done after it), or source is
modified locally (`local_source_edits`).
+5 -3
View File
@@ -127,9 +127,11 @@ update:
### 回滚
`./cyberstrike-ai -update-rollback` 或页面按钮:`git reset --hard` 回到那次更新前的提交,
并把换二进制时留下的 `cyberstrike-ai.prev` 放回原位,随后删除 `.update-state.json`。
它会拒绝的情形:没有更新记录(`no_state`)、没有留下旧二进制(`no_binary`)、记录里的提交在本仓库不存在
(`bad_state`)、**HEAD 自那次更新之后又动过**(`moved_since_update`:回滚只该撤到更新前,不该顺手抹掉之后的工作)、
换过二进制的那次会把 `cyberstrike-ai.prev` 放回原位,随后删除 `.update-state.json`。
编译失败的那次更新从没换过二进制,所以它的回滚只搬源码——磁盘上的二进制本来就属于要回去的那个提交。
它会拒绝的情形:没有更新记录(`no_state`)、需要换回却没有留下 `cyberstrike-ai.prev`(`no_binary`)、
记录里的提交在本仓库不存在(`bad_state`)、磁盘上的二进制不是记录里与该提交配对的那一个(`binary_changed`)、
**HEAD 自那次更新之后又动过**(`moved_since_update`:回滚只该撤到更新前,不该顺手抹掉之后的工作)、
以及存在本地源码改动(`local_source_edits`)。
### 重启的两种情形