repofail 0.2.2__tar.gz → 0.2.4__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.
Files changed (66) hide show
  1. repofail-0.2.4/.github/actions/repofail/README.md +45 -0
  2. repofail-0.2.4/.github/actions/repofail/action.yml +73 -0
  3. repofail-0.2.4/.gitignore +22 -0
  4. repofail-0.2.4/Formula/repofail.rb +66 -0
  5. {repofail-0.2.2 → repofail-0.2.4}/PKG-INFO +114 -7
  6. {repofail-0.2.2 → repofail-0.2.4}/README.md +108 -6
  7. repofail-0.2.4/docs/QUICKSTART.md +42 -0
  8. repofail-0.2.4/homebrew-tap/Formula/repofail.rb +66 -0
  9. repofail-0.2.4/install.sh +21 -0
  10. {repofail-0.2.2 → repofail-0.2.4}/pyproject.toml +11 -1
  11. repofail-0.2.4/repofail/__init__.py +8 -0
  12. {repofail-0.2.2 → repofail-0.2.4}/repofail/cli.py +3 -2
  13. repofail-0.2.2/.gitignore +0 -8
  14. repofail-0.2.2/repofail/__init__.py +0 -3
  15. {repofail-0.2.2 → repofail-0.2.4}/.github/workflows/publish.yml +0 -0
  16. {repofail-0.2.2 → repofail-0.2.4}/.github/workflows/repofail.yml +0 -0
  17. {repofail-0.2.2 → repofail-0.2.4}/CONTRIBUTING.md +0 -0
  18. {repofail-0.2.2 → repofail-0.2.4}/RULES.md +0 -0
  19. {repofail-0.2.2 → repofail-0.2.4}/contract.json +0 -0
  20. {repofail-0.2.2 → repofail-0.2.4}/docs/assets/demo-autogpt.png +0 -0
  21. {repofail-0.2.2 → repofail-0.2.4}/docs/assets/repofail-logo.png +0 -0
  22. {repofail-0.2.2 → repofail-0.2.4}/docs/logo.png +0 -0
  23. {repofail-0.2.2 → repofail-0.2.4}/docs/screenshots/high_failure.png +0 -0
  24. {repofail-0.2.2 → repofail-0.2.4}/docs/screenshots/nodefail.gif +0 -0
  25. {repofail-0.2.2 → repofail-0.2.4}/repofail/contract.py +0 -0
  26. {repofail-0.2.2 → repofail-0.2.4}/repofail/engine.py +0 -0
  27. {repofail-0.2.2 → repofail-0.2.4}/repofail/fleet.py +0 -0
  28. {repofail-0.2.2 → repofail-0.2.4}/repofail/format.py +0 -0
  29. {repofail-0.2.2 → repofail-0.2.4}/repofail/models.py +0 -0
  30. {repofail-0.2.2 → repofail-0.2.4}/repofail/risk.py +0 -0
  31. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/__init__.py +0 -0
  32. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/abi_wheel_mismatch.py +0 -0
  33. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/apple_silicon.py +0 -0
  34. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/base.py +0 -0
  35. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/docker_only.py +0 -0
  36. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/gpu_memory.py +0 -0
  37. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/info_signals.py +0 -0
  38. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/lock_file_missing.py +0 -0
  39. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/ml_niche.py +0 -0
  40. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/native_toolchain.py +0 -0
  41. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/node_engine.py +0 -0
  42. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/node_eol.py +0 -0
  43. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/node_windows.py +0 -0
  44. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/port_collision.py +0 -0
  45. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/python_eol.py +0 -0
  46. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/python_version.py +0 -0
  47. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/registry.py +0 -0
  48. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/spec_drift.py +0 -0
  49. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/system_libs.py +0 -0
  50. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/torch_cuda.py +0 -0
  51. {repofail-0.2.2 → repofail-0.2.4}/repofail/rules/yaml_loader.py +0 -0
  52. {repofail-0.2.2 → repofail-0.2.4}/repofail/scanner/__init__.py +0 -0
  53. {repofail-0.2.2 → repofail-0.2.4}/repofail/scanner/ast_scan.py +0 -0
  54. {repofail-0.2.2 → repofail-0.2.4}/repofail/scanner/host.py +0 -0
  55. {repofail-0.2.2 → repofail-0.2.4}/repofail/scanner/parsers.py +0 -0
  56. {repofail-0.2.2 → repofail-0.2.4}/repofail/scanner/repo.py +0 -0
  57. {repofail-0.2.2 → repofail-0.2.4}/repofail/telemetry.py +0 -0
  58. {repofail-0.2.2 → repofail-0.2.4}/tests/__init__.py +0 -0
  59. {repofail-0.2.2 → repofail-0.2.4}/tests/test_cli_stage2.py +0 -0
  60. {repofail-0.2.2 → repofail-0.2.4}/tests/test_contract.py +0 -0
  61. {repofail-0.2.2 → repofail-0.2.4}/tests/test_fleet.py +0 -0
  62. {repofail-0.2.2 → repofail-0.2.4}/tests/test_integration.py +0 -0
  63. {repofail-0.2.2 → repofail-0.2.4}/tests/test_ml_rules.py +0 -0
  64. {repofail-0.2.2 → repofail-0.2.4}/tests/test_rules.py +0 -0
  65. {repofail-0.2.2 → repofail-0.2.4}/tests/test_scanner.py +0 -0
  66. {repofail-0.2.2 → repofail-0.2.4}/tests/test_telemetry.py +0 -0
