Skip to main content

Mitchell's Ineffable Rules (IR) Linter - A modular linter with customizable rules for SQL, Java, and XML.

Project description

Mitchell's Ineffable Rules (IR) Linter

A highly modular, extensible linting and auto-fixing utility for SQL, Java, and XML codebase assets. The linter can be run locally via uv / python, inside a containerized Docker execution loop, or integrated into your lifecycle as a Git Pre-commit Hook.


Features

  • Extensible Rules Architecture: Add language rules simply by adding Python rule modules under mir/rules/<language>/.
  • User Styling Guides: Detailed style guides explaining each rule, its configuration parameters, auto-fixability, and clean vs. violating examples under docs/.
  • Precedence-Aware Configuration: Merges CLI flags, environment variables, and YAML config file settings: $$\text{CLI arguments} > \text{Environment variables} > \text{Config file} > \text{Defaults}$$
  • Granular Disable Comments:
    • Single-line disable (disables a rule for the next non-comment code line):
      • SQL: -- IR-rule-name
      • Java: // IR-rule-name
      • XML: <!-- IR-rule-name -->
    • Block disable (disables a rule for a range of lines):
      • SQL: -- IR-start-rule-name ... -- IR-end-rule-name
      • Java: // IR-start-rule-name ... // IR-end-rule-name
      • XML: <!-- IR-start-rule-name --> ... <!-- IR-end-rule-name -->
  • Execution Modes:
    • check: Validates files and reports all errors (default).
    • fix: Applies automatic corrections to fixable rules directly to files.
    • dry-run: Runs checks and generates unified diffs showing proposed fixes without modifying the source.
    • verbose: Prints rule descriptions and the offending lines along with errors.

Directory Structure

.
├── docs/                       # Language-specific Style Guides
│   ├── sql.md                  # SQL style guide & rules
│   ├── java.md                 # Java style guide (placeholder)
│   └── xml.md                  # XML style guide (placeholder)
├── Dockerfile                  # Containerization
├── README.md                   # This instruction manual
├── requirements.txt            # Python dependencies (PyYAML)
├── pre-commit-hook.sh          # Shell hook executing linter via Docker
├── mir/
│   ├── main.py                 # Core CLI Entrypoint
│   ├── engine/                 # Core engine mechanics
│   │   ├── config.py           # Precedence-aware configuration loader
│   │   ├── disabler.py         # Parsing and handling disable comments
│   │   ├── rule_interface.py   # BaseRule & Violation class structures
│   │   ├── rules_loader.py     # Dynamic imports of language rules
│   │   └── runner.py           # Processing workflow (Check, Fix, Dry-Run)
│   └── rules/                  # Language specific rules
│       └── sql/
│           ├── line_length.py  # Checks lines exceeding maximum characters
│           └── keyword_case.py # Enforces and fixes uppercase SQL keywords
└── tests/                      # Suite of unit & integration tests
    ├── test_config.py          # Tests config parser
    ├── test_disabler.py        # Tests comment disabler logic
    ├── test_runner.py          # Tests CLI runner integration
    └── rules/                  # Rule specific tests
        └── sql/
            ├── test_line_length.py
            └── test_keyword_case.py

Setup & Local Usage

Ensure you have uv or python installed.

1. Running Tests

You can run the full test suite using uv:

uv run --with pyyaml python -m unittest discover -s tests -p "test_*.py"

2. Style Guides & Documentation

  • Custom Rules Guide: Refer to the Custom Rules Guide for details on writing your own custom linting rules, parameter interfaces, and execution validation.

  • Language Style Guides: The other language-specific files in the docs/ folder (such as docs/sql.md, docs/java.md, and docs/xml.md) are automatically generated by the package from code metadata. To compile and update these guides in sync with rule source definitions, run:

    # Generate style guides directly inside the 'docs/' directory
    uv run mir/main.py --help docs
    
    # Generate style guides inside a custom directory name
    uv run mir/main.py --help docs:my_rules_docs
    

    This scans all rule modules under mir/rules/ and compiles the style guide markdown files directly.

2. Linting execution

# Check files (default path: current directory)
uv run mir/main.py

# Verbose check on a specific file path
uv run mir/main.py -v path/to/file.sql

# Safe auto-fixing rules on a directory (rewrites matching files)
uv run mir/main.py --fix path/to/file.sql

# Show dry-run diffs
uv run mir/main.py --dry-run path/to/file.sql

3. Rules CLI Documentation & Help

You can query and inspect the list of rules and details directly from the command line:

# List all rules across all languages
uv run mir/main.py --help rules

# List only SQL rules
uv run mir/main.py --help rules:sql

# Show detailed documentation & examples for a specific rule across all languages
uv run mir/main.py --help IR-line-length

# Show detailed documentation for a specific rule inside a specific language
uv run mir/main.py --help sql:IR-keyword-case

Docker Usage

Build and execute the linter entirely inside a containerized sandbox.

1. Build the Docker Image

docker build -t mitchells-ineffable-rules:latest .

2. Lint Files via Docker

Mount your workspace volume into /workspace in the container:

# Basic check on the current directory
docker run --rm -v "$(pwd):/workspace" mitchells-ineffable-rules:latest .

# Run auto-fixes
docker run --rm -v "$(pwd):/workspace" mitchells-ineffable-rules:latest --fix .

