Compare commits
9 Commits
b01bfcd38e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccc97f7912 | ||
|
|
f4090cbf1d | ||
|
|
f4d0765c93 | ||
|
|
99bd87c7f6 | ||
|
|
e87b90bf9f | ||
|
|
8557140193 | ||
|
|
86438b11f3 | ||
|
|
3a785832b1 | ||
|
|
a94cd17186 |
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"version": "0.1.3-dev1",
|
"version": "0.1.3-dev6",
|
||||||
"_release_date": "2025-12-14T22:23:00Z",
|
"_release_date": "2025-12-15T00:26:36Z",
|
||||||
"bundle": "https://git.44r0n.cc/44r0n7/pi-kit/releases/download/v0.1.3-dev1/pikit-0.1.3-dev1.tar.gz",
|
"bundle": "https://git.44r0n.cc/44r0n7/pi-kit/releases/download/v0.1.3-dev6/pikit-0.1.3-dev6.tar.gz",
|
||||||
"changelog": "https://git.44r0n.cc/44r0n7/pi-kit/releases/download/v0.1.3-dev1/CHANGELOG-0.1.3-dev1.txt",
|
"changelog": "https://git.44r0n.cc/44r0n7/pi-kit/releases/download/v0.1.3-dev6/CHANGELOG-0.1.3-dev6.txt",
|
||||||
"files": [
|
"files": [
|
||||||
{ "path": "bundle.tar.gz", "sha256": "290bc3ef0acbac8ffc1d283fdf5413bdd0dd6a90a9ccd2253dfd406773951b62" }
|
{ "path": "bundle.tar.gz", "sha256": "2d53fb5fc1b98193defac8566707ef49dd4e69a3befc31646eee1c972c102c9e" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,60 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.release-status-bar {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.release-advanced {
|
||||||
|
border: 1px dashed var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 8px;
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.release-advanced-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.release-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.release-card {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.release-card input[type="radio"] {
|
||||||
|
accent-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.release-card-title {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.release-card-tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.modal-card .status-msg {
|
.modal-card .status-msg {
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
|
|||||||
@@ -25,9 +25,14 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo
|
|||||||
const releaseProgress = document.getElementById("releaseProgress");
|
const releaseProgress = document.getElementById("releaseProgress");
|
||||||
const releaseCheckBtn = document.getElementById("releaseCheckBtn");
|
const releaseCheckBtn = document.getElementById("releaseCheckBtn");
|
||||||
const releaseApplyBtn = document.getElementById("releaseApplyBtn");
|
const releaseApplyBtn = document.getElementById("releaseApplyBtn");
|
||||||
const releaseVersionSelect = document.getElementById("releaseVersionSelect");
|
const releaseAdvancedToggle = document.getElementById("releaseAdvancedToggle");
|
||||||
|
const releaseAdvanced = document.getElementById("releaseAdvanced");
|
||||||
|
const releaseList = document.getElementById("releaseList");
|
||||||
const releaseApplyVersionBtn = document.getElementById("releaseApplyVersionBtn");
|
const releaseApplyVersionBtn = document.getElementById("releaseApplyVersionBtn");
|
||||||
const releaseAutoCheck = document.getElementById("releaseAutoCheck");
|
const releaseAutoCheck = document.getElementById("releaseAutoCheck");
|
||||||
|
const releaseStatusChip = document.getElementById("releaseStatusChip");
|
||||||
|
const releaseChannelChip = document.getElementById("releaseChannelChip");
|
||||||
|
const releaseLastCheckChip = document.getElementById("releaseLastCheckChip");
|
||||||
const releaseLog = document.getElementById("releaseLog");
|
const releaseLog = document.getElementById("releaseLog");
|
||||||
const releaseLogStatus = document.getElementById("releaseLogStatus");
|
const releaseLogStatus = document.getElementById("releaseLogStatus");
|
||||||
const releaseLogCopy = document.getElementById("releaseLogCopy");
|
const releaseLogCopy = document.getElementById("releaseLogCopy");
|
||||||
@@ -68,40 +73,70 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function loadReleaseList() {
|
async function loadReleaseList() {
|
||||||
if (!releaseVersionSelect) return;
|
if (!releaseList) return;
|
||||||
try {
|
try {
|
||||||
const data = await listReleases();
|
const data = await listReleases();
|
||||||
releaseOptions = data.releases || [];
|
releaseOptions = data.releases || [];
|
||||||
releaseVersionSelect.innerHTML = "";
|
renderReleaseList();
|
||||||
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) {
|
} catch (e) {
|
||||||
releaseVersionSelect.innerHTML = "";
|
renderReleaseList(true);
|
||||||
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 renderReleaseList(error = false) {
|
||||||
|
if (!releaseList) return;
|
||||||
|
releaseList.innerHTML = "";
|
||||||
|
if (error) {
|
||||||
|
releaseList.textContent = "Failed to load releases.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!releaseOptions.length) {
|
||||||
|
releaseList.textContent = "No releases found.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
releaseOptions.forEach((r, idx) => {
|
||||||
|
const card = document.createElement("label");
|
||||||
|
card.className = "release-card";
|
||||||
|
card.setAttribute("role", "option");
|
||||||
|
const input = document.createElement("input");
|
||||||
|
input.type = "radio";
|
||||||
|
input.name = "releaseVersion";
|
||||||
|
input.value = r.version;
|
||||||
|
if (idx === 0) input.checked = true;
|
||||||
|
const meta = document.createElement("div");
|
||||||
|
meta.className = "release-card-meta";
|
||||||
|
const title = document.createElement("div");
|
||||||
|
title.className = "release-card-title";
|
||||||
|
title.textContent = r.version;
|
||||||
|
const tags = document.createElement("div");
|
||||||
|
tags.className = "release-card-tags";
|
||||||
|
const chip = document.createElement("span");
|
||||||
|
chip.className = "status-chip ghost";
|
||||||
|
chip.textContent = r.prerelease ? "Dev" : "Stable";
|
||||||
|
tags.appendChild(chip);
|
||||||
|
if (r.published_at) {
|
||||||
|
const date = document.createElement("span");
|
||||||
|
date.className = "hint quiet";
|
||||||
|
date.textContent = fmtDate(r.published_at);
|
||||||
|
tags.appendChild(date);
|
||||||
|
}
|
||||||
|
meta.appendChild(title);
|
||||||
|
meta.appendChild(tags);
|
||||||
|
if (r.changelog_url) {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = r.changelog_url;
|
||||||
|
link.target = "_blank";
|
||||||
|
link.className = "hint";
|
||||||
|
link.textContent = "Changelog";
|
||||||
|
meta.appendChild(link);
|
||||||
|
}
|
||||||
|
card.appendChild(input);
|
||||||
|
card.appendChild(meta);
|
||||||
|
releaseList.appendChild(card);
|
||||||
|
});
|
||||||
|
if (releaseApplyVersionBtn) releaseApplyVersionBtn.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
function setReleaseChip(state) {
|
function setReleaseChip(state) {
|
||||||
if (!releaseFlagTop) return;
|
if (!releaseFlagTop) return;
|
||||||
releaseFlagTop.textContent = "Pi-Kit: n/a";
|
releaseFlagTop.textContent = "Pi-Kit: n/a";
|
||||||
@@ -193,6 +228,7 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo
|
|||||||
current_release_date = null,
|
current_release_date = null,
|
||||||
latest_release_date = null,
|
latest_release_date = null,
|
||||||
changelog_url = null,
|
changelog_url = null,
|
||||||
|
last_check = null,
|
||||||
} = data || {};
|
} = data || {};
|
||||||
releaseChannel = channel || "dev";
|
releaseChannel = channel || "dev";
|
||||||
if (releaseChannelToggle) releaseChannelToggle.checked = releaseChannel === "dev";
|
if (releaseChannelToggle) releaseChannelToggle.checked = releaseChannel === "dev";
|
||||||
@@ -214,6 +250,13 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo
|
|||||||
if (releaseLatest) releaseLatest.textContent = latest_version;
|
if (releaseLatest) releaseLatest.textContent = latest_version;
|
||||||
if (releaseCurrentDate) releaseCurrentDate.textContent = fmtDate(current_release_date);
|
if (releaseCurrentDate) releaseCurrentDate.textContent = fmtDate(current_release_date);
|
||||||
if (releaseLatestDate) releaseLatestDate.textContent = fmtDate(latest_release_date);
|
if (releaseLatestDate) releaseLatestDate.textContent = fmtDate(latest_release_date);
|
||||||
|
if (releaseStatusChip) {
|
||||||
|
releaseStatusChip.textContent = `Status: ${status.replaceAll("_", " ")}`;
|
||||||
|
releaseStatusChip.classList.toggle("chip-warm", status === "update_available");
|
||||||
|
releaseStatusChip.classList.toggle("chip-off", status === "error");
|
||||||
|
}
|
||||||
|
if (releaseChannelChip) releaseChannelChip.textContent = `Channel: ${releaseChannel}`;
|
||||||
|
if (releaseLastCheckChip) releaseLastCheckChip.textContent = `Last check: ${last_check ? fmtDate(last_check) : "—"}`;
|
||||||
if (releaseAutoCheck) releaseAutoCheck.checked = !!auto_check;
|
if (releaseAutoCheck) releaseAutoCheck.checked = !!auto_check;
|
||||||
if (releaseProgress) releaseProgress.textContent = "";
|
if (releaseProgress) releaseProgress.textContent = "";
|
||||||
if (status === "in_progress" && progress) {
|
if (status === "in_progress" && progress) {
|
||||||
@@ -338,14 +381,22 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
releaseAdvancedToggle?.addEventListener("click", async () => {
|
||||||
|
releaseAdvanced?.classList.toggle("hidden");
|
||||||
|
if (!releaseAdvanced?.classList.contains("hidden")) {
|
||||||
|
await loadReleaseList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
releaseApplyVersionBtn?.addEventListener("click", async () => {
|
releaseApplyVersionBtn?.addEventListener("click", async () => {
|
||||||
if (!releaseVersionSelect || !releaseVersionSelect.value) {
|
const selected = releaseList?.querySelector("input[name='releaseVersion']:checked");
|
||||||
|
if (!selected) {
|
||||||
showToast("Select a version first", "error");
|
showToast("Select a version first", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
lastReleaseToastKey = null;
|
lastReleaseToastKey = null;
|
||||||
const ver = releaseVersionSelect.value;
|
const ver = selected.value;
|
||||||
logUi(`Install version ${ver} requested`);
|
logUi(`Install version ${ver} requested`);
|
||||||
releaseBusyActive = true;
|
releaseBusyActive = true;
|
||||||
showBusy(`Installing ${ver}…`, "Applying selected release. This can take up to a minute.");
|
showBusy(`Installing ${ver}…`, "Applying selected release. This can take up to a minute.");
|
||||||
@@ -401,7 +452,8 @@ export function initReleaseUI({ showToast, showBusy, hideBusy, confirmAction, lo
|
|||||||
|
|
||||||
releaseLogCopy?.addEventListener("click", async () => {
|
releaseLogCopy?.addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
const text = releaseLogLines.join("\n") || "No log entries yet.";
|
const lines = logger.getLines ? logger.getLines() : [];
|
||||||
|
const text = lines.join("\n") || "No log entries yet.";
|
||||||
if (navigator.clipboard && window.isSecureContext) {
|
if (navigator.clipboard && window.isSecureContext) {
|
||||||
await navigator.clipboard.writeText(text);
|
await navigator.clipboard.writeText(text);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"version": "0.1.3-dev1"
|
"version": "0.1.3-dev6"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,6 +144,11 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="controls column">
|
<div class="controls column">
|
||||||
|
<div class="release-status-bar">
|
||||||
|
<span id="releaseStatusChip" class="status-chip quiet">Status: n/a</span>
|
||||||
|
<span id="releaseChannelChip" class="status-chip quiet">Channel: n/a</span>
|
||||||
|
<span id="releaseLastCheckChip" class="status-chip quiet">Last check: —</span>
|
||||||
|
</div>
|
||||||
<div class="control-card release-versions">
|
<div class="control-card release-versions">
|
||||||
<div>
|
<div>
|
||||||
<p class="hint quiet">Current version</p>
|
<p class="hint quiet">Current version</p>
|
||||||
@@ -165,9 +170,8 @@
|
|||||||
<button id="releaseApplyBtn" title="Download and install the latest release">
|
<button id="releaseApplyBtn" title="Download and install the latest release">
|
||||||
Upgrade
|
Upgrade
|
||||||
</button>
|
</button>
|
||||||
<select id="releaseVersionSelect" class="ghost" title="Select a specific release to install"></select>
|
<button id="releaseAdvancedToggle" class="ghost" title="Open manual version picker">
|
||||||
<button id="releaseApplyVersionBtn" class="ghost" title="Install the selected release">
|
Manual selection
|
||||||
Install version
|
|
||||||
</button>
|
</button>
|
||||||
<label class="checkbox-row inline">
|
<label class="checkbox-row inline">
|
||||||
<input type="checkbox" id="releaseAutoCheck" />
|
<input type="checkbox" id="releaseAutoCheck" />
|
||||||
@@ -178,6 +182,18 @@
|
|||||||
<span>Allow dev builds</span>
|
<span>Allow dev builds</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="releaseAdvanced" class="release-advanced hidden">
|
||||||
|
<div class="release-advanced-head">
|
||||||
|
<div>
|
||||||
|
<p class="hint quiet">Choose a specific release</p>
|
||||||
|
<span class="hint">Dev builds only appear when “Allow dev builds” is on.</span>
|
||||||
|
</div>
|
||||||
|
<button id="releaseApplyVersionBtn" class="ghost" title="Install selected release">
|
||||||
|
Install selected
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="releaseList" class="release-list" role="listbox" aria-label="Available releases"></div>
|
||||||
|
</div>
|
||||||
<div id="releaseProgress" class="hint status-msg"></div>
|
<div id="releaseProgress" class="hint status-msg"></div>
|
||||||
<div class="log-card">
|
<div class="log-card">
|
||||||
<div class="log-header">
|
<div class="log-header">
|
||||||
|
|||||||
@@ -43,29 +43,62 @@ def read_current_version() -> str:
|
|||||||
|
|
||||||
def load_update_state() -> Dict[str, Any]:
|
def load_update_state() -> Dict[str, Any]:
|
||||||
UPDATE_STATE_DIR.mkdir(parents=True, exist_ok=True)
|
UPDATE_STATE_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
def _reset_if_stale(state: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
If state thinks an update is running but the lock holder is gone,
|
||||||
|
clear it so the UI can recover instead of getting stuck forever.
|
||||||
|
"""
|
||||||
|
lock_alive = False
|
||||||
|
if UPDATE_LOCK.exists():
|
||||||
|
try:
|
||||||
|
pid = int(UPDATE_LOCK.read_text().strip() or "0")
|
||||||
|
if pid > 0:
|
||||||
|
os.kill(pid, 0)
|
||||||
|
lock_alive = True
|
||||||
|
else:
|
||||||
|
UPDATE_LOCK.unlink(missing_ok=True)
|
||||||
|
except OSError:
|
||||||
|
UPDATE_LOCK.unlink(missing_ok=True)
|
||||||
|
except Exception:
|
||||||
|
UPDATE_LOCK.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
if state.get("in_progress") and not lock_alive:
|
||||||
|
state["in_progress"] = False
|
||||||
|
state["progress"] = None
|
||||||
|
if state.get("status") == "in_progress":
|
||||||
|
state["status"] = "up_to_date"
|
||||||
|
state["message"] = state.get("message") or "Recovered from interrupted update"
|
||||||
|
try:
|
||||||
|
save_update_state(state)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return state
|
||||||
|
|
||||||
if UPDATE_STATE.exists():
|
if UPDATE_STATE.exists():
|
||||||
try:
|
try:
|
||||||
state = json.loads(UPDATE_STATE.read_text())
|
state = json.loads(UPDATE_STATE.read_text())
|
||||||
state.setdefault("changelog_url", None)
|
state.setdefault("changelog_url", None)
|
||||||
state.setdefault("latest_release_date", None)
|
state.setdefault("latest_release_date", None)
|
||||||
state.setdefault("current_release_date", None)
|
state.setdefault("current_release_date", None)
|
||||||
return state
|
return _reset_if_stale(state)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return {
|
return _reset_if_stale(
|
||||||
"current_version": read_current_version(),
|
{
|
||||||
"latest_version": None,
|
"current_version": read_current_version(),
|
||||||
"last_check": None,
|
"latest_version": None,
|
||||||
"status": "unknown",
|
"last_check": None,
|
||||||
"message": "",
|
"status": "unknown",
|
||||||
"auto_check": False,
|
"message": "",
|
||||||
"in_progress": False,
|
"auto_check": False,
|
||||||
"progress": None,
|
"in_progress": False,
|
||||||
"channel": os.environ.get("PIKIT_CHANNEL", "dev"),
|
"progress": None,
|
||||||
"changelog_url": None,
|
"channel": os.environ.get("PIKIT_CHANNEL", "dev"),
|
||||||
"latest_release_date": None,
|
"changelog_url": None,
|
||||||
"current_release_date": None,
|
"latest_release_date": None,
|
||||||
}
|
"current_release_date": None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def save_update_state(state: Dict[str, Any]) -> None:
|
def save_update_state(state: Dict[str, Any]) -> None:
|
||||||
@@ -400,6 +433,19 @@ def fetch_text_with_auth(url: str):
|
|||||||
def acquire_lock():
|
def acquire_lock():
|
||||||
try:
|
try:
|
||||||
ensure_dir(UPDATE_LOCK.parent)
|
ensure_dir(UPDATE_LOCK.parent)
|
||||||
|
# Clear stale lock if the recorded PID is not running
|
||||||
|
if UPDATE_LOCK.exists():
|
||||||
|
try:
|
||||||
|
pid = int(UPDATE_LOCK.read_text().strip() or "0")
|
||||||
|
if pid > 0:
|
||||||
|
os.kill(pid, 0)
|
||||||
|
else:
|
||||||
|
UPDATE_LOCK.unlink(missing_ok=True)
|
||||||
|
except OSError:
|
||||||
|
# Process not running
|
||||||
|
UPDATE_LOCK.unlink(missing_ok=True)
|
||||||
|
except Exception:
|
||||||
|
UPDATE_LOCK.unlink(missing_ok=True)
|
||||||
lockfile = UPDATE_LOCK.open("w")
|
lockfile = UPDATE_LOCK.open("w")
|
||||||
fcntl.flock(lockfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
fcntl.flock(lockfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||||
lockfile.write(str(os.getpid()))
|
lockfile.write(str(os.getpid()))
|
||||||
@@ -523,8 +569,9 @@ def _install_manifest(manifest: Dict[str, Any], meta: Optional[Dict[str, Any]],
|
|||||||
shutil.rmtree(API_PACKAGE_DIR, ignore_errors=True)
|
shutil.rmtree(API_PACKAGE_DIR, ignore_errors=True)
|
||||||
shutil.copytree(staged_pkg, API_PACKAGE_DIR, dirs_exist_ok=True)
|
shutil.copytree(staged_pkg, API_PACKAGE_DIR, dirs_exist_ok=True)
|
||||||
|
|
||||||
for svc in ("pikit-api.service", "dietpi-dashboard-frontend.service"):
|
# Restart frontend to pick up new assets; avoid restarting this API process
|
||||||
subprocess.run(["systemctl", "restart", svc], check=False)
|
# mid-apply to prevent leaving state in_progress.
|
||||||
|
subprocess.run(["systemctl", "restart", "dietpi-dashboard-frontend.service"], check=False)
|
||||||
|
|
||||||
VERSION_FILE.parent.mkdir(parents=True, exist_ok=True)
|
VERSION_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||||
VERSION_FILE.write_text(str(latest))
|
VERSION_FILE.write_text(str(latest))
|
||||||
|
|||||||
Reference in New Issue
Block a user