Skip to content

fix(upgrade): use uv tool upgrade for launcher (pip-less) installs#327

Merged
lstein merged 3 commits into
masterfrom
lstein/fix/uv-tool-inline-upgrade
Jun 10, 2026
Merged

fix(upgrade): use uv tool upgrade for launcher (pip-less) installs#327
lstein merged 3 commits into
masterfrom
lstein/fix/uv-tool-inline-upgrade

Conversation

@lstein

@lstein lstein commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Problem

Clicking Update in the About dialog returns a 500 on POST /version/update for launcher installs, with:

{"success": false, "message": "Update failed",
 "error": ".../uv/tools/photomapai/bin/python3: No module named pip\n"}

The endpoint hardcoded python -m pip install --upgrade photomapai, but the desktop launcher installs via uv tool install (launcher/uv.go), which produces a deliberately pip-less environment. So inline upgrade was broken for every launcher install — the primary distribution path. It only worked in pip-based installs (dev .venv, legacy installer scripts).

The X-Requested-With 403 some may hit is unrelated — that's the CSRF guard firing only from Swagger's "Try it out" (which omits the header); the real about.js sends it.

Fix

Choose the upgrade command by install type, branching on whether pip actually imports (the exact failure condition) rather than sniffing install paths:

  • pip importable → python -m pip install --upgrade photomapai (unchanged)
  • pip absent → locate uv (PATH, then the ~/.local/bin location uv self-installs to) → uv tool upgrade photomapai
  • pip absent and no uv → 500 with actionable guidance ("re-run the launcher with --reinstall") instead of the cryptic pip error

Restart is unchanged — the supervisor (photomap_server.py:167) already respawns the worker after an upgrade, so new code loads.

Tests

New tests/backend/test_upgrade.py (9 tests): command selection for both install types, the unavailable case, the X-Requested-With guard, and the subprocess-failure / timeout paths. ruff clean.

Release / "breaking change" note

The uv launcher first shipped in v1.0.6rc1 and has never been in a stable release. Landing this in 1.1.0 (first stable with the launcher) means no public breaking change:

Population Install Inline upgrade
Stable ≤1.0.5 pip works → pulls 1.1.0
New 1.1.0 uv launcher works (uv path)
v1.0.6rc1 testers (only stranded group) uv launcher one-time: re-run launcher with --reinstall

The already-running rc1 code can't be fixed remotely (the pip command is baked in), but that group is small/known and the re-run-launcher recovery needs no uv knowledge. Worth a one-line mention in the 1.1.0 release notes.

🤖 Generated with Claude Code

lstein and others added 3 commits June 9, 2026 20:14
The inline self-upgrade endpoint hardcoded
`python -m pip install --upgrade photomapai`. The desktop launcher
installs PhotoMapAI via `uv tool install`, which produces a deliberately
pip-less environment, so `POST /version/update` failed there with a 500
and `No module named pip` — i.e. inline upgrade was broken for every
launcher install (the primary distribution path; the uv launcher first
shipped in v1.0.6rc1 and has never been in a stable release).

Choose the upgrade command by install type instead:
- pip importable (dev venv, legacy installer scripts) -> keep
  `python -m pip install --upgrade photomapai`
- pip absent (uv tool install) -> locate `uv` (PATH, then the
  `~/.local/bin` location uv self-installs to) and run
  `uv tool upgrade photomapai`
- pip absent and no `uv` -> 500 with actionable guidance ("re-run the
  launcher with --reinstall") instead of the cryptic pip error

Branches on whether pip actually imports rather than sniffing install
paths, since that is the exact condition that made the old command fail.
Restart is unchanged — the supervisor already respawns the worker after
an upgrade.

Adds tests/backend/test_upgrade.py covering command selection for both
install types, the unavailable case, the X-Requested-With guard, and the
subprocess-failure / timeout paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document that #327 fixes the About -> Update button for launcher
(pip-less) installs, and tell 1.0.6rc1 launcher testers to re-run the
launcher with --reinstall if their in-app Update fails with
'No module named pip'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mmit)

The previous commit landed tests/backend/test_upgrade.py but the
corresponding changes to upgrade.py were not committed, so the tests
referenced a non-existent `_pip_available` and CI failed across the
matrix with AttributeError.

This adds the actual implementation: `_pip_available`, `_find_uv`,
`_build_upgrade_command`, and `UpgradeUnavailableError`, and switches
`update_version` to choose pip vs `uv tool upgrade` by install type.
Also drops a header-rejection test that duplicates test_upgrade_router.py.

Verified against a non-editable install (mirroring CI): both
test_upgrade.py and test_upgrade_router.py pass; ruff clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lstein lstein merged commit 792206c into master Jun 10, 2026
9 checks passed
@lstein lstein deleted the lstein/fix/uv-tool-inline-upgrade branch June 10, 2026 00:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant