From eceb1bd79d3b3ab5b3818039aa0d533bb61f3ad7 Mon Sep 17 00:00:00 2001 From: Gijs Burghoorn Date: Sat, 4 Sep 2021 08:10:02 +0200 Subject: [PATCH] Added prompt for when zola build output-dir mentions an existing directory. (#1558) * Next version * Added ask prompt for output-dir flag Added `ask_bool` prompt for `--output-dir` for when the output directory targeted already exists in the file system. [Issue: #1378] * Updated the documentation for #1378 * Added missing "sure" in prompt text * Added timeout to prompt + dirname * Fixed complication errors Co-authored-by: Vincent Prouillet --- CHANGELOG.md | 1 + Cargo.toml | 2 +- .../getting-started/cli-usage.md | 2 +- src/cmd/build.rs | 26 ++++++++++++++++++- src/prompt.rs | 20 ++++++++++++++ 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 133382d0..343dacd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog + ## 0.15.0 diff --git a/Cargo.toml b/Cargo.toml index 02235212..a737e008 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ termcolor = "1.0.4" url = "2" # Below is for the serve cmd hyper = { version = "0.14.1", default-features = false, features = ["runtime", "server", "http2", "http1"] } -tokio = { version = "1.0.1", default-features = false, features = ["rt", "fs"] } +tokio = { version = "1.0.1", default-features = false, features = ["rt", "fs", "time"] } percent-encoding = "2" notify = "4" ws = "0.9" diff --git a/docs/content/documentation/getting-started/cli-usage.md b/docs/content/documentation/getting-started/cli-usage.md index c7de069d..73b0cb22 100644 --- a/docs/content/documentation/getting-started/cli-usage.md +++ b/docs/content/documentation/getting-started/cli-usage.md @@ -46,7 +46,7 @@ $ zola build --base-url $DEPLOY_URL This is useful for example when you want to deploy previews of a site to a dynamic URL, such as Netlify deploy previews. -You can override the default output directory `public` by passing another value to the `output-dir` flag (if this directory already exists, it is deleted). +You can override the default output directory `public` by passing another value to the `output-dir` flag (if this directory already exists, the user will be prompted whether to replace the folder). ```bash $ zola build --output-dir $DOCUMENT_ROOT diff --git a/src/cmd/build.rs b/src/cmd/build.rs index 7a0e85cb..21dd456a 100644 --- a/src/cmd/build.rs +++ b/src/cmd/build.rs @@ -1,9 +1,12 @@ use std::path::Path; -use errors::Result; +use errors::{Error, Result}; use site::Site; use crate::console; +use crate::prompt::ask_bool_timeout; + +const BUILD_PROMPT_TIMEOUT_MILLIS: u64 = 10_000; pub fn build( root_dir: &Path, @@ -14,6 +17,27 @@ pub fn build( ) -> Result<()> { let mut site = Site::new(root_dir, config_file)?; if let Some(output_dir) = output_dir { + // Check whether output directory exists or not + // This way we don't replace already existing files. + if output_dir.exists() { + console::warn(&format!("The directory '{}' already exists. Building to this directory will delete files contained within this directory.", output_dir.display())); + + // Prompt the user to ask whether they want to continue. + let clear_dir = tokio::runtime::Runtime::new() + .expect("Tokio runtime failed to instantiate") + .block_on(ask_bool_timeout( + "Are you sure you want to continue?", + false, + std::time::Duration::from_millis(BUILD_PROMPT_TIMEOUT_MILLIS), + ))?; + + if !clear_dir { + return Err(Error::msg( + "Cancelled build process because output directory already exists.", + )); + } + } + site.set_output_path(output_dir); } if let Some(b) = base_url { diff --git a/src/prompt.rs b/src/prompt.rs index 134cb576..aec14fd4 100644 --- a/src/prompt.rs +++ b/src/prompt.rs @@ -1,8 +1,10 @@ use std::io::{self, BufRead, Write}; +use std::time::Duration; use url::Url; use errors::Result; +use crate::console; /// Wait for user input and return what they typed fn read_line() -> Result { @@ -32,6 +34,24 @@ pub fn ask_bool(question: &str, default: bool) -> Result { } } +/// Ask a yes/no question to the user with a timeout +pub async fn ask_bool_timeout(question: &str, default: bool, timeout: Duration) -> Result { + let (tx, rx) = tokio::sync::oneshot::channel(); + + let q = question.to_string(); + std::thread::spawn(move || { + tx.send(ask_bool(&q, default)).unwrap(); + }); + + match tokio::time::timeout(timeout, rx).await { + Err(_) => { + console::warn("\nWaited too long for response."); + Ok(default) + } + Ok(val) => val.expect("Tokio failed to properly execute"), + } +} + /// Ask a question to the user where they can write a URL pub fn ask_url(question: &str, default: &str) -> Result { print!("{} ({}): ", question, default);