()
| 15040 | return performUpdate(); |
| 15041 | } |
| 15042 | |
| 15043 | // One implementation behind every button that updates the box, so the progress |
| 15044 | // reporting and the failure reporting cannot drift apart between them. |
| 15045 | async function runUpdate(endpoint, idleLabel, startMessage) { |
| 15046 | const updateBtn = document.getElementById('update-btn'); |
| 15047 | |
| 15048 | const setButtonState = (busy, label) => { |
| 15049 | if (!updateBtn) { |
| 15050 | return; |
| 15051 | } |
| 15052 | updateBtn.disabled = !!busy; |
| 15053 | updateBtn.className = busy |
| 15054 | ? 'w-full bg-gray-600 text-white py-2 px-4 rounded cursor-wait' |
| 15055 | : 'w-full bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded transition-colors'; |
| 15056 | updateElement('update-btn-text', label); |
| 15057 | }; |
| 15058 | |
| 15059 | try { |
| 15060 | setButtonState(true, 'Updating...'); |
| 15061 | addConsoleMessage(startMessage, 'info'); |
| 15062 | updateElement('update-info', 'Updating...'); |
| 15063 | |
| 15064 | const data = await postAPI(endpoint, {}); |
| 15065 | |
| 15066 | if (!data.success) { |
| 15067 | throw Object.assign(new Error(data.error || 'Update failed'), { payload: data }); |
| 15068 | } |
| 15069 | |
| 15070 | if (Array.isArray(data.warnings)) { |
| 15071 | data.warnings.forEach(w => addConsoleMessage(w, 'warning')); |
| 15072 | } |
| 15073 | addConsoleMessage(data.output || 'Update applied.', 'success'); |
| 15074 | if (data.requirements_changed) { |
| 15075 | addConsoleMessage('This update changes Python dependencies - installing them before the restart. This can take a few minutes.', 'info'); |
| 15076 | } |
| 15077 | if (data.local_changes_preserved && !data.local_changes_restored) { |
| 15078 | addConsoleMessage("Your local edits are saved in a git stash ('git stash list' to recover them).", 'warning'); |
| 15079 | } |
| 15080 | addConsoleMessage('System will restart automatically...', 'info'); |
nothing calls this directly
no test coverage detected