diff --git a/Cargo.lock b/Cargo.lock index 6a9ebb2..8a373c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,27 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "beyond-launch" +version = "0.3.2" +dependencies = [ + "aho-corasick", + "anyhow", + "clap", + "criterion", + "dockerfile-parser", + "dotenvy", + "ignore", + "phf", + "rayon", + "regex", + "serde", + "serde_json", + "serde_yml", + "thiserror 2.0.18", + "toml", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -519,27 +540,6 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" -[[package]] -name = "beyond-launch" -version = "0.3.1" -dependencies = [ - "aho-corasick", - "anyhow", - "clap", - "criterion", - "dockerfile-parser", - "dotenvy", - "ignore", - "phf", - "rayon", - "regex", - "serde", - "serde_json", - "serde_yml", - "thiserror 2.0.18", - "toml", -] - [[package]] name = "pest" version = "2.8.6" diff --git a/Cargo.toml b/Cargo.toml index 694ef0b..c8f89aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "beyond-launch" -version = "0.3.1" +version = "0.3.2" edition = "2024" description = "Analyze a project and detect deployable services, languages, frameworks, commands, and env vars" license = "MIT" diff --git a/src/discovery.rs b/src/discovery.rs index 51f7029..6e16fe8 100644 --- a/src/discovery.rs +++ b/src/discovery.rs @@ -16,8 +16,10 @@ pub fn discover( fs: &dyn FileSystem, ) -> Result { walk(root, &mut signals, fs)?; - let outputs = generate(&mut signals, fs); - Ok(assemble(outputs, root)) + let (outputs, warnings) = generate(&mut signals, fs); + let mut discovery = assemble(outputs, root); + discovery.warnings = warnings; + Ok(discovery) } // -- Step 1: Walk + Observe -- @@ -161,21 +163,27 @@ pub fn discover_local_impl( fs: &dyn FileSystem, ) -> Result { walk_local(root, &mut signals)?; - let outputs = generate(&mut signals, fs); - Ok(assemble(outputs, root)) + let (outputs, warnings) = generate(&mut signals, fs); + let mut discovery = assemble(outputs, root); + discovery.warnings = warnings; + Ok(discovery) } // -- Step 2: Generate -- -pub fn generate(signals: &mut [Box], fs: &dyn FileSystem) -> Vec { +pub fn generate( + signals: &mut [Box], + fs: &dyn FileSystem, +) -> (Vec, Vec) { let mut outputs = Vec::with_capacity(signals.len()); + let mut warnings = Vec::new(); for signal in signals.iter_mut() { match signal.generate(fs) { Ok(output) => outputs.push(output), - Err(e) => eprintln!("signal {} failed: {e}", signal.name()), + Err(e) => warnings.push(format!("signal {} failed: {e}", signal.name())), } } - outputs + (outputs, warnings) } // -- Step 3: Assemble -- @@ -210,7 +218,7 @@ fn assemble(outputs: Vec, root: &Path) -> Discovery { } } - Discovery { services, monorepo } + Discovery { services, monorepo, ..Default::default() } } /// Get the "dir name" for a path — used for derived name detection. diff --git a/src/signals/docker_compose.rs b/src/signals/docker_compose.rs index 4c6695e..de2d473 100644 --- a/src/signals/docker_compose.rs +++ b/src/signals/docker_compose.rs @@ -387,10 +387,11 @@ impl Signal for DockerComposeSignal { for rel in paths { let resolved = compose_dir.join(rel); if let Ok(bytes) = fs.read_file(&resolved) { - let contents = String::from_utf8_lossy(&bytes); - let file_path = resolved.to_string_lossy(); - let file_env = parse_env_file_keys(&contents, &file_path); - merge_env_vars(&mut env, &file_env); + if let Ok(contents) = String::from_utf8(bytes) { + let file_path = resolved.to_string_lossy(); + let file_env = parse_env_file_keys(&contents, &file_path); + merge_env_vars(&mut env, &file_env); + } } } } diff --git a/src/types.rs b/src/types.rs index 9062c9c..07abfef 100644 --- a/src/types.rs +++ b/src/types.rs @@ -15,6 +15,9 @@ pub struct Discovery { pub services: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub monorepo: Option, + /// Signals that failed during generation; non-empty means the result may be incomplete. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub warnings: Vec, } #[derive(Debug, Clone, Default, Serialize, Deserialize)]