From b01bfcd38edef0a8278c0e9bcc07a49a298ba648 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sun, 14 Dec 2025 18:20:11 -0500 Subject: [PATCH] Replace rollback with manual version selection and simplify updater --- pikit-api.py | 6 +- pikit-web/assets/api.js | 6 +- pikit-web/assets/releases.js | 65 ++++++- pikit-web/index.html | 5 +- pikit_api/__init__.py | 2 +- pikit_api/http_handlers.py | 24 ++- pikit_api/releases.py | 352 ++++++++++++++++++----------------- 7 files changed, 257 insertions(+), 203 deletions(-) diff --git a/pikit-api.py b/pikit-api.py index bbfc536..9d841fe 100644 --- a/pikit-api.py +++ b/pikit-api.py @@ -14,7 +14,7 @@ import argparse import sys from pikit_api import HOST, PORT -from pikit_api.releases import apply_update, check_for_update, rollback_update +from pikit_api.releases import apply_update, check_for_update from pikit_api.server import run_server @@ -22,7 +22,6 @@ def parse_args(): parser = argparse.ArgumentParser(description="Pi-Kit API / updater") parser.add_argument("--apply-update", action="store_true", help="Apply latest release (non-HTTP mode)") parser.add_argument("--check-update", action="store_true", help="Check for latest release (non-HTTP mode)") - parser.add_argument("--rollback-update", action="store_true", help="Rollback to last backup (non-HTTP mode)") parser.add_argument("--host", default=HOST, help="Bind host (default 127.0.0.1)") parser.add_argument("--port", type=int, default=PORT, help="Bind port (default 4000)") return parser.parse_args() @@ -36,9 +35,6 @@ def main(): if args.check_update: check_for_update() return - if args.rollback_update: - rollback_update() - return run_server(host=args.host, port=args.port) diff --git a/pikit-web/assets/api.js b/pikit-web/assets/api.js index a0dad2a..5ecd348 100644 --- a/pikit-web/assets/api.js +++ b/pikit-web/assets/api.js @@ -53,10 +53,12 @@ export const applyRelease = () => api("/api/update/apply", { method: "POST", }); -export const rollbackRelease = () => - api("/api/update/rollback", { +export const applyReleaseVersion = (version) => + api("/api/update/apply_version", { method: "POST", + body: JSON.stringify({ version }), }); +export const listReleases = () => api("/api/update/releases"); export const setReleaseAutoCheck = (enable) => api("/api/update/auto", { method: "POST", diff --git a/pikit-web/assets/releases.js b/pikit-web/assets/releases.js index 55a233c..ec4a9fb 100644 --- a/pikit-web/assets/releases.js +++ b/pikit-web/assets/releases.js @@ -5,7 +5,8 @@ import { getReleaseStatus, checkRelease, applyRelease, - rollbackRelease, + applyReleaseVersion, + listReleases, setReleaseAutoCheck, setReleaseChannel, } from "./api.js"; @@ -24,7 +25,8 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo const releaseProgress = document.getElementById("releaseProgress"); const releaseCheckBtn = document.getElementById("releaseCheckBtn"); const releaseApplyBtn = document.getElementById("releaseApplyBtn"); - const releaseRollbackBtn = document.getElementById("releaseRollbackBtn"); + const releaseVersionSelect = document.getElementById("releaseVersionSelect"); + const releaseApplyVersionBtn = document.getElementById("releaseApplyVersionBtn"); const releaseAutoCheck = document.getElementById("releaseAutoCheck"); const releaseLog = document.getElementById("releaseLog"); const releaseLogStatus = document.getElementById("releaseLogStatus"); @@ -46,6 +48,7 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo let changelogCache = { version: null, text: "" }; let lastChangelogUrl = null; let releaseChannel = "dev"; + let releaseOptions = []; const logger = createReleaseLogger(logUi); logger.attach(releaseLog); @@ -64,6 +67,41 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo } }; + async function loadReleaseList() { + if (!releaseVersionSelect) return; + try { + const data = await listReleases(); + releaseOptions = data.releases || []; + releaseVersionSelect.innerHTML = ""; + if (!releaseOptions.length) { + const opt = document.createElement("option"); + opt.value = ""; + opt.textContent = "No releases found"; + releaseVersionSelect.appendChild(opt); + releaseVersionSelect.disabled = true; + releaseApplyVersionBtn && (releaseApplyVersionBtn.disabled = true); + return; + } + releaseVersionSelect.disabled = false; + releaseOptions.forEach((r) => { + const opt = document.createElement("option"); + opt.value = r.version; + const tag = r.prerelease ? " (dev)" : ""; + opt.textContent = `${r.version}${tag}${r.published_at ? ` — ${fmtDate(r.published_at)}` : ""}`; + releaseVersionSelect.appendChild(opt); + }); + if (releaseApplyVersionBtn) releaseApplyVersionBtn.disabled = false; + } catch (e) { + releaseVersionSelect.innerHTML = ""; + const opt = document.createElement("option"); + opt.value = ""; + opt.textContent = "Failed to load releases"; + releaseVersionSelect.appendChild(opt); + releaseVersionSelect.disabled = true; + if (releaseApplyVersionBtn) releaseApplyVersionBtn.disabled = true; + } + } + function setReleaseChip(state) { if (!releaseFlagTop) return; releaseFlagTop.textContent = "Pi-Kit: n/a"; @@ -237,6 +275,7 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo releaseBtn?.addEventListener("click", () => { releaseModal?.classList.remove("hidden"); loadReleaseStatus(true); + loadReleaseList(); }); releaseClose?.addEventListener("click", () => releaseModal?.classList.add("hidden")); // Do not allow dismiss by clicking backdrop (consistency with other modals) @@ -299,19 +338,24 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo } }); - releaseRollbackBtn?.addEventListener("click", async () => { + releaseApplyVersionBtn?.addEventListener("click", async () => { + if (!releaseVersionSelect || !releaseVersionSelect.value) { + showToast("Select a version first", "error"); + return; + } try { lastReleaseToastKey = null; - logUi("Rollback requested"); + const ver = releaseVersionSelect.value; + logUi(`Install version ${ver} requested`); releaseBusyActive = true; - showBusy("Rolling back…", "Restoring previous backup."); - logRelease("Starting rollback…"); - await rollbackRelease(); + showBusy(`Installing ${ver}…`, "Applying selected release. This can take up to a minute."); + logRelease(`Installing ${ver}…`); + await applyReleaseVersion(ver); pollReleaseStatus(); - showToast("Rollback started", "success"); + showToast(`Installing ${ver}`, "success"); } catch (e) { - showToast(e.error || "Rollback failed", "error"); - logRelease(`Error: ${e.error || "Rollback failed"}`); + showToast(e.error || "Install failed", "error"); + logRelease(`Error: ${e.error || "Install failed"}`); } finally { if (releaseProgress) releaseProgress.textContent = ""; } @@ -334,6 +378,7 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo releaseChannel = chan; logRelease(`Channel set to ${chan}`); await loadReleaseStatus(true); + await loadReleaseList(); } catch (e) { showToast(e.error || "Failed to save channel", "error"); releaseChannelToggle.checked = releaseChannel === "dev"; diff --git a/pikit-web/index.html b/pikit-web/index.html index f4015fc..053dd7b 100644 --- a/pikit-web/index.html +++ b/pikit-web/index.html @@ -165,8 +165,9 @@ -