@@ -61,8 +61,16 @@ impl ManifestProvenance {
6161 }
6262
6363 /// Returns the absolute path to the manifest file.
64+ ///
65+ /// This method canonicalizes the parent directory but preserves the original
66+ /// filename, which allows symlinked manifest files to be treated correctly.
6467 pub fn absolute_path ( & self ) -> PathBuf {
65- dunce:: canonicalize ( self . path . clone ( ) ) . unwrap_or ( self . path . to_path_buf ( ) )
68+ match ( self . path . parent ( ) , self . path . file_name ( ) ) {
69+ ( Some ( parent) , Some ( file_name) ) => dunce:: canonicalize ( parent)
70+ . map ( |canonical_parent| canonical_parent. join ( file_name) )
71+ . unwrap_or_else ( |_| self . path . to_path_buf ( ) ) ,
72+ _ => self . path . to_path_buf ( ) ,
73+ }
6674 }
6775}
6876
@@ -142,3 +150,70 @@ impl<T> AssociateProvenance for T {
142150 WithProvenance :: new ( self , provenance)
143151 }
144152}
153+
154+ #[ cfg( test) ]
155+ mod tests {
156+ use super :: * ;
157+
158+ #[ test]
159+ fn absolute_path_canonicalizes_parent_only ( ) {
160+ let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
161+ let dotfiles_dir = temp_dir. path ( ) . join ( "dotfiles" ) ;
162+ let home_dir = temp_dir. path ( ) . join ( "home" ) ;
163+ fs_err:: create_dir_all ( & dotfiles_dir) . unwrap ( ) ;
164+ fs_err:: create_dir_all ( & home_dir) . unwrap ( ) ;
165+
166+ // Real manifest lives inside the dotfiles directory.
167+ let real_manifest = dotfiles_dir. join ( "pixi.toml" ) ;
168+ fs_err:: write ( & real_manifest, "[workspace]\n name = \" test\" \n " ) . unwrap ( ) ;
169+
170+ // Home directory contains a symlink that points at the real manifest.
171+ let symlink_manifest = home_dir. join ( "pixi.toml" ) ;
172+ #[ cfg( unix) ]
173+ std:: os:: unix:: fs:: symlink ( & real_manifest, & symlink_manifest) . unwrap ( ) ;
174+
175+ let canonical_real_path = dunce:: canonicalize ( & real_manifest) . unwrap ( ) ;
176+ let cases = [
177+ (
178+ "real file" ,
179+ real_manifest. clone ( ) ,
180+ dotfiles_dir. clone ( ) ,
181+ true ,
182+ ) ,
183+ (
184+ "symlinked file" ,
185+ symlink_manifest. clone ( ) ,
186+ home_dir. clone ( ) ,
187+ false ,
188+ ) ,
189+ ] ;
190+
191+ for ( label, manifest_path, expected_parent, should_match_real) in cases {
192+ let provenance = ManifestProvenance :: new ( manifest_path. clone ( ) , ManifestKind :: Pixi ) ;
193+ let absolute = provenance. absolute_path ( ) ;
194+
195+ assert_eq ! (
196+ absolute. file_name( ) ,
197+ manifest_path. file_name( ) ,
198+ "filename changed for {label}"
199+ ) ;
200+ assert_eq ! (
201+ absolute. parent( ) . unwrap( ) ,
202+ dunce:: canonicalize( & expected_parent) . unwrap( ) ,
203+ "parent directory mismatch for {label}"
204+ ) ;
205+
206+ if should_match_real {
207+ assert_eq ! (
208+ absolute, canonical_real_path,
209+ "real file should resolve exactly"
210+ ) ;
211+ } else {
212+ assert_ne ! (
213+ absolute, canonical_real_path,
214+ "symlink should not resolve to the real file path"
215+ ) ;
216+ }
217+ }
218+ }
219+ }
0 commit comments