pub struct DependencyResolver {
pub source_manager: SourceManager,
/* private fields */
}Expand description
Core dependency resolver that transforms manifest dependencies into lockfile entries.
The DependencyResolver is the main entry point for dependency resolution.
It manages source repositories, resolves version constraints, detects conflicts,
and generates deterministic lockfile entries using a centralized SHA-based
resolution strategy for optimal performance.
§SHA-Based Resolution Workflow
Starting in v0.3.2, the resolver uses VersionResolver for centralized version
resolution that minimizes Git operations and maximizes worktree reuse:
- Collection: Gather all (source, version) pairs from dependencies
- Batch Resolution: Resolve all versions to commit SHAs in parallel
- SHA-Based Worktrees: Create worktrees keyed by commit SHA
- Deduplication: Multiple refs to same SHA share one worktree
§Configuration
The resolver can be configured in several ways:
- Standard: Uses manifest sources only via
new() - Global: Includes global config sources via
new_with_global() - Custom Cache: Uses custom cache directory via
with_cache()
§Thread Safety
The resolver is not thread-safe due to its mutable state during resolution. Create separate instances for concurrent operations.
Fields§
§source_manager: SourceManagerManages Git repository operations, source URL resolution, and authentication.
The source manager handles:
- Mapping source names to Git repository URLs
- Git operations (clone, fetch, checkout) for dependency resolution
- Authentication token management for private repositories
- Source validation and configuration management
Implementations§
Source§impl DependencyResolver
impl DependencyResolver
Sourcepub async fn pre_sync_sources(
&mut self,
deps: &[(String, ResourceDependency)],
) -> Result<()>
pub async fn pre_sync_sources( &mut self, deps: &[(String, ResourceDependency)], ) -> Result<()>
Pre-syncs all sources needed for the given dependencies.
This method implements the first phase of the two-phase resolution architecture. It should be called during the “Syncing sources” phase to perform all Git clone/fetch operations upfront, before actual dependency resolution.
This separation provides several benefits:
- Clear separation of network operations from version resolution logic
- Better progress reporting with distinct phases
- Enables batch processing of Git operations for efficiency
- Allows the
resolve_all()method to work purely with local cached data
After calling this method, the internal VersionResolver will have all
necessary source repositories cached and ready for version-to-SHA resolution.
§Arguments
deps- A slice of tuples containing dependency names and their definitions. Only dependencies with Git sources will be processed.
§Example
let manifest = Manifest::new();
let cache = Cache::new()?;
let mut resolver = DependencyResolver::with_cache(manifest.clone(), cache);
// Get all dependencies from manifest
let deps: Vec<(String, ResourceDependency)> = manifest
.all_dependencies()
.into_iter()
.map(|(name, dep)| (name.to_string(), dep.clone()))
.collect();
// Phase 1: Pre-sync all sources (performs Git clone/fetch operations)
resolver.pre_sync_sources(&deps).await?;
// Phase 2: Now sources are ready for version resolution (no network I/O)
let resolved = resolver.resolve().await?;§Two-Phase Resolution Pattern
This method is part of AGPM’s two-phase resolution architecture:
- Sync Phase (
pre_sync_sources): Clone/fetch all Git repositories - Resolution Phase (
resolveorupdate): Resolve versions to SHAs locally
This pattern ensures all network operations happen upfront with clear progress reporting, while version resolution can proceed quickly using cached data.
§Errors
Returns an error if:
- Source repository cloning or fetching fails
- Network connectivity issues occur
- Authentication fails for private repositories
- Source names in dependencies don’t match configured sources
- Git operations fail due to repository corruption or disk space issues
Sourcepub async fn get_available_versions(
&self,
repo_path: &Path,
) -> Result<Vec<String>>
pub async fn get_available_versions( &self, repo_path: &Path, ) -> Result<Vec<String>>
Get available versions (tags) for a repository.
Lists all tags from a Git repository, which typically represent available versions. This is useful for checking what versions are available for updates.
§Arguments
repo_path- Path to the Git repository (typically in the cache directory)
§Returns
A vector of version strings (tag names) available in the repository.
§Examples
use agpm_cli::resolver::DependencyResolver;
use agpm_cli::cache::Cache;
let cache = Cache::new()?;
let repo_path = cache.get_repo_path("community");
let resolver = DependencyResolver::new(manifest, cache, 10)?;
let versions = resolver.get_available_versions(&repo_path).await?;
for version in versions {
println!("Available: {}", version);
}Sourcepub fn new(manifest: Manifest, cache: Cache) -> Result<Self>
pub fn new(manifest: Manifest, cache: Cache) -> Result<Self>
Creates a new resolver using only manifest-defined sources.
This constructor creates a resolver that only considers sources defined
in the manifest file. Global configuration sources from ~/.agpm/config.toml
are ignored, which may cause resolution failures for private repositories
that require authentication.
§Usage
Use this constructor for:
- Public repositories only
- Testing and development
- Backward compatibility with older workflows
For production use with private repositories, prefer new_with_global().
§Errors
Returns an error if the cache cannot be created.
Sourcepub async fn new_with_global(manifest: Manifest, cache: Cache) -> Result<Self>
pub async fn new_with_global(manifest: Manifest, cache: Cache) -> Result<Self>
Creates a new resolver with global configuration support.
This is the recommended constructor for most use cases. It loads both
manifest sources and global sources from ~/.agpm/config.toml, enabling
access to private repositories with authentication tokens.
§Source Priority
When sources are defined in both locations:
- Global sources (from
~/.agpm/config.toml) are loaded first - Local sources (from
agpm.toml) can override global sources
This allows teams to share project configurations while keeping authentication tokens in user-specific global config.
§Errors
Returns an error if:
- The cache cannot be created
- The global config file exists but cannot be parsed
- Network errors occur while validating global sources
Sourcepub fn with_cache(manifest: Manifest, cache: Cache) -> Self
pub fn with_cache(manifest: Manifest, cache: Cache) -> Self
Creates a new resolver with a custom cache.
This constructor is primarily used for testing and specialized deployments
where the default cache location (~/.agpm/cache/) is not suitable.
§Use Cases
- Testing: Isolated cache for test environments
- CI/CD: Custom cache locations for build systems
- Containers: Non-standard filesystem layouts
- Multi-user: Shared cache directories
§Note
This constructor does not load global configuration. If you need both
custom cache location and global config support, create the resolver
with new_with_global() and manually configure the source manager.
Sourcepub async fn resolve(&mut self) -> Result<LockFile>
pub async fn resolve(&mut self) -> Result<LockFile>
Resolve all manifest dependencies into a deterministic lockfile.
This is the primary entry point for dependency resolution. It resolves all dependencies from the manifest (including transitive dependencies) and generates a complete lockfile with resolved versions and commit SHAs.
By default, this method enables transitive dependency resolution. Resources can declare their own dependencies via YAML frontmatter (Markdown) or JSON fields, which will be automatically discovered and resolved.
§Transitive Dependency Resolution
When enabled (default), the resolver:
- Resolves direct manifest dependencies
- Extracts dependency metadata from resource files
- Builds a dependency graph with cycle detection
- Resolves transitive dependencies in topological order
§Returns
A complete LockFile with all resolved dependencies including:
- Resolved commit SHAs for reproducible installations
- Checksums for integrity verification
- Installation paths for all resources
- Source repository information
§Errors
Returns an error if:
- Source repositories cannot be accessed
- Version constraints cannot be satisfied
- Circular dependencies are detected
- Resource files cannot be read or parsed
§Example
let manifest = Manifest::load("agpm.toml".as_ref())?;
let cache = Cache::new()?;
let mut resolver = DependencyResolver::new(manifest, cache)?;
// Resolve all dependencies including transitive ones
let lockfile = resolver.resolve().await?;
lockfile.save("agpm.lock".as_ref())?;
println!("Resolved {} total resources",
lockfile.agents.len() + lockfile.snippets.len());Sourcepub async fn resolve_with_options(
&mut self,
enable_transitive: bool,
) -> Result<LockFile>
pub async fn resolve_with_options( &mut self, enable_transitive: bool, ) -> Result<LockFile>
Resolve dependencies with configurable transitive dependency support.
This method provides fine-grained control over dependency resolution behavior, allowing you to disable transitive dependency resolution when needed. This is useful for debugging, testing, or when you want to install only direct dependencies without their transitive requirements.
§Arguments
enable_transitive- Whether to resolve transitive dependenciestrue: Full transitive resolution (default behavior)false: Only direct manifest dependencies
§Transitive Resolution Details
When enable_transitive is true:
- Resources are checked for embedded dependency metadata
- Markdown files (.md): YAML frontmatter between
---delimiters - JSON files (.json): Top-level
dependenciesfield - Dependency graph is built with cycle detection
- Dependencies are resolved in topological order
When enable_transitive is false:
- Only dependencies explicitly declared in
agpm.tomlare resolved - Resource metadata is not extracted or processed
- Faster resolution for known dependency trees
§Returns
A LockFile containing all resolved dependencies according to the
configuration. When transitive resolution is disabled, the lockfile will
only contain direct dependencies from the manifest.
§Errors
Returns an error if:
- Source repositories are inaccessible or invalid
- Version constraints conflict or cannot be satisfied
- Circular dependencies are detected (when
enable_transitiveis true) - Resource files cannot be read or contain invalid metadata
- Network operations fail during source synchronization
§Performance Considerations
Disabling transitive resolution (enable_transitive = false) can improve
performance when:
- You know all required dependencies are explicitly listed
- Testing specific dependency combinations
- Debugging dependency resolution issues
- Working with large resources that have expensive metadata extraction
§Example
let manifest = Manifest::load("agpm.toml".as_ref())?;
let cache = Cache::new()?;
let mut resolver = DependencyResolver::new(manifest, cache)?;
// Resolve only direct dependencies without transitive resolution
let lockfile = resolver.resolve_with_options(false).await?;
println!("Resolved {} direct dependencies",
lockfile.agents.len() + lockfile.snippets.len());§See Also
resolve(): Convenience method that enables transitive resolution by defaultDependencyGraph: Graph structure used for cycle detection and orderingDependencySpec: Specification format for transitive dependencies
Sourcepub async fn update(
&mut self,
existing: &LockFile,
deps_to_update: Option<Vec<String>>,
) -> Result<LockFile>
pub async fn update( &mut self, existing: &LockFile, deps_to_update: Option<Vec<String>>, ) -> Result<LockFile>
Updates an existing lockfile with new or changed dependencies.
This method performs incremental dependency resolution by comparing the current manifest against an existing lockfile and updating only the specified dependencies (or all if none specified).
§Update Strategy
The update process follows these steps:
- Selective Resolution: Only resolve specified dependencies
- Preserve Existing: Keep unchanged dependencies from existing lockfile
- In-place Updates: Replace matching entries with new versions
- New Additions: Append newly added dependencies
§Use Cases
- Selective Updates: Update specific outdated dependencies
- Security Patches: Update dependencies with known vulnerabilities
- Feature Updates: Pull latest versions for active development
- Manifest Changes: Reflect additions/modifications to agpm.toml
§Parameters
existing: Current lockfile to updatedeps_to_update: Optional list of specific dependencies to update. IfNone, all dependencies are updated.progress: Optional progress bar for user feedback
§Returns
A new LockFile with updated dependencies. The original lockfile
structure is preserved, with only specified entries modified.
§Algorithm Complexity
- Time: O(u + s·log(t)) where u = dependencies to update
- Space: O(n) where n = total dependencies in lockfile
§Performance Benefits
- Network Optimization: Only syncs sources for updated dependencies
- Cache Utilization: Reuses existing source repositories
- Parallel Processing: Updates multiple dependencies concurrently
§Errors
Update can fail due to:
- Network issues accessing source repositories
- Version constraints that cannot be satisfied
- Authentication failures for private sources
- Corrupted or inaccessible cache directories
Sourcepub fn verify(&mut self) -> Result<()>
pub fn verify(&mut self) -> Result<()>
Verifies that all dependencies can be resolved without performing resolution.
This method performs a “dry run” validation of the manifest to detect issues before attempting actual resolution. It’s faster than full resolution since it doesn’t clone repositories or resolve specific versions.
§Validation Steps
- Local Path Validation: Verify local dependencies exist (for absolute paths)
- Source Validation: Ensure all referenced sources are defined
- Constraint Validation: Basic syntax checking of version constraints
§Validation Scope
- Manifest Structure: Validate TOML structure and required fields
- Source References: Ensure all sources used by dependencies exist
- Local Dependencies: Check absolute paths exist on filesystem
§Performance
Verification is designed to be fast:
- No network operations (doesn’t validate remote repositories)
- No Git operations (doesn’t check if versions exist)
- Only filesystem access for absolute local paths
§Parameters
progress: Optional progress bar for user feedback
§Returns
Ok(()) if all dependencies pass basic validation.
§Errors
Verification fails if:
- Local dependencies reference non-existent absolute paths
- Dependencies reference undefined sources
- Manifest structure is invalid or corrupted
§Note
Successful verification doesn’t guarantee resolution will succeed, since network issues or missing versions can still cause failures. Use this method for fast validation before expensive resolution operations.