nesquena/hermes-webui

bug(updates): dirty install reports "up to date" — should detect local changes and offer to apply latest

Aberta

#4.085 aberto em 13 de jun. de 2026

 (3 comentários) (0 reação) (0 responsável)Python (2.316 forks)github user discovery
bugenhancementhelp wantedsprint-candidate

Métricas do repositório

Stars
 (16.946 estrelas)
Métricas de merge de PR
 (Mesclagem média 14h 31m) (314 fundiu PRs em 30d)

Description

Summary

When the hermes-webui install has a dirty working tree (uncommitted/modified tracked files), the update checker reports "Up to date" and offers no way to move to the latest version. A dirty install should instead be detected and offered the latest version (apply / reset-to-clean), since a dirty tree often means the install is in a modified or partially-updated state the user didn't intend.

Reporter

@b3nw, Hermes Discord #report-bugs, 2026-06-13:

if the hermes-webui install is dirty, it treats it as up to date, it should instead offer to apply the latest version

(Screenshot attached showing the settings update panel reporting up-to-date.)

Root cause

The update check path inspects only committed git refs and ignores the working-tree dirty state entirely:

  • _check_repo() (api/updates.py:~646) → _check_repo_release() (:~524) compares HEAD's nearest release tag (git describe --tags --abbrev=0) against the latest fetched tag, and _check_repo_branch() (:~575) counts git rev-list HEAD..<upstream>. Both operate on committed state.
  • A dirty working tree (git diff against HEAD) is only consulted for the version display string via _dirty_suffix() (:~189, appends -dirty/-dirty-<hash>) — it is never fed into the behind/ahead comparison.

So when HEAD is at (or past) the latest release tag but the tree has local modifications, behind == 0 → the checker returns None → the UI shows "Up to date," silently ignoring that the install has been modified. The user is given no signal and no remediation affordance.

Why the fix is cheap

The apply side already has the primitive to fix a dirty install: apply_force_update(target) (api/updates.py:~1165) discards local modifications (git checkout .) and resets to origin/<branch> — exactly the "reset to a clean latest" action. The plain apply_update() (:~1227) does stash → pull --ff-only → pop and requires a clean tree. So the missing piece is purely on the check/surface side: detect dirty and expose the existing force/apply path.

Suggested fix shape

  1. In _check_repo() (or the payload it returns), surface a dirty boolean (reuse the existing dirty detection in _dirty_suffix() / a git diff-index --quiet HEAD probe — already used for the version string).
  2. When dirty is true, don't report a bare "Up to date." Instead surface a distinct state in the Settings update panel — e.g. "Local changes detected" with an action to apply the latest version (wired to apply_force_update, which already discards local mods and resets to clean latest), ideally behind a confirm since it's destructive of local edits.
  3. Keep the clean-tree behavior unchanged.

This is WebUI-only (api/updates.py + the Settings update-panel frontend in static/). Small surface; the destructive reset already exists and is tested.

Scope / considerations

  • The "apply" must clearly warn it discards local modifications (it calls git checkout . + reset). Some users intentionally run patched installs — a confirm + clear copy avoids surprising them. A non-destructive option (stash + ff) is apply_update, but that can't fast-forward if HEAD already contains the tag; force-reset is the reliable "get me to clean latest" path.
  • Distinct from #3875 (message-display regression after update) — unrelated.

Reported by

@b3nw via Discord #report-bugs, 2026-06-13.

Guia do colaborador