Conversation
deff46d to
5331856
Compare
|
Taking a look now! |
|
can you think of any obvious ways to break up tree.rs into a few smaller modules just to make it easier to navigate? not super important |
|
small thought on the graph struct: the |
53df86e to
5447788
Compare
|
How's this coming since I last looked? Want me to take another look? |
387c5e6 to
56cba26
Compare
| let url = match &dependency.location { | ||
| DependencyLocation::Remote(remote) => db | ||
| .dependency_graph() | ||
| .local_for_remote_git(db, remote) |
There was a problem hiding this comment.
This should be tracked in workspace.
There was a problem hiding this comment.
@micahscopes I think little organizational things like this can wait for a later pr.
0b4bda2 to
f12f566
Compare
cburgdorf
left a comment
There was a problem hiding this comment.
Looks great. Just left one comment for a potential source of problems you might want to double check.
There was a problem hiding this comment.
Pull request overview
This PR adds comprehensive support for remote git dependencies to the Fe language compiler, enabling projects to depend on packages hosted in git repositories rather than only local filesystem paths.
Key Changes:
- Introduced git resolution infrastructure with platform-specific implementations (native git2 and WASM stub)
- Extended the dependency graph resolver to support priority-based resolution (local vs remote dependencies)
- Refactored diagnostic handling to use callback-based patterns instead of accumulating diagnostics in resolvers
- Reorganized dependency tree display logic and added remote edge visualization
Reviewed changes
Copilot reviewed 73 out of 76 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
crates/resolver/src/git.rs |
Core git repository cloning and checkout logic using libgit2 |
crates/resolver/src/git_stub.rs |
WASM-compatible stub that returns unsupported errors |
crates/resolver/src/ingot.rs |
Unified resolver combining local files and remote git sources |
crates/resolver/src/graph.rs |
Added priority-based resolution and removed diagnostics field |
crates/resolver/src/lib.rs |
Refactored Resolver trait with callback-based diagnostics |
crates/driver/src/ingot_handler.rs |
New handler implementing resolution callbacks and path conversions |
crates/driver/src/lib.rs |
Simplified init_ingot to use boolean return instead of diagnostic vec |
crates/common/src/dependencies/graph.rs |
Added remote checkout tracking with bidirectional maps |
crates/common/src/dependencies/tree.rs |
Extracted tree display logic with remote edge visualization |
crates/common/src/config.rs |
Added git dependency parsing (source, rev, path fields) |
crates/common/src/cache.rs |
Platform-aware cache directory resolution for git checkouts |
crates/fe/src/tree.rs |
Simplified by delegating to DependencyTree infrastructure |
| Test snapshots | Updated for new tree display format and diagnostic messages |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if self.git.has_valid_cached_checkout(description) | ||
| && let Ok((result, _)) = | ||
| self.load_remote_files(handler, description, checkout_path.as_path(), true) | ||
| { | ||
| return Ok(result); |
There was a problem hiding this comment.
Performance: The code attempts to resolve from cache first (line 320-324), then falls back to network resolution. However, when the cache check succeeds, on_resolution_start is never called (unlike the fallback path on line 328). This inconsistency could confuse handlers expecting symmetric lifecycle callbacks.
| pub trait RemoteProgress { | ||
| fn start(&mut self, description: &GitDescription); | ||
| fn success(&mut self, description: &GitDescription, ingot_url: &Url); | ||
| fn error(&mut self, description: &GitDescription, error: &IngotResolutionError); | ||
| } |
There was a problem hiding this comment.
Missing documentation: The RemoteProgress trait lacks documentation explaining when each method is called and what implementers should do. This is a public API (pub trait) that external code may implement.
| let repo = Repository::clone(&remote, checkout_path.as_std_path()).map_err(|error| { | ||
| GitResolutionError::CloneRepository { | ||
| source: description.source.clone(), | ||
| error, | ||
| } | ||
| })?; | ||
| self.checkout_revision(&repo, description)?; | ||
| Ok(CheckoutStatus::Fresh) | ||
| } | ||
|
|
||
| fn checkout_revision( | ||
| &self, | ||
| repo: &Repository, | ||
| description: &GitDescription, | ||
| ) -> Result<(), GitResolutionError> { | ||
| let oid = git2::Oid::from_str(&description.rev).map_err(|error| { | ||
| GitResolutionError::InvalidRevision { | ||
| rev: description.rev.clone(), | ||
| error, | ||
| } | ||
| })?; | ||
| let object = | ||
| repo.find_object(oid, None) | ||
| .map_err(|error| GitResolutionError::RevisionLookup { | ||
| rev: description.rev.clone(), | ||
| error, | ||
| })?; | ||
| let mut builder = CheckoutBuilder::new(); | ||
| builder.force(); | ||
| repo.checkout_tree(&object, Some(&mut builder)) | ||
| .map_err(|error| GitResolutionError::Checkout { | ||
| rev: description.rev.clone(), | ||
| error, | ||
| })?; | ||
| repo.set_head_detached(oid) | ||
| .map_err(|error| GitResolutionError::Checkout { | ||
| rev: description.rev.clone(), | ||
| error, | ||
| })?; | ||
| Ok(()) |
There was a problem hiding this comment.
The git2 checkout operations (clone, checkout_tree, set_head_detached) have no timeout mechanism. A malicious or very large repository could cause the process to hang indefinitely, creating a potential denial-of-service vector.
| impl GitDescription { | ||
| pub fn new(source: Url, rev: impl Into<String>) -> Self { | ||
| Self { | ||
| source, | ||
| rev: rev.into(), | ||
| path: None, | ||
| } |
There was a problem hiding this comment.
[nitpick] The description field rev accepts any string, but the code only validates it when converting to an OID. This allows invalid revisions to be stored and propagated through the system until resolution time. Consider validating that rev looks like a valid git revision (commit hash, branch, tag) at construction time to fail fast.
| env_path("FE_REMOTE_CACHE_DIR") | ||
| .or_else(|| { | ||
| env_path("FE_CACHE_DIR").map(|path| { | ||
| if path.ends_with("git") { |
There was a problem hiding this comment.
The ends_with("git") check is unreliable for determining if a path is a git cache directory. This could lead to incorrect behavior if a user sets FE_CACHE_DIR to a path ending in "git" (e.g., /home/user/my-git). Consider checking if the last component equals "git" rather than using ends_with().
| if path.ends_with("git") { | |
| if path.file_name().map_or(false, |name| name == "git") { |
| error, | ||
| })?; | ||
| let mut builder = CheckoutBuilder::new(); | ||
| builder.force(); |
There was a problem hiding this comment.
Security concern: Using .force() on CheckoutBuilder can overwrite local modifications without warning. While this may be intentional for a cache, it could lead to data loss if users mistakenly modify checked-out dependencies. Consider documenting this behavior or adding a safety mechanism.
Added remote dependency support.
resolver graph/ingot logic.
common/src/dependencies/tree.rs.
fe treesimplified.