Refuse Hermes removal while its files are in use
An open terminal can retain deleted SQLite WAL files across removal and reinstall, causing the updated desktop to refuse session writes. Check user processes before package/runtime removal and again after data confirmation, without killing sessions. Cover the failure with real SQLite writers in isolated fixtures. Co-Authored-By: GPT-6 Codex (xhigh) <noreply@openai.com>
This commit is contained in:
1 parent
827266fbed
commit
1b4ac8380b
2 files changed
+203
-2
No files matched your search
@@ -6,6 +6,52 @@
|
||||
# -u so an unset HOME is an error rather than a set of rm -rf paths rooted at /.
|
||||
set -euo pipefail
|
||||
|
||||
ensure_hermes_stopped() {
|
||||
python3 - "$HOME" <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
home = Path(sys.argv[1])
|
||||
roots = [str((home / relative).resolve()) for relative in ('.hermes', '.config/Hermes')]
|
||||
roots.append('/opt/hermes-desktop')
|
||||
|
||||
def belongs_to_hermes(target):
|
||||
target = target.removesuffix(' (deleted)')
|
||||
return any(target == root or target.startswith(root + '/') for root in roots)
|
||||
|
||||
holders = []
|
||||
for process in Path('/proc').iterdir():
|
||||
if not process.name.isdigit() or int(process.name) == os.getpid():
|
||||
continue
|
||||
try:
|
||||
if process.stat().st_uid != os.getuid():
|
||||
continue
|
||||
targets = [os.fsdecode(arg) for arg in (process / 'cmdline').read_bytes().split(b'\0')]
|
||||
entries = [process / 'exe', process / 'cwd']
|
||||
try:
|
||||
entries.extend((process / 'fd').iterdir())
|
||||
except PermissionError:
|
||||
pass
|
||||
for entry in entries:
|
||||
try:
|
||||
targets.append(os.readlink(entry))
|
||||
except OSError:
|
||||
pass
|
||||
if any(belongs_to_hermes(target) for target in targets):
|
||||
holders.append(process.name)
|
||||
except (FileNotFoundError, ProcessLookupError, PermissionError):
|
||||
continue
|
||||
|
||||
if holders:
|
||||
print('Close Hermes and processes using its files before removing it (PIDs: '
|
||||
+ ', '.join(holders) + '). Then try again.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
PY
|
||||
}
|
||||
|
||||
# Removing an open SQLite WAL leaves a live writer on a deleted generation.
|
||||
ensure_hermes_stopped
|
||||
omarchy-pkg-drop hermes-desktop
|
||||
|
||||
# The installer leaves a unit waiting to hand the app the Omarchy theme.
|
||||
@@ -19,6 +65,7 @@ systemctl --user stop omarchy-hermes-theme.service 2>/dev/null || true
|
||||
# Tolerated here rather than fatal, so the ~/.hermes handling below still runs;
|
||||
# the failure is answered for at the end instead of being swallowed.
|
||||
cli_removed=true
|
||||
ensure_hermes_stopped
|
||||
omarchy-install-hermes-cli --remove || cli_removed=false
|
||||
|
||||
# The app writes this when the runtime it provisions under ~/.hermes has landed,
|
||||
@@ -78,6 +125,7 @@ if [[ -d $HOME/.hermes || -d $HOME/.config/Hermes ]] && [[ -t 0 ]] && omarchy-cm
|
||||
# turn that into an aborted removal; the size is worth no such thing.
|
||||
size=$(du -shc "$HOME/.hermes" "$HOME/.config/Hermes" 2>/dev/null | tail -1 | cut -f1 || true)
|
||||
if gum confirm --default=false "Also delete ~/.hermes and ~/.config/Hermes ($size: chats, memories, skills, connections and tokens)?"; then
|
||||
ensure_hermes_stopped
|
||||
rm -rf "$HOME/.hermes" "$HOME/.config/Hermes"
|
||||
data_removed=true
|
||||
fi
|
||||
|
||||
Reference in new issue
Block a user