use super::*;
impl BridgeManagementRunner {
pub(super) fn run_install_like(&mut self, operation: &str) -> Result<()> {
let snapshot = self.snapshot_now();
let release = snapshot
.as_ref()
.and_then(|value| value.available_release.clone())
.or_else(|| self.resolve_latest_release().ok())
.context("当前无法解析可安装的最新 bridge release")?;
self.task.target_version = Some(release.version.clone());
let current_version = snapshot
.as_ref()
.and_then(|value| value.install_record.as_ref())
.and_then(|record| record.current_version.clone());
self.task.current_version = current_version.clone();
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::ResolveTarget,
format!("已解析目标版本 {}", release.version),
Some(format!(
"当前版本={},目标来源={}/{}",
current_version.unwrap_or_else(|| "-".to_string()),
release.registry,
release.crate_name,
)),
None,
true,
)?;
if snapshot.as_ref().is_some_and(|value| {
value
.install_record
.as_ref()
.and_then(|record| record.current_version.as_deref())
== Some(release.version.as_str())
&& !value.needs_repair
}) {
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::VerifyHealth,
format!("bridge 已是目标版本 {}", release.version),
Some("无需重复安装,改为直接校验本地健康状态".to_string()),
None,
false,
)?;
let health = wait_for_expected_health(
&self.paths,
Some(release.version.as_str()),
Some(BRIDGE_PROTOCOL_VERSION),
)?;
self.complete_success(
format!("远端 bridge {}完成", operation),
Some(success_detail(&health)),
)
} else {
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::InstallRelease,
format!("正在安装 bridge {}", release.version),
Some("通过 cargo install 拉取最新 release".to_string()),
None,
false,
)?;
let release_root = release::install_release(
&self.paths,
&release.version,
DEFAULT_MANAGEMENT_CARGO_BINARY,
DEFAULT_MANAGEMENT_REGISTRY,
)?;
let metadata = release::load_release_metadata(&release_root)?;
self.task.target_version = Some(metadata.version.clone());
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::ActivateRelease,
format!("正在切换到 bridge {}", metadata.version),
Some(format!("releaseRoot={}", metadata.release_root)),
None,
false,
)?;
let existing_record = environment::read_install_record(&self.paths)?;
let existing_env = environment::read_env_file(&self.paths)?;
let env_values = environment::merge_env_values(
&self.paths,
&existing_env,
&EnvOverrides::default(),
)?;
release::activate_release(
&self.paths,
&metadata,
&env_values,
existing_record.as_ref(),
operation,
)?;
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::RestartService,
format!("bridge {} 已切换,等待服务接管", metadata.version),
Some(SERVICE_NAME.to_string()),
None,
true,
)?;
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::VerifyHealth,
format!("正在校验 bridge {}", metadata.version),
Some("等待 /health 返回新的版本与协议".to_string()),
None,
false,
)?;
let health = wait_for_expected_health(
&self.paths,
Some(metadata.version.as_str()),
Some(metadata.protocol_version as i32),
)?;
self.complete_success(
format!("远端 bridge {}完成", operation),
Some(success_detail(&health)),
)
}
}
pub(super) fn run_repair(&mut self) -> Result<()> {
let snapshot = self.snapshot_now();
let current_version = snapshot
.as_ref()
.and_then(|value| value.install_record.as_ref())
.and_then(|record| record.current_version.clone());
self.task.current_version = current_version.clone();
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::ResolveTarget,
"正在解析需要修复的 release".to_string(),
Some(format!(
"当前版本={}",
current_version.unwrap_or_else(|| "-".to_string())
)),
None,
true,
)?;
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::InstallRelease,
"正在准备修复所需 release".to_string(),
Some("如 current release 缺失,将自动重新安装记录中的版本".to_string()),
None,
false,
)?;
let existing_record = environment::read_install_record(&self.paths)?;
let args = RepairArgs {
env: EnvOverrides::default(),
cargo_binary: DEFAULT_MANAGEMENT_CARGO_BINARY.to_string(),
registry: DEFAULT_MANAGEMENT_REGISTRY.to_string(),
};
let metadata =
commands::resolve_repair_metadata(&self.paths, existing_record.as_ref(), &args)?;
self.task.target_version = Some(metadata.version.clone());
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::ActivateRelease,
format!("正在修复 bridge {}", metadata.version),
Some(format!("releaseRoot={}", metadata.release_root)),
None,
false,
)?;
let existing_env = environment::read_env_file(&self.paths)?;
let env_values =
environment::merge_env_values(&self.paths, &existing_env, &EnvOverrides::default())?;
release::activate_release(
&self.paths,
&metadata,
&env_values,
existing_record.as_ref(),
"repair",
)?;
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::RestartService,
format!("bridge {} 修复完成,等待服务接管", metadata.version),
Some(SERVICE_NAME.to_string()),
None,
true,
)?;
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::VerifyHealth,
format!("正在校验 bridge {}", metadata.version),
Some("等待 /health 返回修复后的状态".to_string()),
None,
false,
)?;
let health = wait_for_expected_health(
&self.paths,
Some(metadata.version.as_str()),
Some(metadata.protocol_version as i32),
)?;
self.complete_success(
"远端 bridge 修复完成".to_string(),
Some(success_detail(&health)),
)
}
pub(super) fn run_rollback(&mut self) -> Result<()> {
let existing_record = environment::read_install_record(&self.paths)?
.context("缺少 install record,无法执行回滚")?;
let previous_version = existing_record
.previous_version
.as_deref()
.filter(|value| !value.trim().is_empty())
.context("当前没有可回滚的上一版")?
.to_string();
self.task.current_version = existing_record.current_version.clone();
self.task.target_version = Some(previous_version.clone());
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::ResolveTarget,
format!("已解析回滚目标 {}", previous_version),
Some(format!(
"当前版本={}",
existing_record
.current_version
.clone()
.unwrap_or_else(|| "-".to_string())
)),
None,
true,
)?;
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::InstallRelease,
format!("正在准备回滚版本 {}", previous_version),
Some("如本地旧 release 缺失,将自动重新安装上一版".to_string()),
None,
false,
)?;
let target_release_root = existing_record
.previous_release_path
.as_deref()
.map(PathBuf::from)
.filter(|path| environment::release_binary_path(path).is_some())
.unwrap_or_else(|| self.paths.release_root_for_version(&previous_version));
let target_release_root =
if environment::release_binary_path(&target_release_root).is_some() {
target_release_root
} else {
release::install_release(
&self.paths,
&previous_version,
DEFAULT_MANAGEMENT_CARGO_BINARY,
DEFAULT_MANAGEMENT_REGISTRY,
)?
};
let metadata =
release::release_metadata_from_previous_record(&existing_record, &target_release_root)
.unwrap_or(release::load_release_metadata(&target_release_root)?);
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::ActivateRelease,
format!("正在回滚到 bridge {}", metadata.version),
Some(format!("releaseRoot={}", metadata.release_root)),
None,
false,
)?;
let existing_env = environment::read_env_file(&self.paths)?;
let env_values =
environment::merge_env_values(&self.paths, &existing_env, &EnvOverrides::default())?;
release::rollback_release(&self.paths, &metadata, &env_values, &existing_record)?;
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::RestartService,
format!("bridge 已回滚到 {},等待服务接管", metadata.version),
Some(SERVICE_NAME.to_string()),
None,
true,
)?;
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::VerifyHealth,
format!("正在校验 bridge {}", metadata.version),
Some("等待 /health 返回回滚后的状态".to_string()),
None,
false,
)?;
let health = wait_for_expected_health(
&self.paths,
Some(metadata.version.as_str()),
Some(metadata.protocol_version as i32),
)?;
self.complete_success(
"远端 bridge 回滚完成".to_string(),
Some(success_detail(&health)),
)
}
fn resolve_latest_release(&self) -> Result<ManagedBridgeReleaseDescriptor> {
let version = release::resolve_latest_registry_version(
&self.paths,
DEFAULT_MANAGEMENT_CARGO_BINARY,
DEFAULT_MANAGEMENT_REGISTRY,
)?;
Ok(ManagedBridgeReleaseDescriptor {
crate_name: CRATE_NAME.to_string(),
version,
registry: DEFAULT_MANAGEMENT_REGISTRY.to_string(),
})
}
}