From 7f83159701860ff7aa73c3452cf9095515af3c12 Mon Sep 17 00:00:00 2001 From: Adrien Derobert-Mazure Date: Wed, 26 Mar 2025 15:54:35 +0100 Subject: [PATCH] Added a way to hook into the serve process. Added a way to hook into the serve process after the build. ``` mdbook serve -c ``` --- src/cmd/serve.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index 7b1ccab6eb..b65ba0ed79 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -10,6 +10,7 @@ use mdbook::utils::fs::get_404_output_file; use mdbook::MDBook; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::PathBuf; +use std::process::Stdio; use tokio::sync::broadcast; use warp::ws::Message; use warp::Filter; @@ -17,6 +18,15 @@ use warp::Filter; /// The HTTP endpoint for the websocket used to trigger reloads when a file changes. const LIVE_RELOAD_ENDPOINT: &str = "__livereload"; +#[cfg(target_family = "unix")] +const LAUNCH_SHELL_COMMAND: &str = "sh"; +#[cfg(target_family = "unix")] +const LAUNCH_SHELL_FLAG: &str = "-c"; +#[cfg(target_family = "windows")] +const LAUNCH_SHELL_COMMAND: &str = "cmd"; +#[cfg(target_family = "windows")] +const LAUNCH_SHELL_FLAG: &str = "/C"; + // Create clap subcommand arguments pub fn make_subcommand() -> Command { Command::new("serve") @@ -41,10 +51,28 @@ pub fn make_subcommand() -> Command { .value_parser(NonEmptyStringValueParser::new()) .help("Port to use for HTTP connections"), ) + .arg( + Arg::new("post-build") + .short('c') + .long("post-build") + .num_args(1) + .value_parser(NonEmptyStringValueParser::new()) + .help("Command to run after the build is completed and before reload notification is sent.") + ) .arg_open() .arg_watcher() } +pub fn run_post_build_command(cmd: &str, book_dir: &PathBuf) -> Result<()> { + std::process::Command::new(LAUNCH_SHELL_COMMAND) + .args([LAUNCH_SHELL_FLAG, cmd]) + .current_dir(book_dir) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn()?; + Ok(()) +} + // Serve command implementation pub fn execute(args: &ArgMatches) -> Result<()> { let book_dir = get_book_dir(args); @@ -53,6 +81,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let port = args.get_one::("port").unwrap(); let hostname = args.get_one::("hostname").unwrap(); let open_browser = args.get_flag("open"); + let post_build = args.get_one::("post-build"); let address = format!("{hostname}:{port}"); @@ -68,6 +97,9 @@ pub fn execute(args: &ArgMatches) -> Result<()> { }; update_config(&mut book); book.build()?; + if let Some(cmd) = post_build { + run_post_build_command(cmd, &book_dir)?; + } let sockaddr: SocketAddr = address .to_socket_addrs()? @@ -99,7 +131,11 @@ pub fn execute(args: &ArgMatches) -> Result<()> { #[cfg(feature = "watch")] { let watcher = watch::WatcherKind::from_str(args.get_one::("watcher").unwrap()); + let book_dir_alt = book_dir.clone(); watch::rebuild_on_change(watcher, &book_dir, &update_config, &move || { + if let Some(cmd) = post_build { + let _ = run_post_build_command(cmd, &book_dir_alt); + } let _ = tx.send(Message::text("reload")); }); }