Skip to content

Structure

structure

Audit checks for project structure (7 checks, 17 pts).

check_contributing(project)

Check 27: CONTRIBUTING.md exists.

Source code in packages/axm-init/src/axm_init/checks/structure.py
def check_contributing(project: Path) -> CheckResult:
    """Check 27: CONTRIBUTING.md exists."""
    if not (project / "CONTRIBUTING.md").exists():
        return CheckResult(
            name="structure.contributing",
            category="structure",
            passed=False,
            weight=2,
            message="CONTRIBUTING.md not found",
            details=[],
            fix="Create CONTRIBUTING.md with dev setup and commit conventions.",
        )
    return CheckResult(
        name="structure.contributing",
        category="structure",
        passed=True,
        weight=2,
        message="CONTRIBUTING.md found",
        details=[],
        fix="",
    )

check_license_file(project)

Check 28: LICENSE file exists.

Source code in packages/axm-init/src/axm_init/checks/structure.py
def check_license_file(project: Path) -> CheckResult:
    """Check 28: LICENSE file exists."""
    if not (project / "LICENSE").exists():
        return CheckResult(
            name="structure.license",
            category="structure",
            passed=False,
            weight=3,
            message="LICENSE file not found",
            details=[],
            fix="Create a LICENSE file (MIT, Apache-2.0, or EUPL-1.2).",
        )
    return CheckResult(
        name="structure.license",
        category="structure",
        passed=True,
        weight=3,
        message="LICENSE file found",
        details=[],
        fix="",
    )

check_py_typed(project)

Check 25: py.typed marker in package.

Source code in packages/axm-init/src/axm_init/checks/structure.py
def check_py_typed(project: Path) -> CheckResult:
    """Check 25: py.typed marker in package."""
    src = project / "src"
    if not src.is_dir():
        return CheckResult(
            name="structure.py_typed",
            category="structure",
            passed=False,
            weight=2,
            message="src/ directory not found",
            details=[],
            fix="Create src/<package_name>/py.typed marker file.",
        )
    packages = [d for d in src.iterdir() if d.is_dir() and (d / "__init__.py").exists()]
    for pkg in packages:
        if (pkg / "py.typed").exists():
            return CheckResult(
                name="structure.py_typed",
                category="structure",
                passed=True,
                weight=2,
                message="py.typed marker found",
                details=[],
                fix="",
            )
    return CheckResult(
        name="structure.py_typed",
        category="structure",
        passed=False,
        weight=2,
        message="py.typed marker not found",
        details=["PEP 561: py.typed marks package as providing type information"],
        fix="Create an empty src/<package_name>/py.typed file.",
    )

check_python_version(project)

Check 33: .python-version file exists.

Source code in packages/axm-init/src/axm_init/checks/structure.py
def check_python_version(project: Path) -> CheckResult:
    """Check 33: .python-version file exists."""
    if not (project / ".python-version").exists():
        return CheckResult(
            name="structure.python_version",
            category="structure",
            passed=False,
            weight=1,
            message=".python-version not found",
            details=["Pin Python version for consistent environments"],
            fix="Run `uv python pin 3.12` to create .python-version.",
        )
    return CheckResult(
        name="structure.python_version",
        category="structure",
        passed=True,
        weight=1,
        message=".python-version found",
        details=[],
        fix="",
    )

check_src_layout(project)

Check 24: src// layout with init.py.

Source code in packages/axm-init/src/axm_init/checks/structure.py
def check_src_layout(project: Path) -> CheckResult:
    """Check 24: src/<pkg>/ layout with __init__.py."""
    src = project / "src"
    if not src.is_dir():
        return CheckResult(
            name="structure.src_layout",
            category="structure",
            passed=False,
            weight=4,
            message="src/ directory not found",
            details=["Expected: src/<package_name>/__init__.py"],
            fix="Migrate to src/ layout: move package into src/<package_name>/.",
        )
    # Find at least one package (dir with __init__.py) under src/
    packages = [d for d in src.iterdir() if d.is_dir() and (d / "__init__.py").exists()]
    if not packages:
        return CheckResult(
            name="structure.src_layout",
            category="structure",
            passed=False,
            weight=4,
            message="No Python package found in src/",
            details=["src/ exists but contains no package with __init__.py"],
            fix="Create src/<package_name>/__init__.py.",
        )
    return CheckResult(
        name="structure.src_layout",
        category="structure",
        passed=True,
        weight=4,
        message=f"src/ layout with {len(packages)} package(s)",
        details=[],
        fix="",
    )

check_tests_dir(project)

Check 26: tests/ directory with at least one test file.

Source code in packages/axm-init/src/axm_init/checks/structure.py
def check_tests_dir(project: Path) -> CheckResult:
    """Check 26: tests/ directory with at least one test file."""
    tests = project / "tests"
    if not tests.is_dir():
        return CheckResult(
            name="structure.tests_dir",
            category="structure",
            passed=False,
            weight=3,
            message="tests/ directory not found",
            details=[],
            fix="Create tests/ directory with test files.",
        )
    test_files = list(tests.rglob("test_*.py"))
    if not test_files:
        return CheckResult(
            name="structure.tests_dir",
            category="structure",
            passed=False,
            weight=3,
            message="No test files found in tests/",
            details=["Expected: tests/test_*.py files"],
            fix="Add test files matching test_*.py pattern.",
        )
    return CheckResult(
        name="structure.tests_dir",
        category="structure",
        passed=True,
        weight=3,
        message=f"{len(test_files)} test file(s) found",
        details=[],
        fix="",
    )

check_uv_lock(project)

Check 32: uv.lock committed for reproducible builds.

Source code in packages/axm-init/src/axm_init/checks/structure.py
def check_uv_lock(project: Path) -> CheckResult:
    """Check 32: uv.lock committed for reproducible builds."""
    lock = _find_uv_lock(project)
    if lock is None:
        return CheckResult(
            name="structure.uv_lock",
            category="structure",
            passed=False,
            weight=2,
            message="uv.lock not found",
            details=["Commit uv.lock for reproducible dependency resolution"],
            fix="Run `uv lock` and commit the generated uv.lock file.",
        )
    at_root = lock.parent != project.resolve()
    msg = "uv.lock found (workspace root)" if at_root else "uv.lock found"
    return CheckResult(
        name="structure.uv_lock",
        category="structure",
        passed=True,
        weight=2,
        message=msg,
        details=[],
        fix="",
    )