From c84638acac3f55977b1a12361a5321948055f899 Mon Sep 17 00:00:00 2001 From: Chris Olszewski Date: Fri, 6 Sep 2024 13:45:02 -0400 Subject: [PATCH 1/4] perf(tui): only rerender if a non-tick event has been received (#9121) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description `ratatui` does a great job of only updating cells that are different between render, but constructing the `vt100` screen can be intensive. This PR avoid constructing the screen if there are no updates to the the app state meaning there's no reason to re-render the TUI. There are some additional changes we can also make to lower CPU usage more: - We're currently spending a lot of time polling for terminal events see if there's a less resource intensive alternative. - Patch vt100 so constructing `Screen` is less resource intensive e.g. https://github.com/doy/vt100-rust/pull/14 ### Testing Instructions Using TUI in [next.js](https://github.com/vercel/next.js) ``` pnpm dev -F next ``` Before Screenshot 2024-09-06 at 12 40 07 PM After Ran via `cargo build -p turbo --release` and `turbo_dev --skip-infer dev -F next` Screenshot 2024-09-06 at 12 31 00 PM --- crates/turborepo-ui/src/tui/app.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/turborepo-ui/src/tui/app.rs b/crates/turborepo-ui/src/tui/app.rs index 95854af8c5c73..4d83a65d10a87 100644 --- a/crates/turborepo-ui/src/tui/app.rs +++ b/crates/turborepo-ui/src/tui/app.rs @@ -586,7 +586,13 @@ fn run_app_inner( let mut last_render = Instant::now(); let mut resize_debouncer = Debouncer::new(RESIZE_DEBOUNCE_DELAY); let mut callback = None; + let mut needs_rerender = true; while let Some(event) = poll(app.input_options()?, &receiver, last_render + FRAMERATE) { + // If we only receive ticks, then there's been no state change so no update + // needed + if !matches!(event, Event::Tick) { + needs_rerender = true; + } let mut event = Some(event); let mut resize_event = None; if matches!(event, Some(Event::Resize { .. })) { @@ -606,9 +612,10 @@ fn run_app_inner( if app.done { break; } - if FRAMERATE <= last_render.elapsed() { + if FRAMERATE <= last_render.elapsed() && needs_rerender { terminal.draw(|f| view(app, f))?; last_render = Instant::now(); + needs_rerender = false; } } } From 8cffabe472cbeb5686d90111453d3218eb39670d Mon Sep 17 00:00:00 2001 From: Saatvik Arya Date: Tue, 10 Sep 2024 05:01:04 -0700 Subject: [PATCH 2/4] docs(crates/turborepo): add capnp to build dependencies (#9127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description I was setting up turborepo locally on macOS. ran into the follow error. installing capnp fixed it ```bash Compiling turborepo-lockfiles v0.1.0 (turborepo/crates/turborepo-lockfiles) error: failed to run custom build command for `turborepo-lib v0.1.0 (turborepo/crates/turborepo-lib)` Caused by: process didn't exit successfully: `turborepo/target/debug/build/turborepo-lib-167cf8cfecddd530/build-script-build` (exit status: 101) --- stdout cargo:rerun-if-changed=./src/daemon/proto/turbod.proto cargo:rerun-if-changed=./src/daemon/proto --- stderr thread 'main' panicked at crates/turborepo-lib/build.rs:27:23: schema compiler command: Error { kind: Failed, extra: "Failed to execute `capnp --version`: No such file or directory (os error 2). Please verify that version 0.5.2 or higher of the capnp executable is installed on your system. See https://capnproto.org/install.html" } note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace warning: build failed, waiting for other jobs to finish... ``` ### Testing Instructions --- crates/turborepo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/turborepo/README.md b/crates/turborepo/README.md index da37b16c35381..378aec0222e68 100644 --- a/crates/turborepo/README.md +++ b/crates/turborepo/README.md @@ -4,7 +4,7 @@ 1. Install `protobuf` and `golang` (note: Go must be pinned to v1.20.x, see https://github.com/vercel/turborepo/issues/5918 for details) -- On macOS: `brew install protobuf protoc-gen-go protoc-gen-go-grpc go@1.20` +- On macOS: `brew install protobuf protoc-gen-go protoc-gen-go-grpc go@1.20 capnp` - On Windows: `choco install golang --version=1.20.7` and `choco install protoc make python3 mingw` - On Ubuntu: `apt-get install golang golang-goprotobuf-dev` From 2adeac984881a4df30eb95cf5eb244fa73eb5200 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:07:06 -0400 Subject: [PATCH 3/4] release(turborepo): 2.1.2-canary.1 (#9131) Co-authored-by: Turbobot --- packages/create-turbo/package.json | 2 +- packages/eslint-config-turbo/package.json | 2 +- packages/eslint-plugin-turbo/package.json | 2 +- packages/turbo-codemod/package.json | 2 +- packages/turbo-gen/package.json | 2 +- packages/turbo-ignore/package.json | 2 +- packages/turbo-types/package.json | 2 +- packages/turbo-workspaces/package.json | 2 +- packages/turbo/package.json | 14 +++++++------- version.txt | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/create-turbo/package.json b/packages/create-turbo/package.json index 6e3533e13342c..e240fb137111b 100644 --- a/packages/create-turbo/package.json +++ b/packages/create-turbo/package.json @@ -1,6 +1,6 @@ { "name": "create-turbo", - "version": "2.1.2-canary.0", + "version": "2.1.2-canary.1", "description": "Create a new Turborepo", "homepage": "https://turbo.build/repo", "license": "MIT", diff --git a/packages/eslint-config-turbo/package.json b/packages/eslint-config-turbo/package.json index 6442288ea0a99..80318beab75c1 100644 --- a/packages/eslint-config-turbo/package.json +++ b/packages/eslint-config-turbo/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-turbo", - "version": "2.1.2-canary.0", + "version": "2.1.2-canary.1", "description": "ESLint config for Turborepo", "repository": { "type": "git", diff --git a/packages/eslint-plugin-turbo/package.json b/packages/eslint-plugin-turbo/package.json index a79142e98a9de..2c2b1163cec99 100644 --- a/packages/eslint-plugin-turbo/package.json +++ b/packages/eslint-plugin-turbo/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-turbo", - "version": "2.1.2-canary.0", + "version": "2.1.2-canary.1", "description": "ESLint plugin for Turborepo", "keywords": [ "turbo", diff --git a/packages/turbo-codemod/package.json b/packages/turbo-codemod/package.json index d0bc0168ad086..9151eea72ee4b 100644 --- a/packages/turbo-codemod/package.json +++ b/packages/turbo-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@turbo/codemod", - "version": "2.1.2-canary.0", + "version": "2.1.2-canary.1", "description": "Provides Codemod transformations to help upgrade your Turborepo codebase when a feature is deprecated.", "homepage": "https://turbo.build/repo", "license": "MIT", diff --git a/packages/turbo-gen/package.json b/packages/turbo-gen/package.json index 12fa7a88e3d46..4cf85719b8d3d 100644 --- a/packages/turbo-gen/package.json +++ b/packages/turbo-gen/package.json @@ -1,6 +1,6 @@ { "name": "@turbo/gen", - "version": "2.1.2-canary.0", + "version": "2.1.2-canary.1", "description": "Extend a Turborepo", "homepage": "https://turbo.build/repo", "license": "MIT", diff --git a/packages/turbo-ignore/package.json b/packages/turbo-ignore/package.json index 09526f92a81fc..f9be1e91e75c3 100644 --- a/packages/turbo-ignore/package.json +++ b/packages/turbo-ignore/package.json @@ -1,6 +1,6 @@ { "name": "turbo-ignore", - "version": "2.1.2-canary.0", + "version": "2.1.2-canary.1", "description": "", "homepage": "https://turbo.build/repo", "keywords": [], diff --git a/packages/turbo-types/package.json b/packages/turbo-types/package.json index df9b1ce01ee8f..1e7b09c4de709 100644 --- a/packages/turbo-types/package.json +++ b/packages/turbo-types/package.json @@ -1,6 +1,6 @@ { "name": "@turbo/types", - "version": "2.1.2-canary.0", + "version": "2.1.2-canary.1", "description": "Turborepo types", "homepage": "https://turbo.build/repo", "license": "MIT", diff --git a/packages/turbo-workspaces/package.json b/packages/turbo-workspaces/package.json index 06e60b8e9fac3..eb76b1f1aa4b5 100644 --- a/packages/turbo-workspaces/package.json +++ b/packages/turbo-workspaces/package.json @@ -1,6 +1,6 @@ { "name": "@turbo/workspaces", - "version": "2.1.2-canary.0", + "version": "2.1.2-canary.1", "description": "Tools for working with package managers", "homepage": "https://turbo.build/repo", "license": "MIT", diff --git a/packages/turbo/package.json b/packages/turbo/package.json index 2c9a576b4ae81..042f049e13d16 100644 --- a/packages/turbo/package.json +++ b/packages/turbo/package.json @@ -1,6 +1,6 @@ { "name": "turbo", - "version": "2.1.2-canary.0", + "version": "2.1.2-canary.1", "description": "Turborepo is a high-performance build system for JavaScript and TypeScript codebases.", "repository": "https://github.com/vercel/turborepo", "bugs": "https://github.com/vercel/turborepo/issues", @@ -17,11 +17,11 @@ "bin" ], "optionalDependencies": { - "turbo-darwin-64": "2.1.2-canary.0", - "turbo-darwin-arm64": "2.1.2-canary.0", - "turbo-linux-64": "2.1.2-canary.0", - "turbo-linux-arm64": "2.1.2-canary.0", - "turbo-windows-64": "2.1.2-canary.0", - "turbo-windows-arm64": "2.1.2-canary.0" + "turbo-darwin-64": "2.1.2-canary.1", + "turbo-darwin-arm64": "2.1.2-canary.1", + "turbo-linux-64": "2.1.2-canary.1", + "turbo-linux-arm64": "2.1.2-canary.1", + "turbo-windows-64": "2.1.2-canary.1", + "turbo-windows-arm64": "2.1.2-canary.1" } } diff --git a/version.txt b/version.txt index 8d4becb05727b..a49141a4a352e 100644 --- a/version.txt +++ b/version.txt @@ -1,2 +1,2 @@ -2.1.2-canary.0 +2.1.2-canary.1 canary From bd2bffa246dd9a2d43705b3d4f6a5ab62b0359b9 Mon Sep 17 00:00:00 2001 From: Chris Olszewski Date: Tue, 10 Sep 2024 13:52:50 -0400 Subject: [PATCH 4/4] chore(config): leverage proc macros (#9111) ### Description This PR leverages [derive_setters](https://crates.io/crates/derive_setters) and [merge](https://docs.rs/merge/latest/merge/index.html) to reduce the amount of changes required when adding a field to the configuration options. The manual setup is error prone as there are 4 places that need to be updated and forgetting the final place will result in code compiling, but the field never getting layered properly. ### Testing Instructions Existing unit & integration tests, but also looking what the proc macros are generating (outputs shown are produced using `rust-analyzer`'s [Expand macro](https://rust-analyzer.github.io/manual.html#expand-macro-recursively) feature: Output of `#[derive(Setters)]` ``` impl TurborepoConfigBuilder { pub fn with_api_url(mut self, value: Option) -> Self { self.override_config.api_url = value; self } ... ``` Which exactly matches what `create_builder` would produce: ``` pub fn with_api_url(mut self, value: Option) -> Self { self.override_config.api_url = value; self } ``` `#derive(Merge)` produces ``` impl ::merge::Merge for ConfigurationOptions { fn merge(&mut self, other: Self) { ::merge::Merge::merge(&mut self.api_url, other.api_url); ... ``` and `Merge` is defined for `Option` as : ``` impl Merge for Option { fn merge(&mut self, mut other: Self) { if !self.is_some() { *self = other.take(); } } } ``` [source](https://docs.rs/merge/latest/src/merge/lib.rs.html#139-145) So `self.api_url` will only be set to `other.api_url` if there isn't a value present which is the behavior we want since we merge configs from highest to lowest precedence. --------- Co-authored-by: Nicholas Yang --- Cargo.lock | 36 +++++++++ Cargo.toml | 2 + crates/turborepo-lib/Cargo.toml | 2 + crates/turborepo-lib/src/config/mod.rs | 103 +++---------------------- 4 files changed, 50 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 128d9683cc4d4..b7778fa8c0eeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1533,6 +1533,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_setters" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "dialoguer" version = "0.10.4" @@ -2992,6 +3004,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "merge" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10bbef93abb1da61525bbc45eeaff6473a41907d19f8f9aa5168d214e10693e9" +dependencies = [ + "merge_derive", + "num-traits", +] + [[package]] name = "merge-streams" version = "0.1.2" @@ -3002,6 +3024,18 @@ dependencies = [ "pin-project", ] +[[package]] +name = "merge_derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209d075476da2e63b4b29e72a2ef627b840589588e71400a25e3565c4f849d07" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "miette" version = "5.10.0" @@ -5865,6 +5899,7 @@ dependencies = [ "convert_case 0.6.0", "crossterm 0.26.1", "ctrlc", + "derive_setters", "dialoguer", "dirs-next", "dunce", @@ -5883,6 +5918,7 @@ dependencies = [ "jsonc-parser 0.21.0", "lazy_static", "libc", + "merge", "miette", "nix 0.26.2", "notify", diff --git a/Cargo.toml b/Cargo.toml index 66798838cba47..400e03d2bbfc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,6 +96,7 @@ console = "0.15.5" console-subscriber = "0.1.8" crossbeam-channel = "0.5.8" dashmap = "5.4.0" +derive_setters = "0.1.6" dialoguer = "0.10.3" dunce = "1.0.3" either = "1.9.0" @@ -108,6 +109,7 @@ indicatif = "0.17.3" indoc = "2.0.0" itertools = "0.10.5" lazy_static = "1.4.0" +merge = "0.1.0" mime = "0.3.16" notify = "6.1.1" once_cell = "1.17.1" diff --git a/crates/turborepo-lib/Cargo.toml b/crates/turborepo-lib/Cargo.toml index 0785c49941ca7..43f802ab32780 100644 --- a/crates/turborepo-lib/Cargo.toml +++ b/crates/turborepo-lib/Cargo.toml @@ -52,6 +52,7 @@ const_format = "0.2.30" convert_case = "0.6.0" crossterm = "0.26" ctrlc = { version = "3.4.0", features = ["termination"] } +derive_setters = { workspace = true } dialoguer = { workspace = true, features = ["fuzzy-select"] } dirs-next = "2.0.0" dunce = { workspace = true } @@ -70,6 +71,7 @@ itertools = { workspace = true } jsonc-parser = { version = "0.21.0" } lazy_static = { workspace = true } libc = "0.2.140" +merge = { workspace = true } miette = { workspace = true, features = ["fancy"] } nix = "0.26.2" notify = { workspace = true } diff --git a/crates/turborepo-lib/src/config/mod.rs b/crates/turborepo-lib/src/config/mod.rs index 398754bea9c4d..6fefa2d435e69 100644 --- a/crates/turborepo-lib/src/config/mod.rs +++ b/crates/turborepo-lib/src/config/mod.rs @@ -6,8 +6,10 @@ use std::{collections::HashMap, ffi::OsString, io}; use camino::{Utf8Path, Utf8PathBuf}; use convert_case::{Case, Casing}; +use derive_setters::Setters; use env::{EnvVars, OverrideEnvVars}; use file::{AuthFile, ConfigFile}; +use merge::Merge; use miette::{Diagnostic, NamedSource, SourceSpan}; use serde::Deserialize; use struct_iterable::Iterable; @@ -173,15 +175,6 @@ pub enum Error { }, } -macro_rules! create_builder { - ($func_name:ident, $property_name:ident, $type:ty) => { - pub fn $func_name(mut self, value: $type) -> Self { - self.override_config.$property_name = value; - self - } - }; -} - const DEFAULT_API_URL: &str = "https://vercel.com/api"; const DEFAULT_LOGIN_URL: &str = "https://vercel.com"; const DEFAULT_TIMEOUT: u64 = 30; @@ -190,8 +183,13 @@ const DEFAULT_UPLOAD_TIMEOUT: u64 = 60; // We intentionally don't derive Serialize so that different parts // of the code that want to display the config can tune how they // want to display and what fields they want to include. -#[derive(Deserialize, Default, Debug, PartialEq, Eq, Clone, Iterable)] +#[derive(Deserialize, Default, Debug, PartialEq, Eq, Clone, Iterable, Merge, Setters)] #[serde(rename_all = "camelCase")] +// Generate setters for the builder type that set these values on its override_config field +#[setters( + prefix = "with_", + generate_delegates(ty = "TurborepoConfigBuilder", field = "override_config") +)] pub struct ConfigurationOptions { #[serde(alias = "apiurl")] #[serde(alias = "ApiUrl")] @@ -336,44 +334,6 @@ impl ConfigurationOptions { } } -macro_rules! create_set_if_empty { - ($func_name:ident, $property_name:ident, $type:ty) => { - fn $func_name(&mut self, value: &mut Option<$type>) { - if self.$property_name.is_none() { - if let Some(value) = value.take() { - self.$property_name = Some(value); - } - } - } - }; -} - -// Private setters used only for construction -impl ConfigurationOptions { - create_set_if_empty!(set_api_url, api_url, String); - create_set_if_empty!(set_login_url, login_url, String); - create_set_if_empty!(set_team_slug, team_slug, String); - create_set_if_empty!(set_team_id, team_id, String); - create_set_if_empty!(set_token, token, String); - create_set_if_empty!(set_signature, signature, bool); - create_set_if_empty!(set_enabled, enabled, bool); - create_set_if_empty!(set_preflight, preflight, bool); - create_set_if_empty!(set_timeout, timeout, u64); - create_set_if_empty!(set_ui, ui, UIMode); - create_set_if_empty!(set_allow_no_package_manager, allow_no_package_manager, bool); - create_set_if_empty!(set_daemon, daemon, bool); - create_set_if_empty!(set_env_mode, env_mode, EnvMode); - create_set_if_empty!(set_cache_dir, cache_dir, Utf8PathBuf); - create_set_if_empty!(set_scm_base, scm_base, String); - create_set_if_empty!(set_scm_head, scm_head, String); - create_set_if_empty!(set_spaces_id, spaces_id, String); - create_set_if_empty!( - set_root_turbo_json_path, - root_turbo_json_path, - AbsoluteSystemPathBuf - ); -} - // Maps Some("") to None to emulate how Go handles empty strings fn non_empty_str(s: Option<&str>) -> Option<&str> { s.filter(|s| !s.is_empty()) @@ -428,30 +388,6 @@ impl TurborepoConfigBuilder { .unwrap_or_else(get_lowercased_env_vars) } - create_builder!(with_api_url, api_url, Option); - create_builder!(with_login_url, login_url, Option); - create_builder!(with_team_slug, team_slug, Option); - create_builder!(with_team_id, team_id, Option); - create_builder!(with_token, token, Option); - create_builder!(with_signature, signature, Option); - create_builder!(with_enabled, enabled, Option); - create_builder!(with_preflight, preflight, Option); - create_builder!(with_timeout, timeout, Option); - create_builder!(with_ui, ui, Option); - create_builder!( - with_allow_no_package_manager, - allow_no_package_manager, - Option - ); - create_builder!(with_daemon, daemon, Option); - create_builder!(with_env_mode, env_mode, Option); - create_builder!(with_cache_dir, cache_dir, Option); - create_builder!( - with_root_turbo_json_path, - root_turbo_json_path, - Option - ); - pub fn build(&self) -> Result { // Priority, from least significant to most significant: // - shared configuration (turbo.json) @@ -483,27 +419,8 @@ impl TurborepoConfigBuilder { let config = sources.into_iter().try_fold( ConfigurationOptions::default(), |mut acc, current_source| { - let mut current_source_config = current_source.get_configuration_options(&acc)?; - acc.set_api_url(&mut current_source_config.api_url); - acc.set_login_url(&mut current_source_config.login_url); - acc.set_team_slug(&mut current_source_config.team_slug); - acc.set_team_id(&mut current_source_config.team_id); - acc.set_token(&mut current_source_config.token); - acc.set_signature(&mut current_source_config.signature); - acc.set_enabled(&mut current_source_config.enabled); - acc.set_preflight(&mut current_source_config.preflight); - acc.set_timeout(&mut current_source_config.timeout); - acc.set_spaces_id(&mut current_source_config.spaces_id); - acc.set_ui(&mut current_source_config.ui); - acc.set_allow_no_package_manager( - &mut current_source_config.allow_no_package_manager, - ); - acc.set_daemon(&mut current_source_config.daemon); - acc.set_env_mode(&mut current_source_config.env_mode); - acc.set_scm_base(&mut current_source_config.scm_base); - acc.set_scm_head(&mut current_source_config.scm_head); - acc.set_cache_dir(&mut current_source_config.cache_dir); - acc.set_root_turbo_json_path(&mut current_source_config.root_turbo_json_path); + let current_source_config = current_source.get_configuration_options(&acc)?; + acc.merge(current_source_config); Ok(acc) }, );