release(v1.7.4-alpha): fix Install Update tar extraction + progress overshoot
Some checks failed
Build Archipelago ISO (dev) / build-iso (push) Has been cancelled

apply_update was extracting the frontend tarball with
`tar -xzf -C /opt/archipelago`, but the tar contents are the *inside*
of web-ui/ (root entries are ./test-aiui.html, ./assets/, etc.). So
the files landed directly in /opt/archipelago instead of under web-ui/,
and tar bailed on nginx-owned paths mid-extraction. First end-to-end
OTA test (.198) found it: "tar: ./assets/SystemUpdate-…js: Cannot
open: No such file or directory".

Now extracts into web-ui.new, chowns, then atomically swaps: move
existing web-ui → web-ui.bak, then web-ui.new → web-ui. Same pattern
as the manual sideload that's been working.

Frontend: SystemUpdate.vue fake download progress was capped at "<90"
with a Math.random()*15 increment — the last tick could push to
~104.99%. Capped at 95% with a smaller step so it stops at 95 and the
real RPC completion jumps it to 100.

Artefacts:
  archipelago                                      a14ad7e4…2a2be3  40361984
  archipelago-frontend-1.7.4-alpha.tar.gz          4fb79664…0172e9  76984615

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-04-20 12:02:14 -04:00
parent 3a479e5b09
commit 170f8ae787
7 changed files with 64 additions and 46 deletions

2
core/Cargo.lock generated
View File