@@ -0,0 +1,45 @@
1
+ # repofail GitHub Action
2
+
3
+ Run repofail on every PR: get a Markdown report as a comment and fail CI when compatibility violations exceed the threshold.
4
+
5
+ ## Usage
6
+
7
+ In your repo, create `.github/workflows/repofail.yml`:
8
+
9
+ ```yaml
10
+ name: repofail
11
+ on:
12
+ pull_request:
13
+ branches: [main, master]
14
+ jobs:
15
+ check:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: jayvenn21/repofail/.github/actions/repofail@main
20
+ with:
21
+ path: .
22
+ fail_on: HIGH
23
+ comment: 'true'
24
+ upload_artifact: 'true'
25
+ pr_number: ${{ github.event.pull_request.number }}
26
+ ```
27
+
28
+ ## Inputs
29
+
30
+ | Input | Default | Description |
31
+ |-------|---------|-------------|
32
+ | `path` | `.` | Repository path to scan |
33
+ | `fail_on` | `HIGH` | Fail CI when severity is HIGH, MEDIUM, or LOW |
34
+ | `comment` | `true` | Comment on PR with Markdown report |
35
+ | `upload_artifact` | `true` | Upload JSON report as workflow artifact |
36
+ | `pr_number` | (empty) | Set to `${{ github.event.pull_request.number }}` for PR comments |
37
+
38
+ ## Behavior
39
+
40
+ 1. Installs repofail from PyPI
41
+ 2. Runs `repofail -m` and saves the report
42
+ 3. Runs `repofail -j` for machine-readable output
43
+ 4. Comments on the PR with the Markdown report (if `comment: true` and `pr_number` set)
44
+ 5. Uploads `repofail.json` as an artifact (if `upload_artifact: true`)
45
+ 6. Runs `repofail --ci` and fails the job if violations meet or exceed `fail_on`
@@ -0,0 +1,73 @@
1
+ name: repofail
2
+ description: Deterministic runtime compatibility check — fail CI or comment on PR with report
3
+ inputs:
4
+ path:
5
+ description: Repository path to scan (default .)
6
+ required: false
7
+ default: .
8
+ fail_on:
9
+ description: Severity threshold to fail CI (HIGH, MEDIUM, LOW)
10
+ required: false
11
+ default: HIGH
12
+ comment:
13
+ description: Comment on PR with Markdown report
14
+ required: false
15
+ default: 'true'
16
+ upload_artifact:
17
+ description: Upload JSON report as artifact
18
+ required: false
19
+ default: 'true'
20
+ pr_number:
21
+ description: Pull request number (for commenting). Set to ${{ github.event.pull_request.number }}
22
+ required: false
23
+ default: ''
24
+ outputs:
25
+ markdown_file:
26
+ description: Path to Markdown report file (repofail-report.md)
27
+ runs:
28
+ using: composite
29
+ steps:
30
+ - name: Install repofail
31
+ shell: bash
32
+ run: pip install repofail
33
+
34
+ - name: Run repofail (Markdown)
35
+ id: md
36
+ shell: bash
37
+ run: |
38
+ repofail -p "${{ inputs.path }}" -m > repofail-report.md || true
39
+ echo "markdown_file=repofail-report.md" >> $GITHUB_OUTPUT
40
+
41
+ - name: Run repofail (JSON)
42
+ if: inputs.upload_artifact == 'true'
43
+ shell: bash
44
+ run: repofail -p "${{ inputs.path }}" -j > repofail.json || true
45
+
46
+ - name: Comment on PR with report
47
+ if: inputs.comment == 'true' && inputs.pr_number != ''
48
+ uses: actions/github-script@v7
49
+ with:
50
+ github-token: ${{ secrets.GITHUB_TOKEN }}
51
+ script: |
52
+ const fs = require('fs');
53
+ const body = fs.existsSync('repofail-report.md')
54
+ ? fs.readFileSync('repofail-report.md', 'utf8')
55
+ : 'repofail report not available.';
56
+ const comment = `## repofail · compatibility report\n\n\`\`\`\n${body}\n\`\`\``;
57
+ await github.rest.issues.createComment({
58
+ issue_number: ${{ inputs.pr_number }},
59
+ owner: context.repo.owner,
60
+ repo: context.repo.repo,
61
+ body: comment,
62
+ });
63
+
64
+ - name: Upload repofail JSON artifact
65
+ if: inputs.upload_artifact == 'true' && always()
66
+ uses: actions/upload-artifact@v4
67
+ with:
68
+ name: repofail-report
69
+ path: repofail.json
70
+
71
+ - name: Run repofail (CI — fail if threshold exceeded)
72
+ shell: bash
73
+ run: repofail -p "${{ inputs.path }}" --ci --fail-on "${{ inputs.fail_on }}"
@@ -0,0 +1,22 @@
1
+ dist/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ *.egg-info/
6
+ .eggs/
7
+ .venv/
8
+ .venv-check/
9
+ venv/
10
+ testenv/
11
+ VISION.md
12
+ repofail-contract.json
13
+
14
+ # Sensitive / local-only (never commit)
15
+ .env
16
+ .env.local
17
+ .env.*.local
18
+ .secrets
19
+ secrets.yaml
20
+ .credentials
21
+ *.pem
22
+ .DS_Store
@@ -0,0 +1,66 @@
1
+ # Homebrew formula for repofail
2
+ # To use: copy to your tap repo (e.g. homebrew-tap/Formula/repofail.rb)
3
+ # Then: brew tap jayvenn21/tap && brew install jayvenn21/tap/repofail
4
+ #
5
+ # Homebrew uses --no-deps when installing the main package, so all Python
6
+ # dependencies must be declared as resources below.
7
+ # If SHA256 fails after pushing a new tag, get the correct hash with:
8
+ # curl -sL https://github.com/jayvenn21/repofail/archive/refs/tags/vX.Y.Z.tar.gz | shasum -a 256
9
+
10
+ class Repofail < Formula
11
+ include Language::Python::Virtualenv
12
+
13
+ desc "Deterministic runtime compatibility analyzer"
14
+ homepage "https://github.com/jayvenn21/repofail"
15
+ url "https://github.com/jayvenn21/repofail/archive/refs/tags/v0.2.4.tar.gz"
16
+ sha256 "d5558cd419c8d46bdc958064cb97f963d1ea793866414c025906ec15033512ed"
17
+ license "MIT"
18
+
19
+ # App supports Python 3.10+. We use 3.12 for current Homebrew; change to
20
+ # python@3.10 or python@3.11 if you need an older interpreter.
21
+ depends_on "libyaml"
22
+ depends_on "python@3.12"
23
+
24
+ resource "click" do
25
+ url "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz"
26
+ sha256 "12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"
27
+ end
28
+
29
+ resource "typer" do
30
+ url "https://files.pythonhosted.org/packages/ac/0a/d55af35db5f50f486e3eda0ada747eed773859e2699d3ce570b682a9b70a/typer-0.12.3.tar.gz"
31
+ sha256 "49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"
32
+ end
33
+
34
+ resource "rich" do
35
+ url "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz"
36
+ sha256 "439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"
37
+ end
38
+
39
+ resource "shellingham" do
40
+ url "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz"
41
+ sha256 "8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"
42
+ end
43
+
44
+ resource "typing-extensions" do
45
+ url "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz"
46
+ sha256 "1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
47
+ end
48
+
49
+ resource "tomli" do
50
+ url "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz"
51
+ sha256 "d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"
52
+ end
53
+
54
+ resource "pyyaml" do
55
+ url "https://github.com/yaml/pyyaml/archive/refs/tags/6.0.1.tar.gz"
56
+ sha256 "57314c984aaa84318eed00cf5a8365afc49f87954969e295efe2ba99f3b21f7a"
57
+ end
58
+
59
+ def install
60
+ virtualenv_install_with_resources
61
+ end
62
+
63
+ test do
64
+ assert_match "repofail", shell_output("#{bin}/repofail --help", 0)
65
+ end
66
+ end
@@ -1,8 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repofail
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Predict why a repository will fail on your machine before you run it.
5
5
  Author: Jayanth
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.10
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
6
11
  Requires-Python: >=3.10
