truthlint 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- truthlint-0.1.0/LICENSE +21 -0
- truthlint-0.1.0/PKG-INFO +281 -0
- truthlint-0.1.0/README.md +249 -0
- truthlint-0.1.0/pyproject.toml +78 -0
- truthlint-0.1.0/setup.cfg +4 -0
- truthlint-0.1.0/src/truthlint/__init__.py +10 -0
- truthlint-0.1.0/src/truthlint/__main__.py +4 -0
- truthlint-0.1.0/src/truthlint/cli.py +408 -0
- truthlint-0.1.0/src/truthlint/config.py +57 -0
- truthlint-0.1.0/src/truthlint/context.py +440 -0
- truthlint-0.1.0/src/truthlint/engine.py +131 -0
- truthlint-0.1.0/src/truthlint/fixes.py +132 -0
- truthlint-0.1.0/src/truthlint/ignore.py +129 -0
- truthlint-0.1.0/src/truthlint/intel.py +237 -0
- truthlint-0.1.0/src/truthlint/models.py +198 -0
- truthlint-0.1.0/src/truthlint/onboarding.py +227 -0
- truthlint-0.1.0/src/truthlint/parsers/__init__.py +1 -0
- truthlint-0.1.0/src/truthlint/parsers/ci.py +201 -0
- truthlint-0.1.0/src/truthlint/parsers/compose.py +101 -0
- truthlint-0.1.0/src/truthlint/parsers/dockerfile.py +150 -0
- truthlint-0.1.0/src/truthlint/parsers/envfile.py +41 -0
- truthlint-0.1.0/src/truthlint/parsers/lines.py +78 -0
- truthlint-0.1.0/src/truthlint/parsers/makefile.py +119 -0
- truthlint-0.1.0/src/truthlint/parsers/markdown.py +193 -0
- truthlint-0.1.0/src/truthlint/parsers/node_meta.py +139 -0
- truthlint-0.1.0/src/truthlint/parsers/python_meta.py +467 -0
- truthlint-0.1.0/src/truthlint/parsers/shell.py +235 -0
- truthlint-0.1.0/src/truthlint/parsers/source.py +506 -0
- truthlint-0.1.0/src/truthlint/parsers/versions.py +273 -0
- truthlint-0.1.0/src/truthlint/parsers/yamlx.py +65 -0
- truthlint-0.1.0/src/truthlint/py.typed +0 -0
- truthlint-0.1.0/src/truthlint/render/__init__.py +1 -0
- truthlint-0.1.0/src/truthlint/render/json_report.py +36 -0
- truthlint-0.1.0/src/truthlint/render/markdown.py +158 -0
- truthlint-0.1.0/src/truthlint/render/terminal.py +355 -0
- truthlint-0.1.0/src/truthlint/rules/__init__.py +34 -0
- truthlint-0.1.0/src/truthlint/rules/base.py +85 -0
- truthlint-0.1.0/src/truthlint/rules/build.py +188 -0
- truthlint-0.1.0/src/truthlint/rules/commands.py +1671 -0
- truthlint-0.1.0/src/truthlint/rules/containers.py +403 -0
- truthlint-0.1.0/src/truthlint/rules/deps.py +713 -0
- truthlint-0.1.0/src/truthlint/rules/docs.py +416 -0
- truthlint-0.1.0/src/truthlint/rules/env.py +380 -0
- truthlint-0.1.0/src/truthlint/rules/runtime.py +552 -0
- truthlint-0.1.0/src/truthlint/scoring.py +83 -0
- truthlint-0.1.0/src/truthlint.egg-info/PKG-INFO +281 -0
- truthlint-0.1.0/src/truthlint.egg-info/SOURCES.txt +55 -0
- truthlint-0.1.0/src/truthlint.egg-info/dependency_links.txt +1 -0
- truthlint-0.1.0/src/truthlint.egg-info/entry_points.txt +2 -0
- truthlint-0.1.0/src/truthlint.egg-info/requires.txt +9 -0
- truthlint-0.1.0/src/truthlint.egg-info/top_level.txt +1 -0
- truthlint-0.1.0/tests/test_cli.py +98 -0
- truthlint-0.1.0/tests/test_fixes.py +94 -0
- truthlint-0.1.0/tests/test_parsers.py +182 -0
- truthlint-0.1.0/tests/test_rules.py +466 -0
- truthlint-0.1.0/tests/test_safety.py +109 -0
- truthlint-0.1.0/tests/test_versions.py +71 -0
truthlint-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 truthlint contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
truthlint-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: truthlint
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Find where your repository contradicts itself: docs, configs, CI, containers and code checked against each other, offline.
|
|
5
|
+
Author: truthlint contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/harshith7002/truthlint
|
|
8
|
+
Project-URL: Issues, https://github.com/harshith7002/truthlint/issues
|
|
9
|
+
Keywords: static-analysis,documentation,consistency,developer-tools,linter,onboarding,repository,drift
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: typer>=0.12
|
|
24
|
+
Requires-Dist: rich>=13.7
|
|
25
|
+
Requires-Dist: pydantic>=2.6
|
|
26
|
+
Requires-Dist: PyYAML>=6.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
<div align="center">
|
|
34
|
+
|
|
35
|
+
# truthlint
|
|
36
|
+
|
|
37
|
+
### Your repository is lying. truthlint finds where.
|
|
38
|
+
|
|
39
|
+
**Find every place where your repository's docs, configs, CI, containers and code contradict each other.**
|
|
40
|
+
|
|
41
|
+
[](https://github.com/harshith7002/truthlint/actions/workflows/ci.yml)
|
|
42
|
+

|
|
43
|
+

|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
<!-- truthlint-ignore TL101: the next line is an illustration, not a requirement -->
|
|
51
|
+
Your README says **Python 3.8+**. `pyproject.toml` says `>=3.10`. The Dockerfile runs **3.9**. CI tests a version
|
|
52
|
+
users can't even install on. The setup guide tells newcomers to run `make lint` — a target that was renamed a year
|
|
53
|
+
ago. The code reads `JWT_SECRET`, which is nowhere in `.env.example`.
|
|
54
|
+
|
|
55
|
+
Every one of those files is individually "fine". Together they lie.
|
|
56
|
+
|
|
57
|
+
**truthlint** reads a repository the way a careful new contributor would — then cross-examines every file against every
|
|
58
|
+
other file and reports, with evidence, where the project's **claims** and its **reality** disagree.
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
HIGH TL101-406201 Python version contradiction high confidence
|
|
62
|
+
README.md says Python 3.8+ is enough, but the project requires >=3.10
|
|
63
|
+
(minimum 3.10). Users following the docs on Python 3.8 will be refused by pip.
|
|
64
|
+
↳ README.md:8 claims Python 3.8+
|
|
65
|
+
↳ pyproject.toml:9 declares requires-python '>=3.10'
|
|
66
|
+
Fix: State Python 3.10+ in README.md, or raise the constraint. [auto-fix available]
|
|
67
|
+
|
|
68
|
+
HIGH TL202-6F78F4 Command runs an undefined make target high confidence
|
|
69
|
+
`README.md` documents `make lint`, but target `lint` is not defined in
|
|
70
|
+
`Makefile` (defined: format, serve, test).
|
|
71
|
+
↳ README.md:28 documents `make lint`
|
|
72
|
+
↳ Makefile defines targets: format, serve, test
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Why truthlint?
|
|
76
|
+
|
|
77
|
+
Linters check one file at a time. Type checkers check code. Link checkers check URLs. **Nothing checks that the
|
|
78
|
+
files agree with each other** — and that is exactly where onboarding breaks, deployments drift and "works on my
|
|
79
|
+
machine" is born.
|
|
80
|
+
|
|
81
|
+
| | |
|
|
82
|
+
|---|---|
|
|
83
|
+
| 🔎 **Cross-file analysis** | 35 rules compare README, docs, `pyproject.toml`, `requirements*.txt`, `package.json`, lockfiles, Dockerfiles, compose files, GitHub Actions / GitLab CI, Makefiles, `.env` examples and source code. |
|
|
84
|
+
| 🧾 **Evidence, always** | Every finding cites the exact file and line on *each side* of the contradiction, a confidence level, and how it was verified. truthlint never reports something it didn't check. |
|
|
85
|
+
| 🧭 **Onboarding audit** | Walks your setup instructions step by step (tracking `cd`, `cp`, `venv` …) and verifies each command statically: does that npm script, make target, file, compose service or pip extra actually exist? |
|
|
86
|
+
| 📊 **Explainable scores** | A 0–100 **Repository Truth Score** and an **Onboarding Reliability Score**, with every deduction itemised. |
|
|
87
|
+
| 🩹 **Reviewable fixes** | Suggested fix for every finding; many come with a unified-diff patch. Nothing is written without your explicit confirmation. |
|
|
88
|
+
| 🔒 **Safe by construction** | Fully offline, no API keys, no AI service. **Never executes anything** from the repository — not even `git` (a repo's `.git/config` can make git run commands). |
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
Requires Python **3.11+**.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pipx install truthlint # recommended: isolated CLI
|
|
96
|
+
# or
|
|
97
|
+
pip install truthlint
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
From source:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git clone https://github.com/harshith7002/truthlint.git
|
|
104
|
+
cd truthlint
|
|
105
|
+
pip install -e ".[dev]"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This installs the `truthlint` command (`python -m truthlint` works too).
|
|
109
|
+
|
|
110
|
+
## Quick start
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
truthlint scan . # investigate the current repository
|
|
114
|
+
truthlint scan . --deep # + heuristic checks (unused deps/env vars, stale doc paths, CI coverage)
|
|
115
|
+
truthlint explain TL202-6F78F4 # evidence in context, rationale and the proposed patch
|
|
116
|
+
truthlint fix # preview every available patch (dry run)
|
|
117
|
+
truthlint fix --interactive # review and apply patches one by one
|
|
118
|
+
truthlint report --format markdown -o truthlint-report.md
|
|
119
|
+
truthlint report --format json -o truthlint.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Try it on the bundled sample repositories:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
truthlint scan examples/drifting-python-api
|
|
126
|
+
truthlint scan examples/drifting-node-service --deep
|
|
127
|
+
truthlint scan examples/healthy-library # 100/100, zero findings
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## What truthlint detects
|
|
131
|
+
|
|
132
|
+
Run `truthlint rules` for the full list, or `truthlint explain TL201` for details on any rule. See [docs/rules.md](docs/rules.md).
|
|
133
|
+
|
|
134
|
+
| Area | Examples |
|
|
135
|
+
|---|---|
|
|
136
|
+
| **Runtime versions** | README / Dockerfile / CI matrix / `.python-version` / `.nvmrc` / classifiers / `ruff target-version` vs `requires-python` or `engines.node`; container runtime drifting from the local pin; declared minimum never tested in CI |
|
|
137
|
+
| **Documented commands** | `npm run X`, `yarn X`, `pnpm X` with no such script · `make X` with no such target · scripts, requirements files, directories and modules that don't exist · `pip install ".[dev]"` with no `dev` extra · `docker compose up svc` with no such service · `npm install` in a pnpm-locked repo · `poetry install` in a non-Poetry project · documented CLI never declared as an entry point |
|
|
138
|
+
| **Documentation** | Broken relative links and heading anchors (case-exact, like GitHub) · outdated project-structure trees · inline references to files that no longer exist |
|
|
139
|
+
| **Environment** | Variables read in code (Python AST incl. pydantic-settings, JS/TS, Go, Ruby, Rust, Java, PHP, C#, Elixir, Prisma, shell) but missing from `.env.example`; documented variables nothing reads; no example file at all |
|
|
140
|
+
| **Dependencies** | Imports not declared in any manifest (works only on the author's machine) · declared deps never imported · runtime code importing dev-only deps — for Python and JS/TS |
|
|
141
|
+
| **Containers** | `COPY` of missing files or files excluded by `.dockerignore` · Docker pinning different versions than the manifest · lockfile not copied before install · `CMD` pointing at missing modules · compose build contexts, Dockerfiles, `env_file`s and `depends_on` that don't exist |
|
|
142
|
+
| **CI** | Workflow `run:` steps calling missing scripts/targets/files · missing `working-directory` · missing local actions |
|
|
143
|
+
| **Build metadata** | Malformed TOML/JSON/YAML · console scripts pointing to missing modules/functions · `readme`, `main`, `bin` files that don't exist |
|
|
144
|
+
| **Onboarding** | No setup instructions · required runtimes/package managers never mentioned · env configuration never explained |
|
|
145
|
+
|
|
146
|
+
## Scores
|
|
147
|
+
|
|
148
|
+
**Repository Truth Score (0–100).** Each finding contributes `severity weight × confidence`
|
|
149
|
+
(critical 20 · high 10 · medium 5 · low 1.5; confidence 1.0 / 0.7 / 0.4). Repeats of the same rule count half as
|
|
150
|
+
much each time and each rule is capped at 25 points, so one noisy area can't dominate. The score is
|
|
151
|
+
`100 × e^(−points/90)`. Every deduction is listed in the Markdown/JSON report.
|
|
152
|
+
|
|
153
|
+
**Onboarding Reliability Score (0–100).** Starts at 100: −40 if there are no runnable setup instructions, −15 per
|
|
154
|
+
documented step that fails verification (max −60), −8 per required tool the docs never mention, −5 if
|
|
155
|
+
environment configuration is never explained. Steps that would need execution to verify (e.g. `git clone`,
|
|
156
|
+
`pytest`) are reported as *unverified* and never counted as passing **or** failing.
|
|
157
|
+
|
|
158
|
+
## Using truthlint in CI
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
# .github/workflows/truthlint.yml
|
|
162
|
+
name: truthlint
|
|
163
|
+
on: [pull_request]
|
|
164
|
+
jobs:
|
|
165
|
+
truthlint:
|
|
166
|
+
runs-on: ubuntu-latest
|
|
167
|
+
steps:
|
|
168
|
+
- uses: actions/checkout@v4
|
|
169
|
+
- uses: actions/setup-python@v5
|
|
170
|
+
with:
|
|
171
|
+
python-version: "3.12"
|
|
172
|
+
- run: pip install truthlint
|
|
173
|
+
- run: truthlint scan . --fail-on high # fail on high/critical contradictions
|
|
174
|
+
- run: truthlint report --format markdown -o truthlint-report.md
|
|
175
|
+
if: always()
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Exit codes: `0` success · `1` a `--fail-under` / `--fail-on` gate tripped · `2` usage error.
|
|
179
|
+
|
|
180
|
+
## Configuration
|
|
181
|
+
|
|
182
|
+
Add a `[tool.truthlint]` table to `pyproject.toml` or create `.truthlint.toml`:
|
|
183
|
+
|
|
184
|
+
```toml
|
|
185
|
+
[tool.truthlint]
|
|
186
|
+
exclude = ["examples/**", "vendor/"] # gitignore syntax, on top of .gitignore
|
|
187
|
+
disable = ["TL402"] # rule IDs to skip
|
|
188
|
+
ignore_env = ["MY_CI_ONLY_VAR"] # env vars that never need documenting
|
|
189
|
+
min_severity = "low" # hide findings below this severity
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Suppress a single finding inline with a comment on the same line (or a comment line directly above):
|
|
193
|
+
|
|
194
|
+
```markdown
|
|
195
|
+
<!-- truthlint-ignore TL302 -->
|
|
196
|
+
Generated files land in `build/output/report.html`.
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
make docs # truthlint-ignore TL202, TL203
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Accuracy principles
|
|
204
|
+
|
|
205
|
+
truthlint is built to be trusted in CI, so it prefers silence to guessing:
|
|
206
|
+
|
|
207
|
+
- **Evidence or nothing.** A finding can't be constructed without at least one file/line citation (enforced in code).
|
|
208
|
+
- **Unverifiable ≠ broken.** Anything that depends on the network or installed tools is reported as *unverified*.
|
|
209
|
+
- **Ambiguity is downgraded.** If a script exists in another package of a monorepo, or a Makefile uses pattern rules/includes, truthlint reports *unverified* instead of *missing*.
|
|
210
|
+
- **Generated paths are respected.** Missing files under `dist/`, `build/`, gitignored paths, or outputs of earlier documented steps (`cp`, `mkdir`, `python -m venv`) aren't reported.
|
|
211
|
+
- **Heuristic rules are opt-in.** Rules with a higher false-positive risk run only with `--deep`.
|
|
212
|
+
- **Malformed files are reported, not guessed at.** If a file can't be parsed, dependent checks are skipped and the file is listed under "Not analysed".
|
|
213
|
+
|
|
214
|
+
The bundled [`examples/healthy-library`](examples/healthy-library) must always score 100 with zero findings — it's part of the test suite.
|
|
215
|
+
|
|
216
|
+
## Architecture
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
┌──────────────────────────────────────────────────────────┐
|
|
220
|
+
repository ─▶ │ discovery own .gitignore engine, no git, no symlink │
|
|
221
|
+
│ escapes, size/binary limits │
|
|
222
|
+
└───────────────────────┬──────────────────────────────────┘
|
|
223
|
+
▼
|
|
224
|
+
┌──────────────────────────────────────────────────────────┐
|
|
225
|
+
│ RepoContext (lazy, cached) │
|
|
226
|
+
│ parsers: markdown · shell · pyproject/requirements · │
|
|
227
|
+
│ package.json · Dockerfile · compose · GH Actions · │
|
|
228
|
+
│ GitLab CI · Makefile · .env · Python AST · JS/TS │
|
|
229
|
+
└───────┬───────────────────────┬──────────────────────────┘
|
|
230
|
+
▼ ▼
|
|
231
|
+
┌──────────────────────┐ ┌──────────────────────────────────────┐
|
|
232
|
+
│ intel: profile │ │ checks → Findings (pydantic models) │
|
|
233
|
+
│ languages, tooling │ │ runtime · commands · docs · env · │
|
|
234
|
+
└──────────┬───────────┘ │ deps · containers · CI · build │
|
|
235
|
+
│ └──────────────┬───────────────────────┘
|
|
236
|
+
│ ▼
|
|
237
|
+
│ onboarding audit · suppression · scoring
|
|
238
|
+
▼ ▼
|
|
239
|
+
┌──────────────────────────────────────────────────────────┐
|
|
240
|
+
│ ScanResult → terminal (Rich) · Markdown · JSON · patches │
|
|
241
|
+
└──────────────────────────────────────────────────────────┘
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
| Module | Responsibility |
|
|
245
|
+
|---|---|
|
|
246
|
+
| `src/truthlint/context.py` | File discovery and the lazily-parsed, cached view every rule reads from |
|
|
247
|
+
| `src/truthlint/ignore.py` | `.gitignore` / `.dockerignore` matcher (git semantics, no subprocess) |
|
|
248
|
+
| `src/truthlint/parsers/` | One parser per format; all line-number aware; none execute code |
|
|
249
|
+
| `src/truthlint/intel.py` | Repository intelligence: languages, frameworks, package managers, CI, key files |
|
|
250
|
+
| `src/truthlint/rules/` | The checks. `commands.py` hosts the static command verifier shared by docs, CI, Makefiles and npm scripts |
|
|
251
|
+
| `src/truthlint/onboarding.py` | Setup-instruction audit and onboarding score |
|
|
252
|
+
| `src/truthlint/scoring.py` | Explainable Truth Score |
|
|
253
|
+
| `src/truthlint/fixes.py` | Edit validation, unified diffs, consent-gated application |
|
|
254
|
+
| `src/truthlint/render/` | Rich terminal UI, Markdown and JSON reports |
|
|
255
|
+
| `src/truthlint/cli.py` | Typer commands |
|
|
256
|
+
|
|
257
|
+
More detail in [docs/architecture.md](docs/architecture.md), including how to write a new rule.
|
|
258
|
+
|
|
259
|
+
## Contributing
|
|
260
|
+
|
|
261
|
+
Contributions are very welcome — especially new rules, parsers for more ecosystems, and **false-positive
|
|
262
|
+
reports** (there's a dedicated issue template). See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
pip install -e ".[dev]"
|
|
266
|
+
pytest
|
|
267
|
+
ruff check src tests
|
|
268
|
+
ruff format --check src tests
|
|
269
|
+
truthlint scan . # truthlint dogfoods itself
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Roadmap
|
|
273
|
+
|
|
274
|
+
- More ecosystems: Go modules, Cargo, Gemfile, Gradle/Maven version checks
|
|
275
|
+
- `truthlint scan --diff origin/main` to report only findings introduced by a change
|
|
276
|
+
- SARIF output for GitHub code scanning
|
|
277
|
+
- Verifying CLI sub-commands documented in the README against argparse/Typer/Click definitions
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# truthlint
|
|
4
|
+
|
|
5
|
+
### Your repository is lying. truthlint finds where.
|
|
6
|
+
|
|
7
|
+
**Find every place where your repository's docs, configs, CI, containers and code contradict each other.**
|
|
8
|
+
|
|
9
|
+
[](https://github.com/harshith7002/truthlint/actions/workflows/ci.yml)
|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<!-- truthlint-ignore TL101: the next line is an illustration, not a requirement -->
|
|
19
|
+
Your README says **Python 3.8+**. `pyproject.toml` says `>=3.10`. The Dockerfile runs **3.9**. CI tests a version
|
|
20
|
+
users can't even install on. The setup guide tells newcomers to run `make lint` — a target that was renamed a year
|
|
21
|
+
ago. The code reads `JWT_SECRET`, which is nowhere in `.env.example`.
|
|
22
|
+
|
|
23
|
+
Every one of those files is individually "fine". Together they lie.
|
|
24
|
+
|
|
25
|
+
**truthlint** reads a repository the way a careful new contributor would — then cross-examines every file against every
|
|
26
|
+
other file and reports, with evidence, where the project's **claims** and its **reality** disagree.
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
HIGH TL101-406201 Python version contradiction high confidence
|
|
30
|
+
README.md says Python 3.8+ is enough, but the project requires >=3.10
|
|
31
|
+
(minimum 3.10). Users following the docs on Python 3.8 will be refused by pip.
|
|
32
|
+
↳ README.md:8 claims Python 3.8+
|
|
33
|
+
↳ pyproject.toml:9 declares requires-python '>=3.10'
|
|
34
|
+
Fix: State Python 3.10+ in README.md, or raise the constraint. [auto-fix available]
|
|
35
|
+
|
|
36
|
+
HIGH TL202-6F78F4 Command runs an undefined make target high confidence
|
|
37
|
+
`README.md` documents `make lint`, but target `lint` is not defined in
|
|
38
|
+
`Makefile` (defined: format, serve, test).
|
|
39
|
+
↳ README.md:28 documents `make lint`
|
|
40
|
+
↳ Makefile defines targets: format, serve, test
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Why truthlint?
|
|
44
|
+
|
|
45
|
+
Linters check one file at a time. Type checkers check code. Link checkers check URLs. **Nothing checks that the
|
|
46
|
+
files agree with each other** — and that is exactly where onboarding breaks, deployments drift and "works on my
|
|
47
|
+
machine" is born.
|
|
48
|
+
|
|
49
|
+
| | |
|
|
50
|
+
|---|---|
|
|
51
|
+
| 🔎 **Cross-file analysis** | 35 rules compare README, docs, `pyproject.toml`, `requirements*.txt`, `package.json`, lockfiles, Dockerfiles, compose files, GitHub Actions / GitLab CI, Makefiles, `.env` examples and source code. |
|
|
52
|
+
| 🧾 **Evidence, always** | Every finding cites the exact file and line on *each side* of the contradiction, a confidence level, and how it was verified. truthlint never reports something it didn't check. |
|
|
53
|
+
| 🧭 **Onboarding audit** | Walks your setup instructions step by step (tracking `cd`, `cp`, `venv` …) and verifies each command statically: does that npm script, make target, file, compose service or pip extra actually exist? |
|
|
54
|
+
| 📊 **Explainable scores** | A 0–100 **Repository Truth Score** and an **Onboarding Reliability Score**, with every deduction itemised. |
|
|
55
|
+
| 🩹 **Reviewable fixes** | Suggested fix for every finding; many come with a unified-diff patch. Nothing is written without your explicit confirmation. |
|
|
56
|
+
| 🔒 **Safe by construction** | Fully offline, no API keys, no AI service. **Never executes anything** from the repository — not even `git` (a repo's `.git/config` can make git run commands). |
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
Requires Python **3.11+**.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pipx install truthlint # recommended: isolated CLI
|
|
64
|
+
# or
|
|
65
|
+
pip install truthlint
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
From source:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/harshith7002/truthlint.git
|
|
72
|
+
cd truthlint
|
|
73
|
+
pip install -e ".[dev]"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This installs the `truthlint` command (`python -m truthlint` works too).
|
|
77
|
+
|
|
78
|
+
## Quick start
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
truthlint scan . # investigate the current repository
|
|
82
|
+
truthlint scan . --deep # + heuristic checks (unused deps/env vars, stale doc paths, CI coverage)
|
|
83
|
+
truthlint explain TL202-6F78F4 # evidence in context, rationale and the proposed patch
|
|
84
|
+
truthlint fix # preview every available patch (dry run)
|
|
85
|
+
truthlint fix --interactive # review and apply patches one by one
|
|
86
|
+
truthlint report --format markdown -o truthlint-report.md
|
|
87
|
+
truthlint report --format json -o truthlint.json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Try it on the bundled sample repositories:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
truthlint scan examples/drifting-python-api
|
|
94
|
+
truthlint scan examples/drifting-node-service --deep
|
|
95
|
+
truthlint scan examples/healthy-library # 100/100, zero findings
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## What truthlint detects
|
|
99
|
+
|
|
100
|
+
Run `truthlint rules` for the full list, or `truthlint explain TL201` for details on any rule. See [docs/rules.md](docs/rules.md).
|
|
101
|
+
|
|
102
|
+
| Area | Examples |
|
|
103
|
+
|---|---|
|
|
104
|
+
| **Runtime versions** | README / Dockerfile / CI matrix / `.python-version` / `.nvmrc` / classifiers / `ruff target-version` vs `requires-python` or `engines.node`; container runtime drifting from the local pin; declared minimum never tested in CI |
|
|
105
|
+
| **Documented commands** | `npm run X`, `yarn X`, `pnpm X` with no such script · `make X` with no such target · scripts, requirements files, directories and modules that don't exist · `pip install ".[dev]"` with no `dev` extra · `docker compose up svc` with no such service · `npm install` in a pnpm-locked repo · `poetry install` in a non-Poetry project · documented CLI never declared as an entry point |
|
|
106
|
+
| **Documentation** | Broken relative links and heading anchors (case-exact, like GitHub) · outdated project-structure trees · inline references to files that no longer exist |
|
|
107
|
+
| **Environment** | Variables read in code (Python AST incl. pydantic-settings, JS/TS, Go, Ruby, Rust, Java, PHP, C#, Elixir, Prisma, shell) but missing from `.env.example`; documented variables nothing reads; no example file at all |
|
|
108
|
+
| **Dependencies** | Imports not declared in any manifest (works only on the author's machine) · declared deps never imported · runtime code importing dev-only deps — for Python and JS/TS |
|
|
109
|
+
| **Containers** | `COPY` of missing files or files excluded by `.dockerignore` · Docker pinning different versions than the manifest · lockfile not copied before install · `CMD` pointing at missing modules · compose build contexts, Dockerfiles, `env_file`s and `depends_on` that don't exist |
|
|
110
|
+
| **CI** | Workflow `run:` steps calling missing scripts/targets/files · missing `working-directory` · missing local actions |
|
|
111
|
+
| **Build metadata** | Malformed TOML/JSON/YAML · console scripts pointing to missing modules/functions · `readme`, `main`, `bin` files that don't exist |
|
|
112
|
+
| **Onboarding** | No setup instructions · required runtimes/package managers never mentioned · env configuration never explained |
|
|
113
|
+
|
|
114
|
+
## Scores
|
|
115
|
+
|
|
116
|
+
**Repository Truth Score (0–100).** Each finding contributes `severity weight × confidence`
|
|
117
|
+
(critical 20 · high 10 · medium 5 · low 1.5; confidence 1.0 / 0.7 / 0.4). Repeats of the same rule count half as
|
|
118
|
+
much each time and each rule is capped at 25 points, so one noisy area can't dominate. The score is
|
|
119
|
+
`100 × e^(−points/90)`. Every deduction is listed in the Markdown/JSON report.
|
|
120
|
+
|
|
121
|
+
**Onboarding Reliability Score (0–100).** Starts at 100: −40 if there are no runnable setup instructions, −15 per
|
|
122
|
+
documented step that fails verification (max −60), −8 per required tool the docs never mention, −5 if
|
|
123
|
+
environment configuration is never explained. Steps that would need execution to verify (e.g. `git clone`,
|
|
124
|
+
`pytest`) are reported as *unverified* and never counted as passing **or** failing.
|
|
125
|
+
|
|
126
|
+
## Using truthlint in CI
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
# .github/workflows/truthlint.yml
|
|
130
|
+
name: truthlint
|
|
131
|
+
on: [pull_request]
|
|
132
|
+
jobs:
|
|
133
|
+
truthlint:
|
|
134
|
+
runs-on: ubuntu-latest
|
|
135
|
+
steps:
|
|
136
|
+
- uses: actions/checkout@v4
|
|
137
|
+
- uses: actions/setup-python@v5
|
|
138
|
+
with:
|
|
139
|
+
python-version: "3.12"
|
|
140
|
+
- run: pip install truthlint
|
|
141
|
+
- run: truthlint scan . --fail-on high # fail on high/critical contradictions
|
|
142
|
+
- run: truthlint report --format markdown -o truthlint-report.md
|
|
143
|
+
if: always()
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Exit codes: `0` success · `1` a `--fail-under` / `--fail-on` gate tripped · `2` usage error.
|
|
147
|
+
|
|
148
|
+
## Configuration
|
|
149
|
+
|
|
150
|
+
Add a `[tool.truthlint]` table to `pyproject.toml` or create `.truthlint.toml`:
|
|
151
|
+
|
|
152
|
+
```toml
|
|
153
|
+
[tool.truthlint]
|
|
154
|
+
exclude = ["examples/**", "vendor/"] # gitignore syntax, on top of .gitignore
|
|
155
|
+
disable = ["TL402"] # rule IDs to skip
|
|
156
|
+
ignore_env = ["MY_CI_ONLY_VAR"] # env vars that never need documenting
|
|
157
|
+
min_severity = "low" # hide findings below this severity
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Suppress a single finding inline with a comment on the same line (or a comment line directly above):
|
|
161
|
+
|
|
162
|
+
```markdown
|
|
163
|
+
<!-- truthlint-ignore TL302 -->
|
|
164
|
+
Generated files land in `build/output/report.html`.
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
make docs # truthlint-ignore TL202, TL203
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Accuracy principles
|
|
172
|
+
|
|
173
|
+
truthlint is built to be trusted in CI, so it prefers silence to guessing:
|
|
174
|
+
|
|
175
|
+
- **Evidence or nothing.** A finding can't be constructed without at least one file/line citation (enforced in code).
|
|
176
|
+
- **Unverifiable ≠ broken.** Anything that depends on the network or installed tools is reported as *unverified*.
|
|
177
|
+
- **Ambiguity is downgraded.** If a script exists in another package of a monorepo, or a Makefile uses pattern rules/includes, truthlint reports *unverified* instead of *missing*.
|
|
178
|
+
- **Generated paths are respected.** Missing files under `dist/`, `build/`, gitignored paths, or outputs of earlier documented steps (`cp`, `mkdir`, `python -m venv`) aren't reported.
|
|
179
|
+
- **Heuristic rules are opt-in.** Rules with a higher false-positive risk run only with `--deep`.
|
|
180
|
+
- **Malformed files are reported, not guessed at.** If a file can't be parsed, dependent checks are skipped and the file is listed under "Not analysed".
|
|
181
|
+
|
|
182
|
+
The bundled [`examples/healthy-library`](examples/healthy-library) must always score 100 with zero findings — it's part of the test suite.
|
|
183
|
+
|
|
184
|
+
## Architecture
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
┌──────────────────────────────────────────────────────────┐
|
|
188
|
+
repository ─▶ │ discovery own .gitignore engine, no git, no symlink │
|
|
189
|
+
│ escapes, size/binary limits │
|
|
190
|
+
└───────────────────────┬──────────────────────────────────┘
|
|
191
|
+
▼
|
|
192
|
+
┌──────────────────────────────────────────────────────────┐
|
|
193
|
+
│ RepoContext (lazy, cached) │
|
|
194
|
+
│ parsers: markdown · shell · pyproject/requirements · │
|
|
195
|
+
│ package.json · Dockerfile · compose · GH Actions · │
|
|
196
|
+
│ GitLab CI · Makefile · .env · Python AST · JS/TS │
|
|
197
|
+
└───────┬───────────────────────┬──────────────────────────┘
|
|
198
|
+
▼ ▼
|
|
199
|
+
┌──────────────────────┐ ┌──────────────────────────────────────┐
|
|
200
|
+
│ intel: profile │ │ checks → Findings (pydantic models) │
|
|
201
|
+
│ languages, tooling │ │ runtime · commands · docs · env · │
|
|
202
|
+
└──────────┬───────────┘ │ deps · containers · CI · build │
|
|
203
|
+
│ └──────────────┬───────────────────────┘
|
|
204
|
+
│ ▼
|
|
205
|
+
│ onboarding audit · suppression · scoring
|
|
206
|
+
▼ ▼
|
|
207
|
+
┌──────────────────────────────────────────────────────────┐
|
|
208
|
+
│ ScanResult → terminal (Rich) · Markdown · JSON · patches │
|
|
209
|
+
└──────────────────────────────────────────────────────────┘
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
| Module | Responsibility |
|
|
213
|
+
|---|---|
|
|
214
|
+
| `src/truthlint/context.py` | File discovery and the lazily-parsed, cached view every rule reads from |
|
|
215
|
+
| `src/truthlint/ignore.py` | `.gitignore` / `.dockerignore` matcher (git semantics, no subprocess) |
|
|
216
|
+
| `src/truthlint/parsers/` | One parser per format; all line-number aware; none execute code |
|
|
217
|
+
| `src/truthlint/intel.py` | Repository intelligence: languages, frameworks, package managers, CI, key files |
|
|
218
|
+
| `src/truthlint/rules/` | The checks. `commands.py` hosts the static command verifier shared by docs, CI, Makefiles and npm scripts |
|
|
219
|
+
| `src/truthlint/onboarding.py` | Setup-instruction audit and onboarding score |
|
|
220
|
+
| `src/truthlint/scoring.py` | Explainable Truth Score |
|
|
221
|
+
| `src/truthlint/fixes.py` | Edit validation, unified diffs, consent-gated application |
|
|
222
|
+
| `src/truthlint/render/` | Rich terminal UI, Markdown and JSON reports |
|
|
223
|
+
| `src/truthlint/cli.py` | Typer commands |
|
|
224
|
+
|
|
225
|
+
More detail in [docs/architecture.md](docs/architecture.md), including how to write a new rule.
|
|
226
|
+
|
|
227
|
+
## Contributing
|
|
228
|
+
|
|
229
|
+
Contributions are very welcome — especially new rules, parsers for more ecosystems, and **false-positive
|
|
230
|
+
reports** (there's a dedicated issue template). See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
pip install -e ".[dev]"
|
|
234
|
+
pytest
|
|
235
|
+
ruff check src tests
|
|
236
|
+
ruff format --check src tests
|
|
237
|
+
truthlint scan . # truthlint dogfoods itself
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Roadmap
|
|
241
|
+
|
|
242
|
+
- More ecosystems: Go modules, Cargo, Gemfile, Gradle/Maven version checks
|
|
243
|
+
- `truthlint scan --diff origin/main` to report only findings introduced by a change
|
|
244
|
+
- SARIF output for GitHub code scanning
|
|
245
|
+
- Verifying CLI sub-commands documented in the README against argparse/Typer/Click definitions
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "truthlint"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Find where your repository contradicts itself: docs, configs, CI, containers and code checked against each other, offline."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "truthlint contributors" }]
|
|
14
|
+
keywords = [
|
|
15
|
+
"static-analysis",
|
|
16
|
+
"documentation",
|
|
17
|
+
"consistency",
|
|
18
|
+
"developer-tools",
|
|
19
|
+
"linter",
|
|
20
|
+
"onboarding",
|
|
21
|
+
"repository",
|
|
22
|
+
"drift",
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 4 - Beta",
|
|
26
|
+
"Environment :: Console",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
34
|
+
"Topic :: Software Development :: Documentation",
|
|
35
|
+
]
|
|
36
|
+
dependencies = [
|
|
37
|
+
"typer>=0.12",
|
|
38
|
+
"rich>=13.7",
|
|
39
|
+
"pydantic>=2.6",
|
|
40
|
+
"PyYAML>=6.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.optional-dependencies]
|
|
44
|
+
dev = ["pytest>=8.0", "pytest-cov>=5.0", "ruff>=0.6"]
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
truthlint = "truthlint.cli:main"
|
|
48
|
+
|
|
49
|
+
[project.urls]
|
|
50
|
+
Homepage = "https://github.com/harshith7002/truthlint"
|
|
51
|
+
Issues = "https://github.com/harshith7002/truthlint/issues"
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.packages.find]
|
|
54
|
+
where = ["src"]
|
|
55
|
+
|
|
56
|
+
[tool.setuptools.package-data]
|
|
57
|
+
truthlint = ["py.typed"]
|
|
58
|
+
|
|
59
|
+
[tool.ruff]
|
|
60
|
+
line-length = 100
|
|
61
|
+
target-version = "py311"
|
|
62
|
+
src = ["src", "tests"]
|
|
63
|
+
extend-exclude = ["examples"]
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint]
|
|
66
|
+
select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF", "C4"]
|
|
67
|
+
ignore = ["E501", "RUF001", "RUF002", "RUF003", "SIM108"]
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint.per-file-ignores]
|
|
70
|
+
"tests/**" = ["B011"]
|
|
71
|
+
|
|
72
|
+
[tool.pytest.ini_options]
|
|
73
|
+
testpaths = ["tests"]
|
|
74
|
+
addopts = "-q"
|
|
75
|
+
|
|
76
|
+
# truthlint dogfoods itself. The sample repositories contain deliberate inconsistencies.
|
|
77
|
+
[tool.truthlint]
|
|
78
|
+
exclude = ["examples/**", "tests/fixtures/**"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""truthlint - find where your repository contradicts itself.
|
|
2
|
+
|
|
3
|
+
Finds the places where a repository's documentation, configuration, CI,
|
|
4
|
+
containers and code contradict each other. Fully offline; never executes
|
|
5
|
+
anything from the repository it inspects.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
|
|
10
|
+
__all__ = ["__version__"]
|