Skip to content

Commit 87e5adc

Browse files
committed
Add new flag --skip-hash-collection
1 parent 5d6c513 commit 87e5adc

File tree

13 files changed

+101
-33
lines changed

13 files changed

+101
-33
lines changed

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pip install -r requirements.txt
1515

1616
# Run specific detectors for testing
1717
python3 -m dependency_resolver docker <container_name>
18-
python3 -m dependency_resolver host --skip-system-scope
18+
python3 -m dependency_resolver host --skip-system-scope --skip-hash-generation
1919

2020
# Execute linting and formatting
2121
pre-commit run --files $(git diff --name-only --diff-filter=ACMR HEAD)

dependency_resolver/__main__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def parse_arguments() -> argparse.Namespace:
3636
%(prog)s --venv-path ~/.virtualenvs/myproject # Use specific virtual environment for pip
3737
%(prog)s --debug # Enable debug output
3838
%(prog)s --skip-system-scope # Skip system scope package managers
39+
%(prog)s --skip-hash-collection # Skip hash collection for improved performance
3940
""",
4041
)
4142

@@ -78,6 +79,12 @@ def parse_arguments() -> argparse.Namespace:
7879
help="Format JSON output with indentation",
7980
)
8081

82+
parser.add_argument(
83+
"--skip-hash-collection",
84+
action="store_true",
85+
help="Skip hash collection for packages and project locations to improve performance",
86+
)
87+
8188
return parser.parse_args()
8289

8390

@@ -122,7 +129,10 @@ def main() -> None:
122129
try:
123130
executor = create_executor(args.environment_type, args.environment_identifier, debug=args.debug)
124131
orchestrator = Orchestrator(
125-
debug=args.debug, skip_system_scope=args.skip_system_scope, venv_path=args.venv_path
132+
debug=args.debug,
133+
skip_system_scope=args.skip_system_scope,
134+
venv_path=args.venv_path,
135+
skip_hash_collection=args.skip_hash_collection,
126136
)
127137
dependencies = orchestrator.resolve_dependencies(executor, args.working_dir, args.only_container_info)
128138
formatter = OutputFormatter(debug=args.debug)

dependency_resolver/core/interfaces.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,21 @@ def is_usable(self, executor: EnvironmentExecutor, working_dir: Optional[str] =
3131
raise NotImplementedError
3232

3333
@abstractmethod
34-
def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[str] = None) -> dict[str, Any]:
35-
"""Extract dependencies with versions and hashes."""
34+
def get_dependencies(
35+
self, executor: EnvironmentExecutor, working_dir: Optional[str] = None, skip_hash_collection: bool = False
36+
) -> dict[str, Any]:
37+
"""Extract dependencies with versions and hashes.
38+
39+
Args:
40+
executor: Environment executor for running commands
41+
working_dir: Working directory to use
42+
skip_hash_collection: Skip hash collection for improved performance
43+
44+
Returns:
45+
tuple: (packages, metadata)
46+
- packages: List of package dicts with name, version, type, and optional hash
47+
- metadata: Dict with location and hash for project scope, empty for system scope
48+
"""
3649
raise NotImplementedError
3750

3851
@abstractmethod

dependency_resolver/core/orchestrator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ def __init__(
1616
debug: bool = False,
1717
skip_system_scope: bool = False,
1818
venv_path: str | None = None,
19+
skip_hash_collection: bool = False,
1920
):
2021
self.debug = debug
2122
self.skip_system_scope = skip_system_scope
23+
self.skip_hash_collection = skip_hash_collection
2224

2325
# Create detector instances
2426
self.detectors: list[PackageManagerDetector] = [
@@ -63,7 +65,9 @@ def resolve_dependencies(
6365
if self.debug:
6466
print(f"{detector_name} is usable, extracting dependencies...")
6567

66-
dependencies = detector.get_dependencies(executor, working_dir)
68+
dependencies = detector.get_dependencies(
69+
executor, working_dir, skip_hash_collection=self.skip_hash_collection
70+
)
6771

6872
# Special handling for docker-info detector (simplified format)
6973
if detector_name == "docker-info":

dependency_resolver/detectors/apk_detector.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,23 @@ def is_usable(self, executor: EnvironmentExecutor, working_dir: Optional[str] =
2121
_, _, apk_exit_code = executor.execute_command("apk --version")
2222
return apk_exit_code == 0
2323

24-
def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[str] = None) -> dict[str, Any]:
24+
def get_dependencies(
25+
self, executor: EnvironmentExecutor, working_dir: Optional[str] = None, skip_hash_collection: bool = False
26+
) -> dict[str, Any]:
2527
"""Extract system packages with versions and architecture using apk list.
2628
2729
Uses 'apk list --installed' for comprehensive package information including architecture.
2830
See docs/technical/detectors/apk_detector.md
31+
32+
Args:
33+
skip_hash_collection: Not applicable for apk (no hash collection implemented)
34+
35+
Returns:
36+
tuple: (packages, metadata)
37+
- packages: List of package dicts with name, version, type
38+
- metadata: Empty dict (system scope has no metadata)
2939
"""
40+
_ = skip_hash_collection # Not applicable for apk detector
3041
command = "apk list --installed"
3142
stdout, _, exit_code = executor.execute_command(command, working_dir)
3243

dependency_resolver/detectors/docker_info_detector.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ def is_usable(self, executor: EnvironmentExecutor, working_dir: Optional[str] =
1313
_ = working_dir # Unused parameter, required by interface
1414
return isinstance(executor, DockerExecutor)
1515

16-
def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[str] = None) -> dict[str, Any]:
16+
def get_dependencies(
17+
self, executor: EnvironmentExecutor, working_dir: Optional[str] = None, skip_hash_collection: bool = False
18+
) -> dict[str, Any]:
1719
"""Extract Docker container metadata.
1820
1921
Note: This detector returns metadata rather than packages, but conforms to the interface.
2022
The orchestrator handles this specially.
2123
"""
2224
_ = working_dir # Unused parameter, required by interface
25+
_ = skip_hash_collection # Not applicable for docker metadata
2326
if not isinstance(executor, DockerExecutor):
2427
return {}
2528

dependency_resolver/detectors/dpkg_detector.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ def is_usable(self, executor: EnvironmentExecutor, working_dir: Optional[str] =
2727
_, _, dpkg_exit_code = executor.execute_command("dpkg-query --version")
2828
return dpkg_exit_code == 0
2929

30-
def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[str] = None) -> dict[str, Any]:
30+
def get_dependencies(
31+
self, executor: EnvironmentExecutor, working_dir: Optional[str] = None, skip_hash_collection: bool = False
32+
) -> dict[str, Any]:
3133
"""Extract system packages with versions using dpkg-query.
3234
3335
Uses dpkg-query -W -f for reliable package information extraction.
@@ -39,8 +41,8 @@ def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[
3941
if exit_code != 0:
4042
return {"scope": "system", "dependencies": {}}
4143

42-
# Collect all package hashes in a single batch operation
43-
batch_hashes = self._collect_all_package_hashes(executor)
44+
# Collect all package hashes in a single batch operation (unless skipped)
45+
batch_hashes = {} if skip_hash_collection else self._collect_all_package_hashes(executor)
4446

4547
dependencies = {}
4648
for line in stdout.strip().split("\n"):
@@ -57,13 +59,15 @@ def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[
5759
"version": full_version,
5860
}
5961

60-
# Use batch-collected hash or fallback to individual lookup
61-
package_hash = batch_hashes.get(package_name)
62-
if not package_hash:
63-
package_hash = self._get_package_hash(executor, package_name, architecture)
62+
# Skip hash collection if requested
63+
if not skip_hash_collection:
64+
# Use batch-collected hash or fallback to individual lookup
65+
package_hash = batch_hashes.get(package_name)
66+
if not package_hash:
67+
package_hash = self._get_package_hash(executor, package_name, architecture)
6468

65-
if package_hash:
66-
package_data["hash"] = package_hash
69+
if package_hash:
70+
package_data["hash"] = package_hash
6771

6872
dependencies[package_name] = package_data
6973

dependency_resolver/detectors/maven_detector.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,16 @@ def is_usable(self, executor: EnvironmentExecutor, working_dir: Optional[str] =
2121
search_dir = working_dir or "."
2222
return executor.path_exists(f"{search_dir}/pom.xml")
2323

24-
def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[str] = None) -> dict[str, Any]:
25-
"""Extract Maven dependencies with versions."""
24+
def get_dependencies(
25+
self, executor: EnvironmentExecutor, working_dir: Optional[str] = None, skip_hash_collection: bool = False
26+
) -> dict[str, Any]:
27+
"""Extract Maven dependencies with versions.
28+
29+
Returns:
30+
tuple: (packages, metadata)
31+
- packages: List of package dicts with name, version, type
32+
- metadata: Dict with location and hash for project scope
33+
"""
2634
search_dir = working_dir or "."
2735
location = self._resolve_absolute_path(executor, search_dir)
2836
dependencies: dict[str, dict[str, str]] = {}
@@ -37,8 +45,8 @@ def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[
3745
# Fallback to pom.xml parsing
3846
dependencies = self._get_dependencies_via_pom_parsing(executor, search_dir)
3947

40-
# Generate location-based hash if we have dependencies
41-
if dependencies:
48+
# Generate location-based hash if appropriate
49+
if dependencies and not skip_hash_collection:
4250
result["hash"] = self._generate_location_hash(executor, location)
4351

4452
result["dependencies"] = dependencies

dependency_resolver/detectors/npm_detector.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ def is_usable(self, executor: EnvironmentExecutor, working_dir: Optional[str] =
3434
# Fallback to package-lock.json
3535
return executor.path_exists(f"{search_dir}/package-lock.json")
3636

37-
def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[str] = None) -> dict[str, Any]:
37+
def get_dependencies(
38+
self, executor: EnvironmentExecutor, working_dir: Optional[str] = None, skip_hash_collection: bool = False
39+
) -> dict[str, Any]:
3840
"""Extract npm dependencies with versions.
3941
4042
Uses 'npm list --json --depth=0' for structured package information.
@@ -67,7 +69,7 @@ def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[
6769
pass
6870

6971
# Generate location-based hash if appropriate
70-
if dependencies and scope == "project":
72+
if dependencies and not skip_hash_collection:
7173
result["hash"] = self._generate_location_hash(executor, location)
7274

7375
result["dependencies"] = dependencies

dependency_resolver/detectors/pip_detector.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ def is_usable(self, executor: EnvironmentExecutor, working_dir: Optional[str] =
2323
_, _, exit_code = executor.execute_command("pip --version", working_dir)
2424
return exit_code == 0
2525

26-
def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[str] = None) -> dict[str, Any]:
26+
def get_dependencies(
27+
self, executor: EnvironmentExecutor, working_dir: Optional[str] = None, skip_hash_collection: bool = False
28+
) -> dict[str, Any]:
2729
"""Extract pip dependencies with versions.
2830
2931
Uses 'pip list --format=freeze' for clean package==version format.
@@ -68,7 +70,7 @@ def get_dependencies(self, executor: EnvironmentExecutor, working_dir: Optional[
6870
if scope == "project":
6971
final_result["location"] = location
7072
# Generate location-based hash if appropriate
71-
if dependencies:
73+
if dependencies and not skip_hash_collection:
7274
final_result["hash"] = self._generate_location_hash(executor, location)
7375

7476
final_result["dependencies"] = dependencies

0 commit comments

Comments
 (0)