7
12
  Requires-Dist: pyyaml>=6.0
8
13
  Requires-Dist: tomli>=2.0; python_version < '3.11'
@@ -12,7 +17,7 @@ Requires-Dist: pytest>=7.0; extra == 'dev'
12
17
  Description-Content-Type: text/markdown
13
18
 
14
19
  <p align="center">
15
- <img src="https://raw.githubusercontent.com/jayvenn21/repofail/v0.2.1/docs/logo.png" width="180" alt="repofail logo">
20
+ <img src="https://raw.githubusercontent.com/jayvenn21/repofail/main/docs/logo.png" width="180" alt="repofail logo">
16
21
  </p>
17
22
 
18
23
  <h1 align="center">repofail</h1>
@@ -22,14 +27,16 @@ Description-Content-Type: text/markdown
22
27
  </p>
23
28
 
24
29
  <p align="center">
25
- <a href="https://pypi.org/project/repofail/"><img src="https://img.shields.io/pypi/v/repofail?cacheSeconds=30"></a>
30
+ <a href="https://pypi.org/project/repofail/"><img src="https://img.shields.io/pypi/v/repofail?color=orange" alt="PyPI"></a>
31
+ <img src="https://img.shields.io/pypi/pyversions/repofail" alt="Python">
32
+ <img src="https://img.shields.io/pypi/dm/repofail" alt="Downloads">
26
33
  <img src="https://img.shields.io/badge/ci-passing-brightgreen">
