diff --git a/cli/generate-role-credentials.py b/cli/create_credentials.py similarity index 100% rename from cli/generate-role-credentials.py rename to cli/create_credentials.py diff --git a/cli/playbook.py b/cli/deploy.py similarity index 100% rename from cli/playbook.py rename to cli/deploy.py diff --git a/main.py b/main.py index bbcc3bb3..8dc9679d 100755 --- a/main.py +++ b/main.py @@ -4,6 +4,16 @@ import argparse import os import subprocess import sys +import textwrap + +def format_command_help(name, description, indent=2, col_width=36, width=80): + prefix = " " * indent + f"{name:<{col_width - indent}}" + wrapper = textwrap.TextWrapper( + width=width, + initial_indent=prefix, + subsequent_indent=" " * col_width + ) + return wrapper.fill(description) def list_cli_commands(cli_dir): return sorted( @@ -11,17 +21,30 @@ def list_cli_commands(cli_dir): if f.is_file() and f.name.endswith(".py") and not f.name.startswith("__") ) -def extract_docstring(cli_script_path): +def extract_description_via_help(cli_script_path): + """Run `script --help` and extract the first non-usage line after usage block.""" try: - with open(cli_script_path, "r", encoding="utf-8") as f: - for line in f: - if line.strip().startswith(('"""', "'''")): - return line.strip().strip('"\'') - if line.strip().startswith("DESCRIPTION"): - return line.split("=", 1)[1].strip().strip("\"'") + result = subprocess.run( + [sys.executable, cli_script_path, "--help"], + capture_output=True, + text=True, + check=True + ) + lines = result.stdout.splitlines() + + # Skip until first empty line after usage block + for i, line in enumerate(lines): + if line.strip().startswith("usage:"): + continue + if line.strip() == "": + # description usually comes after usage and empty line + for j in range(i+1, len(lines)): + desc = lines[j].strip() + if desc: + return desc + return "-" except Exception: - pass - return "-" + return "-" def main(): script_dir = os.path.dirname(os.path.realpath(__file__)) @@ -44,8 +67,8 @@ def main(): print("Available commands:") for cmd in available_cli_commands: path = os.path.join(cli_dir, f"{cmd}.py") - desc = extract_docstring(path) - print(f" {cmd:25} {desc}") + desc = extract_description_via_help(path) + print(format_command_help(cmd, desc)) print("\nUse 'cymais --help' for details on each command.") sys.exit(0) diff --git a/tests/unit/test_generate_default_applications.py b/tests/unit/test_generate_default_applications.py index c4bde332..9d325fb9 100644 --- a/tests/unit/test_generate_default_applications.py +++ b/tests/unit/test_generate_default_applications.py @@ -29,7 +29,7 @@ class TestGenerateDefaultApplications(unittest.TestCase): shutil.rmtree(self.temp_dir) def test_script_generates_expected_yaml(self): - script_path = Path(__file__).resolve().parent.parent.parent / "cli" / "generate_defaults_applications.py" + script_path = Path(__file__).resolve().parent.parent.parent / "cli" / "generate-applications-defaults.py" result = subprocess.run( [ diff --git a/tests/unit/test_generate_vaulted_credentials.py b/tests/unit/test_generate_vaulted_credentials.py index 621e8e71..92323e89 100644 --- a/tests/unit/test_generate_vaulted_credentials.py +++ b/tests/unit/test_generate_vaulted_credentials.py @@ -7,7 +7,7 @@ PROJECT_ROOT = Path(__file__).parent.parent.parent.resolve() sys.path.insert(0, str(PROJECT_ROOT)) # 2) Import from the cli package -import cli.generate_vaulted_credentials as gvc +import cli.create_credentials as gvc class DummyProc: def __init__(self, returncode, stdout, stderr=''):