Skip to content

Commit 9cd81c8

Browse files
committed
small improvements to logging, and logic. as well as rename release workflow
1 parent 5099c3d commit 9cd81c8

File tree

2 files changed

+61
-91
lines changed

2 files changed

+61
-91
lines changed

generate_argument_specs.py

Lines changed: 61 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Supports multiple input methods and validates the generated specs.
77
88
Generated by: claude-4-sonnet
9-
Version: 1.0.0
9+
Version: 1.0.1
1010
"""
1111

1212
import argparse
@@ -197,6 +197,24 @@ def log_error(self, message: str, role_prefix: bool = True):
197197
prefix = f"[{self.current_role}] " if role_prefix and self.current_role else ""
198198
print(f"{prefix}{message}")
199199

200+
def _safe_load_yaml_file(self, file_path: Path, description: str = "") -> Optional[Dict[str, Any]]:
201+
"""Safely load a YAML file with proper error handling"""
202+
try:
203+
with open(file_path, "r", encoding="utf-8") as f:
204+
content = f.read()
205+
if not content.strip():
206+
return {}
207+
return yaml.safe_load(content)
208+
except yaml.YAMLError as e:
209+
self.log_error(f"Invalid YAML in {file_path}: {e}")
210+
return None
211+
except (OSError, IOError) as e:
212+
self.log_error(f"Could not read {file_path}: {e}")
213+
return None
214+
except Exception as e:
215+
self.log_verbose(f"Could not parse {file_path}: {e}")
216+
return None
217+
200218
def log_section(self, title: str):
201219
"""Log a section header"""
202220
if self.verbosity >= 1:
@@ -765,15 +783,15 @@ def parse_task_file_includes(self, task_file_path: Path) -> Set[str]:
765783
print(f" No includes found in {task_file_path.name}")
766784

767785
except UnicodeDecodeError as e:
768-
print(f"Error: Could not decode task file {task_file_path}: {e}")
786+
self.log_error(f"Could not decode task file {task_file_path}: {e}")
769787
except (OSError, IOError) as e:
770-
print(f"Error: Could not read task file {task_file_path}: {e}")
788+
self.log_error(f"Could not read task file {task_file_path}: {e}")
771789
except yaml.YAMLError as e:
772-
print(f"Warning: YAML parsing error in {task_file_path}: {e}")
790+
self.log_verbose(f"YAML parsing error in {task_file_path}: {e}")
773791
except re.error as e:
774-
print(f"Error: Regex pattern error in {task_file_path}: {e}")
792+
self.log_error(f"Regex pattern error in {task_file_path}: {e}")
775793
except Exception as e:
776-
print(f"Warning: Could not parse task file {task_file_path}: {e}")
794+
self.log_verbose(f"Could not parse task file {task_file_path}: {e}")
777795

778796
return includes
779797

@@ -829,49 +847,25 @@ def analyze_role_structure(self, role_path: str) -> Dict[str, Any]:
829847
# Analyze defaults/main.yml
830848
defaults_file = role_dir / "defaults" / "main.yml"
831849
if defaults_file.exists():
832-
try:
833-
with open(defaults_file, "r", encoding="utf-8") as f:
834-
content = f.read().strip()
835-
if content: # Only try to parse if file has content
836-
defaults = yaml.safe_load(content)
837-
# Handle case where YAML file contains only comments or is empty
838-
analysis["defaults"] = (
839-
defaults if isinstance(defaults, dict) else {}
840-
)
841-
else:
842-
analysis["defaults"] = {}
843-
except yaml.YAMLError as e:
844-
print(f"Error: Invalid YAML in {defaults_file}: {e}")
845-
analysis["defaults"] = {}
846-
except (OSError, IOError) as e:
847-
print(f"Error: Could not read {defaults_file}: {e}")
848-
analysis["defaults"] = {}
849-
except Exception as e:
850-
print(f"Warning: Could not parse {defaults_file}: {e}")
850+
defaults = self._safe_load_yaml_file(defaults_file)
851+
if defaults is not None:
852+
# Handle case where YAML file contains only comments or is empty
853+
analysis["defaults"] = (
854+
defaults if isinstance(defaults, dict) else {}
855+
)
856+
else:
851857
analysis["defaults"] = {}
852858

853859
# Analyze vars/main.yml
854860
vars_file = role_dir / "vars" / "main.yml"
855861
if vars_file.exists():
856-
try:
857-
with open(vars_file, "r", encoding="utf-8") as f:
858-
content = f.read().strip()
859-
if content: # Only try to parse if file has content
860-
vars_data = yaml.safe_load(content)
861-
# Handle case where YAML file contains only comments or is empty
862-
analysis["vars"] = (
863-
vars_data if isinstance(vars_data, dict) else {}
864-
)
865-
else:
866-
analysis["vars"] = {}
867-
except yaml.YAMLError as e:
868-
print(f"Error: Invalid YAML in {vars_file}: {e}")
869-
analysis["vars"] = {}
870-
except (OSError, IOError) as e:
871-
print(f"Error: Could not read {vars_file}: {e}")
872-
analysis["vars"] = {}
873-
except Exception as e:
874-
print(f"Warning: Could not parse {vars_file}: {e}")
862+
vars_data = self._safe_load_yaml_file(vars_file)
863+
if vars_data is not None:
864+
# Handle case where YAML file contains only comments or is empty
865+
analysis["vars"] = (
866+
vars_data if isinstance(vars_data, dict) else {}
867+
)
868+
else:
875869
analysis["vars"] = {}
876870

877871
# Analyze meta/main.yml for author information
@@ -887,8 +881,8 @@ def analyze_role_structure(self, role_path: str) -> Dict[str, Any]:
887881
authors = self._extract_authors_from_meta(meta_data)
888882
analysis["authors"] = authors
889883
if authors:
890-
print(
891-
f" Found {len(authors)} author(s): {', '.join(authors)}"
884+
self.log_verbose(
885+
f"Found {len(authors)} author(s): {', '.join(authors)}"
892886
)
893887

894888
# Extract description information
@@ -904,23 +898,23 @@ def analyze_role_structure(self, role_path: str) -> Dict[str, Any]:
904898
if descriptions.get("description") or descriptions.get(
905899
"short_description"
906900
):
907-
print(f" Found description from meta/main.yml")
901+
self.log_verbose(f"Found description from meta/main.yml")
908902
else:
909903
analysis["authors"] = []
910904
analysis["meta_description"] = []
911905
analysis["meta_short_description"] = ""
912906
except yaml.YAMLError as e:
913-
print(f"Warning: Invalid YAML in {meta_file}: {e}")
907+
self.log_verbose(f"Invalid YAML in {meta_file}: {e}")
914908
analysis["authors"] = []
915909
analysis["meta_description"] = []
916910
analysis["meta_short_description"] = ""
917911
except (OSError, IOError) as e:
918-
print(f"Warning: Could not read {meta_file}: {e}")
912+
self.log_verbose(f"Could not read {meta_file}: {e}")
919913
analysis["authors"] = []
920914
analysis["meta_description"] = []
921915
analysis["meta_short_description"] = ""
922916
except Exception as e:
923-
print(f"Warning: Could not parse {meta_file}: {e}")
917+
self.log_verbose(f"Could not parse {meta_file}: {e}")
924918
analysis["authors"] = []
925919
analysis["meta_description"] = []
926920
analysis["meta_short_description"] = ""
@@ -1012,7 +1006,7 @@ def _detect_version_info(self, role_dir: Path) -> Dict[str, Any]:
10121006
version_info["source"] = "collection"
10131007
return version_info
10141008
except Exception as e:
1015-
print(f" Warning: Could not parse galaxy.yml: {e}")
1009+
self.log_verbose(f"Could not parse galaxy.yml: {e}")
10161010

10171011
# Not in a collection or no collection version found, check role version
10181012
meta_file = role_dir / "meta" / "main.yml"
@@ -1038,7 +1032,7 @@ def _detect_version_info(self, role_dir: Path) -> Dict[str, Any]:
10381032
version_info["source"] = "role"
10391033
return version_info
10401034
except Exception as e:
1041-
print(f" Warning: Could not parse meta/main.yml for version: {e}")
1035+
self.log_verbose(f"Could not parse meta/main.yml for version: {e}")
10421036

10431037
return version_info
10441038

@@ -1814,25 +1808,13 @@ def ignore_aliases(self, data):
18141808
# Disable YAML references/anchors (like *id001) for cleaner output
18151809
return True
18161810

1817-
def generate_anchor(self, node):
1818-
# Prevent anchor generation entirely
1819-
return None
1820-
18211811
def ignore_aliases(self, data):
18221812
# Disable YAML references/anchors (like *id001) for cleaner output
18231813
return True
18241814

1825-
def generate_anchor(self, node):
1826-
# Prevent anchor generation entirely
1827-
return None
1828-
1829-
# Deep copy the specs to avoid any shared object references that could create anchors
1830-
import copy
1831-
specs_copy = copy.deepcopy(specs)
1832-
18331815
# Configure YAML output for better readability
18341816
yaml_content = yaml.dump(
1835-
specs_copy,
1817+
specs,
18361818
Dumper=CustomDumper,
18371819
default_flow_style=False,
18381820
sort_keys=False,
@@ -2507,24 +2489,24 @@ def validate_specs(self) -> bool:
25072489
valid = True
25082490

25092491
for entry_name, entry_point in self.entry_points.items():
2510-
print(f"Validating entry point: {entry_name}")
2492+
self.log_info(f"Validating entry point: {entry_name}")
25112493

25122494
# Check for required fields
25132495
if not entry_point.short_description:
2514-
print(f" Warning: No short_description for {entry_name}")
2496+
self.log_verbose(f"No short_description for {entry_name}")
25152497

25162498
# Validate argument types
25172499
for arg_name, arg_spec in entry_point.options.items():
25182500
if arg_spec.type not in [t.value for t in ArgumentType]:
2519-
print(
2520-
f" Error: Invalid type '{arg_spec.type}' for argument '{arg_name}'"
2501+
self.log_error(
2502+
f"Invalid type '{arg_spec.type}' for argument '{arg_name}'"
25212503
)
25222504
valid = False
25232505

25242506
# Check list/dict element types
25252507
if arg_spec.type in ["list", "dict"] and not arg_spec.elements:
2526-
print(
2527-
f" Warning: No elements type specified for {arg_spec.type} argument '{arg_name}'"
2508+
self.log_verbose(
2509+
f"No elements type specified for {arg_spec.type} argument '{arg_name}'"
25282510
)
25292511

25302512
# Validate conditionals reference existing arguments
@@ -2548,23 +2530,23 @@ def validate_specs(self) -> bool:
25482530
)
25492531

25502532
if param not in all_args:
2551-
print(
2552-
f" Error: {condition_type} references unknown argument '{param}'"
2533+
self.log_error(
2534+
f"{condition_type} references unknown argument '{param}'"
25532535
)
25542536
valid = False
25552537

25562538
for req_param in required_params:
25572539
if req_param not in all_args:
2558-
print(
2559-
f" Error: {condition_type} references unknown argument '{req_param}'"
2540+
self.log_error(
2541+
f"{condition_type} references unknown argument '{req_param}'"
25602542
)
25612543
valid = False
25622544
else:
25632545
# Format: [param1, param2, ...]
25642546
for param in condition:
25652547
if param not in all_args:
2566-
print(
2567-
f" Error: {condition_type} references unknown argument '{param}'"
2548+
self.log_error(
2549+
f"{condition_type} references unknown argument '{param}'"
25682550
)
25692551
valid = False
25702552

@@ -2596,17 +2578,9 @@ def ignore_aliases(self, data):
25962578
# Disable YAML references/anchors (like *id001) for cleaner output
25972579
return True
25982580

2599-
def generate_anchor(self, node):
2600-
# Prevent anchor generation entirely
2601-
return None
2602-
2603-
# Deep copy the specs to avoid any shared object references that could create anchors
2604-
import copy
2605-
specs_copy = copy.deepcopy(specs)
2606-
26072581
# Configure YAML output for better readability
26082582
yaml_content = yaml.dump(
2609-
specs_copy,
2583+
specs,
26102584
Dumper=CustomDumper,
26112585
default_flow_style=False,
26122586
sort_keys=False,
@@ -2684,12 +2658,8 @@ def create_example_config():
26842658
}
26852659
}
26862660

2687-
# Deep copy to avoid any shared references
2688-
import copy
2689-
config_copy = copy.deepcopy(example_config)
2690-
26912661
yaml_content = yaml.dump(
2692-
config_copy, default_flow_style=False, sort_keys=False, indent=2
2662+
example_config, default_flow_style=False, sort_keys=False, indent=2
26932663
)
26942664

26952665
# Remove any YAML anchor/reference lines that may have slipped through

0 commit comments

Comments
 (0)