diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d1f612b..0e9656a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,40 @@ --- +## V35 2026-03-20 21:00:00 + +**Harden dpack repo loading and fix search command failure** + +### Changes: +- Fixed `src/dpack/src/main.rs`: + - Search, Remove, Upgrade, and CheckUpdates commands now gracefully handle + repo loading failures — logs a warning to stderr and continues to next repo + instead of aborting the entire command via `?` operator + - This prevents a single broken/unreadable repo or package file from making + the search command return no results +- Fixed `src/dpack/src/resolver/mod.rs`: + - `load_repo()` now gracefully handles unreadable directory entries + (broken symlinks, permission errors) — logs warning and skips instead of + propagating error via `?` + - `file_type()` errors fall back to `path.is_dir()` (follows symlinks) instead + of aborting the entire repo scan +- Improved `tests/run-tests.sh`: + - dpack.cli.search test now captures both stdout and stderr (was suppressing + stderr with `2>/dev/null` which hid error messages) + - On failure, includes first 5 lines of output in the failure message for debugging + +### Plan deviation/changes: +- None + +### What is missing/needs polish: +- The build/install orchestrator (`build/mod.rs`) still uses `?` for load_repo — this + is intentional since installs need complete dependency information +- Root cause of the original search failure (which repo/file triggered the error) + is not yet identified — the improved error handling and diagnostics will reveal + this on the next test run + +--- + ## V34 2026-03-20 17:30:00 **Fix critical gaps: locale-gen, 32-bit multilib, network auto-detect, polkit** diff --git a/src/dpack/src/main.rs b/src/dpack/src/main.rs index b347ee8..19c4a6c 100644 --- a/src/dpack/src/main.rs +++ b/src/dpack/src/main.rs @@ -128,7 +128,13 @@ fn run(cli: Cli) -> Result<()> { // Load repos for reverse-dep checking let mut all_repo_packages = std::collections::HashMap::new(); for repo in &config.repos { - let repo_pkgs = resolver::DependencyGraph::load_repo(&repo.path)?; + let repo_pkgs = match resolver::DependencyGraph::load_repo(&repo.path) { + Ok(pkgs) => pkgs, + Err(e) => { + eprintln!("Warning: failed to load repo '{}': {}", repo.name, e); + continue; + } + }; all_repo_packages.extend(repo_pkgs); } @@ -191,7 +197,13 @@ fn run(cli: Cli) -> Result<()> { // Load all repos to compare available vs installed versions let mut all_repo_packages = std::collections::HashMap::new(); for repo in &config.repos { - let repo_pkgs = resolver::DependencyGraph::load_repo(&repo.path)?; + let repo_pkgs = match resolver::DependencyGraph::load_repo(&repo.path) { + Ok(pkgs) => pkgs, + Err(e) => { + eprintln!("Warning: failed to load repo '{}': {}", repo.name, e); + continue; + } + }; all_repo_packages.extend(repo_pkgs); } @@ -254,7 +266,13 @@ fn run(cli: Cli) -> Result<()> { Commands::Search { query } => { // Search through all repos for matching package names/descriptions for repo in &config.repos { - let packages = resolver::DependencyGraph::load_repo(&repo.path)?; + let packages = match resolver::DependencyGraph::load_repo(&repo.path) { + Ok(pkgs) => pkgs, + Err(e) => { + eprintln!("Warning: failed to load repo '{}': {}", repo.name, e); + continue; + } + }; for (name, pkg) in &packages { if name.contains(&query) || pkg.package.description.to_lowercase().contains(&query.to_lowercase()) { println!( @@ -398,7 +416,13 @@ fn run(cli: Cli) -> Result<()> { // Load all repos let mut all_repo_packages = std::collections::HashMap::new(); for repo in &config.repos { - let repo_pkgs = resolver::DependencyGraph::load_repo(&repo.path)?; + let repo_pkgs = match resolver::DependencyGraph::load_repo(&repo.path) { + Ok(pkgs) => pkgs, + Err(e) => { + eprintln!("Warning: failed to load repo '{}': {}", repo.name, e); + continue; + } + }; all_repo_packages.extend(repo_pkgs); } diff --git a/src/dpack/src/resolver/mod.rs b/src/dpack/src/resolver/mod.rs index d9daf5c..0647435 100644 --- a/src/dpack/src/resolver/mod.rs +++ b/src/dpack/src/resolver/mod.rs @@ -84,8 +84,21 @@ impl DependencyGraph { for entry in std::fs::read_dir(repo_dir) .with_context(|| format!("Failed to read repo: {}", repo_dir.display()))? { - let entry = entry?; - if !entry.file_type()?.is_dir() { + let entry = match entry { + Ok(e) => e, + Err(e) => { + log::warn!("Skipping unreadable entry in {}: {}", repo_dir.display(), e); + continue; + } + }; + let is_dir = match entry.file_type() { + Ok(ft) => ft.is_dir(), + Err(_) => { + // Fallback: try metadata (follows symlinks) + entry.path().is_dir() + } + }; + if !is_dir { continue; } diff --git a/src/repos b/src/repos index 90de455..d4fe8ed 160000 --- a/src/repos +++ b/src/repos @@ -1 +1 @@ -Subproject commit 90de455035fcc93b45e28763f2a7fc44af0706a2 +Subproject commit d4fe8edd52ae9da1dd5a0256fb34d0db0adbb147 diff --git a/tests/run-tests.sh b/tests/run-tests.sh index 883aa86..6c8ca33 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -280,10 +280,11 @@ DCONF record_test "dpack.cli.check" "fail" "Exit code $?" fi - if $DPACK_CMD search zlib 2>/dev/null | grep -q "zlib"; then + SEARCH_OUT=$($DPACK_CMD search zlib 2>&1) + if echo "$SEARCH_OUT" | grep -q "zlib"; then record_test "dpack.cli.search" "pass" else - record_test "dpack.cli.search" "fail" "zlib not found in search" + record_test "dpack.cli.search" "fail" "zlib not found in search. Output: $(echo "$SEARCH_OUT" | head -5)" fi if $DPACK_CMD info zlib 2>/dev/null | grep -qi "compression\|zlib"; then