Metadata-Version: 2.4
Name: modulith-android
Version: 0.1.1
Summary: Architectural audit tool for multi-module Gradle Android projects.
Author: Alberto Pérez
License: MIT License
        
        Copyright (c) 2026 Alberto Pérez
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/Albertoperezs90/modulith-android
Project-URL: Repository, https://github.com/Albertoperezs90/modulith-android
Project-URL: Issues, https://github.com/Albertoperezs90/modulith-android/issues
Keywords: android,gradle,architecture,dependency-graph,modulith
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: tomli>=1.1.0; python_version < "3.11"
Provides-Extra: dev
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Dynamic: license-file

<div align="center">

# 🧱 modulith-android

**Architectural audit for multi-module Gradle Android projects — cycles, layer violations, visibility issues, unused dependencies, and impact cascades, all in a single self-contained HTML report.**

[![Python 3.10+](https://img.shields.io/badge/Python-3.10+-3776AB?logo=python&logoColor=white)](https://www.python.org)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Platform](https://img.shields.io/badge/platform-Gradle%20Android-3DDC84?logo=android&logoColor=white)](#requirements)
[![Status](https://img.shields.io/badge/status-pre--alpha-orange)](#status)

<!-- TODO: add hero screenshot of the HTML report once v0.1.0 ships -->

</div>

---

Point it at **any** Gradle Android repo. It parses your `settings.gradle[.kts]`, walks every module's build file, scans your Kotlin/Java sources for cross-module imports, and produces a **single self-contained HTML file** with:

- 🕸️ **Module graph** — every `:layer:name` module and its dependency edges, colour-coded by layer.
- 🔁 **Cycle detection** — Tarjan-based strongly-connected components; any cyclic coupling between modules is flagged.
- 🚫 **Layer violations** — edges that cross a forbidden layer pair (e.g. `core → feature` under Clean Architecture), with the offending Gradle dependency listed.
- 👁️ **Visibility audit** — for the modules you choose, every `public` symbol is classified as `internalize_candidate`, `di_required`, `external_consumer`, or `star_import_uncertain` so you know what's actually load-bearing.
- 🪦 **Unused module dependencies** — flags `project(":foo")` declarations whose target module's packages the consumer's source never imports.
- 💥 **Impact cascades** — pick a module, see every module that transitively depends on it.
- 📊 **Rankings** — modules sorted by fan-in, fan-out, instability, and abstractness.

> **Configuration-driven and project-agnostic.** Every project-specific value (root package, layer names, forbidden layer pairs, audited modules) lives in a single `modulith.toml` at your repo root. Run `modulith-android init` and the wizard auto-detects sensible defaults in seconds — confirm with Enter, edit otherwise. Drop it into any Gradle Android project and you're scanning in under a minute.

> **A report generator, not a CI gate.** modulith-android **never** fails a build and **never** exits non-zero based on findings. It reads, computes, and writes — interpretation and policy belong to whoever reads the report. By design.

---

## 🚧 Status

Pre-alpha. **v0.1.0 in active development.** The repository scaffold is in place; CLI, wizard, and analysis pipeline land progressively as the milestones in the [roadmap](#roadmap) ship. The quick start below describes the shipped behaviour at v0.1.0.

---

## 🚀 Quick start

```sh
# one-shot, no install — needs uv (https://docs.astral.sh/uv/)
uvx --from modulith-android modulith-android init   # wizard writes ./modulith.toml
uvx --from modulith-android modulith-android        # writes build/dependency-graph/index.html
```

Prefer an installed entry-point? Use [`pipx`](https://pipx.pypa.io/):

```sh
pipx install modulith-android
modulith-android init
modulith-android
```

The default run writes `build/dependency-graph/index.html`. Open it in any browser — no static server, no JavaScript build, no external assets.

---

## 🛠️ Commands

| Command | What it does | Output |
|---------|--------------|--------|
| `modulith-android init` | runs the interactive wizard with auto-detected defaults | `./modulith.toml` |
| `modulith-android` | runs the full analysis using `./modulith.toml` | `build/dependency-graph/index.html` |
| `modulith-android --repo PATH` | analyse a project in a different directory | report in that project's `build/` |
| `modulith-android --out PATH` | write the report somewhere else | HTML at the supplied path |
| `modulith-android --config PATH` | use a config file at a non-default location | unchanged |

Exit codes are **0** on success and **1** on configuration / I/O errors. Findings never change the exit code.

---

## 🪄 The wizard

`modulith-android init` runs four short questions, each with an auto-detected default. Confirm with Enter; edit anything that looks off.

| # | Question | Auto-detected from |
|---|----------|---------------------|
| 1 | **Root package** (e.g. `com.example`) | Scans `.kt`/`.java` files, computes the longest common package prefix. |
| 2 | **Layer names** (e.g. `core`, `data`, `feature`, `app`) | Reads `settings.gradle[.kts]`, extracts the first segment of each module path. |
| 3 | **Modules to audit for visibility** | Pre-selects all modules in the innermost layer; multi-select to customise. |
| 4 | **Forbidden layer pairs** | Offers presets (Clean Architecture, MVI/MVVM) or "Custom" for manual entry. |

The wizard prints the generated `modulith.toml` for review and asks before writing.

---

## ⚙️ Configuration

The full `modulith.toml` schema:

```toml
# Generated by `modulith-android init`. Hand-edit anything you like.

[scan]
# Root Java/Kotlin package. Used by the visibility analyzer to scope
# import-index scanning to symbols declared in this package tree.
root_package = "com.example"

[layers]
# Ordered list of layer names, derived from the first segment of each
# Gradle module path (e.g. ":core:logger" -> "core"). Modules whose
# first segment isn't listed are parsed for edges but excluded from
# violation checking.
names = ["core", "data", "feature", "app"]

[visibility]
# Gradle module paths whose public API will be enumerated and
# cross-checked for external references.
target_modules = [
    ":core:logger",
    ":core:network",
]

# Each [[violations]] entry forbids a directed edge between two layers.
# Multiple entries are independent — they don't compose.
[[violations]]
from = "core"
to   = "data"

[[violations]]
from = "core"
to   = "feature"

[[violations]]
from = "data"
to   = "feature"
```

Missing sections are silently treated as "skip this analysis" — e.g. an absent `[visibility]` skips the visibility audit, an empty `[[violations]]` table means no violation rules are active. A missing `modulith.toml` is a hard error with a "run `modulith-android init`" hint.

---

## 📋 Requirements

| Tool | Why | Notes |
|------|-----|-------|
| Python **3.10+** | runs the analyzer | stdlib-only on 3.11+; one backport (`tomli`) on 3.10 |
| `uv` or `pipx` *(recommended)* | one-shot or isolated install | `pip install --user modulith-android` works too |
| Gradle project | the analysis target | Groovy or Kotlin DSL `settings.gradle` — both are supported |

The analyzer itself has **zero runtime dependencies** on Python 3.11+ and exactly one (`tomli`) on Python 3.10. No HTML/JS framework, no JSON schema library, no graph library — pure stdlib. No third-party Gradle plugin in the consumer's project either: every feature works against a vanilla Gradle build.

---

## 🧩 How it works

```
                       modulith-android
                              │
   ┌──────────────────────────┼─────────────────────────────────────┐
   │ 1. read modulith.toml — every project-specific value           │
   ├────────────────────────────────────────────────────────────────┤
   │ 2. parse settings.gradle[.kts]            → list of modules    │
   │    parse each module's build.gradle[.kts] → edges              │
   ├────────────────────────────────────────────────────────────────┤
   │ 3. core graph algorithms (stdlib only):                        │
   │      Tarjan SCC          → cycles                              │
   │      layer-pair filter   → violations                          │
   │      reverse-BFS         → cascades                            │
   │      fan-in/out, etc.    → metrics                             │
   ├────────────────────────────────────────────────────────────────┤
   │ 4. cross-check declared project deps against source imports    │
   │      → unused module dependencies                              │
   ├────────────────────────────────────────────────────────────────┤
   │ 5. scan public API of audited modules; classify each symbol    │
   │      by external-consumer count + Hilt/Koin DI markers         │
   ├────────────────────────────────────────────────────────────────┤
   │ 6. render a single self-contained HTML file                    │
   │      → build/dependency-graph/index.html                       │
   └────────────────────────────────────────────────────────────────┘
```

Every output is **deterministic**: given identical inputs the HTML is byte-for-byte the same, so regenerating after a code change produces a clean diff you can review.

---

## 🗂️ Project layout

```
modulith-android/
├── src/modulith_android/
│   ├── cli.py                 entry-point; argparse dispatch
│   ├── config.py              ModulithConfig dataclass + TOML loader
│   ├── wizard.py              interactive `init` flow with auto-detect
│   ├── parsers/
│   │   └── gradle_modules.py  settings.gradle[.kts] + build.gradle[.kts] reader
│   ├── analyzers/
│   │   ├── unused_module_deps.py  cross-checks declared project deps against source imports
│   │   └── visibility_audit.py    public-API scanner + DI-marker classifier
│   ├── core/
│   │   ├── model.py           Edge, Module, GraphSnapshot
│   │   ├── cycles.py          Tarjan SCC
│   │   ├── cascades.py        transitive impact computation
│   │   ├── metrics.py         fan-in, fan-out, instability, abstractness
│   │   └── violations.py      layer-rule checker
│   ├── reports/
│   │   ├── html.py            the self-contained HTML+CSS+JS report
│   │   └── json_export.py     machine-readable graph dump
│   ├── common/
│   │   └── paths.py           module-path utilities
│   └── flows/
│       └── analyze.py         orchestrates the full pipeline
├── tests/                     stdlib-unittest suite, no pip deps
├── pyproject.toml
├── LICENSE
└── README.md
```

---

## 🧑‍💻 Direct CLI

`uvx`/`pipx` are the easy paths, but the package is a normal CLI. Once installed:

```
modulith-android [SUBCOMMAND] [OPTIONS]

Subcommands:
  init                run the wizard; writes ./modulith.toml

Options (default run):
  --repo PATH         repository root (default: cwd)
  --out PATH          output directory (default: <repo>/build/dependency-graph)
  --config PATH       config file (default: <repo>/modulith.toml)
  -h, --help          show this help and exit
  --version           show version and exit

Exit codes:
  0  success; HTML report written
  1  configuration or I/O error
```

---

## 🧪 Development

```sh
pip install -e ".[dev]"
python -m pytest tests/                          # full suite
python -m pytest tests/test_visibility_audit.py  # one module
ruff check src/ tests/                           # lint
```

Tests use stdlib `unittest`; pytest discovers them with no configuration. CI runs the suite on Python 3.10 and 3.12.

---

## 🗺️ Roadmap

- [ ] **v0.1.0** — analyzer core, `modulith.toml` config, wizard with auto-detect, self-contained HTML report, PyPI publication.
- [ ] **v0.2.0** — JSON export improvements; richer cascade visualisation; configurable Hilt/Koin marker sets.
- [ ] **v0.3.0** — multi-DSL improvements (KTS multi-line includes, type-safe project accessors).
- [ ] **v1.0.0** — API stability promise.

---

## 🎯 Scope

modulith-android focuses on **modularity**: graph shape, layer rules, visibility, and unused inter-module dependencies. It does NOT audit external Maven dependencies or Gradle configuration hygiene (`api` vs `implementation`, scope choices). For that, plugins like the [Dependency Analysis Gradle Plugin](https://github.com/autonomousapps/dependency-analysis-gradle-plugin) are excellent companions and orthogonal to what this tool does.

---

## 📄 License

MIT — see [LICENSE](LICENSE).
