Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Pre-Commit] Handle API Modules #4175

Merged
merged 17 commits into from
Mar 27, 2024
3 changes: 1 addition & 2 deletions demisto_sdk/commands/common/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -960,8 +960,7 @@ def get_script_or_integration_id(file_path):
"-",
],
)



def get_api_module_integrations_set(changed_api_modules: Set, integration_set: Set):
integrations_set = list()
for integration in integration_set:
Expand Down
2 changes: 1 addition & 1 deletion demisto_sdk/commands/pre_commit/hooks/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def generate_hooks(
docker_extra_args = self._get_property("pass_docker_extra_args", "")
new_hook[
"entry"
] = f'--entrypoint {new_hook.get("entry")} {docker_extra_args} {get_environment_flag(env)} {"--quiet" if quiet else ""} {dev_image}'
] = f'--entrypoint {new_hook.get("entry")} {docker_extra_args} --user demisto {get_environment_flag(env)} {"--quiet" if quiet else ""} {dev_image}'
ilaner marked this conversation as resolved.
Show resolved Hide resolved
ret_hooks = []
for (
integration_script,
Expand Down
87 changes: 66 additions & 21 deletions demisto_sdk/commands/pre_commit/pre_commit_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from packaging.version import Version

from demisto_sdk.commands.common.constants import (
API_MODULES_PACK,
DEFAULT_PYTHON_VERSION,
INTEGRATIONS_DIR,
PACKS_FOLDER,
Expand All @@ -25,10 +26,16 @@
from demisto_sdk.commands.common.tools import (
write_dict,
)
from demisto_sdk.commands.content_graph.commands.update import update_content_graph
from demisto_sdk.commands.content_graph.interface import ContentGraphInterface
from demisto_sdk.commands.content_graph.objects.base_content import BaseContent
from demisto_sdk.commands.content_graph.objects.integration import (
Integration,
)
from demisto_sdk.commands.content_graph.objects.integration_script import (
IntegrationScript,
)
from demisto_sdk.commands.content_graph.objects.script import Script
from demisto_sdk.commands.pre_commit.hooks.docker import DockerHook
from demisto_sdk.commands.pre_commit.hooks.hook import Hook, join_files
from demisto_sdk.commands.pre_commit.hooks.mypy import MypyHook
Expand Down Expand Up @@ -321,8 +328,10 @@ def group_by_language(
Returns:
Dict[str, set]: The files grouped by their python version, and a set of excluded paths
"""
graph = ContentGraphInterface()
ilaner marked this conversation as resolved.
Show resolved Hide resolved
integrations_scripts_mapping = defaultdict(set)
infra_files = []
api_modules = []
for file in files:
if file.is_dir():
continue
Expand All @@ -347,19 +356,41 @@ def group_by_language(
infra_files.append(file)

language_to_files: Dict[str, Set] = defaultdict(set)
integrations_scripts = []
integrations_scripts: Set[IntegrationScript] = set()
for integration_script_paths in more_itertools.chunked_even(
integrations_scripts_mapping.keys(), INTEGRATIONS_BATCH
):
with multiprocessing.Pool(processes=cpu_count()) as pool:
integrations_scripts.extend(
pool.map(BaseContent.from_path, integration_script_paths)
)
chunk = pool.map(BaseContent.from_path, integration_script_paths)
ilaner marked this conversation as resolved.
Show resolved Hide resolved
for i in chunk:
if not i or not isinstance(i, IntegrationScript):
continue
integrations_scripts.add(i)
ilaner marked this conversation as resolved.
Show resolved Hide resolved
exclude_integration_script = set()
for integration_script in integrations_scripts:
if not integration_script or not isinstance(
integration_script, IntegrationScript
):
if (pack := integration_script.in_pack) and pack.object_id == API_MODULES_PACK:
# add api modules to the api_modules list, we will handle them later
api_modules.append(integration_script)
continue
if api_modules:
update_content_graph(graph)
api_modules: List[Script] = graph.search( # type: ignore[no-redef]
object_id=[api_module.object_id for api_module in api_modules]
)
for api_module in api_modules:
assert isinstance(api_module, Script)
for imported_by in api_module.imported_by:
# we need to add the api module for each integration that uses it, so it will execute the api module check
assert isinstance(imported_by, Integration)
GuyAfik marked this conversation as resolved.
Show resolved Hide resolved
integrations_scripts.add(imported_by)
integrations_scripts_mapping[imported_by.path.parent].update(
add_related_files(api_module.path.relative_to(CONTENT_PATH))
| add_related_files(imported_by.path.relative_to(CONTENT_PATH))
)

for integration_script in integrations_scripts:
if (pack := integration_script.in_pack) and pack.object_id == API_MODULES_PACK:
# we dont need to lint them individually, they will be run with the integrations that uses them
continue
if integration_script.deprecated:
# we exclude deprecate integrations and scripts from pre-commit.
Expand Down Expand Up @@ -486,6 +517,33 @@ def pre_commit_manager(
)


def add_related_files(file: Path) -> Set[Path]:
"""This returns the related files set, including the original file
If the file is `.yml`, it will add the `.py` file and the test file.
If the file is `.py` or `.ps1`, it will add the tests file.

Args:
file (Path): The file to add related files for.

Returns:
Set[Path]: The set of related files.
"""
files_to_run = set()
files_to_run.add(file)
if ".yml" in (file.suffix for file in files_to_run):
py_file_path = file.with_suffix(".py")
if py_file_path.exists():
files_to_run.add(py_file_path)
if {".py", ".ps1"}.intersection({file.suffix for file in files_to_run}):
if ".py" in (file.suffix for file in files_to_run):
test_file = file.with_name(f"{file.stem}_test.py")
else:
test_file = file.with_name(f"{file.stem}.Tests.ps1")
if test_file.exists():
files_to_run.add(test_file)
return files_to_run


def preprocess_files(
input_files: Optional[Iterable[Path]] = None,
staged_only: bool = False,
Expand Down Expand Up @@ -515,20 +573,7 @@ def preprocess_files(
if file.is_dir():
files_to_run.update({path for path in file.rglob("*") if path.is_file()})
else:
files_to_run.add(file)
# If the current file is a yml file, add the matching python file to files_to_run
if file.suffix == ".yml":
py_file_path = file.with_suffix(".py")
if py_file_path.exists():
files_to_run.add(py_file_path)
if file.suffix in (".py", ".ps1"):
if file.suffix == ".py":
test_file = file.with_name(f"{file.stem}_test.py")
else:
test_file = file.with_name(f"{file.stem}.Tests.ps1")
if test_file.exists():
files_to_run.add(test_file)

files_to_run.update(add_related_files(file))
# convert to relative file to content path
relative_paths = {
file.relative_to(CONTENT_PATH) if file.is_absolute() else file
Expand Down
Loading