@@ -80,7 +80,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "archipelago"
version = "1.7.3-alpha"
version = "1.7.4-alpha"
dependencies = [
"anyhow",
"archipelago-container",

View File

@@ -1,6 +1,6 @@
[package]
name = "archipelago"
version = "1.7.3-alpha"
version = "1.7.4-alpha"
edition = "2021"
description = "Archipelago Bitcoin Node OS - Native backend"
authors = ["Archipelago Team"]

View File

@@ -305,42 +305,60 @@ pub async fn apply_update(data_dir: &Path) -> Result<()> {
info!(name = %name, "Backend binary applied");
}
_ if name.contains("frontend") && name.ends_with(".tar.gz") => {
let web_ui_dir = Path::new("/opt/archipelago/web-ui");
// Back up current frontend. /opt/archipelago is root-owned;
// the backup goes under our data_dir where we can write.
let frontend_backup = backup_dir.join("web-ui-backup.tar.gz");
if web_ui_dir.exists() {
let status = tokio::process::Command::new("sudo")
.args([
"tar",
"-czf",
&frontend_backup.to_string_lossy(),
"-C",
"/opt/archipelago",
"web-ui",
])
.status()
.await
.context("Failed to backup frontend")?;
if status.success() {
info!("Frontend backed up");
}
// The tarball contents are the *inside* of web-ui/ — root
// entries are `./test-aiui.html`, `./assets/`, etc. Extract
// into a sibling staging dir, then swap atomically so
// nginx never sees a half-written tree.
let new_dir = "/opt/archipelago/web-ui.new";
let backup_path = "/opt/archipelago/web-ui.bak";
// Wipe any previous attempt's staging / backup dirs.
let _ = tokio::process::Command::new("sudo")
.args(["rm", "-rf", new_dir, backup_path])
.status()
.await;
let mk = tokio::process::Command::new("sudo")
.args(["mkdir", "-p", new_dir])
.status()
.await
.context("Failed to create frontend staging dir")?;
if !mk.success() {
anyhow::bail!("mkdir {} failed", new_dir);
}
// Extract new frontend into /opt/archipelago (root-owned dir).
// Extract INTO the staging dir — tar's ./ entries land at
// the right place (web-ui.new/assets/... etc.).
let status = tokio::process::Command::new("sudo")
.args(["tar", "-xzf", &src.to_string_lossy(), "-C", "/opt/archipelago"])
.args(["tar", "-xzf", &src.to_string_lossy(), "-C", new_dir])
.status()
.await
.with_context(|| format!("Failed to extract {}", name))?;
if !status.success() {
anyhow::bail!("tar extraction failed for {}", name);
}
// nginx serves this tree; keep ownership consistent with
// what first-boot + the ISO layout expect.
// Ownership: match what first-boot + the ISO expect.
let _ = tokio::process::Command::new("sudo")
.args(["chown", "-R", "archipelago:archipelago", "/opt/archipelago/web-ui"])
.args(["chown", "-R", "archipelago:archipelago", new_dir])
.status()
.await;
// Atomic-ish swap: move old aside, new into place.
let web_ui = "/opt/archipelago/web-ui";
if Path::new(web_ui).exists() {
let mv_old = tokio::process::Command::new("sudo")
.args(["mv", web_ui, backup_path])
.status()
.await
.context("Failed to rotate old web-ui")?;
if !mv_old.success() {
anyhow::bail!("failed to move old web-ui aside");
}
}
let mv_new = tokio::process::Command::new("sudo")
.args(["mv", new_dir, web_ui])
.status()
.await
.context("Failed to swap new web-ui into place")?;
if !mv_new.success() {
anyhow::bail!("failed to move new web-ui into place");
}
info!(name = %name, "Frontend archive extracted to /opt/archipelago/web-ui");
}
_ => {

View File

@@ -345,10 +345,12 @@ async function downloadUpdate() {
downloadPercent.value = 0
statusMessage.value = ''
// Simulate incremental progress while waiting for the RPC
// Simulate incremental progress while waiting for the RPC. Capped at
// 95% so the bar never shows >100% before the real completion jumps it
// to 100 — previously the random increment could overshoot.
const progressInterval = setInterval(() => {
if (downloadPercent.value < 90) {
downloadPercent.value += Math.random() * 15
if (downloadPercent.value < 95) {
downloadPercent.value = Math.min(95, downloadPercent.value + Math.random() * 3)
}
}, 500)

View File

@@ -1,28 +1,26 @@
{
"version": "1.7.3-alpha",
"version": "1.7.4-alpha",
"release_date": "2026-04-20",
"changelog": [
"The version number in the sidebar now always matches the actual running version — no more lying to you about being on an older release after an update.",
"FIPS Mesh card on the server page: cleaner layout on desktop (no more awkward gaps), and a one-click Reconnect button when the public anchor is unreachable — it restarts the FIPS daemon so it can re-bootstrap from the anchor.",
"Profile pictures now show correctly in the identity list and editor. Before, uploaded images silently failed to render because the URL was only reachable over Tor; the UI now rewrites them to a local path while keeping the external URL for other Nostr clients.",
"Identity rows now show your Display Name first (from your Nostr profile) with the internal identity name beside it in parentheses, so you see the name other people will see — not just the one you picked when creating it."
"Install Update actually installs now. Before, the final step extracted the new UI into the wrong folder and bailed with 'Failed to apply update' — your node ended up backing up cleanly but never swapping in the new files. Fixed.",
"Download progress no longer overshoots 100%. You'll see the bar climb smoothly to 95% and then jump to 100% when the download actually finishes."
],
"components": [
{
"name": "archipelago",
"current_version": "1.7.2-alpha",
"new_version": "1.7.3-alpha",
"download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.3-alpha/archipelago",
"sha256": "99184b95f8d3041b7714ec9e58a194e466fa470c117992a4715c40980022dc1b",
"size_bytes": 40350664
"current_version": "1.7.3-alpha",
"new_version": "1.7.4-alpha",
"download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.4-alpha/archipelago",
"sha256": "a14ad7e4dbcb8f74377d44a4bd5e600b285481df3b30c08f8bea2cd17e2a2be3",
"size_bytes": 40361984
},
{
"name": "archipelago-frontend-1.7.3-alpha.tar.gz",
"current_version": "1.7.2-alpha",
"new_version": "1.7.3-alpha",
"download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.3-alpha/archipelago-frontend-1.7.3-alpha.tar.gz",
"sha256": "7b933cf458754faba18224d12b4793d7e152fc8296c3ee0441240fdc2374a8bc",
"size_bytes": 76987031
"name": "archipelago-frontend-1.7.4-alpha.tar.gz",
"current_version": "1.7.3-alpha",
"new_version": "1.7.4-alpha",
"download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.4-alpha/archipelago-frontend-1.7.4-alpha.tar.gz",
"sha256": "4fb796643cc9dc8469078ca3392f7cc5541071f6849979922b3259e5f20172e9",
"size_bytes": 76984615
}
]
}

BIN
releases/v1.7.4-alpha/archipelago Executable file

Binary file not shown.