chore: release v1.7.46-alpha

Follow-up to v1.7.45-alpha closing the remaining tasks identified by the
resilience sweeps + the new bitcoin orphan / install-fail-vanish bugs.

User-visible:
- Health monitor: stop paging on orphaned containers from variant switches
- Install fail: card stays visible (was vanishing) with error message
- Stack pull progress: interpolate 20→70% (was stuck at 20%)
- docker.io → lfg2025 mirror: bitcoin/gitea/nextcloud/valkey

Internal:
- Resilience harness — install-wait uses expected_containers_for, ui+auth
  probes retry with 60s backoff, dep-snapshot fix
- InstallProgress gains optional `message` field (frontend renders it
  when phase is None)

binary  $(stat -c %s releases/v1.7.46-alpha/archipelago)  sha256:$(sha256sum releases/v1.7.46-alpha/archipelago | awk '{print $1}')
tarball $(stat -c %s releases/v1.7.46-alpha/archipelago-frontend-1.7.46-alpha.tar.gz)  sha256:$(sha256sum releases/v1.7.46-alpha/archipelago-frontend-1.7.46-alpha.tar.gz | awk '{print $1}')

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-04-29 14:50:33 -04:00
parent 4ec6ca98c1
commit 992b673b20
14 changed files with 137 additions and 27 deletions

2
core/Cargo.lock generated
View File

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

View File

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

View File

@@ -113,11 +113,26 @@ impl RpcHandler {
Err(e) => {
error!("package.install {} failed: {:#}", package_id_spawn, e);
install_log(&format!("INSTALL FAIL: {}{:#}", package_id_spawn, e)).await;
// No pre-state to revert to — remove the entry entirely so
// the UI shows the app as not installed. The next package
// scan will re-create it only if podman actually has a
// container for it (partial install recovery).
remove_package_entry(&handler.state_manager, &package_id_spawn).await;
// Don't remove the entry — that's what made the card
// vanish from My Apps mid-install / between retry-loop
// attempts (e.g. tailscale's entrypoint failure). Leave
// the entry visible with state=Stopped + the install
// error in install_progress.message so the user can see
// what went wrong and decide whether to retry or
// uninstall. clear_install_progress would erase the
// message, so we set it explicitly here instead.
let err_msg = format!("Install failed: {:#}", e);
let (mut data, _) = handler.state_manager.get_snapshot().await;
if let Some(entry) = data.package_data.get_mut(&package_id_spawn) {
entry.state = PackageState::Stopped;
entry.install_progress = Some(crate::data_model::InstallProgress {
size: 0,
downloaded: 0,
phase: None,
message: Some(err_msg),
});
handler.state_manager.update_data(data).await;
}
}
}
});

View File

@@ -25,6 +25,7 @@ impl RpcHandler {
size,
downloaded,
phase: existing_phase,
message: None,
});
self.state_manager.update_data(data).await;
}
@@ -55,6 +56,7 @@ impl RpcHandler {
size,
downloaded,
phase: Some(phase),
message: None,
});
self.state_manager.update_data(data).await;
}
@@ -97,6 +99,7 @@ impl RpcHandler {
size: total,
downloaded,
phase: existing_phase,
message: None,
});
state_manager.update_data(data).await;
}

View File

@@ -201,7 +201,7 @@ impl RpcHandler {
let images = [
"146.59.87.168:3000/lfg2025/immich-postgres:14-vectorchord0.4.3-pgvectors0.2.0",
"docker.io/valkey/valkey:7-alpine",
"146.59.87.168:3000/lfg2025/valkey:7-alpine",
"146.59.87.168:3000/lfg2025/immich-server:release",
];
self.set_install_phase("immich", InstallPhase::PullingImage)
@@ -300,7 +300,7 @@ impl RpcHandler {
"--health-cmd=valkey-cli ping || exit 1",
"--health-interval=30s",
"--health-retries=3",
"docker.io/valkey/valkey:7-alpine",
"146.59.87.168:3000/lfg2025/valkey:7-alpine",
])
.output()
.await;

View File

@@ -255,6 +255,12 @@ pub struct InstallProgress {
/// a fixed UI percentage and a descriptive label.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub phase: Option<InstallPhase>,
/// Optional explicit message — used to surface install failures so
/// the UI can keep the app card visible with an error description
/// instead of silently removing the entry on fail. UI's PHASE_INFO
/// label takes precedence when phase is set.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
/// Phases of the install / update pipeline, surfaced to the UI so users

View File

@@ -539,6 +539,20 @@ pub fn spawn_health_monitor(state: Arc<StateManager>, data_dir: PathBuf) {
debug!("Skipping uninstalled container: {}", container.name);
continue;
}
} else {
// Orphan: container exists in podman but archipelago has
// no package_data entry for it. Common after a variant
// switch (bitcoin-core ↔ bitcoin-knots) where the
// uninstall removed the package entry but the prior
// variant's container survived in stopped state. Without
// this guard the health monitor pages every minute with
// "Auto-restart failed (attempt N/10)" for an app the
// user can no longer see in the dashboard.
debug!(
"Skipping orphan container (not in package_data): {}",
container.name
);
continue;
}
if container.healthy {