Skip to content

Commit

Permalink
fix(mfe): factor in configs to hashes (#9582)
Browse files Browse the repository at this point in the history
### Description

Start factoring in any MFE configs found into task hashes so changes to
the config will invalidate any applications that are a part of a MFE
setup.

⚠️ We achieve this by adding this to the global cache. This is
sub-optimal as it means tasks unrelated to MFEs get invalidated, but we
make this temporary trade off in favor of correctness. See TODO for the
planned path forward.

### Testing Instructions

Added unit tests, verified that changing a MFE config file will now
invalidates all MFE apps:
```
[0 olszewski@chriss-mbp] /Users/olszewski/code/vercel/front $ turbo_dev --skip-infer microfrontends-example-docs#build --output-logs=hash-only
turbo 2.3.4-canary.1

• Running microfrontends-example-docs#build in 84 packages
• Remote caching enabled
@vercel/microfrontends:build: cache hit (outputs already on disk), suppressing logs ea832ad348202d61
microfrontends-example-docs:build: cache hit (outputs already on disk), suppressing logs 2775f59cd3c9f808

 Tasks:    2 successful, 2 total
Cached:    2 cached, 2 total
  Time:    563ms >>> FULL TURBO

[0 olszewski@chriss-mbp] /Users/olszewski/code/vercel/front $ vim apps/microfrontends-example-web/microfrontends.json 
[0 olszewski@chriss-mbp] /Users/olszewski/code/vercel/front $ git diff
diff --git a/apps/microfrontends-example-web/microfrontends.json b/apps/microfrontends-example-web/microfrontends.json
index 0a6883bfc62..13d73b0d47c 100644
--- a/apps/microfrontends-example-web/microfrontends.json
+++ b/apps/microfrontends-example-web/microfrontends.json
@@ -28,7 +28,7 @@
         },
         {
           "group": "docs",
-          "paths": ["/docs"]
+          "paths": ["/docs", "/other"]
         }
       ]
     }
[0 olszewski@chriss-mbp] /Users/olszewski/code/vercel/front $ turbo_dev --skip-infer microfrontends-example-docs#build --output-logs=hash-only
turbo 2.3.4-canary.1

• Running microfrontends-example-docs#build in 84 packages
• Remote caching enabled
@vercel/microfrontends:build: cache hit, suppressing logs 51679cdd0650be9f
microfrontends-example-docs:build: cache miss, executing b0641267d9eb8cef

 Tasks:    2 successful, 2 total
Cached:    1 cached, 2 total
  Time:    8.936s 


```
  • Loading branch information
chris-olszewski authored Dec 9, 2024
1 parent a1db09a commit f55f520
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 59 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 57 additions & 13 deletions crates/turborepo-lib/src/microfrontends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};

use itertools::Itertools;
use tracing::warn;
use turbopath::AbsoluteSystemPath;
use turbopath::{AbsoluteSystemPath, RelativeUnixPathBuf};
use turborepo_microfrontends::{Config as MFEConfig, Error, MICROFRONTENDS_PACKAGES};
use turborepo_repository::package_graph::{PackageGraph, PackageName};

