Skip to content

Commit

Permalink
improved CLI progress for ares-install
Browse files Browse the repository at this point in the history
  • Loading branch information
mariotaku committed Sep 30, 2023
1 parent 53a3ce4 commit ff31243
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 49 deletions.
53 changes: 52 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[workspace]
resolver = "2"
members = [
"common/device",
"common/connection",
Expand All @@ -9,6 +10,15 @@ members = [
"ares-device",
]

[workspace.dependencies]
clap = "4.2.4"
serde = "1.0.160"
serde_json = "1.0.96"
libssh-rs = "0.2.0"
sha256 = "1.1.3"
regex = "1.8.1"
indicatif = "0.17.7"

[patch.crates-io]
libssh-rs = { git = "https://github.com/mariotaku/libssh-rs.git", branch = "feature/more-auth-options" }
libssh-rs-sys = { git = "https://github.com/mariotaku/libssh-rs.git", branch = "feature/more-auth-options" }
15 changes: 8 additions & 7 deletions ares-install/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ares-install"
version = "0.1.3"
version = "0.1.4"
edition = "2021"
authors = ["Mariotaku Lee <[email protected]>"]
license = "Apache-2.0"
Expand All @@ -11,12 +11,13 @@ description = "Install or Remove app from a device"
[dependencies]
common-device = { path = "../common/device" }
common-connection = { path = "../common/connection" }
clap = { version = "4.2.4", features = ["derive", "env"] }
serde = { version = "1.0.160", features = ["derive"] }
serde_json = "1.0.96"
libssh-rs = { version = "0.2.0" }
sha256 = "1.1.3"
regex = "1.8.1"
clap = { workspace = true, features = ["derive", "env"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
libssh-rs = { workspace = true }
sha256 = { workspace = true }
regex = { workspace = true }
indicatif = { workspace = true }

[package.metadata.deb]
section = "devel"
65 changes: 60 additions & 5 deletions ares-install/src/install.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use std::fmt::Write;
use std::fs::File;
use std::io::{Error as IoError, ErrorKind};
use std::path::Path;
use std::time::Duration;

use indicatif::{ProgressBar, ProgressStyle};
use regex::Regex;
use serde::{Deserialize, Serialize};
use serde_json::Error as JsonError;
Expand Down Expand Up @@ -47,6 +50,7 @@ struct InstallResponseDetails {
impl InstallApp for DeviceSession {
fn install_app<P: AsRef<Path>>(&self, package: P) -> Result<String, InstallError> {
let mut file = File::open(&package)?;
let file_size = file.metadata()?.len();
let checksum = sha256::try_digest(package.as_ref()).map_err(|e| {
IoError::new(
ErrorKind::Other,
Expand All @@ -59,8 +63,41 @@ impl InstallApp for DeviceSession {
})?;
let ipk_path = format!("/media/developer/temp/ares_install_{}.ipk", &checksum[..10]);

let package_display_name = package
.as_ref()
.file_name()
.map(|s| s.to_string_lossy())
.unwrap_or_else(|| package.as_ref().to_string_lossy());

self.mkdir(&mut Path::new("/media/developer/temp"), 0o777)?;
self.put(&mut file, &ipk_path)?;

let pb = ProgressBar::new(file_size);
pb.suspend(|| {
println!(
"Uploading {} to {}...",
package_display_name, self.device.name
)
});
pb.enable_steady_tick(Duration::from_millis(50));
pb.set_prefix("Uploading");
pb.set_style(ProgressStyle::with_template("{prefix:.bold.dim} {spinner} {percent}% [{wide_bar}] {bytes}/{total_bytes} {eta} ETA")
.unwrap());

self.put(&mut file, &ipk_path, |transferred| {
pb.set_position(transferred as u64);
})?;

pb.suspend(|| {
println!(
"Installing {} on {}...",
package_display_name, self.device.name
)
});
pb.set_prefix("Installing");

let spinner_style =
ProgressStyle::with_template("{prefix:.bold.dim} {spinner} {wide_msg}").unwrap();
pb.set_style(spinner_style);

let result = match self.subscribe(
"luna://com.webos.appInstallService/dev/install",
Expand All @@ -72,10 +109,24 @@ impl InstallApp for DeviceSession {
true,
) {
Ok(subscription) => subscription
.filter_map(|item| map_install_message(item))
.filter_map(|item| {
map_installer_message(
item,
&Regex::new(r"(?i)installed").unwrap(),
|progress| {
pb.set_message(
progress
.strip_prefix("installing : ")
.unwrap_or(&progress)
.to_string(),
);
},
)
})
.next(),
Err(e) => Some(Err(e.into())),
};
pb.finish_and_clear();

if let Err(e) = self.rm(&ipk_path) {
eprintln!("Failed to delete {}: {:?}", ipk_path, e);
Expand All @@ -85,7 +136,11 @@ impl InstallApp for DeviceSession {
}
}

fn map_install_message(item: std::io::Result<Message>) -> Option<Result<String, InstallError>> {
pub(crate) fn map_installer_message<F: Fn(String)>(
item: std::io::Result<Message>,
expected: &Regex,
progress: F,
) -> Option<Result<String, InstallError>> {
return match item {
Ok(message) => match message.deserialize::<InstallResponse>() {
Ok(resp) => {
Expand All @@ -97,11 +152,11 @@ fn map_install_message(item: std::io::Result<Message>) -> Option<Result<String,
reason: details.reason.unwrap_or(String::from("unknown error")),
}));
} else if Regex::new(r"(?i)^SUCCESS").unwrap().is_match(&state)
|| Regex::new(r"(?i)installed").unwrap().is_match(&state)
|| expected.is_match(&state)
{
return Some(Ok(details.package_id.unwrap_or(String::from(""))));
} else {
println!("Install: {}", state);
progress(state);
}
}
}
Expand Down
22 changes: 9 additions & 13 deletions ares-install/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ use std::process::exit;

use clap::Parser;

use crate::remove::RemoveApp;
use ares_connection_lib::session::NewSession;
use ares_device_lib::DeviceManager;
use install::InstallApp;
use list::ListApps;

mod install;
mod list;
mod remove;

#[derive(Parser, Debug)]
#[command(about)]
Expand Down Expand Up @@ -54,20 +56,14 @@ fn main() {
session.list_apps();
} else if let Some(id) = cli.remove {
println!("Removing {id}...");
} else if let Some(package) = cli.package {
if let Some(file_name) = package.file_name() {
println!(
"Installing {} on {}...",
file_name.to_string_lossy(),
device.name
);
} else {
println!(
"Installing {} on {}...",
package.to_string_lossy(),
device.name
);
match session.remove_app(&id) {
Ok(_) => println!("{id} removed."),
Err(e) => {
eprintln!("Failed to remove {id}: {e:?}");
exit(1);
}
}
} else if let Some(package) = cli.package {
match session.install_app(package) {
Ok(package_id) => println!("{package_id} installed."),
Err(e) => {
Expand Down
42 changes: 42 additions & 0 deletions ares-install/src/remove.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use regex::Regex;
use serde::Serialize;

use ares_connection_lib::luna::Luna;
use ares_connection_lib::session::DeviceSession;

use crate::install::{map_installer_message, InstallError};

pub(crate) trait RemoveApp {
fn remove_app(&self, package_id: &str) -> Result<String, InstallError>;
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct RemovePayload {
id: String,
subscribe: bool,
}

impl RemoveApp for DeviceSession {
fn remove_app(&self, package_id: &str) -> Result<String, InstallError> {
let result = match self.subscribe(
"luna://com.webos.appInstallService/dev/remove",
RemovePayload {
id: String::from(package_id),
subscribe: true,
},
true,
) {
Ok(subscription) => subscription
.filter_map(|item| {
map_installer_message(item, &Regex::new(r"(?i)removed").unwrap(), |progress| {
println!("{}", progress);
})
})
.next(),
Err(e) => Some(Err(e.into())),
};

return result.unwrap();
}
}
6 changes: 3 additions & 3 deletions common/connection/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ name = "ares_connection_lib"

[dependencies]
common-device = { path = "../device" }
serde = { version = "1.0.160", features = ["derive"] }
serde_json = "1.0.96"
libssh-rs = { version = "0.2.0" }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
libssh-rs = { workspace = true }
reqwest = { version = "0.11.16", features = ["blocking"] }
snailquote = "0.3.1"
path-slash = "0.2.1"
Expand Down
2 changes: 1 addition & 1 deletion common/connection/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::io::Error as IoError;
use std::ops::Deref;
use std::time::Duration;

use libssh_rs::{AuthStatus, Error as SshError, LogLevel, Session, SshKey, SshOption};
use libssh_rs::{AuthStatus, Error as SshError, Session, SshKey, SshOption};

use ares_device_lib::Device;

Expand Down
Loading

0 comments on commit ff31243

Please sign in to comment.