27
- <img src="https://img.shields.io/badge/python-3.10+-blue">
28
34
  <img src="https://img.shields.io/badge/runtime-validated-success">
29
35
  <img src="https://img.shields.io/badge/rules-20+-informational">
30
36
  </p>
31
37
 
32
38
  <p align="center">
39
+ <strong>The static analyzer for runtime compatibility.</strong><br>
33
40
  Predict why a repository will fail on your machine before you run it.
34
41
  </p>
35
42
 
@@ -46,11 +53,24 @@ Description-Content-Type: text/markdown
46
53
  <a href="#usage">Usage</a> ·
47
54
  <a href="#rules">Rules</a> ·
48
55
  <a href="#ci-integration">CI</a> ·
49
- <a href="#contracts">Contracts</a>
56
+ <a href="#contracts">Contracts</a> ·
57
+ <a href="#faq">FAQ</a>
50
58
  </p>
51
59
 
52
60
  ---
53
61
 
62
+ ## Quickstart (30 seconds)
63
+
64
+ ```bash
65
+ pip install repofail
66
+ cd /path/to/any/repo
67
+ repofail
68
+ ```
69
+
70
+ You get a compatibility score and a list of deterministic blockers (Node version, Python range, CUDA, lock file, spec drift, etc.). No install of the repo’s dependencies. No cloud. No AI.
71
+
72
+ ---
73
+
54
74
  ## Why This Exists
55
75
 
56
76
  Most tools install dependencies.
@@ -90,6 +110,18 @@ Deterministic spec violation detected — engines.node requires 22.x, host is 20
90
110
 
91
111
  ---
92
112
 
