Development Guide¶
This project uses Hatch, pytest, Ruff, Black, mypy, and Bandit.
The development workflow is designed to keep diagnostics deterministic, rules schema-validated, and release behavior stable across supported Python versions.
Prerequisites¶
- Python 3.10+
- Hatch
- Make
Setup¶
Common Commands¶
Project Areas¶
src/azure_functions_doctor/api.py: stable programmatic API (run_diagnostics)src/azure_functions_doctor/cli.py: Typer-based CLI (azure-functions doctor)src/azure_functions_doctor/doctor.py: orchestration, rule loading, section aggregationsrc/azure_functions_doctor/handlers.py: handler implementations and registry dispatchsrc/azure_functions_doctor/assets/rules/v2.json: built-in diagnostics rulessrc/azure_functions_doctor/schemas/rules.schema.json: schema for rule validationtests/: unit tests, CLI tests, rule/schema tests, integration-style checksdocs/: MkDocs contentexamples/v2/: representative sample apps used for smoke checks
Architecture Overview¶
Execution path in normal usage:
- CLI parses options (
--path,--profile,--rules, output format options). - CLI calls
run_diagnostics(path, profile, rules_path). Doctorloads rules (built-inv2.jsonor custom rules file).- Rule schema validation runs before any handler execution.
HandlerRegistrydispatches each rule bytype.- Results are canonicalized as
pass/warn/failand emitted.
Testing Strategy¶
The project follows layered testing to keep regressions localized:
- Unit tests for handler behavior and edge conditions.
- Unit tests for rule loading and schema validation.
- CLI tests for command behavior, options, and exit codes.
- Smoke tests against sample Azure Functions v2 projects.
Recommended local sequence before opening a PR:
Adding a New Rule¶
When adding or modifying a built-in check, keep the rule-first flow:
- Edit
src/azure_functions_doctor/assets/rules/v2.json. - Ensure the rule object satisfies
rules.schema.json. - If needed, extend handler behavior in
handlers.py. - Add tests for both pass and non-pass outcomes.
- Update docs (
diagnostics.md,rule_inventory.md, and this guide if needed).
Rule Authoring Checklist¶
| Field | Required | Notes |
|---|---|---|
id |
Yes | Unique and stable identifier for tests and release notes. |
type |
Yes | Must map to a registered handler type. |
label |
Yes | User-facing check name shown in output. |
section |
Yes | Used for grouped output and section summaries. |
required |
Yes | Controls fail vs warn mapping. |
condition |
Usually | Handler-specific contract (for example target, jsonpath, patterns). |
check_order |
Recommended | Keeps output deterministic and stable for CI snapshots. |
Adding a New Handler¶
Use this process to introduce a new rule type:
- Add the new literal to
Rule["type"]inhandlers.py. - Implement
_handle_<name>(self, rule, path). - Register the handler in
HandlerRegistry.__init__. - Extend
rules.schema.jsonso the new type is schema-valid. - Add tests in
tests/test_handler.pyand registry tests if needed.
Minimal handler example¶
from pathlib import Path
def _handle_sample(self, rule: dict, path: Path) -> dict[str, str]:
_ = rule
marker = path / ".doctor-marker"
if marker.exists():
return {"status": "pass", "detail": "Marker exists"}
return {"status": "fail", "detail": "Marker missing"}
Debugging Tips¶
- Use
azure-functions doctor --debugto enable debug logging. - Use
--profile minimalto isolate required checks first. - Run with
--format jsonto inspect raw section/item output precisely. - Use
--rules <path>to reproduce issues with a minimal custom ruleset. - Validate rule JSON structure against the schema before debugging handlers.
Programmatic debugging pattern:
from pathlib import Path
from azure_functions_doctor.doctor import Doctor
def debug_run(project_path: str) -> list[dict]:
doctor = Doctor(path=project_path, profile="full", rules_path=None)
rules = doctor.load_rules()
print(f"Loaded {len(rules)} rules")
return doctor.run_all_checks(rules=rules)
if __name__ == "__main__":
results = debug_run(str(Path(".").resolve()))
print(f"Sections: {len(results)}")
CI/CD Pipeline Explanation¶
CI enforces code quality and compatibility before merge:
- Formatting and lint checks verify style and static quality.
- Type checks validate annotations and API contracts.
- Test jobs run the suite across supported Python versions.
- Security checks (Bandit and dependency auditing where configured) run in CI.
- Documentation changes are expected to remain consistent with runtime behavior.
For release confidence, rule changes should include tests and documentation updates in the same PR.
Workflow¶
- Update runtime code or the v2 ruleset.
- Add or update tests.
- Run
make check-all. - Use English for code comments, docs, and commit messages.