Skip to content

Commit abd81e6

Browse files
committed
feat: print release notes before upgrade
1 parent 2d7d023 commit abd81e6

File tree

12 files changed

+184
-40
lines changed

12 files changed

+184
-40
lines changed

AgentCrew/main.py

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def check_and_update():
8686
current_version = get_current_version()
8787

8888
click.echo(f"Current version: {current_version}\nChecking for updates...")
89-
latest_version = get_latest_github_version()
89+
latest_version, release_notes = get_latest_release_info()
9090

9191
if not current_version or not latest_version:
9292
click.echo("⚠️ Could not determine version information", err=True)
@@ -98,8 +98,20 @@ def check_and_update():
9898
system = platform.system().lower()
9999

100100
if system == "linux" or system == "darwin":
101+
click.echo("\n" + "=" * 60)
102+
click.echo("🔄 New version available!")
103+
click.echo("=" * 60)
104+
105+
if release_notes:
106+
click.echo("\n📝 Release Notes:")
107+
click.echo("-" * 40)
108+
click.echo(release_notes)
109+
click.echo("-" * 40 + "\n")
110+
else:
111+
click.echo("\n⚠️ Could not fetch release notes.")
112+
101113
if click.confirm(
102-
"🔄 New version available! Do you want to update now?",
114+
"\nDo you want to update now?",
103115
default=False,
104116
):
105117
click.echo("🔄 Starting update...")
@@ -109,7 +121,18 @@ def check_and_update():
109121
click.echo("⏭️ Skipping update. Starting application...")
110122
else:
111123
command = "uv tool install --python=3.12 --reinstall agentcrew-ai[cpu]@latest --index https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match"
112-
click.echo(f"🔄 New version available!\nRun {command} to update.")
124+
125+
click.echo("\n" + "=" * 60)
126+
click.echo("🔄 New version available!")
127+
click.echo("=" * 60)
128+
129+
if release_notes:
130+
click.echo("\n📝 Release Notes:")
131+
click.echo("-" * 40)
132+
click.echo(release_notes)
133+
click.echo("-" * 40 + "\n")
134+
135+
click.echo(f"Run the following command to update:\n\n{command}")
113136
else:
114137
click.echo("✅ You are running the latest version")
115138

@@ -157,6 +180,35 @@ def get_latest_github_version():
157180
return None
158181

159182

