diff --git a/Cargo.lock b/Cargo.lock index b887fdf59e423..4ee3e7029cc6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11898,6 +11898,7 @@ dependencies = [ "turborepo-graph-utils", "turborepo-lockfiles", "turborepo-scm", + "turborepo-telemetry", "wax", "which", ] diff --git a/crates/turborepo-lib/src/run/mod.rs b/crates/turborepo-lib/src/run/mod.rs index f3055c188f169..1b405386534f2 100644 --- a/crates/turborepo-lib/src/run/mod.rs +++ b/crates/turborepo-lib/src/run/mod.rs @@ -284,7 +284,8 @@ impl Run { let mut pkg_dep_graph = { let builder = PackageGraph::builder(&self.base.repo_root, root_package_json.clone()) - .with_single_package_mode(self.opts.run_opts.single_package); + .with_single_package_mode(self.opts.run_opts.single_package) + .with_telemetry(Some(run_telemetry.clone())); #[cfg(feature = "daemon-package-discovery")] let builder = { diff --git a/crates/turborepo-repository/Cargo.toml b/crates/turborepo-repository/Cargo.toml index f9b43dbede73f..b97b62f6d27dd 100644 --- a/crates/turborepo-repository/Cargo.toml +++ b/crates/turborepo-repository/Cargo.toml @@ -27,6 +27,7 @@ turbopath = { workspace = true } turborepo-graph-utils = { path = "../turborepo-graph-utils" } turborepo-lockfiles = { workspace = true } turborepo-scm = { workspace = true } +turborepo-telemetry = { version = "0.1.0", path = "../turborepo-telemetry" } wax = { workspace = true } which = { workspace = true } diff --git a/crates/turborepo-repository/src/package_graph/builder.rs b/crates/turborepo-repository/src/package_graph/builder.rs index 52ce87b4d5864..a1c5d18a1f41a 100644 --- a/crates/turborepo-repository/src/package_graph/builder.rs +++ b/crates/turborepo-repository/src/package_graph/builder.rs @@ -1,7 +1,7 @@ use std::{ backtrace::Backtrace, collections::{BTreeMap, HashMap, HashSet}, - fmt, + fmt, io, }; use petgraph::graph::{Graph, NodeIndex}; @@ -12,6 +12,7 @@ use turbopath::{ }; use turborepo_graph_utils as graph; use turborepo_lockfiles::Lockfile; +use turborepo_telemetry::events::{generic::GenericEventBuilder, TrackedErrors}; use super::{PackageGraph, WorkspaceInfo, WorkspaceName, WorkspaceNode}; use crate::{ @@ -20,7 +21,7 @@ use crate::{ PackageDiscoveryBuilder, }, package_graph::{PackageName, PackageVersion}, - package_json::PackageJson, + package_json::{self, PackageJson}, }; pub struct PackageGraphBuilder<'a, T> { @@ -29,6 +30,7 @@ pub struct PackageGraphBuilder<'a, T> { is_single_package: bool, package_jsons: Option>, lockfile: Option>, + telemetry: Option, package_discovery: T, } @@ -75,6 +77,7 @@ impl<'a> PackageGraphBuilder<'a, LocalPackageDiscoveryBuilder> { is_single_package: false, package_jsons: None, lockfile: None, + telemetry: None, } } } @@ -94,6 +97,11 @@ impl<'a, P> PackageGraphBuilder<'a, P> { self } + pub fn with_telemetry(mut self, telemetry: Option) -> Self { + self.telemetry = telemetry; + self + } + #[allow(dead_code)] pub fn with_lockfile(mut self, lockfile: Option>) -> Self { self.lockfile = lockfile; @@ -114,6 +122,7 @@ impl<'a, P> PackageGraphBuilder<'a, P> { package_jsons: self.package_jsons, lockfile: self.lockfile, package_discovery: discovery, + telemetry: self.telemetry, } } } @@ -151,6 +160,7 @@ struct BuildState<'a, S, T> { package_jsons: Option>, state: std::marker::PhantomData, package_discovery: T, + telemetry: Option, } // Allows us to perform workspace discovery and parse package jsons @@ -197,6 +207,7 @@ where package_jsons, lockfile, package_discovery, + telemetry, } = builder; let mut workspaces = HashMap::new(); workspaces.insert( @@ -221,6 +232,7 @@ where package_discovery: CachingPackageDiscovery::new( package_discovery.build().map_err(Into::into)?, ), + telemetry, }) } } @@ -272,13 +284,28 @@ impl<'a, T: PackageDiscovery> BuildState<'a, ResolvedPackageManager, T> { None => { let mut jsons = HashMap::new(); for path in self.package_discovery.discover_packages().await?.workspaces { - let json = PackageJson::load(&path.package_json)?; - jsons.insert(path.package_json, json); + match PackageJson::load(&path.package_json) { + Ok(json) => { + jsons.insert(path.package_json, json); + } + // if we get here, it could stem from a package watch error, so we should + // fall back to the more expensive local discovery and log a telemetry event + Err(package_json::Error::Io(io)) + if io.kind() == io::ErrorKind::NotFound => + { + if let Some(telemetry) = self.telemetry { + telemetry.track_error(TrackedErrors::InvalidPackageDiscovery); + } + return Err(package_json::Error::Io(io).into()); + } + Err(e) => return Err(e.into()), + }; } Ok::<_, Error>(jsons) } }?; + // if package discovery produces for (path, json) in package_jsons { match self.add_json(path, json) { Ok(()) => {} @@ -302,6 +329,7 @@ impl<'a, T: PackageDiscovery> BuildState<'a, ResolvedPackageManager, T> { node_lookup, lockfile, package_discovery, + telemetry, .. } = self; Ok(BuildState { @@ -314,6 +342,7 @@ impl<'a, T: PackageDiscovery> BuildState<'a, ResolvedPackageManager, T> { package_discovery, package_jsons: None, state: std::marker::PhantomData, + telemetry, }) } @@ -439,6 +468,7 @@ impl<'a, T: PackageDiscovery> BuildState<'a, ResolvedWorkspaces, T> { workspace_graph, node_lookup, package_discovery, + telemetry, .. } = self; Ok(BuildState { @@ -451,6 +481,7 @@ impl<'a, T: PackageDiscovery> BuildState<'a, ResolvedWorkspaces, T> { package_jsons: None, state: std::marker::PhantomData, package_discovery, + telemetry, }) } } diff --git a/crates/turborepo-telemetry/src/events/mod.rs b/crates/turborepo-telemetry/src/events/mod.rs index b5d0e5e7be7f6..23fd06faf9587 100644 --- a/crates/turborepo-telemetry/src/events/mod.rs +++ b/crates/turborepo-telemetry/src/events/mod.rs @@ -55,6 +55,11 @@ pub enum TrackedErrors { ErrorFetchingFromCache, FailedToPipeOutputs, UnknownChildExit, + /// Yielded when package discovery yields a + /// list of packages that fails downstream. + /// Currently only indicates a package being + /// reported when it does not exist. + InvalidPackageDiscovery, } impl Display for TrackedErrors { @@ -72,6 +77,7 @@ impl Display for TrackedErrors { TrackedErrors::ErrorFetchingFromCache => write!(f, "error_fetching_from_cache"), TrackedErrors::FailedToPipeOutputs => write!(f, "failed_to_pipe_outputs"), TrackedErrors::UnknownChildExit => write!(f, "unknown_child_exit"), + TrackedErrors::InvalidPackageDiscovery => write!(f, "invalid_package_discovery"), } } }