Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improve dfx start messages. #3946

Merged
merged 12 commits into from
Nov 12, 2024
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ Allow setting permissions lists in init arguments just like in upgrade arguments
- Module hash: f45db224b40fac516c877e3108dc809d4b22fa42d05ee8dfa5002536a3a3daed
- Bump agent-js to fix error code

### chore: improve `dfx start` messages.

For `dfx start`, show below messages to users to indicate what to do next.
```
Success! The dfx server is running.
You must open a new terminal to continue developing. If you'd prefer to stop, quit with 'Ctrl-C'.
```

# 0.24.2

### feat: Support canister log allowed viewer list
Expand Down
3 changes: 2 additions & 1 deletion e2e/tests-dfx/start.bash
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ teardown() {
}

@test "start and stop outside project" {
dfx_start
assert_command dfx_start
assert_contains "Success! The dfx server is running in the background."

mkdir subdir
cd subdir || exit 1
Expand Down
16 changes: 16 additions & 0 deletions src/dfx/src/actors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use dfx_core::config::model::replica_config::ReplicaConfig;
use fn_error_context::context;
use pocketic_proxy::signals::PortReadySubscribe;
use pocketic_proxy::{PocketIcProxy, PocketIcProxyConfig};
use post_start::PostStart;
use std::fs;
use std::path::PathBuf;

Expand All @@ -22,6 +23,7 @@ pub mod btc_adapter;
pub mod canister_http_adapter;
pub mod pocketic;
pub mod pocketic_proxy;
pub mod post_start;
pub mod replica;
mod shutdown;
pub mod shutdown_controller;
Expand Down Expand Up @@ -214,3 +216,17 @@ pub fn start_pocketic_actor(
};
Ok(pocketic::PocketIc::new(actor_config).start())
}

#[context("Failed to start PostStart actor.")]
pub fn start_post_start_actor(
env: &dyn Environment,
background: bool,
pocketic_proxy: Option<Addr<PocketIcProxy>>,
) -> DfxResult<Addr<PostStart>> {
let config = post_start::Config {
logger: env.get_logger().clone(),
background,
pocketic_proxy,
};
Ok(PostStart::new(config).start())
}
36 changes: 32 additions & 4 deletions src/dfx/src/actors/pocketic_proxy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::actors::pocketic_proxy::signals::{PortReadySignal, PortReadySubscribe};
use crate::actors::post_start::signals::{PocketIcProxyReadySignal, PocketIcProxyReadySubscribe};
use crate::actors::shutdown::{wait_for_child_or_receiver, ChildOrReceiver};
use crate::actors::shutdown_controller::signals::outbound::Shutdown;
use crate::actors::shutdown_controller::signals::ShutdownSubscribe;
Expand Down Expand Up @@ -70,6 +71,9 @@ pub struct PocketIcProxy {

stop_sender: Option<Sender<()>>,
thread_join: Option<JoinHandle<()>>,

/// Ready Signal subscribers.
ready_subscribers: Vec<Recipient<PocketIcProxyReadySignal>>,
}

impl PocketIcProxy {
Expand All @@ -81,10 +85,11 @@ impl PocketIcProxy {
stop_sender: None,
thread_join: None,
logger,
ready_subscribers: Vec::new(),
}
}

