From 260aa46ab33956d2aa942f0e0347e3a5ee682909 Mon Sep 17 00:00:00 2001 From: Klimenty Titov Date: Wed, 18 Dec 2024 21:39:56 +0300 Subject: [PATCH] Add build log + make `project_name` is no longer optional --- Cargo.toml | 2 ++ src/build.rs | 28 ++++++++++++++++++------- src/configs.rs | 3 +-- src/entities/environment.rs | 1 + src/main.rs | 20 +++++++----------- src/project.rs | 4 ++-- src/rw.rs | 42 +++++++++++++++++++++++++++++++++++-- 7 files changed, 74 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c656ef2..d604f86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] anyhow = "1.0" +chrono = "0.4" clap = { version = "4.5", features = ["derive"] } colored = "2" dirs = "5.0" @@ -13,6 +14,7 @@ mimalloc = "0.1.43" regex = "1.11" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +strip-ansi-escapes = "0.2" uuid = { version = "1.11", features = ["v4", "fast-rng"] } [features] diff --git a/src/build.rs b/src/build.rs index c19e112..a7d2cfc 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,5 +1,5 @@ use colored::Colorize; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use uuid::Uuid; use crate::{CACHE_DIR, ARTIFACTS_DIR, PROJECT_CONF}; @@ -11,7 +11,7 @@ use crate::entities::{ use crate::cmd::{BuildArgs, CleanArgs}; use crate::configs::DeployerProjectOptions; use crate::pipelines::DescribedPipeline; -use crate::rw::{copy_all, write, symlink, log}; +use crate::rw::{copy_all, write, symlink, log, generate_build_log_filepath, build_log}; use crate::utils::get_current_working_dir; fn enplace_artifacts( @@ -34,7 +34,7 @@ fn enplace_artifacts( Ok(()) } -pub(crate) fn prepare_artifacts_folder( +fn prepare_artifacts_folder( current_dir: &std::path::Path, ) -> anyhow::Result { let artifacts_dir = current_dir.join(ARTIFACTS_DIR); @@ -43,10 +43,10 @@ pub(crate) fn prepare_artifacts_folder( Ok(artifacts_dir) } -pub(crate) fn prepare_build_folder( +fn prepare_build_folder( config: &mut DeployerProjectOptions, current_dir: &std::path::Path, - cache_dir: &str, + cache_dir: &Path, mut fresh: bool, link_cache: bool, copy_cache: bool, @@ -103,7 +103,7 @@ pub(crate) fn prepare_build_folder( pub(crate) fn build( config: &mut DeployerProjectOptions, - cache_dir: &str, + cache_dir: &Path, args: &BuildArgs, ) -> anyhow::Result<()> { if *config == Default::default() { panic!("Config is invalid!"); } @@ -128,6 +128,7 @@ pub(crate) fn build( let env = BuildEnvironment { build_dir: &build_path, + cache_dir, artifacts_dir: &artifacts_dir, new_build, silent_build: args.silent, @@ -170,7 +171,15 @@ fn execute_pipeline( use std::io::{stdout, Write}; use std::time::Instant; + let log_file = generate_build_log_filepath( + &config.project_name, + &pipeline.title, + env.cache_dir, + ); + if !env.silent_build { println!("Starting the `{}` Pipeline...", pipeline.title); } + build_log(&log_file, &[format!("Starting the `{}` Pipeline...", pipeline.title)])?; + let mut cntr = 1usize; let total = pipeline.actions.len(); for action in &pipeline.actions { @@ -180,6 +189,7 @@ fn execute_pipeline( } else { println!("[{}/{}] `{}` Action... ", cntr, total, action.title.blue().italic()); } + build_log(&log_file, &[format!("[{}/{}] `{}` Action... ", cntr, total, action.title)])?; } stdout().flush()?; let now = Instant::now(); @@ -220,10 +230,14 @@ fn execute_pipeline( if !env.no_pipe { println!("{} ({}).", status_str, format!("{:.2?}", elapsed).green()); + build_log(&log_file, &output)?; for line in output { println!("{}", line); } } else { println!("[{}/{}] `{}` Action - {} ({}).", cntr, total, action.title.blue().italic(), status_str, format!("{:.2?}", elapsed).green()); } + build_log(&log_file, &[ + format!("[{}/{}] `{}` Action - {} ({:.2?}).", cntr, total, action.title, if status { "done" } else { "got an error!" }, elapsed), + ])?; } cntr += 1; @@ -236,7 +250,7 @@ fn execute_pipeline( pub(crate) fn clean_builds( config: &mut DeployerProjectOptions, - cache_dir: &str, + cache_dir: &Path, args: &CleanArgs, ) -> anyhow::Result<()> { let mut path = PathBuf::new(); diff --git a/src/configs.rs b/src/configs.rs index 6c497ca..a7cfca6 100644 --- a/src/configs.rs +++ b/src/configs.rs @@ -17,8 +17,7 @@ use crate::utils::ordered_map; #[derive(Deserialize, Serialize, PartialEq, Default, Debug)] pub(crate) struct DeployerProjectOptions { /// Название проекта. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) project_name: Option, + pub(crate) project_name: String, /// Языки pub(crate) langs: Vec, /// Таргеты diff --git a/src/entities/environment.rs b/src/entities/environment.rs index 6374e79..caa54fc 100644 --- a/src/entities/environment.rs +++ b/src/entities/environment.rs @@ -3,6 +3,7 @@ use std::path::Path; #[derive(Clone, Copy)] pub(crate) struct BuildEnvironment<'a> { pub(crate) build_dir: &'a Path, + pub(crate) cache_dir: &'a Path, pub(crate) artifacts_dir: &'a Path, pub(crate) new_build: bool, pub(crate) silent_build: bool, diff --git a/src/main.rs b/src/main.rs index 50a9124..a3e2861 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,7 +43,7 @@ static PROJECT_CONF: &str = "deploy-config.json"; static GLOBAL_CONF: &str = "deploy-global.json"; pub(crate) static CACHE_DIR: &str = "deploy-cache"; -// pub(crate) static LOGS_DIR: &str = "logs"; +pub(crate) static LOGS_DIR: &str = "logs"; pub(crate) static ARTIFACTS_DIR: &str = "artifacts"; @@ -72,22 +72,16 @@ fn main() { // Определение рабочих директорий let cache_folder = if args.cache_folder.is_none() { - cache_dir() - .expect("Can't get `cache` directory's location automatically, please specify one.") - .to_str() - .expect("Can't convert `PathBuf` to `str` for `cache` folder!") - .to_string() + cache_dir().expect("Can't get `cache` directory's location automatically, please specify one.") } else { - args.cache_folder.as_ref().unwrap().to_string() + let path = std::path::PathBuf::new(); + path.join(args.cache_folder.as_ref().unwrap()) }; let config_folder = if args.config_folder.is_none() { - config_dir() - .expect("Can't get `config` directory's location automatically, please specify one.") - .to_str() - .expect("Can't convert `PathBuf` to `str` for `config` folder!") - .to_string() + config_dir().expect("Can't get `config` directory's location automatically, please specify one.") } else { - args.config_folder.as_ref().unwrap().to_string() + let path = std::path::PathBuf::new(); + path.join(args.config_folder.as_ref().unwrap()) }; // Чтение конфигов diff --git a/src/project.rs b/src/project.rs index a948290..817ce8d 100644 --- a/src/project.rs +++ b/src/project.rs @@ -13,7 +13,7 @@ impl DeployerProjectOptions { pub(crate) fn init_from_prompt(&mut self) -> anyhow::Result<()> { use inquire::Text; - self.project_name = Text::new("Enter the project's name (or hit `esc`):").prompt_skippable()?; + self.project_name = Text::new("Enter the project's name:").prompt()?; self.cache_files.push(".git".to_string()); println!("Please, specify the project's programming languages to setup default cache folders."); @@ -56,7 +56,7 @@ impl DeployerProjectOptions { actions.clone(), ).prompt_skippable()? { match action { - "Edit project name" => self.project_name = inquire::Text::new("Enter the project's name (or hit `esc`):").prompt_skippable()?, + "Edit project name" => self.project_name = inquire::Text::new("Enter the project's name:").prompt()?, "Edit cache files" => self.cache_files.edit_from_prompt()?, "Edit programming languages" => self.langs.edit_from_prompt()?, "Edit targets" => self.targets.edit_from_prompt()?, diff --git a/src/rw.rs b/src/rw.rs index 9cb7df3..cb1f6d0 100644 --- a/src/rw.rs +++ b/src/rw.rs @@ -3,9 +3,10 @@ use std::fs::File; use std::io::{BufReader, BufWriter}; use std::sync::OnceLock; use std::path::{Path, PathBuf}; -use crate::PROJECT_CONF; +use crate::{CACHE_DIR, LOGS_DIR, PROJECT_CONF}; pub(crate) static VERBOSE: OnceLock = OnceLock::new(); +const LOG_FILE_DELIMETER: &str = "================================================================"; pub(crate) fn read(folder: impl AsRef, file: impl AsRef) -> T { let mut path = PathBuf::new(); @@ -53,7 +54,6 @@ pub(crate) fn write(folder: impl AsRef, file: impl AsRef, dst: impl AsRef, ignore: &[&str]) -> anyhow::Result<()> { if src.as_ref().is_file() { if let Some(parent) = dst.as_ref().parent() { @@ -116,3 +116,41 @@ pub(crate) fn log(s: impl AsRef) { println!("{}", s.as_ref()); } } + +pub(crate) fn generate_build_log_filepath( + project_name: &str, + pipeline_short_name: &str, + cache_dir: &Path, +) -> PathBuf { + use chrono::Utc; + + let mut logs_path = PathBuf::new(); + logs_path.push(cache_dir); + logs_path.push(CACHE_DIR); + logs_path.push(LOGS_DIR); + if !logs_path.exists() { std::fs::create_dir_all(logs_path.as_path()).unwrap_or_else(|_| panic!("Can't create `{:?}` folder!", logs_path)); } + + let curr_dt = Utc::now(); + + let log_path = logs_path.join(format!("{}-{}-{}.txt", project_name, pipeline_short_name, curr_dt.format("%Y-%m-%d-%H:%M"))); + if log_path.exists() { build_log(&log_path, &[LOG_FILE_DELIMETER.to_string()]).expect("Current log file is unwriteable!"); } + + log_path +} + +pub(crate) fn build_log( + path: &Path, + output: &[String], +) -> anyhow::Result<()> { + use std::io::Write; + + let file = File::options().create(true).append(true).open(path)?; + let mut writer = BufWriter::new(file); + for line in output { + let line = strip_ansi_escapes::strip(line.as_bytes()); + writer.write_all(&line)?; + writer.write_all("\n".as_bytes())?; + } + + Ok(()) +}