-
Notifications
You must be signed in to change notification settings - Fork 7
Dev Testing Update Mechanism
This page documents how to test the full in-place update flow without a real GitHub release. The mechanism has three distinct layers — the manifest, the updater script, and the Lua plugin — which can each be exercised independently.
Lightroom (Lua)
└─ UpdateCheck.getLatestReleaseInfo() → GitHub API (or stub)
└─ UpdateCheck.fetchManifest() → manifest JSON URL
└─ SearchIndexAPI.applyUpdate(manifest) → POST /update/apply
└─ services/update.py → spawns updater.py + shuts down backend
└─ scripts/updater.py → downloads files, applies, restarts backend
The test scaffolding lives in test_update_env/source_files/. It contains a small set of replacement files and a pre-built update-manifest-test.json pointing at localhost:8080.
Tests server/src/scripts/updater.py directly: download, SHA verification, backup, apply, and backend restart.
cd test_update_env/source_files
python3 -m http.server 8080The directory layout must match the URL paths in the manifest:
-
plugin/<file>→ plugin files -
backend_src/<file>→ backend source files
Run this from the repo root (the existing update-manifest-test.json may have stale hashes):
python3 - <<'EOF'
import hashlib, json
from pathlib import Path
def sha(p):
return hashlib.sha256(Path(p).read_bytes()).hexdigest()
manifest = {
"version": "9.9.9",
"tag": "v9.9.9",
"update_type": "code_only",
"breaking_changes": False,
"requires_restart": True,
"total_size_bytes": 0,
"file_counts": {"plugin": 1, "backend_src": 1},
"files": {
"plugin": [
{
"path": "Init.lua",
"url": "http://localhost:8080/plugin/Init.lua",
"sha256": sha("test_update_env/source_files/plugin/Init.lua"),
}
],
"backend_src": [
{
"path": "geniusai_server.py",
"url": "http://localhost:8080/backend_src/geniusai_server.py",
"sha256": sha("test_update_env/source_files/backend_src/geniusai_server.py"),
}
]
}
}
Path("/tmp/test-manifest.json").write_text(json.dumps(manifest, indent=2))
print("Written to /tmp/test-manifest.json")
EOFDo not point the updater at your working checkout — it will overwrite files.
cp -r plugin/LrGeniusAI.lrdevplugin /tmp/test-plugin
cp -r server /tmp/test-servercd server
uv run python src/scripts/updater.py \
/tmp/test-manifest.json \
/tmp/test-plugin \
/tmp/test-serverThe Tkinter progress window appears. After completion, verify:
# Replacement file should now be in place
diff /tmp/test-plugin/Init.lua test_update_env/source_files/plugin/Init.lua
# No leftover .bak files
find /tmp/test-plugin /tmp/test-server -name "*.bak"
# No leftover temp dir
ls ~/.lrgeniusai/update_tmp 2>/dev/null || echo "cleaned up OK"Tests services/update.py and the /update/apply route end-to-end, including the subprocess spawn and backend shutdown.
cd server && uv run python src/geniusai_server.pycurl -s -X POST http://localhost:57430/update/apply \
-H "Content-Type: application/json" \
-d @/tmp/test-manifest.json | jq .Expected response: {"results": "Update started", "error": null, "warning": null}
The backend logs Spawning updater GUI then Requesting backend shutdown. The updater GUI window appears and the backend process exits after ~2 seconds.
Send two POST requests simultaneously — the second should return an error:
curl -s -X POST http://localhost:57430/update/apply \
-H "Content-Type: application/json" \
-d @/tmp/test-manifest.json &
curl -s -X POST http://localhost:57430/update/apply \
-H "Content-Type: application/json" \
-d @/tmp/test-manifest.jsonExpected: the second call returns "An update is already in progress".
Set "breaking_changes": true in /tmp/test-manifest.json and POST it to the backend as above. The backend will still return success (it passes the manifest straight through), but when triggered from Lightroom the Lua layer (TaskUpdate.lua:72–88) will intercept this flag and show the "Full Installer Required" dialog instead of applying.
To test the Lua path specifically, use the Lightroom stub approach in Layer 4.
Exercises the complete path including UpdateCheck.lua, TaskUpdate.lua, and the backend.
Use the HTTP server from Layer 1, or drop the manifest behind any URL reachable from the machine running Lightroom.
Add this override in UpdateCheck.lua (revert before committing):
function UpdateCheck.getLatestReleaseInfo()
return {
tag_name = "v9.9.9",
release_url = "http://localhost:8080",
manifest_url = "http://localhost:8080/update-manifest-test.json",
is_code_only = true,
is_newer = true,
}
endIn Lightroom: Library → Plug-in Extras → Check for Updates
The confirmation dialog should show version 9.9.9 with the file counts from the manifest. Clicking Install fires the full chain.
| Scenario | How to trigger |
|---|---|
| SHA mismatch rejected | Edit a sha256 value in the manifest to a wrong hash |
| Stale temp dir cleaned | Pre-create ~/.lrgeniusai/update_tmp/ with junk files before running |
| Backup created and cleaned on success | Run with a real existing file at the target path; confirm no .bak left behind |
| Backup survives a mid-apply failure | Kill the updater process during the apply phase; confirm .bak file still exists |
| Backend restart fails gracefully | Remove the geniusai_server.py entry point from the throwaway directory |
breaking_changes: true blocks apply |
Set the flag in the manifest and trigger from Lightroom |
Inline content field (version_info.py) |
Generate a manifest with generate_update_manifest.py and confirm no URL is fetched for that entry |