fn start_pocketic_proxy(&mut self, replica_url: Url) -> DfxResult {
fn start_pocketic_proxy(&mut self, replica_url: Url, addr: Addr<Self>) -> DfxResult {
let logger = self.logger.clone();
let config = &self.config.pocketic_proxy_config;
let pocketic_proxy_path = self.config.pocketic_proxy_path.clone();
Expand All @@ -100,6 +105,7 @@ impl PocketIcProxy {
pocketic_proxy_path,
pocketic_proxy_pid_path,
pocketic_proxy_port_path,
addr,
receiver,
config.verbose,
config.domains.clone(),
Expand Down Expand Up @@ -164,7 +170,7 @@ impl Actor for PocketIcProxy {
.do_send(ShutdownSubscribe(ctx.address().recipient::<Shutdown>()));

if let Some(replica_url) = &self.config.pocketic_proxy_config.replica_url {
self.start_pocketic_proxy(replica_url.clone())
self.start_pocketic_proxy(replica_url.clone(), ctx.address())
.expect("Could not start PocketIC HTTP gateway");
}
}
Expand All @@ -179,7 +185,7 @@ impl Actor for PocketIcProxy {
impl Handler<PortReadySignal> for PocketIcProxy {
type Result = ();

fn handle(&mut self, msg: PortReadySignal, _ctx: &mut Self::Context) {
fn handle(&mut self, msg: PortReadySignal, ctx: &mut Self::Context) {
debug!(
self.logger,
"replica ready on {}, so re/starting HTTP gateway", msg.url
Expand All @@ -189,11 +195,29 @@ impl Handler<PortReadySignal> for PocketIcProxy {

let replica_url = Url::parse(&msg.url).unwrap();

self.start_pocketic_proxy(replica_url)
self.start_pocketic_proxy(replica_url, ctx.address())
.expect("Could not start PocketIC HTTP gateway");
}
}

impl Handler<PocketIcProxyReadySubscribe> for PocketIcProxy {
type Result = ();

fn handle(&mut self, msg: PocketIcProxyReadySubscribe, _ctx: &mut Self::Context) {
self.ready_subscribers.push(msg.0);
}
}

impl Handler<PocketIcProxyReadySignal> for PocketIcProxy {
type Result = ();

fn handle(&mut self, _msg: PocketIcProxyReadySignal, _ctx: &mut Self::Context) {
for sub in &self.ready_subscribers {
sub.do_send(PocketIcProxyReadySignal);
}
}
}

impl Handler<Shutdown> for PocketIcProxy {
type Result = ResponseActFuture<Self, Result<(), ()>>;

Expand All @@ -217,6 +241,7 @@ fn pocketic_proxy_start_thread(
pocketic_proxy_path: PathBuf,
pocketic_proxy_pid_path: PathBuf,
pocketic_proxy_port_path: PathBuf,
addr: Addr<PocketIcProxy>,
receiver: Receiver<()>,
verbose: bool,
domains: Option<Vec<String>>,
Expand Down Expand Up @@ -277,6 +302,9 @@ fn pocketic_proxy_start_thread(
}
info!(logger, "Replica API running on {address}");

// Send PocketIcProxyReadySignal to PocketIcProxy.
addr.do_send(PocketIcProxyReadySignal);

// This waits for the child to stop, or the receiver to receive a message.
// We don't restart pocket-ic if done = true.
match wait_for_child_or_receiver(&mut child, &receiver) {
Expand Down
62 changes: 62 additions & 0 deletions src/dfx/src/actors/post_start.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::actors::pocketic_proxy::PocketIcProxy;
use crate::actors::post_start::signals::{PocketIcProxyReadySignal, PocketIcProxyReadySubscribe};
use actix::{Actor, Addr, AsyncContext, Context, Handler};
use slog::{info, Logger};

pub mod signals {
use actix::prelude::*;

#[derive(Message)]
#[rtype(result = "()")]
pub struct PocketIcProxyReadySignal;

#[derive(Message)]
#[rtype(result = "()")]
pub struct PocketIcProxyReadySubscribe(pub Recipient<PocketIcProxyReadySignal>);
}

pub struct Config {
pub logger: Logger,
pub background: bool,
pub pocketic_proxy: Option<Addr<PocketIcProxy>>,
}

pub struct PostStart {
config: Config,
}

impl PostStart {
pub fn new(config: Config) -> Self {
Self { config }
}
}

impl Actor for PostStart {
type Context = Context<Self>;

fn started(&mut self, ctx: &mut Self::Context) {
// Register the PostStart recipent to PocketIcProxy.
if let Some(pocketic_proxy) = &self.config.pocketic_proxy {
pocketic_proxy.do_send(PocketIcProxyReadySubscribe(ctx.address().recipient()));
}
}
}

impl Handler<PocketIcProxyReadySignal> for PostStart {
type Result = ();

fn handle(&mut self, _msg: PocketIcProxyReadySignal, _ctx: &mut Self::Context) -> Self::Result {
let logger = &self.config.logger;
if self.config.background {
info!(
logger,
"Success! The dfx server is running in the background."
)
} else {
info!(
logger,
"Success! The dfx server is running.\nYou must open a new terminal to continue developing. If you'd prefer to stop, quit with 'Ctrl-C'."
)
}
}
}
16 changes: 13 additions & 3 deletions src/dfx/src/commands/start.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::actors::pocketic_proxy::{signals::PortReadySubscribe, PocketIcProxyConfig};
use crate::actors::{
start_btc_adapter_actor, start_canister_http_adapter_actor, start_pocketic_actor,
start_pocketic_proxy_actor, start_replica_actor, start_shutdown_controller,
start_pocketic_proxy_actor, start_post_start_actor, start_replica_actor,
start_shutdown_controller,
};
use crate::config::dfx_version_str;
use crate::error_invalid_argument;
Expand Down Expand Up @@ -49,6 +50,10 @@ pub struct StartOpts {
#[arg(long)]
background: bool,

/// Indicates if the actual dfx process is running in the background.
#[arg(long, env = "DFX_RUNNING_IN_BACKGROUND", hide = true)]
running_in_background: bool,

/// Cleans the state of the current project.
#[arg(long)]
clean: bool,
Expand Down Expand Up @@ -138,6 +143,7 @@ pub fn exec(
StartOpts {
host,
background,
running_in_background,
clean,
force,
bitcoin_node,
Expand Down Expand Up @@ -413,7 +419,10 @@ pub fn exec(
pocketic_proxy_pid_file_path,
pocketic_proxy_port_file_path,
)?;
Ok::<_, Error>(proxy)

let post_start = start_post_start_actor(env, running_in_background, Some(proxy))?;

Ok::<_, Error>(post_start)
})?;
system.run()?;

Expand Down Expand Up @@ -574,7 +583,8 @@ fn send_background() -> DfxResult<()> {
.skip(1)
.filter(|a| !a.eq("--background"))
.filter(|a| !a.eq("--clean")),
);
)
.env("DFX_RUNNING_IN_BACKGROUND", "true"); // Set the `DFX_RUNNING_IN_BACKGROUND` environment variable which will be used by the second start.

cmd.spawn().context("Failed to spawn child process.")?;
Ok(())
Expand Down
Loading