Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 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 crates/fbuild-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,13 @@ tempfile = { workspace = true }
walkdir = { workspace = true }
owo-colors = { workspace = true }
semver = { workspace = true }

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.52", features = [
"Win32_Foundation",
"Win32_System_Registry",
"Win32_System_Threading",
"Win32_Storage_FileSystem",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
] }
17 changes: 17 additions & 0 deletions crates/fbuild-cli/src/cli/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,23 @@ pub enum Commands {
/// Disable all shrink optimizations. Equivalent to `--shrink=off`.
#[arg(long = "no-shrink", conflicts_with = "shrink")]
no_shrink: bool,
/// Permit one UAC-gated, one-shot USB PnP recovery only when the
/// daemon returns a typed recoverable endpoint. This never elevates
/// the daemon, build, cache, or normal deployment process.
#[arg(long, conflicts_with = "no_admin")]
admin: bool,
/// Never request UAC; print physical recovery guidance instead.
#[arg(long = "no-admin", conflicts_with = "admin")]
no_admin: bool,
},
/// Internal one-shot elevated USB recovery entry point. It is hidden so
/// users cannot mistake it for general administrator mode.
#[command(name = "__usb-recovery-helper", hide = true)]
UsbRecoveryHelper {
#[arg(long)]
request: std::path::PathBuf,
#[arg(long)]
result: std::path::PathBuf,
},
/// Monitor serial output
Monitor {
Expand Down
2 changes: 2 additions & 0 deletions crates/fbuild-cli/src/cli/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ pub async fn run_deploy(
emulator: Option<String>,
target: Option<String>,
output_dir: Option<String>,
usb_recovery_policy: fbuild_core::usb::UsbRecoveryPolicy,
) -> fbuild_core::Result<()> {
daemon_client::ensure_daemon_running().await?;
let client = DaemonClient::new();
Expand Down Expand Up @@ -218,6 +219,7 @@ pub async fn run_deploy(
.filter(|s| !s.is_empty()),
output_dir,
pio_env: daemon_client::capture_pio_env(),
usb_recovery_policy,
};

let resp = client.deploy(&req).await?;
Expand Down
25 changes: 25 additions & 0 deletions crates/fbuild-cli/src/cli/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,22 @@ use super::serial_probe::run_serial;
use super::show::run_show;
use super::symbols_cmd::run_symbols;
use super::sync_cmd::run_sync_cmd;
use super::usb_recovery::run_hidden_helper;

pub async fn async_main() {
let cli = Cli::parse_from(rewrite_args());

// This must remain before tracing, environment capture, update checks, or
// any daemon client call. The elevated helper is a one-shot PnP process,
// never an alternate daemon host. FastLED/fbuild#1148.
if let Some(Commands::UsbRecoveryHelper { request, result }) = &cli.command {
if let Err(error) = run_hidden_helper(request, result) {
eprintln!("USB recovery helper failed: {error}");
std::process::exit(1);
}
return;
}

tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
Expand Down Expand Up @@ -247,6 +259,8 @@ pub async fn async_main() {
output_dir,
shrink: _,
no_shrink: _,
admin,
no_admin,
}) => {
let project_dir = resolve_project_dir(project_dir, &top_level_project_dir);
if platformio {
Expand Down Expand Up @@ -288,6 +302,13 @@ pub async fn async_main() {
emulator,
target,
output_dir,
if admin {
fbuild_core::usb::UsbRecoveryPolicy::AllowAdmin
} else if no_admin {
fbuild_core::usb::UsbRecoveryPolicy::DenyAdmin
} else {
fbuild_core::usb::UsbRecoveryPolicy::Default
},
)
.await
}
Expand Down Expand Up @@ -600,6 +621,7 @@ pub async fn async_main() {
None,
None,
None,
fbuild_core::usb::UsbRecoveryPolicy::Default,
)
.await
}
Expand All @@ -608,6 +630,9 @@ pub async fn async_main() {
Some(Commands::Bringup(args)) => run_bringup(args),
Some(Commands::Port { action }) => run_port(action),
Some(Commands::Cache { action }) => run_cache(action),
// Routed before any daemon-adjacent initialization above. This arm is
// unreachable but keeps the exhaustive command match honest.
Some(Commands::UsbRecoveryHelper { .. }) => unreachable!("helper was routed early"),
};

if let Err(e) = result {
Expand Down
7 changes: 7 additions & 0 deletions crates/fbuild-cli/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ pub mod serial_probe;
pub mod show;
pub mod symbols_cmd;
pub mod sync_cmd;
// #1148 supplies the helper foundation; #1147 is deliberately its first
// production consumer once RP2040 deployment can emit a typed request.
#[allow(
dead_code,
reason = "USB recovery integration belongs exclusively to #1147"
)]
pub mod usb_recovery;

#[cfg(test)]
mod tests;
Expand Down
22 changes: 22 additions & 0 deletions crates/fbuild-cli/src/cli/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@ use super::args::{Cli, Commands, DaemonAction};
use super::compile_many::{build_ci_pio_env, normalize_ci_sketch_entry, normalize_ci_sketches};
use clap::Parser;

#[test]
fn deploy_admin_and_no_admin_conflict() {
assert!(Cli::try_parse_from(["fbuild", "deploy", "--admin", "--no-admin"]).is_err());
}

#[test]
fn hidden_usb_recovery_helper_arguments_parse() {
let cli = Cli::try_parse_from([
"fbuild",
"__usb-recovery-helper",
"--request",
"request.json",
"--result",
"result.json",
])
.expect("parse hidden helper");
assert!(matches!(
cli.command,
Some(Commands::UsbRecoveryHelper { .. })
));
}

#[test]
fn normalize_ino_path_strips_to_parent_dir() {
let entry = "examples/Blink/Blink.ino";
Expand Down
Loading
Loading