-
Notifications
You must be signed in to change notification settings - Fork 0
v0.8.2: Quality Rules CLI and Integration #88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
f2deda3
Add CLI commands and executor for quality rules
octo-youcef 7a57aff
Fix quality rules to work with current Neo4j schema
octo-youcef 5d5eadc
Fix linting: remove unused import and format code
octo-youcef 9561474
Rename test file to avoid pytest collection conflict
octo-youcef 6233bfb
Update CHANGELOG for v0.8.2 release
octo-youcef af07aa8
Bump version: 0.8.1 → 0.8.2
octo-youcef 96e20e5
Refactor quality CLI to match queries API pattern
octo-youcef File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| """Internal helper functions for quality CLI commands.""" | ||
|
|
||
| import sys | ||
|
|
||
| import typer | ||
| from neo4j.exceptions import DriverError | ||
| from rich.console import Console | ||
|
|
||
| from mapper import config_manager, graph | ||
| from mapper.quality import config, executor, formatters | ||
| from mapper.quality.formatters import OutputFormat | ||
|
|
||
| console = Console() | ||
|
|
||
|
|
||
| def run_quality_checks( | ||
| check_name: str, | ||
| package: str, | ||
| output_format: OutputFormat, | ||
| config_path: str | None = None, | ||
| ) -> int: | ||
| """Execute quality check(s) and return exit code. | ||
|
|
||
| Args: | ||
| check_name: Name of check to run, or "all" for all checks | ||
| package: Package name to check | ||
| output_format: Output format (console, JSON, CSV) | ||
| config_path: Optional path to config file | ||
|
|
||
| Returns: | ||
| Exit code (0 for pass, 1 for fail) | ||
|
|
||
| Raises: | ||
| typer.Exit: On errors | ||
| """ | ||
| try: | ||
| # Get Neo4j credentials and config | ||
| user, password = config_manager.get_neo4j_credentials() | ||
| db_config = config_manager.load_config() | ||
|
|
||
| # Create connection | ||
| connection = graph.Neo4jConnection( | ||
| uri=db_config.neo4j.uri, | ||
| user=user, | ||
| password=password, | ||
| database=db_config.neo4j.database, | ||
| ) | ||
|
|
||
| # Test connection | ||
| success, message = connection.test_connection() | ||
| if not success: | ||
| console.print(f"[red]Neo4j connection failed:[/red] {message}") | ||
| console.print("\nEnsure Neo4j is running and credentials are correct.") | ||
| console.print("Run 'mapper status' to check configuration.") | ||
| raise typer.Exit(code=1) | ||
|
|
||
| # Load quality configuration | ||
| quality_config = config.load_quality_config(config_path) | ||
|
|
||
| # Execute check(s) | ||
| exec = executor.QualityExecutor(connection) | ||
|
|
||
| if check_name.lower() == "all": | ||
| results = exec.execute_all(package, quality_config) | ||
| else: | ||
| result = exec.execute(check_name, package, quality_config) | ||
| results = [result] | ||
|
|
||
| # Format and output | ||
| formatter = formatters.get_formatter(output_format) | ||
| output = formatter.format_results(results) | ||
|
|
||
| # Print output | ||
| match output_format: | ||
| case OutputFormat.CONSOLE: | ||
| # Rich console with colors | ||
| console.print(output, end="") | ||
| case OutputFormat.JSON: | ||
| # JSON - write to stdout with newline | ||
| sys.stdout.write(output) | ||
| sys.stdout.write("\n") | ||
| case OutputFormat.CSV: | ||
| # CSV - write to stdout | ||
| sys.stdout.write(output) | ||
|
|
||
| # Cleanup | ||
| connection.close() | ||
|
|
||
| # Return exit code | ||
| all_passed = all(result.status == "pass" for result in results) | ||
| return 0 if all_passed else 1 | ||
|
|
||
| except ValueError as e: | ||
| console.print(f"[red]Error:[/red] {e}") | ||
| raise typer.Exit(code=1) from None | ||
| except (FileNotFoundError, OSError, DriverError) as e: | ||
| console.print(f"[red]Error:[/red] {e}") | ||
| raise typer.Exit(code=1) from e |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| """Quality check commands for Mapper CLI.""" | ||
|
|
||
| import typer | ||
| from rich.console import Console | ||
|
|
||
| from mapper.cli import _quality_helpers | ||
| from mapper.quality import registry | ||
| from mapper.quality.formatters import OutputFormat | ||
|
|
||
| console = Console() | ||
|
|
||
| app = typer.Typer(help="Run code quality checks") | ||
|
|
||
|
|
||
| @app.command(name="list") | ||
| def list_rules() -> None: | ||
| """List all available quality rules. | ||
|
|
||
| Shows quality rules with their descriptions and default thresholds. | ||
| """ | ||
| reg = registry.get_registry() | ||
| rules = reg.list_all() | ||
|
|
||
| console.print(f"\n[bold]Available Quality Rules[/bold] ({len(rules)} total)\n") | ||
| console.print(f"{'Rule':<25} {'Description'}") | ||
| console.print(f"{'-' * 25} {'-' * 50}") | ||
|
|
||
| for rule in rules: | ||
| console.print(f"[cyan]{rule.name:<25}[/cyan] {rule.description}") | ||
|
|
||
| console.print() | ||
| console.print("[dim]Use 'mapper quality run <rule> --package <pkg>' to run a check[/dim]") | ||
| console.print( | ||
| "[dim]Use 'mapper quality run all --package <pkg>' to run all enabled checks[/dim]" | ||
| ) | ||
| console.print() | ||
|
|
||
|
|
||
| @app.command(name="run") | ||
| def run( | ||
| check: str = typer.Argument(..., help="Quality check to run (or 'all' for all enabled checks)"), | ||
| package: str = typer.Option(..., help="Package name to check"), | ||
| format_type: OutputFormat = typer.Option( | ||
| OutputFormat.CONSOLE, "--format", help="Output format: console, json, csv" | ||
| ), | ||
| json_flag: bool = typer.Option( | ||
| False, "--json", help="Output as JSON (shorthand for --format json)" | ||
| ), | ||
| csv_flag: bool = typer.Option( | ||
| False, "--csv", help="Output as CSV (shorthand for --format csv)" | ||
| ), | ||
| config_path: str | None = typer.Option( | ||
| None, "--config", help="Path to mapper.toml config file" | ||
| ), | ||
| ) -> None: | ||
| """Run a quality check against an analyzed package. | ||
|
|
||
| Exit code 0 if all checks pass, 1 if any check fails. | ||
|
|
||
| Examples: | ||
| mapper quality run type-coverage --package mypackage | ||
| mapper quality run all --package mypackage | ||
| mapper quality run docstring-coverage --package mypackage --json | ||
| """ | ||
| # Resolve format (flags override --format option) | ||
| output_format = ( | ||
| OutputFormat.JSON if json_flag else OutputFormat.CSV if csv_flag else format_type | ||
| ) | ||
|
|
||
| # Execute checks and exit with appropriate code | ||
| exit_code = _quality_helpers.run_quality_checks(check, package, output_format, config_path) | ||
| raise typer.Exit(code=exit_code) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| """Quality rule executor for running quality checks.""" | ||
|
|
||
| from mapper import graph | ||
| from mapper.quality import config, models, registry | ||
|
|
||
|
|
||
| class QualityExecutor: | ||
| """Executes quality rules against a package in Neo4j.""" | ||
|
|
||
| def __init__(self, connection: graph.Neo4jConnection): | ||
| """Initialize executor with Neo4j connection. | ||
|
|
||
| Args: | ||
| connection: Neo4j database connection | ||
| """ | ||
| self.connection = connection | ||
| self.registry = registry.get_registry() | ||
|
|
||
| def execute( | ||
| self, rule_name: str, package: str, quality_config: models.QualityConfig | None = None | ||
| ) -> models.CoverageQualityResult | models.ComplexityQualityResult: | ||
| """Execute a single quality rule. | ||
|
|
||
| Args: | ||
| rule_name: Name of the rule to execute (e.g., 'type-coverage') | ||
| package: Package name to check | ||
| quality_config: Quality configuration (loads from file if None) | ||
|
|
||
| Returns: | ||
| Quality result for the rule | ||
|
|
||
| Raises: | ||
| ValueError: If rule not found or disabled | ||
| """ | ||
| # Load config if not provided | ||
| if quality_config is None: | ||
| quality_config = config.load_quality_config() | ||
|
|
||
| # Get rule from registry | ||
| rule = self.registry.get(rule_name) | ||
| if rule is None: | ||
| available = ", ".join(self.registry.get_rule_names()) | ||
| raise ValueError(f"Quality rule '{rule_name}' not found. Available rules: {available}") | ||
|
|
||
| # Check if rule is enabled | ||
| if not rule.is_enabled(quality_config): | ||
| raise ValueError(f"Quality rule '{rule_name}' is disabled in configuration") | ||
|
|
||
| # Execute rule | ||
| return rule.run(self.connection, package) | ||
|
|
||
| def execute_all( | ||
| self, package: str, quality_config: models.QualityConfig | None = None | ||
| ) -> list[models.CoverageQualityResult | models.ComplexityQualityResult]: | ||
| """Execute all enabled quality rules. | ||
|
|
||
| Args: | ||
| package: Package name to check | ||
| quality_config: Quality configuration (loads from file if None) | ||
|
|
||
| Returns: | ||
| List of quality results for all enabled rules | ||
|
|
||
| Raises: | ||
| ValueError: If no rules are enabled | ||
| """ | ||
| # Load config if not provided | ||
| if quality_config is None: | ||
| quality_config = config.load_quality_config() | ||
|
|
||
| # Get all rules and filter enabled | ||
| all_rules = self.registry.list_all() | ||
| enabled_rules = [rule for rule in all_rules if rule.is_enabled(quality_config)] | ||
|
|
||
| if not enabled_rules: | ||
| raise ValueError("No quality rules are enabled in configuration") | ||
|
|
||
| # Execute all enabled rules | ||
| results = [] | ||
| for rule in enabled_rules: | ||
| result = rule.run(self.connection, package) | ||
| results.append(result) | ||
|
|
||
| return results |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.