Expand All @@ -15,7 +15,7 @@ use crate::{
#[derive(Debug, Clone)]
pub struct MicrofrontendsConfigs {
configs: HashMap<String, HashSet<TaskId<'static>>>,
config_filenames: HashMap<String, String>,
config_paths: HashMap<String, RelativeUnixPathBuf>,
mfe_package: Option<&'static str>,
}

Expand All @@ -28,7 +28,7 @@ impl MicrofrontendsConfigs {
Self::from_configs(package_graph.packages().map(|(name, info)| {
(
name.as_str(),
MFEConfig::load_from_dir(&repo_root.resolve(info.package_path())),
MFEConfig::load_from_dir(repo_root, info.package_path()),
)
}))
}
Expand All @@ -39,7 +39,7 @@ impl MicrofrontendsConfigs {
) -> Result<Option<Self>, Error> {
let PackageGraphResult {
configs,
config_filenames,
config_paths,
missing_default_apps,
unsupported_version,
mfe_package,
Expand All @@ -58,7 +58,7 @@ impl MicrofrontendsConfigs {

Ok((!configs.is_empty()).then_some(Self {
configs,
config_filenames,
config_paths,
mfe_package,
}))
}
Expand All @@ -82,15 +82,27 @@ impl MicrofrontendsConfigs {
}

pub fn config_filename(&self, package_name: &str) -> Option<&str> {
let filename = self.config_filenames.get(package_name)?;
Some(filename.as_str())
let path = self.config_paths.get(package_name)?;
Some(path.as_str())
}

pub fn update_turbo_json(
&self,
package_name: &PackageName,
turbo_json: Result<TurboJson, config::Error>,
mut turbo_json: Result<TurboJson, config::Error>,
) -> Result<TurboJson, config::Error> {
// We add all of the microfrontend configurations as global dependencies so any
// changes will invalidate all tasks.
// TODO: Move this to only apply to packages that are part of the
// microfrontends. This will require us operating on task definitions
// instead of `turbo.json`s which currently isn't feasible.
if matches!(package_name, PackageName::Root) {
if let Ok(turbo_json) = &mut turbo_json {
turbo_json
.global_deps
.append(&mut self.configuration_file_paths());
}
}
// If package either
// - contains the proxy task
// - a member of one of the microfrontends
Expand Down Expand Up @@ -140,12 +152,20 @@ impl MicrofrontendsConfigs {
dev_task.or(proxy_owner)
})
}

// Returns a list of repo relative paths to all MFE configurations
fn configuration_file_paths(&self) -> Vec<String> {
self.config_paths
.values()
.map(|config_path| config_path.as_str().to_string())
.collect()
}
}

// Internal struct used to capture the results of checking the package graph
struct PackageGraphResult {
configs: HashMap<String, HashSet<TaskId<'static>>>,
config_filenames: HashMap<String, String>,
config_paths: HashMap<String, RelativeUnixPathBuf>,
missing_default_apps: Vec<String>,
unsupported_version: Vec<(String, String)>,
mfe_package: Option<&'static str>,
Expand All @@ -156,7 +176,7 @@ impl PackageGraphResult {
packages: impl Iterator<Item = (&'a str, Result<Option<MFEConfig>, Error>)>,
) -> Result<Self, Error> {
let mut configs = HashMap::new();
let mut config_filenames = HashMap::new();
let mut config_paths = HashMap::new();
let mut referenced_default_apps = HashSet::new();
let mut unsupported_version = Vec::new();
let mut mfe_package = None;
Expand Down Expand Up @@ -192,7 +212,9 @@ impl PackageGraphResult {
})
.collect();
configs.insert(package_name.to_string(), tasks);
config_filenames.insert(package_name.to_string(), config.filename().to_owned());
if let Some(path) = config.path() {
config_paths.insert(package_name.to_string(), path.to_unix());
}
}
let default_apps_found = configs.keys().cloned().collect();
let mut missing_default_apps = referenced_default_apps
Expand All @@ -202,7 +224,7 @@ impl PackageGraphResult {
missing_default_apps.sort();
Ok(Self {
configs,
config_filenames,
config_paths,
missing_default_apps,
unsupported_version,
mfe_package,
Expand Down Expand Up @@ -324,7 +346,7 @@ mod test {
);
let mfe = MicrofrontendsConfigs {
configs,
config_filenames: HashMap::new(),
config_paths: HashMap::new(),
mfe_package: None,
};
assert_eq!(
Expand Down Expand Up @@ -430,4 +452,26 @@ mod test {
)
)
}

#[test]
fn test_configs_added_as_global_deps() {
let configs = MicrofrontendsConfigs {
configs: vec![("web".to_owned(), Default::default())]
.into_iter()
.collect(),
config_paths: vec![(
"web".to_owned(),
RelativeUnixPathBuf::new("web/microfrontends.json").unwrap(),
)]
.into_iter()
.collect(),
mfe_package: None,
};

let turbo_json = TurboJson::default();
let actual = configs
.update_turbo_json(&PackageName::Root, Ok(turbo_json))
.unwrap();
assert_eq!(actual.global_deps, &["web/microfrontends.json".to_owned()]);
}
}
1 change: 1 addition & 0 deletions crates/turborepo-microfrontends/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ turborepo-errors = { workspace = true }
insta = { workspace = true }
pretty_assertions = { workspace = true }
tempfile = { workspace = true }
test-case = { workspace = true }

[lints]
workspace = true
Loading

0 comments on commit f55f520

Please sign in to comment.