Skip to content

Commit f3ebe1b

Browse files
refactor(SideCar): Robust runtime path resolution for workspace builds
The previous implementation of `GetBaseSidecarDirectory` relied on a hardcoded directory traversal depth (3 levels up), which failed to locate the project root when the SideCar binary was built within the Land monorepo's workspace target structure (e.g., `.../Target/release/SideCar`). This commit refactors the discovery logic into a tree-walking algorithm that intelligently identifies the SideCar root regardless of build configuration. It verifies the root using three heuristics: 1. The presence of the `Source/Library.rs` entry point. 2. Parsing `Cargo.toml` to confirm `package.name` is "SideCar". 3. Detecting the monorepo-specific `Element/SideCar` directory structure. Changes include: - Added `toml` to `Cargo.toml` dependencies for manifest parsing. - Implemented the loop-based search strategy in `Source/Download.rs`. - Added `use toml;` import. This ensures the SideCar manager can reliably resolve asset paths for both standalone builds and those integrated into the Land monorepo CI/CD pipeline.
1 parent 0c1d8da commit f3ebe1b

2 files changed

Lines changed: 81 additions & 15 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ num_cpus = { workspace = true }
2626
log = { workspace = true }
2727
env_logger = { workspace = true }
2828
fs_extra = { workspace = true }
29+
toml = { workspace = true }
2930

3031
[lib]
3132
path = "Source/Library.rs"

Source/Download.rs

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ impl DownloadCache {
145145

146146
// Create a temporary struct to hold the sorted entries for serialization
147147
let CacheToSerialize = serde_json::json!({
148+
148149
"Entries": SortedEntries
149150
});
150151

@@ -172,26 +173,88 @@ impl DownloadCache {
172173

173174
/// Returns the root directory where all sidecars will be stored.
174175
/// This is determined dynamically by navigating up from the executable's
175-
/// location. It assumes the executable is located in a path like
176-
/// `.../SideCar/Target/release/`, and it will resolve the base path to
177-
/// `.../SideCar/`.
176+
/// location and detecting the SideCar project root. It handles both:
177+
/// - Standalone builds: `.../SideCar/Target/release/`
178+
/// - Workspace builds: `.../workspace/Target/release/SideCar` (where the
179+
/// workspace root contains multiple crates including Element/SideCar)
178180
fn GetBaseSidecarDirectory() -> Result<PathBuf> {
179181
// Get the full path to the currently running executable.
180182
let CurrentExePath = env::current_exe().context("Failed to get the path of the current executable.")?;
181183

182-
// The first .parent() gets the directory containing the exe (e.g., `release`).
183-
// We then navigate up two more levels to get to the intended `SideCar`
184-
// directory.
185-
let BaseDirectory = CurrentExePath
184+
// Start from the directory containing the executable and walk up the tree.
185+
let mut CurrentDir = CurrentExePath
186186
.parent()
187-
.and_then(|p| p.parent())
188-
.and_then(|p| p.parent())
189-
.context(
190-
"Could not determine the base sidecar directory. Expected to be run from a subdirectory like \
191-
'Target/release' within the sidecar project.",
192-
)?;
193-
194-
Ok(BaseDirectory.to_path_buf())
187+
.context("Executable must be in a directory (not the root).")?;
188+
189+
loop {
190+
// Check A: Does Source/Library.rs exist in current directory? → return current directory
191+
let LibraryRsPath = CurrentDir.join("Source").join("Library.rs");
192+
193+
if LibraryRsPath.exists() {
194+
return Ok(CurrentDir.to_path_buf());
195+
}
196+
197+
// Check B: Does a Cargo.toml exist in current directory with package.name = "SideCar"? → return current directory
198+
let CargoTomlPath = CurrentDir.join("Cargo.toml");
199+
200+
if CargoTomlPath.exists() {
201+
if let Ok(CargoContents) = fs::read_to_string(&CargoTomlPath) {
202+
if let Ok(Toml) = toml::from_str::<toml::Value>(&CargoContents) {
203+
if let Some(Package) = Toml.get("package") {
204+
if let Some(PackageName) = Package.get("name").and_then(|v| v.as_str()) {
205+
if PackageName == "SideCar" {
206+
// Verify that Source subdirectory exists as additional confirmation.
207+
let SourceDir = CurrentDir.join("Source");
208+
209+
if SourceDir.exists() {
210+
return Ok(CurrentDir.to_path_buf());
211+
}
212+
}
213+
}
214+
}
215+
}
216+
}
217+
}
218+
219+
// Check C: Does Element/SideCar/Cargo.toml exist relative to current directory
220+
// AND does it have package.name = "SideCar"? → return Element/SideCar subdirectory path
221+
let SubdirCargoTomlPath = CurrentDir.join("Element").join("SideCar").join("Cargo.toml");
222+
223+
if SubdirCargoTomlPath.exists() {
224+
if let Ok(CargoContents) = fs::read_to_string(&SubdirCargoTomlPath) {
225+
if let Ok(Toml) = toml::from_str::<toml::Value>(&CargoContents) {
226+
if let Some(Package) = Toml.get("package") {
227+
if let Some(PackageName) = Package.get("name").and_then(|v| v.as_str()) {
228+
if PackageName == "SideCar" {
229+
// Verify that the Element/SideCar/Source subdirectory exists.
230+
let SourceDir = CurrentDir.join("Element").join("SideCar").join("Source");
231+
232+
if SourceDir.exists() {
233+
// Return the full path to the Element/SideCar subdirectory.
234+
return Ok(CurrentDir.join("Element").join("SideCar"));
235+
}
236+
}
237+
}
238+
}
239+
}
240+
}
241+
}
242+
243+
// Move up one level.
244+
let NextDir = match CurrentDir.parent() {
245+
Some(Parent) => Parent,
246+
247+
None => break, // Reached filesystem root without finding the project
248+
};
249+
250+
CurrentDir = NextDir;
251+
}
252+
253+
Err(anyhow!(
254+
"Could not determine the SideCar base directory. The executable should be built from within the SideCar \
255+
crate or from the workspace containing Element/SideCar. Searched up from: {}",
256+
CurrentExePath.display()
257+
))
195258
}
196259

197260
/// Defines the matrix of platforms to target. Each entry specifies how to
@@ -269,6 +332,7 @@ fn UpdateGitattributes(BaseDirectory:&Path) -> Result<()> {
269332
# main repository history small and fast.
270333
#
271334
# The `-text` attribute is used to prevent Git from normalizing line endings,
335+
272336
# which is critical for binary files and scripts.
273337
#
274338
# This file is automatically managed by the sidecar vendor script.
@@ -745,3 +809,4 @@ use log::{LevelFilter, error, info, warn};
745809
use reqwest::Client;
746810
use serde::{Deserialize, Serialize};
747811
use tempfile::Builder;
812+
use toml;

0 commit comments

Comments
 (0)