113
+ ## Case studies
114
+
115
+ | Scenario | Without repofail | With repofail |
116
+ |----------|------------------|----------------|
117
+ | **Node engine mismatch** | Clone → `npm install` → `EBADENGINE` → search, fix, retry | `repofail .` → "Node 22.x required, host is 20.x" + suggested fix in &lt;1s |
118
+ | **CUDA on laptop** | Clone → `pip install` → run → `RuntimeError: CUDA unavailable` | `repofail .` → "Hard-coded CUDA path, host has no GPU" before you run |
119
+ | **Spec drift (Python)** | CI passes, local fails; pyproject says 3.11, Docker uses 3.9 | `repofail .` → "Spec drift — 3 distinct Python targets" + where they differ |
120
+
121
+ Try the demos: [node engine](https://github.com/jayvenn21/repofail-demo-node-engine), [spec drift](https://github.com/jayvenn21/repofail-demo-spec-drift), [CUDA hardcoded](https://github.com/jayvenn21/repofail-demo-cuda-hardcoded).
122
+
123
+ ---
124
+
93
125
  ## Install
94
126
 
95
127
  **From PyPI (recommended)**
@@ -98,12 +130,25 @@ Deterministic spec violation detected — engines.node requires 22.x, host is 20
98
130
  pip install repofail
99
131
  ```
100
132
 
101
- Or with pipx (isolated CLI install):
133
+ **One-liner (curl)**
134
+
135
+ ```bash
136
+ curl -sSL https://raw.githubusercontent.com/jayvenn21/repofail/main/install.sh | bash
137
+ ```
138
+
139
+ **pipx (isolated CLI)**
102
140
 
103
141
  ```bash
104
142
  pipx install repofail
105
143
  ```
106
144
 
145
+ **Homebrew** (formula: [jayvenn21/homebrew-tap](https://github.com/jayvenn21/homebrew-tap))
146
+
147
+ ```bash
148
+ brew tap jayvenn21/tap
149
+ brew install jayvenn21/tap/repofail
150
+ ```
151
+
107
152
  **From source (development)**
108
153
 
109
154
  ```bash
@@ -142,10 +187,41 @@ repofail s # Stats: local failure counts (from -r reports)
142
187
  repofail s -j # Stats with JSON output
143
188
  ```
144
189
 
190
+ ## Exit codes
191
+
192
+ - **0** — No deterministic violations (or scan completed successfully)
193
+ - **1** — Violations detected (with `--ci`) or target host has issues (with `sim`)
194
+ - **2** — Invalid usage / bad input (e.g. not a directory, contract violation)
195
+
145
196
  ---
146
197
 
147
198
  ## CI integration
148
199
 
200
+ **Option A — Reusable action (comment on PR + fail CI)**
201
+
202
+ ```yaml
203
+ name: repofail
204
+ on:
205
+ pull_request:
206
+ branches: [main, master]
207
+ jobs:
208
+ check:
209
+ runs-on: ubuntu-latest
210
+ steps:
211
+ - uses: actions/checkout@v4
212
+ - uses: jayvenn21/repofail/.github/actions/repofail@main
213
+ with:
214
+ path: .
215
+ fail_on: HIGH
216
+ comment: 'true'
217
+ upload_artifact: 'true'
218
+ pr_number: ${{ github.event.pull_request.number }}
219
+ ```
220
+
221
+ The action installs repofail, runs a compatibility check, comments the Markdown report on the PR, uploads the JSON artifact, and fails the job if violations meet the threshold.
222
+
223
+ **Option B — Inline (no comment)**
224
+
149
225
  ```yaml
150
226
  - uses: actions/checkout@v4
151
227
  - uses: actions/setup-python@v5
@@ -164,7 +240,7 @@ repofail gen . -o contract.json
164
240
  repofail check contract.json
165
241
  ```
166
242
 
167
- Versioned runtime expectations. Teams share contracts. CI checks drift.
243
+ Versioned runtime expectations. Teams share contracts. CI checks drift. Generated contracts report the installed repofail version (from PyPI/Homebrew) so tooling stays traceable.
168
244
 
169
245
  ---
170
246
 
@@ -237,8 +313,39 @@ Extensible via `.repofail/rules.yaml`.
237
313
 
238
314
  ---
239
315
 
316
+ ## FAQ
317
+
318
+ **Does repofail install or run my project?**
319
+ No. It only reads configs and (optionally) inspects Python/JS for patterns. No `pip install`, no `npm install`, no execution.
320
+
321
+ **Does it need the internet?**
322
+ No. It runs fully offline. Host inspection uses local subprocesses (e.g. `node --version`).
323
+
324
+ **Why “deterministic”?**
325
+ Same repo + same host → same result. No ML, no heuristics that change between runs. Rules are based on config and code.
326
+
327
+ **Can I add my own rules?**
328
+ Yes. Put a `.repofail/rules.yaml` (or `repofail-rules.yaml`) in the repo and define conditions on `repo.*` and `host.*`. See `repofail -e list` for built-in rule IDs.
329
+
330
+ **What if my repo is clean?**
331
+ You get a high score (e.g. 96–100%) and “No deterministic blockers detected.” repofail does not invent problems.
332
+
333
+ ---
334
+
335
+ ## When not to use it
336
+
337
+ - **You need dependency resolution** — use pip, npm, poetry, etc. repofail does not install or resolve.
338
+ - **You need security scanning** — use Dependabot, Snyk, or similar. repofail is compatibility-only.
339
+ - **You want “AI suggested fixes”** — repofail gives deterministic, rule-based suggestions only.
340
+ - **You run only in one environment** — if every dev and CI use the same OS/runtime, the value is smaller (still useful for drift and contracts).
341
+
342
+ ---
343
+
240
344
  ## Testing
241
345
 
242
346
  ```bash
347
+ pip install -e ".[dev]"
243
348
  pytest tests/ -v
244
349
  ```
350
+
351
+ **Quick checks:** `bash -n install.sh` (syntax). The GitHub Action runs on every PR in this repo (see [.github/workflows/repofail.yml](.github/workflows/repofail.yml)); to test the reusable action, use it in another repo’s workflow on a PR.
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/jayvenn21/repofail/v0.2.1/docs/logo.png" width="180" alt="repofail logo">
2
+ <img src="https://raw.githubusercontent.com/jayvenn21/repofail/main/docs/logo.png" width="180" alt="repofail logo">
3
3
  </p>
4
4
 
5
5
  <h1 align="center">repofail</h1>
@@ -9,14 +9,16 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="https://pypi.org/project/repofail/"><img src="https://img.shields.io/pypi/v/repofail?cacheSeconds=30"></a>
12
+ <a href="https://pypi.org/project/repofail/"><img src="https://img.shields.io/pypi/v/repofail?color=orange" alt="PyPI"></a>
13
+ <img src="https://img.shields.io/pypi/pyversions/repofail" alt="Python">
14
+ <img src="https://img.shields.io/pypi/dm/repofail" alt="Downloads">
13
15
  <img src="https://img.shields.io/badge/ci-passing-brightgreen">
14
- <img src="https://img.shields.io/badge/python-3.10+-blue">
15
16
  <img src="https://img.shields.io/badge/runtime-validated-success">
16
17
  <img src="https://img.shields.io/badge/rules-20+-informational">
17
18
  </p>
18
19
 
19
20
  <p align="center">
21
+ <strong>The static analyzer for runtime compatibility.</strong><br>
20
22
  Predict why a repository will fail on your machine before you run it.
21
23
  </p>
22
24
 
@@ -33,11 +35,24 @@
33
35
  <a href="#usage">Usage</a> ·
34
36
  <a href="#rules">Rules</a> ·
35
37
  <a href="#ci-integration">CI</a> ·
36
- <a href="#contracts">Contracts</a>
38
+ <a href="#contracts">Contracts</a> ·
39
+ <a href="#faq">FAQ</a>
37
40
  </p>
38
41
 
39
42
  ---
40
43
 
44
+ ## Quickstart (30 seconds)
45
+
46
+ ```bash
47
+ pip install repofail
48
+ cd /path/to/any/repo
49
+ repofail
50
+ ```
51
+
52
+ You get a compatibility score and a list of deterministic blockers (Node version, Python range, CUDA, lock file, spec drift, etc.). No install of the repo’s dependencies. No cloud. No AI.
53
+
54
+ ---
55
+
41
56
  ## Why This Exists
42
57
 
43
58
  Most tools install dependencies.
@@ -77,6 +92,18 @@ Deterministic spec violation detected — engines.node requires 22.x, host is 20
77
92
 
78
93
  ---
79
94
 
95
+ ## Case studies
96
+
97
+ | Scenario | Without repofail | With repofail |
98
+ |----------|------------------|----------------|
99
+ | **Node engine mismatch** | Clone → `npm install` → `EBADENGINE` → search, fix, retry | `repofail .` → "Node 22.x required, host is 20.x" + suggested fix in &lt;1s |
100
+ | **CUDA on laptop** | Clone → `pip install` → run → `RuntimeError: CUDA unavailable` | `repofail .` → "Hard-coded CUDA path, host has no GPU" before you run |
101
+ | **Spec drift (Python)** | CI passes, local fails; pyproject says 3.11, Docker uses 3.9 | `repofail .` → "Spec drift — 3 distinct Python targets" + where they differ |
102
+
103
+ Try the demos: [node engine](https://github.com/jayvenn21/repofail-demo-node-engine), [spec drift](https://github.com/jayvenn21/repofail-demo-spec-drift), [CUDA hardcoded](https://github.com/jayvenn21/repofail-demo-cuda-hardcoded).
104
+
105
+ ---
106
+
80
107
  ## Install
81
108
 
82
109
  **From PyPI (recommended)**
@@ -85,12 +112,25 @@ Deterministic spec violation detected — engines.node requires 22.x, host is 20
85
112
  pip install repofail
86
113
  ```
87
114
 
88
- Or with pipx (isolated CLI install):
115
+ **One-liner (curl)**
116
+
117
+ ```bash
118
+ curl -sSL https://raw.githubusercontent.com/jayvenn21/repofail/main/install.sh | bash
119
+ ```
120
+
121
+ **pipx (isolated CLI)**
89
122
 
90
123
  ```bash
91
124
  pipx install repofail
92
125
  ```
93
126
 
127
+ **Homebrew** (formula: [jayvenn21/homebrew-tap](https://github.com/jayvenn21/homebrew-tap))
128
+
129
+ ```bash
130
+ brew tap jayvenn21/tap
131
+ brew install jayvenn21/tap/repofail
132
+ ```
133
+
94
134
  **From source (development)**
95
135
 
96
136
  ```bash
@@ -129,10 +169,41 @@ repofail s # Stats: local failure counts (from -r reports)
129
169
  repofail s -j # Stats with JSON output
130
170
  ```
131
171
 
172
+ ## Exit codes
173
+
174
+ - **0** — No deterministic violations (or scan completed successfully)
175
+ - **1** — Violations detected (with `--ci`) or target host has issues (with `sim`)
176
+ - **2** — Invalid usage / bad input (e.g. not a directory, contract violation)
177
+
132
178
  ---
133
179
 
134
180
  ## CI integration
135
181
 
182
+ **Option A — Reusable action (comment on PR + fail CI)**
183
+
184
+ ```yaml
185
+ name: repofail
186
+ on:
187
+ pull_request:
188
+ branches: [main, master]
189
+ jobs:
190
+ check:
191
+ runs-on: ubuntu-latest
192
+ steps:
193
+ - uses: actions/checkout@v4
194
+ - uses: jayvenn21/repofail/.github/actions/repofail@main
195
+ with:
196
+ path: .
197
+ fail_on: HIGH
198
+ comment: 'true'
199
+ upload_artifact: 'true'
200
+ pr_number: ${{ github.event.pull_request.number }}
201
+ ```
202
+
203
+ The action installs repofail, runs a compatibility check, comments the Markdown report on the PR, uploads the JSON artifact, and fails the job if violations meet the threshold.
204
+
205
+ **Option B — Inline (no comment)**
206
+
136
207
  ```yaml
137
208
  - uses: actions/checkout@v4
138
209
  - uses: actions/setup-python@v5
@@ -151,7 +222,7 @@ repofail gen . -o contract.json
151
222
  repofail check contract.json
152
223
  ```
153
224
 
154
- Versioned runtime expectations. Teams share contracts. CI checks drift.
225
+ Versioned runtime expectations. Teams share contracts. CI checks drift. Generated contracts report the installed repofail version (from PyPI/Homebrew) so tooling stays traceable.
155
226
 
156
227
  ---
157
228
 
@@ -224,8 +295,39 @@ Extensible via `.repofail/rules.yaml`.
224
295
 
225
296
  ---
226
297
 
298
+ ## FAQ
299
+
300
+ **Does repofail install or run my project?**
301
+ No. It only reads configs and (optionally) inspects Python/JS for patterns. No `pip install`, no `npm install`, no execution.
302
+
303
+ **Does it need the internet?**
304
+ No. It runs fully offline. Host inspection uses local subprocesses (e.g. `node --version`).
305
+
306
+ **Why “deterministic”?**
307
+ Same repo + same host → same result. No ML, no heuristics that change between runs. Rules are based on config and code.
308
+
309
+ **Can I add my own rules?**
310
+ Yes. Put a `.repofail/rules.yaml` (or `repofail-rules.yaml`) in the repo and define conditions on `repo.*` and `host.*`. See `repofail -e list` for built-in rule IDs.
311
+
312
+ **What if my repo is clean?**
313
+ You get a high score (e.g. 96–100%) and “No deterministic blockers detected.” repofail does not invent problems.
314
+
315
+ ---
316
+
317
+ ## When not to use it
318
+
319
+ - **You need dependency resolution** — use pip, npm, poetry, etc. repofail does not install or resolve.
320
+ - **You need security scanning** — use Dependabot, Snyk, or similar. repofail is compatibility-only.
321
+ - **You want “AI suggested fixes”** — repofail gives deterministic, rule-based suggestions only.
322
+ - **You run only in one environment** — if every dev and CI use the same OS/runtime, the value is smaller (still useful for drift and contracts).
323
+
324
+ ---
325
+
227
326
  ## Testing
228
327
 
229
328
  ```bash
329
+ pip install -e ".[dev]"
230
330
  pytest tests/ -v
231
331
  ```
332
+
333
+ **Quick checks:** `bash -n install.sh` (syntax). The GitHub Action runs on every PR in this repo (see [.github/workflows/repofail.yml](.github/workflows/repofail.yml)); to test the reusable action, use it in another repo’s workflow on a PR.
@@ -0,0 +1,42 @@
1
+ # repofail — 30-second quickstart
2
+
3
+ ## What it does
4
+
5
+ Answers one question: **Will this repository run on this machine?**
6
+
7
+ It does **not** install your project. It reads configs and your environment, then reports compatibility issues (Node version, Python range, CUDA, lock file, spec drift, etc.) with a single score and clear evidence.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install repofail
13
+ ```
14
+
15
+ Or: `pipx install repofail` | [install.sh](https://raw.githubusercontent.com/jayvenn21/repofail/main/install.sh) | Homebrew (see README).
16
+
17
+ ## Run
18
+
19
+ ```bash
20
+ cd /path/to/any/repo
21
+ repofail
22
+ ```
23
+
24
+ You get:
25
+
26
+ - A **compatibility score** (0–100%)
27
+ - **HARD FAILURES** (deterministic blockers) with evidence and suggested fixes
28
+ - Optional: **RUNTIME RISKS**, **STRUCTURAL PROFILE**
29
+
30
+ ## CI
31
+
32
+ ```bash
33
+ repofail --ci
34
+ ```
35
+
36
+ Exits 1 if HIGH (or configured) severity violations are found. Use in GitHub Actions or any CI.
37
+
38
+ ## Next
39
+
40
+ - `repofail -e list` — list all rules
41
+ - `repofail -e spec_drift` — explain a rule
42
+ - [README](https://github.com/jayvenn21/repofail#readme) — full docs, FAQ, when not to use
@@ -0,0 +1,66 @@
1
+ # Homebrew formula for repofail
2
+ # To use: copy to your tap repo (e.g. homebrew-tap/Formula/repofail.rb)
3
+ # Then: brew tap jayvenn21/tap && brew install jayvenn21/tap/repofail
4
+ #
5
+ # Homebrew uses --no-deps when installing the main package, so all Python
6
+ # dependencies must be declared as resources below.
7
+ # If SHA256 fails after pushing a new tag, get the correct hash with:
8
+ # curl -sL https://github.com/jayvenn21/repofail/archive/refs/tags/vX.Y.Z.tar.gz | shasum -a 256
9
+
10
+ class Repofail < Formula
11
+ include Language::Python::Virtualenv
12
+
13
+ desc "Deterministic runtime compatibility analyzer"
14
+ homepage "https://github.com/jayvenn21/repofail"
15
+ url "https://github.com/jayvenn21/repofail/archive/refs/tags/v0.2.4.tar.gz"
16
+ sha256 "d5558cd419c8d46bdc958064cb97f963d1ea793866414c025906ec15033512ed"
17
+ license "MIT"
18
+
19
+ # App supports Python 3.10+. We use 3.12 for current Homebrew; change to
20
+ # python@3.10 or python@3.11 if you need an older interpreter.
21
+ depends_on "libyaml"
22
+ depends_on "python@3.12"
23
+
24
+ resource "click" do
25
+ url "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz"
26
+ sha256 "12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"
27
+ end
28
+
29
+ resource "typer" do
30
+ url "https://files.pythonhosted.org/packages/ac/0a/d55af35db5f50f486e3eda0ada747eed773859e2699d3ce570b682a9b70a/typer-0.12.3.tar.gz"
31
+ sha256 "49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"
32
+ end
33
+
34
+ resource "rich" do
35
+ url "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz"
36
+ sha256 "439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"
37
+ end
38
+
39
+ resource "shellingham" do
40
+ url "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz"
41
+ sha256 "8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"
42
+ end
43
+
44
+ resource "typing-extensions" do
45
+ url "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz"
46
+ sha256 "1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
47
+ end
48
+
49
+ resource "tomli" do
50
+ url "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz"
51
+ sha256 "d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"
52
+ end
53
+
54
+ resource "pyyaml" do
55
+ url "https://github.com/yaml/pyyaml/archive/refs/tags/6.0.1.tar.gz"
56
+ sha256 "57314c984aaa84318eed00cf5a8365afc49f87954969e295efe2ba99f3b21f7a"
57
+ end
58
+
59
+ def install
60
+ virtualenv_install_with_resources
61
+ end
62
+
63
+ test do
64
+ assert_match "repofail", shell_output("#{bin}/repofail --help", 0)
65
+ end
66
+ end
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env bash
2
+ # Install repofail — deterministic runtime compatibility analyzer
3
+ # Usage: curl -sSL https://raw.githubusercontent.com/jayvenn21/repofail/main/install.sh | bash
4
+
5
+ set -e
6
+
7
+ if command -v pipx &>/dev/null; then
8
+ echo "Installing repofail via pipx (isolated)..."
9
+ pipx install repofail
10
+ elif command -v pip3 &>/dev/null; then
11
+ echo "Installing repofail via pip..."
12
+ pip3 install repofail
13
+ elif command -v pip &>/dev/null; then
14
+ echo "Installing repofail via pip..."
15
+ pip install repofail
16
+ else
17
+ echo "Error: need pip or pipx. Install Python 3.10+ and run: pip install repofail"
18
+ exit 1
19
+ fi
20
+
21
+ echo "Done. Run: repofail --help"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "repofail"
7
- version = "0.2.2"
7
+ version = "0.2.4"
8
8
  description = "Predict why a repository will fail on your machine before you run it."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -14,6 +14,13 @@ dependencies = [
14
14
  "tomli>=2.0; python_version < '3.11'",
15
15
  "pyyaml>=6.0",
16
16
  ]
17
+ classifiers = [
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ ]
17
24
 
18
25
  [project.scripts]
19
26
  repofail = "repofail.cli:_main"
@@ -23,3 +30,6 @@ dev = [
23
30
  "pytest>=7.0",
24
31
  ]
25
32
 
33
+ [tool.hatch.build.targets.sdist]
34
+ exclude = [".venv-check", ".venv-check/**", "testenv", "testenv/**", ".venv", ".venv/**", "venv", "venv/**"]
35
+
@@ -0,0 +1,8 @@
1
+ """repofail — Failure-oriented repo introspection."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("repofail")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.0.0.dev0"
@@ -2,6 +2,7 @@
2
2
 
3
3
  import sys
4
4
  from pathlib import Path
5
+ from typing import Optional
5
6
 
6
7
  import click
7
8
  import typer
@@ -46,7 +47,7 @@ from .engine import run_rules
46
47
  from .contract import generate_contract, validate_contract, EnvironmentContract
47
48
  from .telemetry import save_report, get_stats
48
49
  from .rules.base import Severity
49
- from .rules.registry import RULE_INFO, RULE_SUGGESTIONS
50
+ from .rules.registry import RULE_INFO
50
51
  from .risk import estimate_success_probability
51
52
  from .format import format_human
52
53
 
@@ -263,7 +264,7 @@ def _print_json(repo_profile, host_profile, results, verbose: bool = False) -> N
263
264
  @app.command("gen")
264
265
  def gen_cmd(
265
266
  path: Path = typer.Argument(Path("."), exists=True, file_okay=False, dir_okay=True, resolve_path=True, help="Repo path"),
266
- output: Path | None = typer.Option(None, "-o", help="Output file"),
267
+ output: Optional[Path] = typer.Option(None, "-o", help="Output file"),
267
268
  ) -> None:
268
269
  """Generate an environment contract from the repository."""
269
270
  try:
repofail-0.2.2/.gitignore DELETED
@@ -1,8 +0,0 @@
1
- dist/
2
- __pycache__/
3
- *.pyc
4
- .pytest_cache/
5
- *.egg-info/
6
- .eggs/
7
- VISION.md
8
- repofail-contract.json
@@ -1,3 +0,0 @@
1
- """repofail — Failure-oriented repo introspection."""
2
-
3
- __version__ = "0.1.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes