feat(cli): add server restart command

Stops any running server then starts a fresh one, passing through all
the same flags as `server start` (--watch-web, --foreground, etc.).
Works even if no server is currently running.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-04-16 07:49:16 -04:00
parent c24b78d18d
commit 77c8ac8ed3
No known key found for this signature in database
2 changed files with 47 additions and 1 deletions

View file

@ -1076,6 +1076,7 @@ impl Commands {
Self::Server(ns) => match &ns.command {
ServerCommand::Start(_) => "server start",
ServerCommand::Stop(_) => "server stop",
ServerCommand::Restart(_) => "server restart",
ServerCommand::Status(_) => "server status",
ServerCommand::Serve(_) => "server __serve",
},
@ -1239,6 +1240,23 @@ pub(crate) struct ServerStatusArgs {
pub(crate) json: bool,
}
#[derive(Args)]
pub(crate) struct ServerRestartArgs {
#[command(flatten)]
pub(crate) storage_dir: StorageDirArgs,
/// Seconds to wait for graceful shutdown before SIGKILL
#[arg(long, default_value = "10")]
pub(crate) timeout: u64,
/// Run in the foreground instead of daemonizing
#[arg(long)]
pub(crate) foreground: bool,
#[command(flatten)]
pub(crate) serve_args: ServeArgs,
}
#[derive(Args)]
pub(crate) struct ServerServeArgs {
#[command(flatten)]
@ -1258,6 +1276,8 @@ pub(crate) enum ServerCommand {
Start(ServerStartArgs),
/// Stop the HTTP API server
Stop(ServerStopArgs),
/// Stop and restart the HTTP API server
Restart(ServerRestartArgs),
/// Show server status
Status(ServerStatusArgs),
/// Internal: run the server process (spawned by `start`)

View file

@ -12,7 +12,8 @@ use fabro_util::printer::Printer;
use fabro_util::terminal::Styles;
use crate::args::{
GlobalArgs, ServerCommand, ServerServeArgs, ServerStartArgs, ServerStatusArgs, ServerStopArgs,
GlobalArgs, ServerCommand, ServerRestartArgs, ServerServeArgs, ServerStartArgs,
ServerStatusArgs, ServerStopArgs,
};
use crate::user_config;
@ -54,6 +55,31 @@ pub(crate) async fn dispatch(
stop::execute(&storage_dir, Duration::from_secs(timeout), printer).await;
Ok(())
}
ServerCommand::Restart(ServerRestartArgs {
storage_dir,
timeout,
foreground,
serve_args,
}) => {
let settings = user_config::load_settings_with_config_and_storage_dir(
serve_args.config.as_deref(),
storage_dir.as_deref(),
)?;
let storage_dir = user_config::storage_dir(&settings)?;
stop::stop_server(&storage_dir, Duration::from_secs(timeout)).await;
let bind_addr =
serve::resolve_bind_request_from_settings(&settings, serve_args.bind.as_deref())?;
let styles: &'static Styles = Box::leak(Box::new(Styles::detect_stderr()));
Box::pin(start::execute(
bind_addr,
foreground,
serve_args,
storage_dir,
styles,
printer,
))
.await
}
ServerCommand::Status(ServerStatusArgs { storage_dir, json }) => {
let settings = user_config::load_settings_with_storage_dir(storage_dir.as_deref())?;
let storage_dir = user_config::storage_dir(&settings)?;