The in-app updater ran `git fetch origin main && git reset --hard
origin/main` regardless of which version the GitHub releases API
reported as latest. So whenever the latest release lived on a branch
other than main — e.g. during a beta cycle when 0.2.4b1 sits on its
own branch and main still points at the previous stable — clicking
Apply Update appeared to succeed but the user actually stayed pinned
to old main HEAD.
Fix: extract `_discover_target_release(db)` mirroring the same
release-API + include_beta_updates selection the GUI's update-check
already uses, pass the resolved tag (e.g. `v0.2.4b1`) into
`_perform_update(target_ref)`, and run `git fetch --prune --tags
origin && git reset --hard <target_ref>`. The fetch now pulls --tags
so a tag ref is locally resolvable; the reset takes the caller's
ref instead of a hardcoded branch. apply_update now returns a clear
error if no release resolves, instead of silently kicking off an
update that can't land.
The in-app Apply Update path unconditionally ran `git remote set-url
origin https://github.com/maziggy/bambuddy.git` before fetching, on
the theory that systemd service users wouldn't have SSH keys. True
in production, but it also clobbered every developer's SSH origin
the moment they tested the upgrade flow against their own checkout.
Next `git push` then prompted for HTTPS credentials and bounced.
New behaviour: read `origin` first via `git remote get-url`, parse
out the (owner, repo) pair using a small helper that handles all
four canonical forms (git@github.com:owner/repo[.git] and
https://github.com/owner/repo[.git]), and only rewrite if it doesn't
already resolve to maziggy/bambuddy. Native installs with no remote
or pointing at a fork still get reset to the canonical HTTPS URL.
Three new regression tests in test_updates_api.py:
- parser accepts SSH/HTTPS, with/without .git, rejects non-GitHub
- SSH origin pointing at maziggy/bambuddy is preserved (the
developer-footgun case)
- origin pointing at a fork still gets rewritten to HTTPS (the
original behaviour we don't want to lose)
Native-install upgrade via the in-app Apply Update button got the new
code in via `git reset --hard origin/main` but then logged
ERROR: Could not open requirements file:
[Errno 2] No such file or directory: 'requirements.txt'
and continued. The new deps never installed, leaving the user with
new code but stale dependencies — surfaces as cryptic import errors
on the next restart.
Root cause: `pip install -r requirements.txt` ran with
`cwd=settings.base_dir`. On a native install, systemd sets
DATA_DIR=$INSTALL_PATH/data so base_dir resolves to the data dir
(e.g. /opt/bambuddy/data), not the source tree. Pip doesn't walk up
looking for the requirements file the way git walks up looking for
.git, so it fails. Same bug affected the optional npm step
(`frontend_dir = base_dir / "frontend"` doesn't exist).
Fix: introduce `settings.app_dir` pointing at the source-tree root
(distinct from `base_dir` only on native installs) and run pip +
npm with `cwd=settings.app_dir`. Git ops keep using `base_dir`
because they already work (git walks up).
Docker users were unaffected — Docker doesn't use the in-app updater
(image pull replaces it).
Regression test in test_updates_api.py mocks every subprocess in
_perform_update, captures their cwd, and asserts the pip step runs
in app_dir and that requirements.txt actually exists there. Any
future refactor that re-introduces cwd=base_dir for the pip step
fails CI before another user trips over it.
- Remove 28 unused imports across 22 test files
- Prefix 4 unused local variables with _ in app code
(archives, bambu_mqtt, main) and remove 1 dead store
- Consolidate import/import-from in test_plate_detection.py
- Fix unreachable statement in test_archive_service.py
- Simplify redundant comparison in timelapse_processor.py
Resolves ~50 CodeQL py/unused-import, py/unused-local-variable,
py/import-and-import-from, py/unreachable-statement, and
py/redundant-comparison findings.