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
6 changes: 3 additions & 3 deletions src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1469,14 +1469,14 @@ async fn check_and_populate_dependencies(
if path.is_dir() {
if path.join(RUST_SRC_PATH).exists() && !checked_rust && !skip_deps_check {
let deps = check_rust_deps()?;
get_deps(deps, &mut recv_kill, verbose).await?;
get_deps(deps, &mut recv_kill, false, verbose).await?;
checked_rust = true;
} else if path.join(PYTHON_SRC_PATH).exists() && !checked_py {
check_py_deps()?;
checked_py = true;
} else if path.join(JAVASCRIPT_SRC_PATH).exists() && !checked_js && !skip_deps_check {
let deps = check_js_deps()?;
get_deps(deps, &mut recv_kill, verbose).await?;
get_deps(deps, &mut recv_kill, false, verbose).await?;
checked_js = true;
} else if Some("api") == path.file_name().and_then(|s| s.to_str()) {
// read api files: to be used in build
Expand Down Expand Up @@ -1859,7 +1859,7 @@ pub async fn execute(
if !skip_deps_check {
let mut recv_kill = make_fake_kill_chan();
let deps = check_js_deps()?;
get_deps(deps, &mut recv_kill, verbose).await?;
get_deps(deps, &mut recv_kill, false, verbose).await?;
}
let valid_node = get_newest_valid_node_version(None, None)?;
for ui_dir in ui_dirs {
Expand Down
2 changes: 1 addition & 1 deletion src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ pub async fn start_chain(
tracing: bool,
) -> Result<Option<Child>> {
let deps = check_foundry_deps()?;
get_deps(deps, &mut recv_kill, verbose).await?;
get_deps(deps, &mut recv_kill, false, verbose).await?;

info!("Checking for Anvil on port {}...", port);
if wait_for_anvil(port, 1, None).await.is_ok() {
Expand Down
2 changes: 1 addition & 1 deletion src/dev_ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub async fn execute(
if !skip_deps_check {
let deps = check_js_deps()?;
let mut recv_kill = make_fake_kill_chan();
get_deps(deps, &mut recv_kill, false).await?;
get_deps(deps, &mut recv_kill, false, false).await?;
}
let valid_node = get_newest_valid_node_version(None, None)?;

Expand Down
50 changes: 49 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,9 +468,23 @@ async fn execute(
}
Some(("setup", matches)) => {
let verbose = matches.get_one::<bool>("VERBOSE").unwrap();
let docker_optional = matches.get_one::<bool>("DOCKER_OPTIONAL").unwrap();
let python_optional = matches.get_one::<bool>("PYTHON_OPTIONAL").unwrap();
let foundry_optional = matches.get_one::<bool>("FOUNDRY_OPTIONAL").unwrap();
let javascript_optional = matches.get_one::<bool>("JAVASCRIPT_OPTIONAL").unwrap();
let non_interactive = matches.get_one::<bool>("NON_INTERACTIVE").unwrap();

let mut recv_kill = build::make_fake_kill_chan();
setup::execute(&mut recv_kill, *verbose).await
setup::execute(
&mut recv_kill,
*docker_optional,
*python_optional,
*foundry_optional,
*javascript_optional,
*non_interactive,
*verbose,
)
.await
}
Some(("start-package", matches)) => {
let package_dir = PathBuf::from(matches.get_one::<String>("DIR").unwrap());
Expand Down Expand Up @@ -1249,6 +1263,40 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result<Command> {
.help("If set, output stdout and stderr")
.required(false)
)
.arg(Arg::new("DOCKER_OPTIONAL")
.action(ArgAction::SetTrue)
.short('d')
.long("docker-optional")
.help("If set, don't require Docker dep (just warn if missing)")
.required(false)
)
.arg(Arg::new("PYTHON_OPTIONAL")
.action(ArgAction::SetTrue)
.short('p')
.long("python-optional")
.help("If set, don't require Python dep (just warn if missing)")
.required(false)
)
.arg(Arg::new("FOUNDRY_OPTIONAL")
.action(ArgAction::SetTrue)
.short('f')
.long("foundry-optional")
.help("If set, don't require Foundry dep (just warn if missing)")
.required(false)
)
.arg(Arg::new("JAVASCRIPT_OPTIONAL")
.action(ArgAction::SetTrue)
.short('j')
.long("javascript-optional")
.help("If set, don't require Javascript deps (just warn if missing)")
.required(false)
)
.arg(Arg::new("NON_INTERACTIVE")
.action(ArgAction::SetTrue)
.long("non-interactive")
.help("If set, do not prompt and instead always reply `Y` to prompts (i.e. automatically install dependencies without user input)")
.required(false)
)
)
.subcommand(Command::new("start-package")
.about("Start a built Hyprware package")
Expand Down
178 changes: 116 additions & 62 deletions src/setup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ impl std::fmt::Display for Dependency {
}
}

// use Display
impl std::fmt::Debug for Dependency {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self)
}
}

// hack to allow definition of Display
struct Dependencies(Vec<Dependency>);
impl std::fmt::Display for Dependencies {
Expand Down Expand Up @@ -347,11 +354,11 @@ pub fn check_foundry_deps() -> Result<Vec<Dependency>> {
Ok(vec![])
}

/// install forge+anvil+others, could be separated into binary extractions from github releases.
/// install Foundry, could be separated into binary extractions from github releases.
#[instrument(level = "trace", skip_all)]
fn install_foundry(verbose: bool) -> Result<()> {
let download_cmd = "curl -L https://foundry.paradigm.xyz | bash";
let install_cmd = ". ~/.bashrc && foundryup";
let install_cmd = "export PATH=\"$PATH:$HOME/.foundry/bin\" && foundryup";
run_command(Command::new("bash").args(&["-c", download_cmd]), verbose)?;
run_command(Command::new("bash").args(&["-c", install_cmd]), verbose)?;

Expand Down Expand Up @@ -397,83 +404,130 @@ pub fn check_docker_deps() -> Result<Vec<Dependency>> {
pub async fn get_deps(
deps: Vec<Dependency>,
recv_kill: &mut BroadcastRecvBool,
non_interactive: bool,
verbose: bool,
) -> Result<()> {
if deps.is_empty() {
return Ok(());
}

// If setup required, request user permission
print!(
"kit requires {} missing {}: {}. Install? [Y/n]: ",
if deps.len() == 1 { "this" } else { "these" },
if deps.len() == 1 {
"dependency"
} else {
"dependencies"
},
Dependencies(deps.clone()),
);
// Flush to ensure the prompt is displayed before input
io::stdout().flush().unwrap();

// Read the user's response
let (sender, mut receiver) = tokio::sync::mpsc::channel(1);
tokio::spawn(async move {
let mut response = String::new();
io::stdin().read_line(&mut response).unwrap();
sender.send(response).await.unwrap();
});

// Process the response
let response = tokio::select! {
Some(response) = receiver.recv() => response,
k = recv_kill.recv() => {
match k {
Err(tokio::sync::broadcast::error::RecvError::Closed) => {
// some systems drop the fake sender produced in build/mod.rs:57
// make_fake_kill_chan() and so we handle this by ignoring the
// Closed message that comes through
// https://docs.rs/tokio/latest/tokio/sync/broadcast/struct.Receiver.html#method.recv
receiver.recv().await.unwrap()
if non_interactive {
install_deps(deps, verbose)?;
} else {
// If setup required, request user permission
print!(
"kit requires {} missing {}: {}. Install? [Y/n]: ",
if deps.len() == 1 { "this" } else { "these" },
if deps.len() == 1 {
"dependency"
} else {
"dependencies"
},
Dependencies(deps.clone()),
);
// Flush to ensure the prompt is displayed before input
io::stdout().flush().unwrap();

// Read the user's response
let (sender, mut receiver) = tokio::sync::mpsc::channel(1);
tokio::spawn(async move {
let mut response = String::new();
io::stdin().read_line(&mut response).unwrap();
sender.send(response).await.unwrap();
});

// Process the response
let response = tokio::select! {
Some(response) = receiver.recv() => response,
k = recv_kill.recv() => {
match k {
Err(tokio::sync::broadcast::error::RecvError::Closed) => {
// some systems drop the fake sender produced in build/mod.rs:57
// make_fake_kill_chan() and so we handle this by ignoring the
// Closed message that comes through
// https://docs.rs/tokio/latest/tokio/sync/broadcast/struct.Receiver.html#method.recv
receiver.recv().await.unwrap()
}
_ => return Err(eyre!("got exit code")),
}
_ => return Err(eyre!("got exit code")),
}
};
let response = response.trim().to_lowercase();
match response.as_str() {
"y" | "yes" | "" => install_deps(deps, verbose)?,
r => warn!("Got '{}'; not getting deps.", r),
}
};
let response = response.trim().to_lowercase();
match response.as_str() {
"y" | "yes" | "" => {
for dep in deps {
match dep {
Dependency::Nvm => install_nvm(verbose)?,
Dependency::Npm => call_with_nvm(&format!("nvm install-latest-npm"), verbose)?,
Dependency::Node => call_with_nvm(
&format!("nvm install {}.{}", REQUIRED_NODE_MAJOR, MINIMUM_NODE_MINOR,),
verbose,
)?,
Dependency::Rust => install_rust(verbose)?,
Dependency::RustWasm32Wasi => call_rustup("target add wasm32-wasip1", verbose)?,
Dependency::WasmTools => call_cargo("install wasm-tools", verbose)?,
Dependency::Foundry => install_foundry(verbose)?,
Dependency::Docker => {}
}
}
}
Ok(())
}

#[instrument(level = "trace", skip_all)]
fn install_deps(deps: Vec<Dependency>, verbose: bool) -> Result<()> {
for dep in deps {
match dep {
Dependency::Nvm => install_nvm(verbose)?,
Dependency::Npm => call_with_nvm(&format!("nvm install-latest-npm"), verbose)?,
Dependency::Node => call_with_nvm(
&format!("nvm install {}.{}", REQUIRED_NODE_MAJOR, MINIMUM_NODE_MINOR,),
verbose,
)?,
Dependency::Rust => install_rust(verbose)?,
Dependency::RustWasm32Wasi => call_rustup("target add wasm32-wasip1", verbose)?,
Dependency::WasmTools => call_cargo("install wasm-tools", verbose)?,
Dependency::Foundry => install_foundry(verbose)?,
Dependency::Docker => {}
}
r => warn!("Got '{}'; not getting deps.", r),
}
Ok(())
}

#[instrument(level = "trace", skip_all)]
pub async fn execute(recv_kill: &mut BroadcastRecvBool, verbose: bool) -> Result<()> {
pub async fn execute(
recv_kill: &mut BroadcastRecvBool,
docker_optional: bool,
python_optional: bool,
foundry_optional: bool,
javascript_optional: bool,
non_interactive: bool,
verbose: bool,
) -> Result<()> {
info!("Setting up...");

check_py_deps()?;
let mut missing_deps = check_js_deps()?;
missing_deps.append(&mut check_rust_deps()?);
missing_deps.append(&mut check_docker_deps()?);
get_deps(missing_deps, recv_kill, verbose).await?;
let py_result = check_py_deps();
if !python_optional {
py_result?;
} else {
if let Err(e) = py_result {
warn!("Python deps are not satisfied: {e}");
}
}

let mut missing_deps = check_rust_deps()?;

let mut js_deps = check_js_deps()?;
if !javascript_optional {
missing_deps.append(&mut js_deps);
} else {
warn!("JavaScript deps are not satisfied: {js_deps:?}");
}

let docker_result = check_docker_deps();
if !docker_optional {
missing_deps.append(&mut docker_result?);
} else {
if let Err(e) = docker_result {
warn!("Docker deps are not satisfied: {e}");
}
}

let mut foundry_deps = check_foundry_deps()?;
if !foundry_optional {
missing_deps.append(&mut foundry_deps);
} else {
warn!("Foundry deps are not satisfied: {foundry_deps:?}");
}

get_deps(missing_deps, recv_kill, non_interactive, verbose).await?;

info!("Done setting up.");
Ok(())
Expand Down