forked from 1b-agent/fieldcraft-upstream
CLI: fieldcraft-cli update --check | --install (#231)
--check runs app.update.check and prints its JSON; --install downloads the latest release for this install, verifies the checksum list's signature and the asset's checksum, and swaps it in (or, for .deb/.rpm, prints the package manager command for the verified download). Tested against a local release server through FIELDCRAFT_UPDATE_HOST. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
12487794a3
commit
69c1440cbe
5 files changed
+103
No files matched your search
@@ -0,0 +1,56 @@
|
||||
//! `fieldcraft-cli update --check | --install` (docs/updates.md): the same check as the app's
|
||||
//! Help › Check for Updates (`app.update.check`), and installing the latest release over this
|
||||
//! copy of the CLI's install, verified against the release key first. `FIELDCRAFT_UPDATE_HOST`
|
||||
//! points both at a local test server.
|
||||
|
||||
use fieldcraft_engine::Session;
|
||||
use fieldcraft_update::install::{self, Finish};
|
||||
use fieldcraft_update::net::Client;
|
||||
use fieldcraft_update::stage::{self, Options};
|
||||
use serde_json::json;
|
||||
|
||||
pub const USAGE: &str = "usage: fieldcraft-cli update --check | --install";
|
||||
|
||||
pub fn run(args: &[String]) -> Result<(), String> {
|
||||
match args.iter().map(String::as_str).collect::<Vec<_>>().as_slice() {
|
||||
["--check"] => check(),
|
||||
["--install"] => install(),
|
||||
_ => Err(USAGE.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn check() -> Result<(), String> {
|
||||
fieldcraft_engine::enable_update_checks(true);
|
||||
let report = Session::default().execute("app.update.check", &json!({})).map_err(|e| e.to_string())?;
|
||||
println!("{}", serde_json::to_string_pretty(&report).map_err(|e| e.to_string())?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn install() -> Result<(), String> {
|
||||
let client = Client::from_env();
|
||||
let check = client.check().map_err(|e| e.to_string())?;
|
||||
if !check.newer() {
|
||||
println!("FieldCraft {} is up to date (the latest release is {}).", check.current, check.latest.version);
|
||||
return Ok(());
|
||||
}
|
||||
let here = install::here();
|
||||
let downloads = fieldcraft_io::recovery::data_dir().map(|d| d.join("updates")).unwrap_or_else(|| std::env::temp_dir().join("fieldcraft-updates"));
|
||||
eprintln!("Downloading FieldCraft {} for {}…", check.latest.version, here.describe());
|
||||
let progress = |done: u64, total: u64| {
|
||||
if total > 0 && done == total {
|
||||
eprintln!("Downloaded {} MB; verifying…", total / (1024 * 1024));
|
||||
}
|
||||
};
|
||||
let options = Options { key: fieldcraft_update::PUBLIC_KEY, downloads: &downloads, arch: std::env::consts::ARCH, progress: &progress };
|
||||
let staged = stage::stage(&client, &check.latest, &here, &options).map_err(|e| e.to_string())?;
|
||||
match install::plan(&staged, false).map_err(|e| e.to_string())? {
|
||||
Finish::Swap(plan) => {
|
||||
stage::apply(&plan).map_err(|e| e.to_string())?;
|
||||
println!("Installed FieldCraft {} ({}). A running FieldCraft app uses it from its next start.", staged.version, here.describe());
|
||||
}
|
||||
Finish::Package { file, command } => {
|
||||
println!("Downloaded and verified {}.\nInstall it with the system's package manager:\n {command}", file.display());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in new issue
Block a user