Skip to content

Commit 823e776

Browse files
authored
CM-40909 - Add lock file restore for Golang, NPM and Nuget (#266)
1 parent 0da58ba commit 823e776

8 files changed

+70
-34
lines changed

cycode/cli/files_collector/sca/base_restore_dependencies.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,27 @@ def build_dep_tree_path(path: str, generated_file_name: str) -> str:
1313
return join_paths(get_file_dir(path), generated_file_name)
1414

1515

16-
def execute_command(
17-
command: List[str],
16+
def execute_commands(
17+
commands: List[List[str]],
1818
file_name: str,
1919
command_timeout: int,
2020
dependencies_file_name: Optional[str] = None,
2121
working_directory: Optional[str] = None,
2222
) -> Optional[str]:
2323
try:
24-
dependencies = shell(command=command, timeout=command_timeout, working_directory=working_directory)
25-
# Write stdout output to the file if output_file_path is provided
24+
all_dependencies = []
25+
26+
# Run all commands and collect outputs
27+
for command in commands:
28+
dependencies = shell(command=command, timeout=command_timeout, working_directory=working_directory)
29+
all_dependencies.append(dependencies) # Collect each command's output
30+
31+
dependencies = '\n'.join(all_dependencies)
32+
33+
# Write all collected outputs to the file if dependencies_file_name is provided
2634
if dependencies_file_name:
27-
with open(dependencies_file_name, 'w') as output_file:
28-
output_file.write(dependencies)
35+
with open(dependencies_file_name, 'w') as output_file: # Open once in 'w' mode to start fresh
36+
output_file.writelines(dependencies)
2937
except Exception as e:
3038
logger.debug('Failed to restore dependencies via shell command, %s', {'filename': file_name}, exc_info=e)
3139
return None
@@ -62,8 +70,8 @@ def try_restore_dependencies(self, document: Document) -> Optional[Document]:
6270
restore_file_content = get_file_content(restore_file_path)
6371
else:
6472
output_file_path = restore_file_path if self.create_output_file_manually else None
65-
execute_command(
66-
self.get_command(manifest_file_path),
73+
execute_commands(
74+
self.get_commands(manifest_file_path),
6775
manifest_file_path,
6876
self.command_timeout,
6977
output_file_path,
@@ -85,7 +93,7 @@ def is_project(self, document: Document) -> bool:
8593
pass
8694

8795
@abstractmethod
88-
def get_command(self, manifest_file_path: str) -> List[str]:
96+
def get_commands(self, manifest_file_path: str) -> List[List[str]]:
8997
pass
9098

9199
@abstractmethod
Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,51 @@
1+
import logging
12
import os
2-
from typing import List
3+
from typing import List, Optional
34

45
import click
56

67
from cycode.cli.files_collector.sca.base_restore_dependencies import BaseRestoreDependencies
78
from cycode.cli.models import Document
89

9-
GO_PROJECT_FILE_EXTENSIONS = ['.mod']
10-
GO_RESTORE_FILE_NAME = 'go.sum'
10+
GO_PROJECT_FILE_EXTENSIONS = ['.mod', '.sum']
11+
GO_RESTORE_FILE_NAME = 'go.mod.graph'
1112
BUILD_GO_FILE_NAME = 'go.mod'
13+
BUILD_GO_LOCK_FILE_NAME = 'go.sum'
1214

1315

1416
class RestoreGoDependencies(BaseRestoreDependencies):
1517
def __init__(self, context: click.Context, is_git_diff: bool, command_timeout: int) -> None:
1618
super().__init__(context, is_git_diff, command_timeout, create_output_file_manually=True)
1719

20+
def try_restore_dependencies(self, document: Document) -> Optional[Document]:
21+
manifest_exists = os.path.isfile(self.get_working_directory(document) + os.sep + BUILD_GO_FILE_NAME)
22+
lock_exists = os.path.isfile(self.get_working_directory(document) + os.sep + BUILD_GO_LOCK_FILE_NAME)
23+
24+
if not manifest_exists or not lock_exists:
25+
logging.info('No manifest go.mod file found' if not manifest_exists else 'No manifest go.sum file found')
26+
27+
manifest_files_exists = manifest_exists & lock_exists
28+
29+
if not manifest_files_exists:
30+
return None
31+
32+
return super().try_restore_dependencies(document)
33+
1834
def is_project(self, document: Document) -> bool:
1935
return any(document.path.endswith(ext) for ext in GO_PROJECT_FILE_EXTENSIONS)
2036

21-
def get_command(self, manifest_file_path: str) -> List[str]:
22-
return ['cd', self.prepare_tree_file_path_for_command(manifest_file_path), '&&', 'go', 'list', '-m', '-json']
37+
def get_commands(self, manifest_file_path: str) -> List[List[str]]:
38+
return [
39+
['go', 'list', '-m', '-json', 'all'],
40+
['echo', '------------------------------------------------------'],
41+
['go', 'mod', 'graph'],
42+
]
2343

2444
def get_lock_file_name(self) -> str:
2545
return GO_RESTORE_FILE_NAME
2646

2747
def verify_restore_file_already_exist(self, restore_file_path: str) -> bool:
2848
return os.path.isfile(restore_file_path)
2949

30-
def prepare_tree_file_path_for_command(self, manifest_file_path: str) -> str:
31-
return manifest_file_path.replace(os.sep + BUILD_GO_FILE_NAME, '')
50+
def get_working_directory(self, document: Document) -> Optional[str]:
51+
return os.path.dirname(document.absolute_path)

cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def __init__(self, context: click.Context, is_git_diff: bool, command_timeout: i
1818
def is_project(self, document: Document) -> bool:
1919
return document.path.endswith(BUILD_GRADLE_FILE_NAME) or document.path.endswith(BUILD_GRADLE_KTS_FILE_NAME)
2020

21-
def get_command(self, manifest_file_path: str) -> List[str]:
22-
return ['gradle', 'dependencies', '-b', manifest_file_path, '-q', '--console', 'plain']
21+
def get_commands(self, manifest_file_path: str) -> List[List[str]]:
22+
return [['gradle', 'dependencies', '-b', manifest_file_path, '-q', '--console', 'plain']]
2323

2424
def get_lock_file_name(self) -> str:
2525
return BUILD_GRADLE_DEP_TREE_FILE_NAME

cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from cycode.cli.files_collector.sca.base_restore_dependencies import (
88
BaseRestoreDependencies,
99
build_dep_tree_path,
10-
execute_command,
10+
execute_commands,
1111
)
1212
from cycode.cli.models import Document
1313
from cycode.cli.utils.path_utils import get_file_content, get_file_dir, join_paths
@@ -24,8 +24,8 @@ def __init__(self, context: click.Context, is_git_diff: bool, command_timeout: i
2424
def is_project(self, document: Document) -> bool:
2525
return path.basename(document.path).split('/')[-1] == BUILD_MAVEN_FILE_NAME
2626

27-
def get_command(self, manifest_file_path: str) -> List[str]:
28-
return ['mvn', 'org.cyclonedx:cyclonedx-maven-plugin:2.7.4:makeAggregateBom', '-f', manifest_file_path]
27+
def get_commands(self, manifest_file_path: str) -> List[List[str]]:
28+
return [['mvn', 'org.cyclonedx:cyclonedx-maven-plugin:2.7.4:makeAggregateBom', '-f', manifest_file_path]]
2929

3030
def get_lock_file_name(self) -> str:
3131
return join_paths('target', MAVEN_CYCLONE_DEP_TREE_FILE_NAME)
@@ -52,7 +52,7 @@ def restore_from_secondary_command(
5252
) -> Optional[Document]:
5353
# TODO(MarshalX): does it even work? Ignored restore_dependencies_document arg
5454
secondary_restore_command = create_secondary_restore_command(manifest_file_path)
55-
backup_restore_content = execute_command(secondary_restore_command, manifest_file_path, self.command_timeout)
55+
backup_restore_content = execute_commands(secondary_restore_command, manifest_file_path, self.command_timeout)
5656
restore_dependencies_document = Document(
5757
build_dep_tree_path(document.path, MAVEN_DEP_TREE_FILE_NAME), backup_restore_content, self.is_git_diff
5858
)

cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,17 @@ def __init__(self, context: click.Context, is_git_diff: bool, command_timeout: i
1818
def is_project(self, document: Document) -> bool:
1919
return any(document.path.endswith(ext) for ext in NPM_PROJECT_FILE_EXTENSIONS)
2020

21-
def get_command(self, manifest_file_path: str) -> List[str]:
21+
def get_commands(self, manifest_file_path: str) -> List[List[str]]:
2222
return [
23-
'npm',
24-
'install',
25-
'--prefix',
26-
self.prepare_manifest_file_path_for_command(manifest_file_path),
27-
'--package-lock-only',
28-
'--ignore-scripts',
29-
'--no-audit',
23+
[
24+
'npm',
25+
'install',
26+
'--prefix',
27+
self.prepare_manifest_file_path_for_command(manifest_file_path),
28+
'--package-lock-only',
29+
'--ignore-scripts',
30+
'--no-audit',
31+
]
3032
]
3133

3234
def get_lock_file_name(self) -> str:

cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ def __init__(self, context: click.Context, is_git_diff: bool, command_timeout: i
1717
def is_project(self, document: Document) -> bool:
1818
return any(document.path.endswith(ext) for ext in NUGET_PROJECT_FILE_EXTENSIONS)
1919

20-
def get_command(self, manifest_file_path: str) -> List[str]:
21-
return ['dotnet', 'restore', manifest_file_path, '--use-lock-file', '--verbosity', 'quiet']
20+
def get_commands(self, manifest_file_path: str) -> List[List[str]]:
21+
return [['dotnet', 'restore', manifest_file_path, '--use-lock-file', '--verbosity', 'quiet']]
2222

2323
def get_lock_file_name(self) -> str:
2424
return NUGET_LOCK_FILE_NAME

cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ class RestoreSbtDependencies(BaseRestoreDependencies):
1212
def is_project(self, document: Document) -> bool:
1313
return any(document.path.endswith(ext) for ext in SBT_PROJECT_FILE_EXTENSIONS)
1414

15-
def get_command(self, manifest_file_path: str) -> List[str]:
16-
return ['sbt', 'dependencyLockWrite', '--verbose']
15+
def get_commands(self, manifest_file_path: str) -> List[List[str]]:
16+
return [['sbt', 'dependencyLockWrite', '--verbose']]
1717

1818
def get_lock_file_name(self) -> str:
1919
return SBT_LOCK_FILE_NAME

cycode/cli/files_collector/sca/sca_code_scanner.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55

66
from cycode.cli import consts
77
from cycode.cli.files_collector.sca.base_restore_dependencies import BaseRestoreDependencies
8+
from cycode.cli.files_collector.sca.go.restore_go_dependencies import RestoreGoDependencies
89
from cycode.cli.files_collector.sca.maven.restore_gradle_dependencies import RestoreGradleDependencies
910
from cycode.cli.files_collector.sca.maven.restore_maven_dependencies import RestoreMavenDependencies
11+
from cycode.cli.files_collector.sca.npm.restore_npm_dependencies import RestoreNpmDependencies
12+
from cycode.cli.files_collector.sca.nuget.restore_nuget_dependencies import RestoreNugetDependencies
1013
from cycode.cli.files_collector.sca.sbt.restore_sbt_dependencies import RestoreSbtDependencies
1114
from cycode.cli.models import Document
1215
from cycode.cli.utils.git_proxy import git_proxy
@@ -132,6 +135,9 @@ def restore_handlers(context: click.Context, is_git_diff: bool) -> List[BaseRest
132135
RestoreGradleDependencies(context, is_git_diff, BUILD_DEP_TREE_TIMEOUT),
133136
RestoreMavenDependencies(context, is_git_diff, BUILD_DEP_TREE_TIMEOUT),
134137
RestoreSbtDependencies(context, is_git_diff, BUILD_DEP_TREE_TIMEOUT),
138+
RestoreGoDependencies(context, is_git_diff, BUILD_DEP_TREE_TIMEOUT),
139+
RestoreNugetDependencies(context, is_git_diff, BUILD_DEP_TREE_TIMEOUT),
140+
RestoreNpmDependencies(context, is_git_diff, BUILD_DEP_TREE_TIMEOUT),
135141
]
136142

137143

0 commit comments

Comments
 (0)