Add in-app self-update via git (Hilfe menu)
New Updater class + "Hilfe" menubar: - "Nach Update suchen": git fetch + count of new upstream commits. - Quiet background check on start; surfaces via status bar + menu label. - Install: git merge --ff-only, pip install if requirements.txt changed, then restart via os.execv. data/ is git-ignored and untouched. - Fast-forward only; diverged history or offline -> clear message, no action. - Inert unless run from a git clone with git on PATH. Also: "Version…" menu item shows the installed commit. Verified against throwaway git repos (check / ff-update / diverged / no-op / non-clone) and via GUI build on Python 3.14 / Tk 9. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+29
-1
@@ -12,7 +12,8 @@ Everything lives in [`app.py`](../app.py). There is no package structure.
|
||||
| `parse_date()` / `parse_coord()` / helpers | Module-level pure functions for normalizing user input (date → ISO, coordinate strings → float, `"City (PLZ)"` splitting). Shared by the entry tab and `EditDialog`. |
|
||||
| `generate_map()` | Builds `karte.html` from `DataStore.get_map_data()` with Folium. |
|
||||
| `EditDialog` | `tk.Toplevel` modal dialog for editing one row. Returns its result via `self.result`. |
|
||||
| `App` | `ttkbootstrap.Window`. Builds the UI, owns the `DataStore`, the `GeocoderWorker`, and the in-memory `_queue` list for Tab 1. |
|
||||
| `Updater` | Wraps `git` for self-update. `available` is true only from a git clone with `git` on `PATH`. `check()` → new-commit count (`fetch` + `rev-list`); `update()` → `fetch` + `merge --ff-only` + conditional `pip install`. All git calls are `subprocess.run` with timeouts. Raises `UpdateError` (user-facing German text) on any failure. |
|
||||
| `App` | `ttkbootstrap.Window`. Builds the UI + menubar, owns the `DataStore`, the `GeocoderWorker`, the `Updater`, and the in-memory `_queue` list for Tab 1. |
|
||||
|
||||
## Data flow
|
||||
|
||||
@@ -71,6 +72,33 @@ user clicks "Alle speichern" -> _on_save_all()
|
||||
app is open, the next in-app save overwrites those changes (but the pre-write
|
||||
backup captures them).
|
||||
|
||||
## Self-update flow
|
||||
|
||||
```
|
||||
App start ─► daemon thread: sleep 2s ─► Updater.check()
|
||||
(fetch + count) └─ on error: log only, stay quiet
|
||||
│
|
||||
N > 0 ─► self.after(0, …) ─► status hint + menu label
|
||||
+ "Update verfügbar?" dialog
|
||||
|
||||
Hilfe ▸ Nach Update suchen ─► worker thread ─► Updater.check()
|
||||
│
|
||||
error ─► warning dialog (offline?)
|
||||
N = 0 ─► "aktuell" dialog
|
||||
N > 0 ─► "jetzt installieren?" dialog
|
||||
|
||||
install ─► worker thread ─► Updater.update() (fetch, ff-only merge, pip)
|
||||
success + changed ─► info dialog ─► _restart()
|
||||
save window state,
|
||||
stop geocoder,
|
||||
os.chdir(BASE_DIR),
|
||||
os.execv(python, [python, app.py])
|
||||
ff-only fails / pip fails ─► error dialog, no restart
|
||||
```
|
||||
|
||||
Everything network- or subprocess-bound runs off the UI thread; results are
|
||||
marshalled back with `self.after(0, …)` (same rule as the geocoder).
|
||||
|
||||
## Logging
|
||||
|
||||
`_setup_logging()` (called from `__main__`) attaches a `RotatingFileHandler` to
|
||||
|
||||
Reference in New Issue
Block a user