Skip to content

Commit

Permalink
feat: ctrl+c/sigint handling and working directory override fix (#27)
Browse files Browse the repository at this point in the history
* feat: Add ctrl+c exit handling and optional force exit 
* fix: update quiet example in readme
* fix: make sure custom directory is absolute
  • Loading branch information
dav1do authored Jan 10, 2024
1 parent def31f0 commit 2c89583
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 49 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ by option.

If you don't want to step through prompts at all, you can use wheel in "quiet" mode, which will default to clay

wheel --working-directory <path to setup your work in> quiet --network <one of inmemory|local|dev|clay|mainnet> --did <did> --pk <pk>
wheel --working-directory <path to setup your work in> quiet --network <one of in-memory|local|dev|clay|mainnet> generate

This requires you to have already setup a DID and [CAS Auth](#cas-auth). Please run `wheel --help` for more options.
You can also pass an existing DID and PK via the `specify` option instead of `generate`. This requires you to have already setup a DID and [CAS Auth](#cas-auth). Please run `wheel --help` for more options.

### CAS Auth

Expand Down
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ serde_json.workspace = true
spinners = "4.1"
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio-rustls", "sqlite"] }
ssi = "0.7"
tokio = { version = "1.25", default-features = false, features = ["fs", "macros", "process", "rt", "rt-multi-thread"] }
tokio = { version = "1.25", default-features = false, features = ["fs", "macros", "process", "rt", "rt-multi-thread", "signal"] }
which = "4.4"
zip = "0.6"

Expand Down
2 changes: 1 addition & 1 deletion cli/src/install/ceramic_daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub async fn install_ceramic_daemon(
if let Ok(exit) = process.wait().await {
let _ = tx.send(exit.clone()).await;
log::info!(
"Ceramic exited with code {}",
"\nCeramic exited with code {}",
exit.code().unwrap_or_else(|| 0)
);
if !exit.success() {
Expand Down
152 changes: 107 additions & 45 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use anyhow::{bail, Ok};
use clap::{Parser, Subcommand, ValueEnum};
use log::LevelFilter;
use std::fmt::Formatter;
use std::io::Write;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};

#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
enum Network {
Expand Down Expand Up @@ -92,6 +94,8 @@ struct ProgramArgs {
command: Option<Commands>,
}

static CANCEL_REQUEST_CNT: AtomicUsize = AtomicUsize::new(0);

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let _ = env_logger::builder()
Expand All @@ -104,63 +108,121 @@ async fn main() -> anyhow::Result<()> {
let working_directory = args
.working_directory
.map(PathBuf::from)
.and_then(|p| {
if p.is_absolute() {
Some(p)
} else {
Some(current_directory.join(p))
}
})
.unwrap_or_else(|| current_directory);
let mut versions = wheel_3box::Versions::default();
if let Some(v) = args.ceramic_version {
if let Some(ref v) = args.ceramic_version {
versions.ceramic = Some(v.parse()?);
}
if let Some(v) = args.composedb_version {
if let Some(ref v) = args.composedb_version {
versions.composedb = Some(v.parse()?);
}
if let Some(v) = args.template_branch {
versions.template_branch = Some(v);
}

let opt_child = match args.command {
None => {
log::info!("Starting wheel interactive configuration");
wheel_3box::interactive_default(working_directory, versions).await?
}
Some(Commands::Quiet(q)) => {
let network = match q.network {
Network::InMemory => wheel_3box::NetworkIdentifier::InMemory,
Network::Local => wheel_3box::NetworkIdentifier::Local,
Network::Dev => wheel_3box::NetworkIdentifier::Dev,
Network::Clay => wheel_3box::NetworkIdentifier::Clay,
Network::Mainnet => wheel_3box::NetworkIdentifier::Mainnet,
};
let with_composedb = q.setup == Setup::ComposeDB;
let with_app_template = q.setup == Setup::DemoApplication;
let did = if let DidCommand::Specify(opts) = q.did {
Some(wheel_3box::DidOptions {
did: opts.did,
private_key: opts.private_key,
})
} else {
None
};
wheel_3box::quiet(wheel_3box::QuietOptions {
project_name: q.project_name,
working_directory: working_directory,
network_identifier: network,
versions,
did,
with_ceramic: with_app_template || with_composedb || q.setup == Setup::CeramicOnly,
with_composedb: with_app_template || with_composedb,
with_app_template: with_app_template,
})
.await?
let (shutdown_tx, mut shutdown_rx) = tokio::sync::broadcast::channel::<()>(8);

let main_task = tokio::spawn(async move {
let opt_child = match args.command {
None => {
log::info!("Starting wheel interactive configuration");

tokio::select! {
res = wheel_3box::interactive_default(working_directory, versions) => {
res?
},
_shutdown = shutdown_rx.recv() => {
log::info!("\nReceived shutdown request, exiting interactive setup");
return Ok(())
}
}
}
Some(Commands::Quiet(q)) => {
let network = match q.network {
Network::InMemory => wheel_3box::NetworkIdentifier::InMemory,
Network::Local => wheel_3box::NetworkIdentifier::Local,
Network::Dev => wheel_3box::NetworkIdentifier::Dev,
Network::Clay => wheel_3box::NetworkIdentifier::Clay,
Network::Mainnet => wheel_3box::NetworkIdentifier::Mainnet,
};
let with_composedb = q.setup == Setup::ComposeDB;
let with_app_template = q.setup == Setup::DemoApplication;
let did = if let DidCommand::Specify(opts) = q.did {
Some(wheel_3box::DidOptions {
did: opts.did,
private_key: opts.private_key,
})
} else {
None
};

let opts = wheel_3box::QuietOptions {
project_name: q.project_name,
working_directory: working_directory,
network_identifier: network,
versions,
did,
with_ceramic: with_app_template
|| with_composedb
|| q.setup == Setup::CeramicOnly,
with_composedb: with_app_template || with_composedb,
with_app_template: with_app_template,
};

tokio::select! {
res = wheel_3box::quiet(opts) => {
res?
},
_shutdown = shutdown_rx.recv() => {
log::info!("\nReceived shutdown request, exiting quiet setup");
return Ok(())
}
}
}
};

log::info!("Wheel setup is complete. If running a clay or mainnet node, please check out https://github.com/ceramicstudio/simpledeploy to deploy with k8s.");

if let Some(child) = opt_child {
log::info!("Ceramic is now running in the background. Please use another terminal for additional commands. You can interrupt ceramic using ctrl-c.");
// we could select!, but the child daemon handles ctrl+c, so we ignore it so it gets a chance to exit gracefully.
child.await?;
}
};

log::info!("Wheel setup is complete. If running a clay or mainnet node, please check out https://github.com/ceramicstudio/simpledeploy to deploy with k8s.");
Ok(())
});

if let Some(child) = opt_child {
log::info!("Ceramic is now running in the background. Please use another terminal for additional commands. You can interrupt ceramic using ctrl-c.");
child.await?;
} else {
log::info!("Wheel setup is complete.");
}
let abort_handle = main_task.abort_handle();

let resp = tokio::select! {
res = main_task => {
res?
},
ctrl_c = tokio::signal::ctrl_c() => {
if let Err(e) = ctrl_c {
log::error!("\nError attaching ctrl-c handler: {}", e);
bail!("Error listening for ctrl-c")
}
log::info!("\nReceived ctrl-c, shutting down gracefully. Press ctrl-c again to force shutdown.");
if let Err(_e) = shutdown_tx.send(()) {
log::warn!("\nError notifying child tasks of shutdown request");
}

let cnt = CANCEL_REQUEST_CNT.fetch_add(1, Ordering::SeqCst);
if cnt > 0 {
log::info!("\nReceived additional ctrl-c, forcing shutdown");
abort_handle.abort();
}
Ok(())
}
};

Ok(())
resp
}

0 comments on commit 2c89583

Please sign in to comment.