# Verbose dry-run on a file
docker run --rm -v "$(pwd):/workspace" mitchells-ineffable-rules:latest --dry-run -v query.sql

Git Pre-commit Hook Integration

Integrate the linter to block bad code from being committed:

  1. Copy the hook script into your repository's hook directory:
    cp pre-commit-hook.sh .git/hooks/pre-commit
    
  2. Make sure it is executable:
    chmod +x .git/hooks/pre-commit
    

When committing files, the hook automatically detects staged SQL, Java, and XML changes, running them through the Docker container before allowing the commit to succeed.


Configuration

You can create an .ir-config.yaml file in the root of your project:

# Rules to globally disable
disable:
  - IR-some-disabled-rule

# Global flags
verbose: true
fix: false

# External rule directories (to extend or replace built-in rulesets)
include_dirs:
  - /path/to/my/custom_rules

# Rule mode for common languages: "extend" (default, built-in + custom) or "replace" (custom only)
rule_mode: extend

# Individual rule configuration
rules:
  IR-line-length:
    max_length: 120

Rule Configuration Fallbacks

To keep rules independent and decoupled, certain rules can dynamically inherit configuration parameters from other rules if they are not explicitly set.

For example, the SQL Column Layout formatter (IR-column-layout) dynamically falls back to query its parameters from the canonical rule configurations:

  • max_length is inherited from the Line Length rule (IR-line-length)
  • indent_size is inherited from the Indentation rule (IR-indent)

You can override these parameters specifically for the dependent rule or set them once on the canonical rule:

rules:
  # Sets line length limit to 80 (affects both IR-line-length and IR-column-layout fallback)
  IR-line-length:
    max_length: 80

  # Specifically overrides column layout to use 100 instead
  IR-column-layout:
    max_length: 100

Order of Precedence

Configuration parameters are merged with the following priority hierarchy (highest overrides lowest):

  1. Command Line Arguments (e.g. --fix, --disable IR-line-length, --include-dir /path, --rule-mode replace)
  2. Environment Variables (e.g. IR_FIX=true, IR_DISABLE=IR-line-length, IR_INCLUDE_DIRS=/path, IR_RULE_MODE=replace)
  3. YAML Config File (e.g. disable: [IR-line-length], include_dirs: [/path], rule_mode: replace inside .ir-config.yaml)

Writing Custom Rules

You can write custom rules to run against your codebase, either placing them inside the built-in rules directory or in an external custom rules directory. For full details on class specifications, default parameters, and strict execution validation, refer to the Custom Rules Guide.

1. Built-in Directory

Create your rule Python module under mir/rules/<language>/my_rule_name.py.

2. External Directory

  1. Create a directory structure, e.g. /my-custom-rules/<language>/my_rule_name.py.
  2. Provide the path to the linter via CLI (--include-dir /my-custom-rules), Env (IR_INCLUDE_DIRS), or config (include_dirs: ["/my-custom-rules"]).
  3. Set the --rule-mode to specify whether custom rules should extend (default) or replace the built-in rule sets.

Custom Rule Implementation Example

Each custom rule must subclass BaseRule (from mir.engine.rule_interface):

from typing import List, Dict, Any
from mir.engine.rule_interface import BaseRule, Violation

class MyXmlRule(BaseRule):
    rule_id = "IR-xml-rule"
    description = "A custom description of my rule."
    category = "general"
    is_fixable = "yes"  # "yes", "no", or "sometimes"

    def check(self, content: str, file_path: str, rule_config: Dict[str, Any]) -> List[Violation]:
        violations = []
        # Implement check logic...
        # If a violation is found, append Violation(...) to list.
        return violations

    def fix(self, content: str, file_path: str, rule_config: Dict[str, Any]) -> str:
        # If is_fixable is "yes" or "sometimes", return the modified content string
        return content

Publishing to PyPI

For instructions on building distribution archives, TestPyPI validation, and publishing to the live Python Package Index, refer to the PyPI Publishing Guide.


This entire package was built using Antigravity, a powerful agentic AI coding assistant designed by the Google DeepMind team.

Total Cumulative Development Time: ~4.1 hours

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mir_linter-0.1.0a1.tar.gz (37.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mir_linter-0.1.0a1-py3-none-any.whl (36.1 kB view details)

Uploaded Python 3

File details

Details for the file mir_linter-0.1.0a1.tar.gz.

File metadata

  • Download URL: mir_linter-0.1.0a1.tar.gz
  • Upload date:
  • Size: 37.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for mir_linter-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 e322142e9b8e4b53cbedbcbec08f49586bd2641fc5770115be415a81e76d4a51
MD5 9659c04757828b0b8c8774008075a091
BLAKE2b-256 5c4566d57a4265a2da2a9ee6e277be60285febf6f04d5635c1c513fad917de39

See more details on using hashes here.

File details

Details for the file mir_linter-0.1.0a1-py3-none-any.whl.

File metadata

  • Download URL: mir_linter-0.1.0a1-py3-none-any.whl
  • Upload date:
  • Size: 36.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for mir_linter-0.1.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 a02c100360c1d96c9cf3252fbbc17372b44225b8dfcd11e47bf531a6853fd534
MD5 308609d19ce8ce651dc84cee3be8c7b9
BLAKE2b-256 7ca24895f94931793aa10413923a6cd19461492593ef466699b0ea0b4e735bcc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page