@@ -93,59 +93,75 @@ fn get_conda_package_info_from_history(path: &Path, name: &Package) -> Option<Co
9393 let package_entry = format ! ( ":{}-" , name. to_name( ) ) ;
9494
9595 let history_contents = fs:: read_to_string ( history) . ok ( ) ?;
96- for line in history_contents
96+
97+ // Filter to only include lines that:
98+ // 1. Start with '+' (installed packages, not '-' for removed packages)
99+ // 2. Contain the package entry (e.g., ":python-")
100+ //
101+ // We need the LAST matching entry because conda appends to history chronologically.
102+ // When a package is upgraded, the old version is removed (-) and new version installed (+).
103+ // The last '+' entry represents the currently installed version.
104+ //
105+ // Sample history for Python upgrade from 3.9.18 to 3.9.21:
106+ // +defaults::python-3.9.18-h123456_0 <- initial install
107+ // ...
108+ // -defaults::python-3.9.18-h123456_0 <- removed during upgrade
109+ // +defaults::python-3.9.21-h789abc_0 <- current version (we want this)
110+ let matching_lines: Vec < & str > = history_contents
97111 . lines ( )
98- . filter ( |l| l. contains ( & package_entry) )
99- {
100- // Sample entry in the history file
101- // +conda-forge/osx-arm64::psutil-5.9.8-py312he37b823_0
102- // +conda-forge/osx-arm64::python-3.12.2-hdf0ec26_0_cpython
103- // +conda-forge/osx-arm64::python_abi-3.12-4_cp312
104- let regex = get_package_version_history_regex ( name) ;
105- if let Some ( captures) = regex. captures ( line) {
106- if let Some ( version) = captures. get ( 1 ) {
107- if let Some ( hash) = captures. get ( 2 ) {
108- let package_path = format ! (
109- "{}-{}-{}.json" ,
110- name. to_name( ) ,
111- version. as_str( ) ,
112- hash. as_str( )
113- ) ;
114- let package_path = path. join ( package_path) ;
115- let mut arch: Option < Architecture > = None ;
116- // Sample contents
117- // {
118- // "build": "h966fe2a_2",
119- // "build_number": 2,
120- // "channel": "https://repo.anaconda.com/pkgs/main/win-64",
121- // "constrains": [],
122- // }
123- // 32bit channel is https://repo.anaconda.com/pkgs/main/win-32/
124- // 64bit channel is "channel": "https://repo.anaconda.com/pkgs/main/osx-arm64",
125- if let Ok ( contents) = read_to_string ( & package_path) {
126- if let Ok ( js) = serde_json:: from_str :: < CondaMetaPackageStructure > ( & contents)
127- {
128- if let Some ( channel) = js. channel {
129- if channel. ends_with ( "64" ) {
130- arch = Some ( Architecture :: X64 ) ;
131- } else if channel. ends_with ( "32" ) {
132- arch = Some ( Architecture :: X86 ) ;
133- }
134- }
135- if let Some ( version) = js. version {
136- return Some ( CondaPackageInfo {
137- package : name. clone ( ) ,
138- path : package_path,
139- version,
140- arch,
141- } ) ;
142- } else {
143- warn ! (
144- "Unable to find version for package {} in {:?}" ,
145- name, package_path
146- ) ;
112+ . filter ( |l| l. starts_with ( '+' ) && l. contains ( & package_entry) )
113+ . collect ( ) ;
114+
115+ // Get the last matching line (most recent installation)
116+ let line = matching_lines. last ( ) ?;
117+
118+ // Sample entry in the history file
119+ // +conda-forge/osx-arm64::psutil-5.9.8-py312he37b823_0
120+ // +conda-forge/osx-arm64::python-3.12.2-hdf0ec26_0_cpython
121+ // +conda-forge/osx-arm64::python_abi-3.12-4_cp312
122+ let regex = get_package_version_history_regex ( name) ;
123+ if let Some ( captures) = regex. captures ( line) {
124+ if let Some ( version) = captures. get ( 1 ) {
125+ if let Some ( hash) = captures. get ( 2 ) {
126+ let package_path = format ! (
127+ "{}-{}-{}.json" ,
128+ name. to_name( ) ,
129+ version. as_str( ) ,
130+ hash. as_str( )
131+ ) ;
132+ let package_path = path. join ( package_path) ;
133+ let mut arch: Option < Architecture > = None ;
134+ // Sample contents
135+ // {
136+ // "build": "h966fe2a_2",
137+ // "build_number": 2,
138+ // "channel": "https://repo.anaconda.com/pkgs/main/win-64",
139+ // "constrains": [],
140+ // }
141+ // 32bit channel is https://repo.anaconda.com/pkgs/main/win-32/
142+ // 64bit channel is "channel": "https://repo.anaconda.com/pkgs/main/osx-arm64",
143+ if let Ok ( contents) = read_to_string ( & package_path) {
144+ if let Ok ( js) = serde_json:: from_str :: < CondaMetaPackageStructure > ( & contents) {
145+ if let Some ( channel) = js. channel {
146+ if channel. ends_with ( "64" ) {
147+ arch = Some ( Architecture :: X64 ) ;
148+ } else if channel. ends_with ( "32" ) {
149+ arch = Some ( Architecture :: X86 ) ;
147150 }
148151 }
152+ if let Some ( version) = js. version {
153+ return Some ( CondaPackageInfo {
154+ package : name. clone ( ) ,
155+ path : package_path,
156+ version,
157+ arch,
158+ } ) ;
159+ } else {
160+ warn ! (
161+ "Unable to find version for package {} in {:?}" ,
162+ name, package_path
163+ ) ;
164+ }
149165 }
150166 }
151167 }
0 commit comments