repofix 0.1.0__py3-none-any.whl
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.
- repofix/__init__.py +3 -0
- repofix/branch/__init__.py +1 -0
- repofix/branch/cache.py +152 -0
- repofix/cli.py +931 -0
- repofix/config.py +156 -0
- repofix/core/__init__.py +0 -0
- repofix/core/artifact_installer.py +561 -0
- repofix/core/docker_compose_bind_fix.py +314 -0
- repofix/core/executor.py +262 -0
- repofix/core/git.py +117 -0
- repofix/core/process_registry.py +176 -0
- repofix/core/runner.py +2397 -0
- repofix/detection/__init__.py +0 -0
- repofix/detection/artifacts.py +291 -0
- repofix/detection/commands.py +1174 -0
- repofix/detection/deploy_mode.py +424 -0
- repofix/detection/environment.py +69 -0
- repofix/detection/multi.py +196 -0
- repofix/detection/stack.py +390 -0
- repofix/fixing/__init__.py +0 -0
- repofix/fixing/ai_fixer.py +291 -0
- repofix/fixing/classifier.py +659 -0
- repofix/fixing/detector.py +525 -0
- repofix/fixing/llm_cloud.py +183 -0
- repofix/fixing/llm_json.py +140 -0
- repofix/fixing/local_llm.py +337 -0
- repofix/fixing/retry.py +457 -0
- repofix/fixing/rules.py +1140 -0
- repofix/fixing/safety.py +158 -0
- repofix/memory/__init__.py +0 -0
- repofix/memory/store.py +297 -0
- repofix/output/__init__.py +0 -0
- repofix/output/display.py +821 -0
- repofix-0.1.0.dist-info/METADATA +408 -0
- repofix-0.1.0.dist-info/RECORD +38 -0
- repofix-0.1.0.dist-info/WHEEL +4 -0
- repofix-0.1.0.dist-info/entry_points.txt +2 -0
- repofix-0.1.0.dist-info/licenses/LICENSE +21 -0
repofix/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Branch-aware dependency caching for repofix."""
|
repofix/branch/cache.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Branch-aware dependency cache utilities.
|
|
2
|
+
|
|
3
|
+
Each git branch tracks its own dependency fingerprint so repofix can skip
|
|
4
|
+
reinstalling packages when switching back to a previously-set-up branch.
|
|
5
|
+
|
|
6
|
+
Isolation strategy by runtime:
|
|
7
|
+
Python → branch-specific venv: .venv-<branch-slug> (full isolation)
|
|
8
|
+
Node → shared node_modules; dep hash decides whether npm/yarn/pnpm re-runs
|
|
9
|
+
Go/Rust → module caches are global; dep hash decides whether to re-run tidy/fetch
|
|
10
|
+
Ruby → shared vendor/bundle; dep hash controls bundle install
|
|
11
|
+
Others → dep hash tracked; no extra isolation needed
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import hashlib
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Dependency manifest files — any change here invalidates the branch cache.
|
|
21
|
+
DEP_FILE_NAMES: list[str] = [
|
|
22
|
+
# Python
|
|
23
|
+
"requirements.txt",
|
|
24
|
+
"requirements-dev.txt",
|
|
25
|
+
"requirements-prod.txt",
|
|
26
|
+
"pyproject.toml",
|
|
27
|
+
"setup.py",
|
|
28
|
+
"setup.cfg",
|
|
29
|
+
"Pipfile",
|
|
30
|
+
"Pipfile.lock",
|
|
31
|
+
"uv.lock",
|
|
32
|
+
# Node
|
|
33
|
+
"package.json",
|
|
34
|
+
"package-lock.json",
|
|
35
|
+
"yarn.lock",
|
|
36
|
+
"pnpm-lock.yaml",
|
|
37
|
+
# Go
|
|
38
|
+
"go.mod",
|
|
39
|
+
"go.sum",
|
|
40
|
+
# Rust
|
|
41
|
+
"Cargo.toml",
|
|
42
|
+
"Cargo.lock",
|
|
43
|
+
# Ruby
|
|
44
|
+
"Gemfile",
|
|
45
|
+
"Gemfile.lock",
|
|
46
|
+
# PHP
|
|
47
|
+
"composer.json",
|
|
48
|
+
"composer.lock",
|
|
49
|
+
# Dart / Flutter
|
|
50
|
+
"pubspec.yaml",
|
|
51
|
+
"pubspec.lock",
|
|
52
|
+
# Java / Kotlin
|
|
53
|
+
"pom.xml",
|
|
54
|
+
"build.gradle",
|
|
55
|
+
"build.gradle.kts",
|
|
56
|
+
"gradle.lockfile",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_current_branch(repo_path: Path) -> str:
|
|
61
|
+
"""Return the active git branch name, or 'HEAD' for detached/non-git repos."""
|
|
62
|
+
try:
|
|
63
|
+
from git import InvalidGitRepositoryError, Repo # type: ignore[import]
|
|
64
|
+
repo = Repo(repo_path, search_parent_directories=True)
|
|
65
|
+
return repo.active_branch.name
|
|
66
|
+
except Exception:
|
|
67
|
+
return "HEAD"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def compute_dep_hash(repo_path: Path) -> tuple[str, list[str]]:
|
|
71
|
+
"""
|
|
72
|
+
SHA-256 hash of the contents of every present dependency manifest.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
(hex_digest, list_of_found_filenames)
|
|
76
|
+
|
|
77
|
+
An empty repo (no dep files at all) returns a stable sentinel hash so it
|
|
78
|
+
can still be cached and compared correctly.
|
|
79
|
+
"""
|
|
80
|
+
hasher = hashlib.sha256()
|
|
81
|
+
found: list[str] = []
|
|
82
|
+
for name in DEP_FILE_NAMES:
|
|
83
|
+
dep_file = repo_path / name
|
|
84
|
+
if dep_file.exists() and dep_file.is_file():
|
|
85
|
+
found.append(name)
|
|
86
|
+
hasher.update(name.encode())
|
|
87
|
+
hasher.update(dep_file.read_bytes())
|
|
88
|
+
|
|
89
|
+
if not found:
|
|
90
|
+
# Stable sentinel so a no-dep-file repo still gets a consistent hash
|
|
91
|
+
hasher.update(b"__no_dep_files__")
|
|
92
|
+
|
|
93
|
+
return hasher.hexdigest(), found
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def normalize_repo_key(source: str, repo_path: Path) -> str:
|
|
97
|
+
"""Stable, lowercase string key identifying a repo (URL or absolute path)."""
|
|
98
|
+
if source.startswith(("http://", "https://", "git@")):
|
|
99
|
+
key = source.rstrip("/")
|
|
100
|
+
if key.endswith(".git"):
|
|
101
|
+
key = key[:-4]
|
|
102
|
+
return key.lower()
|
|
103
|
+
return str(repo_path.resolve())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def branch_slug(branch: str) -> str:
|
|
107
|
+
"""Convert a branch name to a safe, short filesystem slug.
|
|
108
|
+
|
|
109
|
+
Examples:
|
|
110
|
+
"main" → "main"
|
|
111
|
+
"feature/my-work" → "feature-my-work"
|
|
112
|
+
"HEAD" → "HEAD"
|
|
113
|
+
"""
|
|
114
|
+
safe = (
|
|
115
|
+
branch
|
|
116
|
+
.replace("/", "-")
|
|
117
|
+
.replace("\\", "-")
|
|
118
|
+
.replace(" ", "-")
|
|
119
|
+
.replace(":", "-")
|
|
120
|
+
)
|
|
121
|
+
# Keep only alphanumeric, dash, underscore, dot
|
|
122
|
+
safe = "".join(c for c in safe if c.isalnum() or c in "-_.")
|
|
123
|
+
return safe[:48] or "branch"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def branch_venv_name(branch: str) -> str:
|
|
127
|
+
"""Return the .venv directory name to use for a given branch."""
|
|
128
|
+
slug = branch_slug(branch)
|
|
129
|
+
# main / master keep the canonical name for backwards compatibility
|
|
130
|
+
if slug in ("main", "master"):
|
|
131
|
+
return ".venv"
|
|
132
|
+
return f".venv-{slug}"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def is_env_valid(repo_path: Path, runtime: str, env_dir: str) -> bool:
|
|
136
|
+
"""
|
|
137
|
+
Sanity-check that the cached isolated environment is still usable.
|
|
138
|
+
|
|
139
|
+
For Python: the branch-specific venv python binary must exist.
|
|
140
|
+
For Node: node_modules directory must exist.
|
|
141
|
+
For others: assume valid (global caches are outside the repo).
|
|
142
|
+
"""
|
|
143
|
+
rt = runtime.lower()
|
|
144
|
+
if rt in ("python", "pip"):
|
|
145
|
+
venv = Path(env_dir) if env_dir else repo_path / ".venv"
|
|
146
|
+
return (venv / "bin" / "python").exists()
|
|
147
|
+
if rt in ("node", "npm"):
|
|
148
|
+
return (repo_path / "node_modules").exists()
|
|
149
|
+
if rt == "ruby":
|
|
150
|
+
return (repo_path / "vendor" / "bundle").exists()
|
|
151
|
+
# Go, Rust, Java, PHP, Docker — their caches live in global dirs; trust them
|
|
152
|
+
return True
|