Skip to content

v2.12.1 β€” "Update Now" actually requests an update - #52

Merged
rancur merged 1 commit into
mainfrom
fix/manual-update-signal
Aug 9, 2026
Merged

v2.12.1 β€” "Update Now" actually requests an update#52
rancur merged 1 commit into
mainfrom
fix/manual-update-signal

Conversation

@rancur

@rancur rancur commented Aug 9, 2026

Copy link
Copy Markdown
Owner

The bug

POST /api/admin/update wrote the literal string requested at <timestamp> into the signal file.

The updater added in 2.12.0 reads target_version out of that file as JSON and refuses anything that isn't semver β€” correctly, since the tag originates from the GitHub API and ends up in docker pull. So the button parsed to an empty target and was refused every time. "Update Now" silently did nothing.

Verified both directions:

signal payload parsed target_version result
new JSON 2.12.0 passes semver guard
old string '' refused

The fix

The endpoint now resolves the latest release from GitHub itself rather than trusting a caller-supplied tag, so a request can't point the updater at an arbitrary image. It returns up_to_date instead of queueing a no-op when you're already current, and takes ?force=true to re-apply the current version and recover a half-applied update.

New GET /api/admin/update-result exposes the outcome of the last attempt, including rollbacks, so the UI can report what happened instead of being a button that reports nothing back.

Also: deploy wasn't a mirror

scripts/deploy-to-nas.sh extracts a tar, which adds and overwrites but never deletes. Files removed upstream lingered on the remote indefinitely β€” found by diffing a live deployment against main: the three dead services deleted in 2.11.0 were still there.

It now prunes tracked-but-absent files before extracting, so a deploy mirrors the repo. Host-local files (.env, docker-compose.override.yml, logs) are protected by name, and only paths git knows about are considered.

Minor

  • _read_version() replaces three inlined copies of the same VERSION read in routes/admin.py, one of which had drifted.
  • Example Plex URLs in docstrings no longer use a real LAN address.

17 tests pass.

πŸ€– Generated with Claude Code

https://claude.ai/code/session_01HmUiLHPmKoz215WAWV5eHe

POST /api/admin/update wrote the literal string "requested at <timestamp>". The
updater added in 2.12.0 reads target_version out of that file as JSON and refuses
anything that is not semver β€” correctly, since the tag originates from the GitHub
API and ends up in `docker pull`. The button therefore parsed to an empty target
and was refused every single time: "Update Now" silently did nothing.

Verified both directions: the new JSON payload parses to target_version=2.12.0
and passes the semver guard; the old string parses to '' and is refused.

The endpoint now resolves the latest release from GitHub itself rather than
trusting a caller-supplied tag, returns up_to_date instead of queueing a no-op
when already current, and accepts force=true to re-apply the current version and
recover a half-applied update. GET /admin/update-result exposes the outcome β€”
including rollbacks β€” so the UI can say what happened.

Also: deploy-to-nas.sh extracted a tar, which adds and overwrites but never
deletes, so files removed upstream lingered on the remote forever (the dead
services deleted in 2.11.0 were still on the NAS today, found by diffing the
deployment against main). It now prunes tracked-but-absent files first so a
deploy mirrors the repo, protecting host-local files by name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HmUiLHPmKoz215WAWV5eHe
@rancur
rancur merged commit 9be0cc6 into main Aug 9, 2026
@rancur
rancur deleted the fix/manual-update-signal branch August 9, 2026 21:18
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