183+
def get_latest_release_info():
184+
"""Get the latest release information including version and release notes from GitHub
185+
186+
Returns:
187+
tuple: (version, release_notes) where both can be None if not found.
188+
"""
189+
try:
190+
api_url = (
191+
"https://api.github.com/repos/saigontechnology/AgentCrew/releases/latest"
192+
)
193+
response = requests.get(api_url, timeout=10)
194+
195+
if response.status_code == 200:
196+
release_data = response.json()
197+
tag_name = release_data.get("tag_name", "").lstrip("v")
198+
name = release_data.get("name", "")
199+
body = release_data.get("body", "")
200+
201+
release_notes = None
202+
if body:
203+
release_notes = f"## {name or tag_name}\n\n{body}"
204+
205+
return tag_name, release_notes
206+
207+
return None, None
208+
except Exception:
209+
return None, None
210+
211+
160212
def version_is_older(current: str, latest: str) -> bool:
161213
"""
162214
Compare two semantic version strings to check if current is older than latest.

AgentCrew/modules/code_analysis/parsers/base.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,4 @@ def _is_significant_node(self, result: Dict[str, Any]) -> bool:
9090
"variable_declaration",
9191
"arrow_function",
9292
}
93-
return (
94-
result.get("type") in significant_types or "children" in result
95-
)
93+
return result.get("type") in significant_types or "children" in result

AgentCrew/modules/code_analysis/parsers/cpp_parser.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ def process_node(
4141
elif child.type == "function_declarator":
4242
for subchild in child.children:
4343
if subchild.type in ["identifier", "field_identifier"]:
44-
result["name"] = self.extract_node_text(subchild, source_code)
44+
result["name"] = self.extract_node_text(
45+
subchild, source_code
46+
)
4547
return result
4648
return result
4749

@@ -69,7 +71,12 @@ def _handle_field_declaration(
6971
field_type = None
7072

7173
for child in node.children:
72-
if child.type in ["primitive_type", "type_identifier", "qualified_identifier", "template_type"]:
74+
if child.type in [
75+
"primitive_type",
76+
"type_identifier",
77+
"qualified_identifier",
78+
"template_type",
79+
]:
7380
field_type = self.extract_node_text(child, source_code)
7481
elif child.type == "field_identifier":
7582
field_name = self.extract_node_text(child, source_code)
@@ -90,7 +97,12 @@ def _handle_declaration(
9097
var_type = None
9198

9299
for child in node.children:
93-
if child.type in ["primitive_type", "type_identifier", "qualified_identifier", "template_type"]:
100+
if child.type in [
101+
"primitive_type",
102+
"type_identifier",
103+
"qualified_identifier",
104+
"template_type",
105+
]:
94106
var_type = self.extract_node_text(child, source_code)
95107
elif child.type in ["init_declarator", "declarator"]:
96108
for subchild in child.children:
@@ -100,7 +112,9 @@ def _handle_declaration(
100112
elif subchild.type == "pointer_declarator":
101113
for ptr_child in subchild.children:
102114
if ptr_child.type == "identifier":
103-
var_name = self.extract_node_text(ptr_child, source_code)
115+
var_name = self.extract_node_text(
116+
ptr_child, source_code
117+
)
104118
break
105119

106120
if var_name:

AgentCrew/modules/code_analysis/parsers/csharp_parser.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ def _handle_property_declaration(
101101
for child in node.children:
102102
if child.type == "modifier":
103103
modifiers.append(self.extract_node_text(child, source_code))
104-
elif child.type in ["predefined_type", "nullable_type", "generic_name", "array_type"]:
104+
elif child.type in [
105+
"predefined_type",
106+
"nullable_type",
107+
"generic_name",
108+
"array_type",
109+
]:
105110
property_type = self.extract_node_text(child, source_code)
106111
elif child.type == "identifier":
107112
if property_type is None:
@@ -130,14 +135,21 @@ def _handle_field_declaration(
130135
modifiers.append(self.extract_node_text(child, source_code))
131136
elif child.type == "variable_declaration":
132137
for subchild in child.children:
133-
if subchild.type in ["predefined_type", "nullable_type", "generic_name", "array_type"]:
138+
if subchild.type in [
139+
"predefined_type",
140+
"nullable_type",
141+
"generic_name",
142+
"array_type",
143+
]:
134144
field_type = self.extract_node_text(subchild, source_code)
135145
elif subchild.type == "identifier" and field_type is None:
136146
field_type = self.extract_node_text(subchild, source_code)
137147
elif subchild.type == "variable_declarator":
138148
for var_child in subchild.children:
139149
if var_child.type == "identifier":
140-
field_name = self.extract_node_text(var_child, source_code)
150+
field_name = self.extract_node_text(
151+
var_child, source_code
152+
)
141153
break
142154

143155
if field_name:

AgentCrew/modules/code_analysis/parsers/generic_parser.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ def process_node(
3232
if child.type in ["identifier", "field_identifier"]:
3333
result["name"] = self.extract_node_text(child, source_code)
3434
result["first_line"] = (
35-
self.extract_node_text(node, source_code).split("\n")[0].strip("{")
35+
self.extract_node_text(node, source_code)
36+
.split("\n")[0]
37+
.strip("{")
3638
)
3739
return result
3840
return result

AgentCrew/modules/code_analysis/parsers/go_parser.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@ def process_node(
2020
result = self._create_base_result(node)
2121

2222
if node.type == "type_declaration":
23-
return self._handle_type_declaration(node, source_code, result, process_children_callback)
23+
return self._handle_type_declaration(
24+
node, source_code, result, process_children_callback
25+
)
2426

2527
elif node.type in ["function_declaration", "method_declaration"]:
2628
for child in node.children:
2729
if child.type in ["identifier", "field_identifier"]:
2830
result["name"] = self.extract_node_text(child, source_code)
2931
result["first_line"] = (
30-
self.extract_node_text(node, source_code).split("\n")[0].strip("{")
32+
self.extract_node_text(node, source_code)
33+
.split("\n")[0]
34+
.strip("{")
3135
)
3236
return result
3337
return result
@@ -57,7 +61,11 @@ def process_node(
5761
return result
5862

5963
def _handle_type_declaration(
60-
self, node, source_code: bytes, result: Dict[str, Any], process_children_callback
64+
self,
65+
node,
66+
source_code: bytes,
67+
result: Dict[str, Any],
68+
process_children_callback,
6169
) -> Dict[str, Any]:
6270
for child in node.children:
6371
if child.type == "type_spec":
@@ -72,7 +80,9 @@ def _handle_type_declaration(
7280
children = []
7381
for field in struct_child.children:
7482
field_result = process_children_callback(field)
75-
if field_result and self._is_significant_node(field_result):
83+
if field_result and self._is_significant_node(
84+
field_result
85+
):
7686
children.append(field_result)
7787
if children:
7888
result["children"] = children
@@ -92,7 +102,15 @@ def _handle_var_declaration(
92102
for subchild in child.children:
93103
if subchild.type == "identifier" and var_name is None:
94104
var_name = self.extract_node_text(subchild, source_code)
95-
elif subchild.type in ["type_identifier", "pointer_type", "array_type", "slice_type", "map_type", "channel_type", "qualified_type"]:
105+
elif subchild.type in [
106+
"type_identifier",
107+
"pointer_type",
108+
"array_type",
109+
"slice_type",
110+
"map_type",
111+
"channel_type",
112+
"qualified_type",
113+
]:
96114
var_type = self.extract_node_text(subchild, source_code)
97115

98116
if var_name:
@@ -113,7 +131,17 @@ def _handle_field_declaration(
113131
for child in node.children:
114132
if child.type == "field_identifier":
115133
field_name = self.extract_node_text(child, source_code)
116-
elif child.type in ["type_identifier", "pointer_type", "array_type", "slice_type", "map_type", "channel_type", "qualified_type", "struct_type", "interface_type"]:
134+
elif child.type in [
135+
"type_identifier",
136+
"pointer_type",
137+
"array_type",
138+
"slice_type",
139+
"map_type",
140+
"channel_type",
141+
"qualified_type",
142+
"struct_type",
143+
"interface_type",
144+
]:
117145
field_type = self.extract_node_text(child, source_code)
118146

119147
if field_name:

AgentCrew/modules/code_analysis/parsers/java_parser.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,14 @@ def _handle_field_declaration(
8181
field_name = None
8282

8383
for child in node.children:
84-
if child.type in ["type_identifier", "generic_type", "array_type", "integral_type", "floating_point_type", "boolean_type"]:
84+
if child.type in [
85+
"type_identifier",
86+
"generic_type",
87+
"array_type",
88+
"integral_type",
89+
"floating_point_type",
90+
"boolean_type",
91+
]:
8592
field_type = self.extract_node_text(child, source_code)
8693
elif child.type == "variable_declarator":
8794
name_node = child.child_by_field_name("name")

AgentCrew/modules/code_analysis/parsers/javascript_parser.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ def _handle_export_statement(
8383
return None
8484

8585
def _handle_arrow_function(
86-
self, node, source_code: bytes, result: Dict[str, Any], process_children_callback
86+
self,
87+
node,
88+
source_code: bytes,
89+
result: Dict[str, Any],
90+
process_children_callback,
8791
) -> Dict[str, Any]:
8892
parent = node.parent
8993
if parent and parent.type == "variable_declarator":
@@ -101,7 +105,11 @@ def _handle_arrow_function(
101105
return result
102106

103107
def _handle_lexical_declaration(
104-
self, node, source_code: bytes, result: Dict[str, Any], process_children_callback
108+
self,
109+
node,
110+
source_code: bytes,
111+
result: Dict[str, Any],
112+
process_children_callback,
105113
) -> Dict[str, Any]:
106114
for child in node.children:
107115
if child.type == "variable_declarator":
@@ -139,17 +147,21 @@ def _handle_regular_declaration(
139147
for child in node.children:
140148
if child.type in ["identifier", "type_identifier", "property_identifier"]:
141149
result["name"] = self.extract_node_text(child, source_code)
142-
elif (
143-
child.type == "formal_parameters"
144-
and node.type
145-
in ["function_declaration", "method_declaration", "method_definition"]
146-
):
150+
elif child.type == "formal_parameters" and node.type in [
151+
"function_declaration",
152+
"method_declaration",
153+
"method_definition",
154+
]:
147155
params = self._extract_parameters_with_types(child, source_code)
148156
if params:
149157
result["parameters"] = params
150158

151159
def _handle_variable_statement(
152-
self, node, source_code: bytes, result: Dict[str, Any], process_children_callback
160+
self,
161+
node,
162+
source_code: bytes,
163+
result: Dict[str, Any],
164+
process_children_callback,
153165
) -> Dict[str, Any]:
154166
for child in node.children:
155167
if child.type == "variable_declaration_list":
@@ -175,7 +187,10 @@ def _handle_variable_statement(
175187
arrow_result = process_children_callback(
176188
declarator_child
177189
)
178-
if arrow_result and "parameters" in arrow_result:
190+
if (
191+
arrow_result
192+
and "parameters" in arrow_result
193+
):
179194
result["parameters"] = arrow_result[
180195
"parameters"
181196
]
@@ -196,7 +211,7 @@ def _handle_property_declaration(
196211
) -> Dict[str, Any]:
197212
prop_name = None
198213
prop_type = None
199-
214+
200215
for child in node.children:
201216
if child.type in ["property_identifier", "identifier"]:
202217
prop_name = self.extract_node_text(child, source_code)

AgentCrew/modules/code_analysis/parsers/php_parser.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ def process_node(
1919
) -> Optional[Dict[str, Any]]:
2020
result = self._create_base_result(node)
2121

22-
if node.type in ["class_declaration", "interface_declaration", "trait_declaration"]:
22+
if node.type in [
23+
"class_declaration",
24+
"interface_declaration",
25+
"trait_declaration",
26+
]:
2327
for child in node.children:
2428
if child.type == "name":
2529
result["name"] = self.extract_node_text(child, source_code)
@@ -64,7 +68,12 @@ def _handle_property_declaration(
6468
prop_type = None
6569

6670
for child in node.children:
67-
if child.type in ["primitive_type", "named_type", "optional_type", "union_type"]:
71+
if child.type in [
72+
"primitive_type",
73+
"named_type",
74+
"optional_type",
75+
"union_type",
76+
]:
6877
prop_type = self.extract_node_text(child, source_code)
6978
elif child.type == "property_element":
7079
for subchild in child.children:

0 commit comments

Comments
 (0)