2
2
import glob
3
3
import re
4
4
from json import load
5
+ from datetime import datetime
5
6
6
7
# Third Party
7
8
from requests import get
11
12
JSON_FILE = "resources/table.json"
12
13
REPORT_FILE = "docs/report.md"
13
14
API_V1_ENDPOINT = "https://rubygems.org/api/v1/versions/"
15
+ GEM_INFO_ENDPOINT = "https://rubygems.org/api/v1/gems/"
14
16
15
- def get_bundle_install_output ():
16
17
18
+ def get_bundle_install_output ():
17
19
libraries_from_logs = {
18
20
"cuba" : "cuba_40_ruby_3.3." ,
19
21
"excon" : "excon_100_ruby_3.3." ,
@@ -28,22 +30,23 @@ def get_bundle_install_output():
28
30
"dalli" : "dalli_32_ruby_3.3." ,
29
31
"resque" : "resque_20_ruby_3.3." ,
30
32
"sidekiq" : "sidekiq_70_ruby_3.3." ,
31
- "sequel" : "sequel_58_ruby_3.3."
33
+ "sequel" : "sequel_58_ruby_3.3." ,
32
34
}
33
35
34
36
bundle_install_output = ""
35
37
36
38
for library , pattern in libraries_from_logs .items ():
37
39
glob_result = glob .glob (f"../../dep_{ pattern } *" )
40
+
38
41
if not glob_result :
39
42
print (f"Could not find bundle install log for gem '{ library } '." )
40
43
continue
41
44
42
- with open (glob_result [0 ], 'r' ) as file :
43
- logs = file .read ().replace (' \n ' , ' ' )
45
+ with open (glob_result [0 ], "r" ) as file :
46
+ logs = file .read ().replace (" \n " , " " )
44
47
45
48
if "Installing" not in logs :
46
- print ( f"Unable to retrieve logs from for gem '{ library } '." )
49
+ print (f"Unable to retrieve logs from for gem '{ library } '." )
47
50
continue
48
51
49
52
print (f"Retrieving currency for gem '{ library } '." )
@@ -53,27 +56,176 @@ def get_bundle_install_output():
53
56
return bundle_install_output
54
57
55
58
59
+ def estimate_days_behind (release_date , last_supported_release_date ):
60
+ """Calculate days between release dates"""
61
+ latest_date = datetime .strptime (release_date , "%Y-%m-%d" )
62
+ supported_date = datetime .strptime (last_supported_release_date , "%Y-%m-%d" )
63
+ days_diff = (latest_date - supported_date ).days
64
+ return max (0 , days_diff )
65
+
66
+
56
67
def get_upstream_version (dependency ):
57
- """get the latest version available upstream"""
68
+ """get the latest version available upstream and release date """
58
69
if dependency != "rails lts" :
59
- response = get (f"{ API_V1_ENDPOINT } /{ dependency } /latest.json" )
60
- response_json = response .json ()
61
- latest_version = response_json ["version" ]
70
+ try :
71
+ response = get (f"{ API_V1_ENDPOINT } { dependency } /latest.json" )
72
+ if not response :
73
+ return "Unknown" , "Unknown"
74
+
75
+ response_json = response .json ()
76
+ latest_version = response_json ["version" ]
77
+
78
+ gem_info_response = get (f"{ GEM_INFO_ENDPOINT } { dependency } .json" )
79
+ if not gem_info_response :
80
+ return latest_version , "Unknown"
81
+ gem_info = gem_info_response .json ()
82
+
83
+ version_info_response = get (f"{ API_V1_ENDPOINT } { dependency } .json" )
84
+ if not version_info_response :
85
+ return latest_version , "Unknown"
86
+ version_info = version_info_response .json ()
87
+
88
+ latest_version_release_date = None
89
+ for version_data in version_info :
90
+ if version_data ["number" ] == latest_version :
91
+ if "created_at" in version_data :
92
+ created_at = version_data ["created_at" ]
93
+ latest_version_release_date = datetime .strptime (
94
+ created_at , "%Y-%m-%dT%H:%M:%S.%fZ"
95
+ ).strftime ("%Y-%m-%d" )
96
+ break
97
+
98
+ if not latest_version_release_date and "created_at" in gem_info :
99
+ created_at = gem_info ["created_at" ]
100
+ latest_version_release_date = datetime .strptime (
101
+ created_at , "%Y-%m-%dT%H:%M:%S.%fZ"
102
+ ).strftime ("%Y-%m-%d" )
103
+
104
+ if not latest_version_release_date :
105
+ latest_version_release_date = "Unknown"
106
+ print (f"Could not find release date for { dependency } " )
107
+
108
+ return latest_version , latest_version_release_date
109
+ except Exception as e :
110
+ return "Unknown" , "Unknown"
62
111
else :
63
- url = "https://makandracards.com/railslts/16137-installing-rails-lts/read"
64
- page = get (url )
65
- soup = BeautifulSoup (page .text , "html.parser" )
66
- text = soup .findAll ("li" )[- 1 ].text
67
- pattern = "(\d+\.\d+\.?\d*)"
68
- latest_version = re .search (pattern , text )[1 ]
69
- return latest_version
112
+ try :
113
+ initial_response = get (
114
+ "https://makandracards.com/railslts/16137-installing-rails-lts/read"
115
+ )
116
+ if not initial_response :
117
+ return "Unknown" , "Unknown"
118
+
119
+ initial_soup = BeautifulSoup (initial_response .text , "html.parser" )
120
+
121
+ text = initial_soup .find_all ("li" )[- 1 ].text
122
+ pattern = "(\d+\.\d+\.?\d*)"
123
+ latest_version_match = re .search (pattern , text )
124
+
125
+ latest_version = "Unknown"
126
+ if latest_version_match :
127
+ latest_version = latest_version_match [1 ]
128
+ rails_lts_links = initial_soup .find_all ("a" , href = re .compile (r"railslts" ))
129
+ latest_rails_lts_link = None
130
+ latest_version_num = 0.0
131
+
132
+ for link in rails_lts_links :
133
+ href = link .get ("href" , "" )
134
+ link_text = link .text .strip ()
135
+
136
+ if "Installing Rails" in link_text :
137
+ version_match = re .search (r"Installing Rails (\d+\.\d+)" , link_text )
138
+ if version_match :
139
+ version_str = version_match .group (1 )
140
+ try :
141
+ version_num = float (version_str )
142
+ if version_num > latest_version_num :
143
+ latest_version_num = version_num
144
+ latest_rails_lts_link = href
145
+ latest_version = version_str
146
+ except ValueError :
147
+ pass
148
+
149
+ latest_version_release_date = None
150
+
151
+ if latest_rails_lts_link :
152
+ if latest_rails_lts_link .startswith ("/" ):
153
+ full_url = f"https://makandracards.com{ latest_rails_lts_link } "
154
+ else :
155
+ full_url = latest_rails_lts_link
156
+
157
+ detail_response = get (full_url )
158
+ if detail_response :
159
+ detail_soup = BeautifulSoup (detail_response .text , "html.parser" )
160
+
161
+ time_tags = detail_soup .find_all (
162
+ "time" , attrs = {"data-relative" : "true" }
163
+ )
164
+ for time_tag in time_tags :
165
+ if "Updated" in time_tag .parent .text :
166
+ if "datetime" in time_tag .attrs :
167
+ datetime_str = time_tag ["datetime" ]
168
+ try :
169
+ date_obj = datetime .strptime (
170
+ datetime_str , "%Y-%m-%dT%H:%M:%SZ"
171
+ )
172
+ latest_version_release_date = date_obj .strftime (
173
+ "%Y-%m-%d"
174
+ )
175
+ break
176
+ except ValueError :
177
+ try :
178
+ date_obj = datetime .strptime (
179
+ datetime_str , "%Y-%m-%dT%H:%M:%S.%fZ"
180
+ )
181
+ latest_version_release_date = date_obj .strftime (
182
+ "%Y-%m-%d"
183
+ )
184
+ break
185
+ except ValueError as e :
186
+ print (f"Error parsing datetime: { e } " )
187
+
188
+ if not latest_version_release_date :
189
+ latest_version_release_date = datetime .now ().strftime ("%Y-%m-%d" )
190
+ return latest_version , latest_version_release_date
191
+
192
+ except Exception as e :
193
+ print (f"Error getting Rails LTS information: { str (e )} " )
194
+ return "Unknown" , datetime .now ().strftime ("%Y-%m-%d" )
195
+
196
+
197
+ def get_version_release_date (dependency , version ):
198
+ if dependency == "rails lts" :
199
+ _ , release_date = get_upstream_version (dependency )
200
+ return release_date
201
+
202
+ try :
203
+ response = get (f"{ API_V1_ENDPOINT } /{ dependency } .json" )
204
+ if not response :
205
+ print (f"Failed to get version info for { dependency } " )
206
+ return "Unknown"
207
+
208
+ version_info = response .json ()
209
+ for version_data in version_info :
210
+ if version_data ["number" ] == version and "created_at" in version_data :
211
+ created_at = version_data ["created_at" ]
212
+ release_date = datetime .strptime (
213
+ created_at , "%Y-%m-%dT%H:%M:%S.%fZ"
214
+ ).strftime ("%Y-%m-%d" )
215
+ return release_date
216
+ except Exception as e :
217
+ print (f"Error getting release date for { dependency } { version } : { str (e )} " )
218
+
219
+ return "Unknown"
70
220
71
221
72
222
def get_last_supported_version (bundle_install_output , dependency ):
73
223
"""get up-to-date supported version"""
74
224
pattern = r" ([^\s]+)"
75
225
76
- last_supported_version = re .search (dependency + pattern , bundle_install_output , flags = re .I | re .M )
226
+ last_supported_version = re .search (
227
+ dependency + pattern , bundle_install_output , flags = re .I | re .M
228
+ )
77
229
78
230
return last_supported_version [1 ]
79
231
@@ -86,6 +238,7 @@ def isUptodate(last_supported_version, latest_version):
86
238
87
239
return up_to_date
88
240
241
+
89
242
def main ():
90
243
# Read the JSON file
91
244
with open (JSON_FILE ) as file :
@@ -99,23 +252,41 @@ def main():
99
252
package = item ["Package name" ]
100
253
package = package .lower ().replace ("::" , "-" )
101
254
102
- latest_version = get_upstream_version (package )
103
-
104
- if not package in ["rails lts" , "rails-api" ]:
105
- last_supported_version = get_last_supported_version (bundle_install_output , package )
255
+ latest_version , release_date = get_upstream_version (package )
256
+ if package not in ["rails lts" , "rails-api" ]:
257
+ last_supported_version = get_last_supported_version (
258
+ bundle_install_output , package
259
+ )
106
260
else :
107
261
last_supported_version = latest_version
108
262
263
+ last_supported_version_release_date = get_version_release_date (
264
+ package , last_supported_version
265
+ )
266
+
109
267
up_to_date = isUptodate (last_supported_version , latest_version )
110
268
269
+ days_behind = "0 day/s"
270
+ if (
271
+ up_to_date == "No"
272
+ and release_date != "Unknown"
273
+ and last_supported_version_release_date != "Unknown"
274
+ ):
275
+ days = estimate_days_behind (
276
+ release_date , last_supported_version_release_date
277
+ )
278
+ days_behind = f"{ days } day/s"
279
+
111
280
item .update (
112
281
{
113
282
"Last Supported Version" : last_supported_version ,
114
283
"Latest version" : latest_version ,
115
284
"Up-to-date" : up_to_date ,
285
+ "Release date" : release_date ,
286
+ "Latest Version Published At" : last_supported_version_release_date ,
287
+ "Days behind" : days_behind ,
116
288
},
117
289
)
118
-
119
290
# Create a DataFrame from the list of dictionaries
120
291
df = DataFrame (items )
121
292
df .insert (len (df .columns ) - 1 , "Cloud Native" , df .pop ("Cloud Native" ))
@@ -132,5 +303,6 @@ def main():
132
303
with open (REPORT_FILE , "w" ) as file :
133
304
file .write (final_markdown )
134
305
306
+
135
307
if __name__ == "__main__" :
136
308
main ()
0 commit comments