use std::process::Command;
use anyhow::anyhow;
use anyhow::bail;
use anyhow::Result;
use argh::FromArgs;
use lium::cros::ensure_testing_rsa_is_there;
use lium::cros::lookup_full_version;
use lium::dut::DutInfo;
use lium::repo::get_repo_dir;
use lium::util::shell_helpers::launch_command_with_stdout_label;
use regex::Regex;
fn determine_board_to_flash(
arg_dut: &Option<String>,
arg_board: &Option<String>,
) -> Result<String> {
let board_from_dut = arg_dut
.clone()
.ok_or(anyhow!("--dut is not specified"))
.map(|dut| -> Result<String> {
let dut = DutInfo::new(&dut)?;
dut.info()
.get("board")
.cloned()
.ok_or(anyhow!("Failed to get --board from "))
})?;
let board_from_arg = arg_board
.as_ref()
.ok_or("--board is not specified")
.cloned();
match (board_from_dut, board_from_arg) {
(Err(_), Err(_)) => bail!("Please specify --board or --dut"),
(Ok(board_from_dut), Err(_)) => Ok(board_from_dut),
(Err(_), Ok(board_from_arg)) => Ok(board_from_arg),
(Ok(board_from_dut), Ok(board_from_arg)) => {
let re = Regex::new(r"(^[[:alpha:]]*)")?;
let cap_arg = if let Some(cap) = re.captures(&board_from_arg) {
cap
} else {
return Err(anyhow!(
"{} doesn not match the board name pattern.",
board_from_arg
));
};
let cap_dut = re.captures(&board_from_dut).unwrap();
if cap_arg[1] != cap_dut[1] {
return Err(anyhow!(
"Given BOARD does not match with DUT: {} is given but {} is installed on the \
DUT",
board_from_arg,
board_from_dut
));
}
Ok(board_from_arg)
}
}
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "flash")]
pub struct Args {
#[argh(switch)]
usb: bool,
#[argh(option)]
dut: Option<String>,
#[argh(option)]
repo: Option<String>,
#[argh(option)]
board: Option<String>,
#[argh(option)]
image: Option<String>,
#[argh(option, default = "String::from(\"latest-dev\")")]
version: String,
#[argh(switch)]
use_local_image: bool,
#[argh(switch)]
recovery: bool,
#[argh(switch)]
enable_rootfs_verification: bool,
}
#[tracing::instrument(level = "trace")]
pub fn run(args: &Args) -> Result<()> {
let repo = &get_repo_dir(&args.repo)?;
let image_path = if let Some(image) = &args.image {
image.clone()
} else {
let board_to_flash = determine_board_to_flash(&args.dut, &args.board)?;
let host = if args.use_local_image {
"local"
} else {
"remote"
};
let version = if &args.version == "latest"
|| &args.version == "latest-dev"
|| &args.version == "latest-official"
{
args.version.clone()
} else {
lookup_full_version(&args.version, &board_to_flash)?
};
if host == "local" && version != "latest" {
return Err(anyhow!(
"flashing local image other than `--version latest` is not yet supported"
));
}
let variant = if args.recovery { "recovery" } else { "test" };
format!("xBuddy://{host}/{board_to_flash}/{version}/{variant}")
};
let destination = match (&args.dut, args.usb) {
(Some(dut), false) => {
ensure_testing_rsa_is_there()?;
let dut = &DutInfo::new(dut)?;
dut.ssh().host_and_port()
}
(None, true) => "usb://".to_string(),
_ => bail!("Please specify either --dut or --usb"),
};
let mut cmd_args: Vec<&str> =
Vec::from(["flash", "--clobber-stateful", "--clear-tpm-owner", "-vvv"]);
if !args.enable_rootfs_verification {
cmd_args.push("--disable-rootfs-verification");
}
cmd_args.push(&destination);
cmd_args.push(&image_path);
let cmd_result = launch_command_with_stdout_label(
Command::new("cros").current_dir(repo).args(cmd_args),
"cros flash".to_string().into(),
)?;
if !cmd_result.success() {
println!("cros sdk failed");
}
Ok(())
}