From ebc4cb414ace2c18afb7152fcc1817eae9ef77b6 Mon Sep 17 00:00:00 2001 From: nicholaslyang Date: Mon, 7 Aug 2023 10:40:30 -0400 Subject: [PATCH] Factor out turborepo-ui crate --- Cargo.lock | 16 ++++ Cargo.toml | 1 + crates/turborepo-lib/Cargo.toml | 1 + crates/turborepo-lib/src/cli.rs | 2 +- crates/turborepo-lib/src/commands/info.rs | 2 +- crates/turborepo-lib/src/commands/link.rs | 8 +- crates/turborepo-lib/src/commands/login.rs | 4 +- crates/turborepo-lib/src/commands/logout.rs | 3 +- crates/turborepo-lib/src/commands/mod.rs | 5 +- crates/turborepo-lib/src/commands/prune.rs | 2 +- crates/turborepo-lib/src/commands/unlink.rs | 3 +- crates/turborepo-lib/src/daemon/server.rs | 3 +- crates/turborepo-lib/src/lib.rs | 1 - .../turborepo-lib/src/package_manager/mod.rs | 2 +- crates/turborepo-lib/src/run/global_hash.rs | 3 +- crates/turborepo-lib/src/run/mod.rs | 2 +- crates/turborepo-lib/src/shim.rs | 3 +- crates/turborepo-lib/src/tracing.rs | 3 +- crates/turborepo-ui/Cargo.toml | 18 ++++ .../src/ui/mod.rs => turborepo-ui/src/lib.rs} | 11 +++ crates/turborepo-ui/src/log_replayer.rs | 89 +++++++++++++++++++ 21 files changed, 161 insertions(+), 21 deletions(-) create mode 100644 crates/turborepo-ui/Cargo.toml rename crates/{turborepo-lib/src/ui/mod.rs => turborepo-ui/src/lib.rs} (94%) create mode 100644 crates/turborepo-ui/src/log_replayer.rs diff --git a/Cargo.lock b/Cargo.lock index d849fff0ebd872..13562fa1c90e58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9824,6 +9824,7 @@ dependencies = [ "turborepo-fs", "turborepo-lockfiles", "turborepo-scm", + "turborepo-ui", "uds_windows", "url", "vercel-api-mock", @@ -9871,6 +9872,21 @@ dependencies = [ "which", ] +[[package]] +name = "turborepo-ui" +version = "0.1.0" +dependencies = [ + "anyhow", + "atty", + "console", + "indicatif", + "lazy_static", + "tempfile", + "thiserror", + "tracing", + "turbopath", +] + [[package]] name = "twox-hash" version = "1.6.3" diff --git a/Cargo.toml b/Cargo.toml index e97745a9526441..a2a66563f1c70c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,6 +139,7 @@ turborepo-ffi = { path = "crates/turborepo-ffi" } turborepo-fs = { path = "crates/turborepo-fs" } turborepo-lib = { path = "crates/turborepo-lib", default-features = false } turborepo-lockfiles = { path = "crates/turborepo-lockfiles" } +turborepo-ui = { path = "crates/turborepo-ui" } turborepo-scm = { path = "crates/turborepo-scm" } wax = { path = "crates/turborepo-wax" } vercel-api-mock = { path = "crates/turborepo-vercel-api-mock" } diff --git a/crates/turborepo-lib/Cargo.toml b/crates/turborepo-lib/Cargo.toml index 64fc04c2e3fb3d..e9ebed0589b47a 100644 --- a/crates/turborepo-lib/Cargo.toml +++ b/crates/turborepo-lib/Cargo.toml @@ -105,6 +105,7 @@ turborepo-cache = { workspace = true } turborepo-env = { workspace = true } turborepo-lockfiles = { workspace = true } turborepo-scm = { workspace = true } +turborepo-ui = { workspace = true } wax = { workspace = true } webbrowser = { workspace = true } which = { workspace = true } diff --git a/crates/turborepo-lib/src/cli.rs b/crates/turborepo-lib/src/cli.rs index 643dda7b759e0c..c2c5767ca21f9c 100644 --- a/crates/turborepo-lib/src/cli.rs +++ b/crates/turborepo-lib/src/cli.rs @@ -7,6 +7,7 @@ use clap_complete::{generate, Shell}; use serde::Serialize; use tracing::{debug, error}; use turbopath::AbsoluteSystemPathBuf; +use turborepo_ui::UI; #[cfg(feature = "run-stub")] use crate::commands::run; @@ -15,7 +16,6 @@ use crate::{ get_version, shim::{RepoMode, RepoState}, tracing::TurboSubscriber, - ui::UI, Payload, }; diff --git a/crates/turborepo-lib/src/commands/info.rs b/crates/turborepo-lib/src/commands/info.rs index d5786b8d37f2aa..3b57e9bb3a163e 100644 --- a/crates/turborepo-lib/src/commands/info.rs +++ b/crates/turborepo-lib/src/commands/info.rs @@ -1,11 +1,11 @@ use anyhow::Result; +use turborepo_ui::GREY; use crate::{ commands::CommandBase, package_graph::{PackageGraph, WorkspaceName, WorkspaceNode}, package_json::PackageJson, package_manager::PackageManager, - ui::GREY, }; pub fn run(base: &mut CommandBase, workspace: Option<&str>) -> Result<()> { diff --git a/crates/turborepo-lib/src/commands/link.rs b/crates/turborepo-lib/src/commands/link.rs index a83dd0a01d2a0f..b8fcf13e3f11e7 100644 --- a/crates/turborepo-lib/src/commands/link.rs +++ b/crates/turborepo-lib/src/commands/link.rs @@ -17,14 +17,14 @@ use dirs_next::home_dir; #[cfg(test)] use rand::Rng; use turborepo_api_client::{APIClient, CachingStatus, Space, Team}; - #[cfg(not(test))] -use crate::ui::CYAN; +use turborepo_ui::CYAN; +use turborepo_ui::{BOLD, GREY, UNDERLINE}; + use crate::{ cli::LinkTarget, commands::CommandBase, config::{RawTurboJSON, SpacesJson}, - ui::{BOLD, GREY, UNDERLINE}, }; #[derive(Clone)] @@ -478,13 +478,13 @@ mod test { use tempfile::{NamedTempFile, TempDir}; use tokio::sync::OnceCell; use turbopath::AbsoluteSystemPathBuf; + use turborepo_ui::UI; use vercel_api_mock::start_test_server; use crate::{ cli::LinkTarget, commands::{link, CommandBase}, config::{ClientConfigLoader, RawTurboJSON, RepoConfigLoader, UserConfigLoader}, - ui::UI, Args, }; diff --git a/crates/turborepo-lib/src/commands/login.rs b/crates/turborepo-lib/src/commands/login.rs index cc58936baa3da7..23125e70690f8a 100644 --- a/crates/turborepo-lib/src/commands/login.rs +++ b/crates/turborepo-lib/src/commands/login.rs @@ -11,6 +11,7 @@ use tokio::sync::OnceCell; use tracing::debug; #[cfg(not(test))] use tracing::warn; +use turborepo_ui::{start_spinner, BOLD, CYAN, GREY, UNDERLINE}; use crate::{ commands::{ @@ -18,7 +19,6 @@ use crate::{ CommandBase, }, get_version, - ui::{start_spinner, BOLD, CYAN, GREY, UNDERLINE}, }; const DEFAULT_HOST_NAME: &str = "127.0.0.1"; @@ -305,6 +305,7 @@ mod test { use tempfile::{tempdir, NamedTempFile}; use tokio::sync::OnceCell; use turbopath::AbsoluteSystemPathBuf; + use turborepo_ui::UI; use vercel_api_mock::start_test_server; use crate::{ @@ -314,7 +315,6 @@ mod test { CommandBase, }, config::{ClientConfigLoader, RepoConfigLoader, UserConfigLoader}, - ui::UI, Args, }; diff --git a/crates/turborepo-lib/src/commands/logout.rs b/crates/turborepo-lib/src/commands/logout.rs index c77073b079da00..56661ec2484f9e 100644 --- a/crates/turborepo-lib/src/commands/logout.rs +++ b/crates/turborepo-lib/src/commands/logout.rs @@ -1,7 +1,8 @@ use anyhow::Result; use tracing::error; +use turborepo_ui::GREY; -use crate::{commands::CommandBase, ui::GREY}; +use crate::commands::CommandBase; pub fn logout(base: &mut CommandBase) -> Result<()> { if let Err(err) = base.user_config_mut()?.set_token(None) { diff --git a/crates/turborepo-lib/src/commands/mod.rs b/crates/turborepo-lib/src/commands/mod.rs index 9265982c82833e..855cca3664df7a 100644 --- a/crates/turborepo-lib/src/commands/mod.rs +++ b/crates/turborepo-lib/src/commands/mod.rs @@ -5,13 +5,13 @@ use sha2::{Digest, Sha256}; use tokio::sync::OnceCell; use turbopath::AbsoluteSystemPathBuf; use turborepo_api_client::APIClient; +use turborepo_ui::UI; use crate::{ config::{ default_user_config_path, get_repo_config_path, ClientConfig, ClientConfigLoader, RepoConfig, RepoConfigLoader, UserConfig, UserConfigLoader, }, - ui::UI, Args, }; @@ -176,8 +176,9 @@ impl CommandBase { mod test { use test_case::test_case; use turbopath::AbsoluteSystemPathBuf; + use turborepo_ui::UI; - use crate::{get_version, ui::UI}; + use crate::get_version; #[cfg(not(target_os = "windows"))] #[test_case("/tmp/turborepo", "6e0cfa616f75a61c"; "basic example")] diff --git a/crates/turborepo-lib/src/commands/prune.rs b/crates/turborepo-lib/src/commands/prune.rs index e9073238669b6b..471ab9a34fcf71 100644 --- a/crates/turborepo-lib/src/commands/prune.rs +++ b/crates/turborepo-lib/src/commands/prune.rs @@ -7,13 +7,13 @@ use tracing::trace; use turbopath::{ AbsoluteSystemPathBuf, AnchoredSystemPath, AnchoredSystemPathBuf, RelativeUnixPath, }; +use turborepo_ui::BOLD; use super::CommandBase; use crate::{ config::RawTurboJSON, package_graph::{PackageGraph, WorkspaceName, WorkspaceNode}, package_json::PackageJson, - ui::BOLD, }; #[derive(Debug, thiserror::Error)] diff --git a/crates/turborepo-lib/src/commands/unlink.rs b/crates/turborepo-lib/src/commands/unlink.rs index fbeee31c576e38..1d5b478e3e0dde 100644 --- a/crates/turborepo-lib/src/commands/unlink.rs +++ b/crates/turborepo-lib/src/commands/unlink.rs @@ -1,8 +1,9 @@ use std::{fs, fs::File}; use anyhow::{Context, Result}; +use turborepo_ui::GREY; -use crate::{cli::LinkTarget, commands::CommandBase, config::RawTurboJSON, ui::GREY}; +use crate::{cli::LinkTarget, commands::CommandBase, config::RawTurboJSON}; enum UnlinkSpacesResult { Unlinked, diff --git a/crates/turborepo-lib/src/daemon/server.rs b/crates/turborepo-lib/src/daemon/server.rs index 67f23b0c89c867..c57df90282f029 100644 --- a/crates/turborepo-lib/src/daemon/server.rs +++ b/crates/turborepo-lib/src/daemon/server.rs @@ -332,9 +332,10 @@ mod test { use tokio::select; use turbopath::AbsoluteSystemPathBuf; + use turborepo_ui::UI; use super::DaemonServer; - use crate::{commands::CommandBase, ui::UI, Args}; + use crate::{commands::CommandBase, Args}; // the windows runner starts a new thread to accept uds requests, // so we need a multi-threaded runtime diff --git a/crates/turborepo-lib/src/lib.rs b/crates/turborepo-lib/src/lib.rs index f78680f2574fa6..fa21f94ee5dccc 100644 --- a/crates/turborepo-lib/src/lib.rs +++ b/crates/turborepo-lib/src/lib.rs @@ -20,7 +20,6 @@ mod run; mod shim; mod task_graph; mod tracing; -mod ui; use ::tracing::error; use anyhow::Result; diff --git a/crates/turborepo-lib/src/package_manager/mod.rs b/crates/turborepo-lib/src/package_manager/mod.rs index ee569cf53ac30e..3b32f0e37b6a70 100644 --- a/crates/turborepo-lib/src/package_manager/mod.rs +++ b/crates/turborepo-lib/src/package_manager/mod.rs @@ -16,12 +16,12 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf, RelativeUnixPath}; use turborepo_lockfiles::Lockfile; +use turborepo_ui::{UI, UNDERLINE}; use wax::{Any, Glob, Pattern}; use crate::{ package_json::PackageJson, package_manager::{npm::NpmDetector, pnpm::PnpmDetector, yarn::YarnDetector}, - ui::{UI, UNDERLINE}, }; #[derive(Debug, Deserialize)] diff --git a/crates/turborepo-lib/src/run/global_hash.rs b/crates/turborepo-lib/src/run/global_hash.rs index acbdea1f15ae81..53649e57b5df3a 100644 --- a/crates/turborepo-lib/src/run/global_hash.rs +++ b/crates/turborepo-lib/src/run/global_hash.rs @@ -4,8 +4,9 @@ use anyhow::Result; use turbopath::{AbsoluteSystemPath, RelativeUnixPathBuf}; use turborepo_env::{BySource, DetailedMap, EnvironmentVariableMap}; use turborepo_lockfiles::Lockfile; +use turborepo_ui::UI; -use crate::{cli::EnvMode, package_json::PackageJson, package_manager::PackageManager, ui::UI}; +use crate::{cli::EnvMode, package_json::PackageJson, package_manager::PackageManager}; static DEFAULT_ENV_VARS: [&str; 1] = ["VERCEL_ANALYTICS_ID"]; diff --git a/crates/turborepo-lib/src/run/mod.rs b/crates/turborepo-lib/src/run/mod.rs index a97fe3577f3055..5c8070db8050a5 100644 --- a/crates/turborepo-lib/src/run/mod.rs +++ b/crates/turborepo-lib/src/run/mod.rs @@ -142,13 +142,13 @@ mod test { use anyhow::Result; use tempfile::tempdir; use turbopath::AbsoluteSystemPathBuf; + use turborepo_ui::UI; use crate::{ cli::{Command, RunArgs}, commands::CommandBase, get_version, run::Run, - ui::UI, Args, }; diff --git a/crates/turborepo-lib/src/shim.rs b/crates/turborepo-lib/src/shim.rs index 4763bd6387c1d4..c541ba8b970221 100644 --- a/crates/turborepo-lib/src/shim.rs +++ b/crates/turborepo-lib/src/shim.rs @@ -17,10 +17,11 @@ use tiny_gradient::{GradientStr, RGB}; use tracing::debug; use turbo_updater::check_for_updates; use turbopath::{AbsoluteSystemPath, AbsoluteSystemPathBuf}; +use turborepo_ui::UI; use crate::{ cli, get_version, package_manager::WorkspaceGlobs, spawn_child, tracing::TurboSubscriber, - ui::UI, PackageManager, Payload, + PackageManager, Payload, }; // all arguments that result in a stdout that much be directly parsable and diff --git a/crates/turborepo-lib/src/tracing.rs b/crates/turborepo-lib/src/tracing.rs index 96f53d5bc456a1..a3861c4393e7d9 100644 --- a/crates/turborepo-lib/src/tracing.rs +++ b/crates/turborepo-lib/src/tracing.rs @@ -22,8 +22,7 @@ use tracing_subscriber::{ reload::{self, Error, Handle}, EnvFilter, Layer, Registry, }; - -use crate::ui::UI; +use turborepo_ui::UI; type StdOutLog = Filtered< tracing_subscriber::fmt::Layer, diff --git a/crates/turborepo-ui/Cargo.toml b/crates/turborepo-ui/Cargo.toml new file mode 100644 index 00000000000000..32f2c144f25bf3 --- /dev/null +++ b/crates/turborepo-ui/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "turborepo-ui" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dev-dependencies] +anyhow = { workspace = true } +tempfile = { workspace = true } + +[dependencies] +atty = { workspace = true } +console = { workspace = true } +indicatif = { workspace = true } +lazy_static = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } +turbopath = { workspace = true } diff --git a/crates/turborepo-lib/src/ui/mod.rs b/crates/turborepo-ui/src/lib.rs similarity index 94% rename from crates/turborepo-lib/src/ui/mod.rs rename to crates/turborepo-ui/src/lib.rs index 050e2c3d29a7c5..5a7ea0bae8c7a7 100644 --- a/crates/turborepo-lib/src/ui/mod.rs +++ b/crates/turborepo-ui/src/lib.rs @@ -1,8 +1,19 @@ +mod log_replayer; + use std::{borrow::Cow, env, f64::consts::PI, time::Duration}; use console::{Style, StyledObject}; use indicatif::{ProgressBar, ProgressStyle}; use lazy_static::lazy_static; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error("cannot read logs: {0}")] + CannotReadLogs(std::io::Error), + #[error("cannot write logs: {0}")] + CannotWriteLogs(std::io::Error), +} pub fn start_spinner(message: &str) -> ProgressBar { let pb = ProgressBar::new_spinner(); diff --git a/crates/turborepo-ui/src/log_replayer.rs b/crates/turborepo-ui/src/log_replayer.rs new file mode 100644 index 00000000000000..e3e935eaa4818f --- /dev/null +++ b/crates/turborepo-ui/src/log_replayer.rs @@ -0,0 +1,89 @@ +use std::{ + fmt::Display, + fs::File, + io::{BufRead, BufReader, Write}, +}; + +use console::style; +use tracing::{debug, warn}; +use turbopath::AbsoluteSystemPath; + +use crate::{Error, StyledObject, UI}; + +#[allow(dead_code)] +pub struct PrefixedUI { + ui: UI, + prefix: StyledObject, + output: W, +} + +#[allow(dead_code)] +impl PrefixedUI { + pub fn new(ui: UI, prefix: StyledObject, output: W) -> Self { + Self { ui, prefix, output } + } + + pub fn output(&mut self, message: impl Into) -> Result<(), Error> { + let message = style(format!("{} {}", self.prefix, message.into())); + writeln!(self.output, "{}", self.ui.apply(message)).map_err(Error::CannotWriteLogs)?; + + Ok(()) + } +} + +#[allow(dead_code)] +pub fn replay_logs( + mut output: PrefixedUI, + log_file_name: &AbsoluteSystemPath, +) -> Result<(), Error> { + debug!("start replaying logs"); + + let log_file = File::open(log_file_name).map_err(|err| { + warn!("error opening log file: {:?}", err); + Error::CannotReadLogs(err) + })?; + + let log_reader = BufReader::new(log_file); + + for line in log_reader.lines() { + let line = line.map_err(Error::CannotReadLogs)?; + output.output(line)?; + } + + debug!("finish replaying logs"); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::fs; + + use anyhow::Result; + use tempfile::tempdir; + use turbopath::AbsoluteSystemPathBuf; + + use crate::{ + log_replayer::{replay_logs, PrefixedUI}, + CYAN, UI, + }; + + #[test] + fn test_replay_logs() -> Result<()> { + let ui = UI::infer(); + let mut output = Vec::new(); + let prefixed_ui = PrefixedUI::new(ui, CYAN.apply_to(">"), &mut output); + let dir = tempdir()?; + let log_file_path = AbsoluteSystemPathBuf::try_from(dir.path().join("test.txt"))?; + fs::write(&log_file_path, "\none fish\ntwo fish\nred fish\nblue fish")?; + replay_logs(prefixed_ui, &log_file_path)?; + + assert_eq!( + String::from_utf8(output)?, + "\u{1b}[36m>\u{1b}[0m \n\u{1b}[36m>\u{1b}[0m one fish\n\u{1b}[36m>\u{1b}[0m two \ + fish\n\u{1b}[36m>\u{1b}[0m red fish\n\u{1b}[36m>\u{1b}[0m blue fish\n" + ); + + Ok(()) + } +}