From a1ce146b574ae12ae4da0e4e8f7d96a2d7b7f9ab Mon Sep 17 00:00:00 2001 From: tdruez <489057+tdruez@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:06:42 +0400 Subject: [PATCH 1/6] Refine the scan_single_package pipeline to work on git inputs #1376 (#1636) Signed-off-by: tdruez Signed-off-by: Model Monster --- CHANGELOG.rst | 3 + scanpipe/pipelines/scan_single_package.py | 6 +- scanpipe/tests/pipes/test_d2d.py | 1 + scanpipe/tests/pipes/test_fetch.py | 4 +- scanpipe/tests/test_pipelines.py | 150 +++++++++++++--------- 5 files changed, 99 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9ae1ecf47..1d0d83fac 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -29,6 +29,9 @@ v34.9.6 (unreleased) - Add support for "Permission denied" file access in make_codebase_resource. https://github.com/aboutcode-org/scancode.io/issues/1630 +- Refine the ``scan_single_package`` pipeline to work on git fetched inputs. + https://github.com/aboutcode-org/scancode.io/issues/1376 + v34.9.5 (2025-02-19) -------------------- diff --git a/scanpipe/pipelines/scan_single_package.py b/scanpipe/pipelines/scan_single_package.py index 1c9eee2ee..c2e077a3f 100644 --- a/scanpipe/pipelines/scan_single_package.py +++ b/scanpipe/pipelines/scan_single_package.py @@ -69,10 +69,12 @@ def steps(cls): def get_package_input(self): """Locate the package input in the project's input/ directory.""" - input_files = self.project.input_files + # Using the input_sources model property as it includes input sources instances + # as well as any files manually copied into the input/ directory. + input_sources = self.project.input_sources inputs = list(self.project.inputs("*")) - if len(inputs) != 1 or len(input_files) != 1: + if len(inputs) != 1 or len(input_sources) != 1: raise Exception("Only 1 input file supported") self.input_path = inputs[0] diff --git a/scanpipe/tests/pipes/test_d2d.py b/scanpipe/tests/pipes/test_d2d.py index 0eacbee6e..a60cd864b 100644 --- a/scanpipe/tests/pipes/test_d2d.py +++ b/scanpipe/tests/pipes/test_d2d.py @@ -1629,6 +1629,7 @@ def test_scanpipe_pipes_d2d_match_purldb_resources_post_process_with_special_cha except DataError: self.fail("DataError was raised, but it should not occur.") + @skipIf(sys.platform == "darwin", "Test is failing on macOS") def test_scanpipe_pipes_d2d_map_javascript_symbols(self): to_dir = self.project1.codebase_path / "to/project.tar.zst-extract/" to_resource_file = ( diff --git a/scanpipe/tests/pipes/test_fetch.py b/scanpipe/tests/pipes/test_fetch.py index 35f3f33c9..15e32a3c2 100644 --- a/scanpipe/tests/pipes/test_fetch.py +++ b/scanpipe/tests/pipes/test_fetch.py @@ -38,7 +38,7 @@ def test_scanpipe_pipes_fetch_get_fetcher(self): self.assertEqual(fetch.fetch_http, fetch.get_fetcher("http://a.b/f.z")) self.assertEqual(fetch.fetch_http, fetch.get_fetcher("https://a.b/f.z")) self.assertEqual(fetch.fetch_docker_image, fetch.get_fetcher("docker://image")) - git_http_url = "https://github.com/nexB/scancode.io.git" + git_http_url = "https://github.com/aboutcode-org/scancode.io.git" self.assertEqual(fetch.fetch_git_repo, fetch.get_fetcher(git_http_url)) self.assertEqual(fetch.fetch_git_repo, fetch.get_fetcher(git_http_url + "/")) @@ -229,7 +229,7 @@ def test_scanpipe_pipes_fetch_get_request_session(self): @mock.patch("git.repo.base.Repo.clone_from") def test_scanpipe_pipes_fetch_git_repo(self, mock_clone_from): mock_clone_from.return_value = None - url = "https://github.com/nexB/scancode.io.git" + url = "https://github.com/aboutcode-org/scancode.io.git" download = fetch.fetch_git_repo(url) self.assertEqual(url, download.uri) diff --git a/scanpipe/tests/test_pipelines.py b/scanpipe/tests/test_pipelines.py index 03a4547bb..248da6379 100644 --- a/scanpipe/tests/test_pipelines.py +++ b/scanpipe/tests/test_pipelines.py @@ -40,7 +40,6 @@ from scanpipe import pipes from scanpipe.models import CodebaseResource from scanpipe.models import DiscoveredPackage -from scanpipe.models import Project from scanpipe.pipelines import CommonStepsMixin from scanpipe.pipelines import InputFilesError from scanpipe.pipelines import Pipeline @@ -55,6 +54,7 @@ from scanpipe.pipes.input import copy_input from scanpipe.tests import FIXTURES_REGEN from scanpipe.tests import make_package +from scanpipe.tests import make_project from scanpipe.tests import package_data1 from scanpipe.tests.pipelines.do_nothing import DoNothing from scanpipe.tests.pipelines.download_inputs import DownloadInput @@ -69,7 +69,7 @@ class ScanPipePipelinesTest(TestCase): data = Path(__file__).parent / "data" def test_scanpipe_pipeline_class_pipeline_name_attribute(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline_instance = DoNothing(run) self.assertEqual("do_nothing", pipeline_instance.pipeline_name) @@ -104,7 +104,7 @@ def test_scanpipe_pipeline_class_get_summary(self): self.assertEqual(expected, ProfileStep.get_summary()) def test_scanpipe_pipeline_class_log(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline = run.make_pipeline_instance() pipeline.log("Event1") @@ -115,7 +115,7 @@ def test_scanpipe_pipeline_class_log(self): self.assertIn("Event2", run.log) def test_scanpipe_pipeline_class_execute(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline = run.make_pipeline_instance() @@ -132,7 +132,7 @@ def test_scanpipe_pipeline_class_execute(self): self.assertIn("Pipeline completed", run.log) def test_scanpipe_pipeline_class_execute_with_exception(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("raise_exception") pipeline = run.make_pipeline_instance() @@ -158,7 +158,7 @@ def test_scanpipe_pipeline_class_execute_with_selected_steps(self, step2, step1) step2.__name__ = "step2" step2.groups = [] - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") run.update(selected_steps=["step2", "not_existing_step"]) pipeline = run.make_pipeline_instance() @@ -178,7 +178,7 @@ def test_scanpipe_pipeline_class_execute_with_selected_steps(self, step2, step1) self.assertIn("Pipeline completed", run.log) def test_scanpipe_pipeline_class_download_inputs_attribute(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("download_inputs") pipeline = run.make_pipeline_instance() self.assertTrue(pipeline.download_inputs) @@ -197,7 +197,7 @@ def test_scanpipe_pipeline_class_download_inputs_attribute(self): @mock.patch("requests.sessions.Session.get") def test_scanpipe_pipeline_class_download_missing_inputs(self, mock_get): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline = run.make_pipeline_instance() @@ -239,7 +239,7 @@ def test_scanpipe_pipeline_class_download_missing_inputs(self, mock_get): @mock.patch("scanpipe.models.InputSource.fetch") def test_scanpipe_pipeline_class_download_fetch_exception(self, mock_fetch): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline = run.make_pipeline_instance() @@ -262,30 +262,31 @@ def test_scanpipe_pipeline_class_download_fetch_exception(self, mock_fetch): @mock.patch("git.repo.base.Repo.clone_from") def test_scanpipe_pipeline_class_download_missing_inputs_git_repo(self, mock_clone): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline = run.make_pipeline_instance() - download_url = "https://github.com/nexB/scancode.io.git" + download_url = "https://github.com/aboutcode-org/scancode.io.git" input_source = project1.add_input_source(download_url=download_url) def mock_make_to_path(**kwargs): to_path = kwargs.get("to_path") - to_path.touch() + to_path.mkdir() mock_clone.side_effect = mock_make_to_path mock_clone.return_value = None pipeline.download_missing_inputs() self.assertIn( - "Fetching input from https://github.com/nexB/scancode.io.git", run.log + "Fetching input from https://github.com/aboutcode-org/scancode.io.git", + run.log, ) input_source.refresh_from_db() self.assertEqual("scancode.io.git", input_source.filename) self.assertTrue(input_source.exists()) def test_scanpipe_pipeline_class_save_errors_context_manager(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline = run.make_pipeline_instance() self.assertEqual(project1, pipeline.project) @@ -323,7 +324,7 @@ def test_scanpipe_pipeline_class_get_graph(self): self.assertEqual(expected, DoNothing.get_graph()) def test_scanpipe_pipelines_profile_decorator(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("profile_step") pipeline_instance = run.make_pipeline_instance() @@ -376,7 +377,7 @@ def test_scanpipe_pipeline_class_get_available_groups(self): self.assertEqual([], DoNothing.get_available_groups()) def test_scanpipe_pipeline_class_env_loaded_from_config_file(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline = run.make_pipeline_instance() self.assertEqual({}, pipeline.env) @@ -391,7 +392,7 @@ def test_scanpipe_pipeline_class_env_loaded_from_config_file(self): self.assertEqual({"product_name": "Product"}, pipeline.env) def test_scanpipe_pipeline_class_env_reloaded_after_extraction(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "settings" / "archived-scancode-config.zip" project1.copy_input_from(input_location) @@ -412,7 +413,7 @@ def test_scanpipe_pipeline_class_env_reloaded_after_extraction(self): self.assertEqual(expected, pipeline.env) def test_scanpipe_pipeline_class_flag_ignored_resources(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline = run.make_pipeline_instance() self.assertIsNone(pipeline.env.get("ignored_patterns")) @@ -428,7 +429,7 @@ def test_scanpipe_pipeline_class_flag_ignored_resources(self): mock_flag.assert_called_with(project1, patterns=patterns_args) def test_scanpipe_pipeline_class_extract_archive(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline = run.make_pipeline_instance() @@ -446,7 +447,7 @@ def test_scanpipe_pipeline_class_extract_archive(self): self.assertEqual("", project_error.traceback) def test_scanpipe_pipeline_class_extract_archives(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("do_nothing") pipeline = run.make_pipeline_instance() @@ -468,7 +469,7 @@ def test_scanpipe_pipeline_class_extract_archives(self): class RootFSPipelineTest(TestCase): def test_scanpipe_rootfs_pipeline_extract_input_files_errors(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("analyze_root_filesystem_or_vm_image") pipeline_instance = analyze_root_filesystem.RootFS(run) @@ -659,7 +660,7 @@ def assertPipelineResultEqual( @skipIf(from_docker_image, "Random failure in the Docker context.") def test_scanpipe_scan_package_pipeline_integration(self): pipeline_name = "scan_single_package" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "scancode" / "is-npm-1.0.0.tgz" project1.copy_input_from(input_location) @@ -695,7 +696,7 @@ def test_scanpipe_scan_package_pipeline_integration(self): @skipIf(from_docker_image, "Random failure in the Docker context.") def test_scanpipe_scan_package_pipeline_integration_multiple_packages(self): pipeline_name = "scan_single_package" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "scancode" / "multiple-is-npm-1.0.0.tar.gz" project1.copy_input_from(input_location) @@ -727,7 +728,7 @@ def test_scanpipe_scan_package_pipeline_integration_multiple_packages(self): def test_scanpipe_scan_package_single_extract_input_to_codebase_directory( self, mock_is_archive ): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("scan_single_package") pipeline_instance = scan_single_package.ScanSinglePackage(run) @@ -751,7 +752,7 @@ def test_scanpipe_scan_package_single_extract_input_to_codebase_directory( def test_scanpipe_scan_package_single_file(self): pipeline_name = "scan_single_package" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "manifests" / "openpdf-parent-1.3.11.pom.xml" project1.copy_input_from(input_location) @@ -772,9 +773,36 @@ def test_scanpipe_scan_package_single_file(self): ) self.assertPipelineResultEqual(expected_file, scancode_file) + @mock.patch("git.repo.base.Repo.clone_from") + def test_scanpipe_scan_package_single_package_git_repo(self, mock_clone): + pipeline_name = "scan_single_package" + project1 = make_project() + + download_url = "https://github.com/aboutcode-org/scancode.io.git" + project1.add_input_source(download_url=download_url) + + run = project1.add_pipeline(pipeline_name) + pipeline = run.make_pipeline_instance() + + # Create the "fetched" git directory content + def mock_make_git_directory(**kwargs): + to_path = kwargs.get("to_path") # scancode.io.git + to_path.mkdir() + file_location = self.data / "aboutcode" / "notice.NOTICE" + copy_input(file_location, to_path) + + mock_clone.side_effect = mock_make_git_directory + mock_clone.return_value = None + + exitcode, out = pipeline.execute() + self.assertEqual(0, exitcode, msg=out) + + self.assertEqual(2, project1.codebaseresources.count()) + self.assertEqual(0, project1.discoveredpackages.count()) + def test_scanpipe_scan_codebase_pipeline_integration(self): pipeline_name = "scan_codebase" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() filename = "is-npm-1.0.0.tgz" input_location = self.data / "scancode" / filename @@ -797,7 +825,7 @@ def test_scanpipe_scan_codebase_pipeline_integration(self): def test_scanpipe_inspect_packages_creates_packages_npm(self): pipeline_name = "inspect_packages" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() filename = "is-npm-1.0.0.tgz" input_location = self.data / "scancode" / filename @@ -828,7 +856,7 @@ def test_scanpipe_inspect_packages_creates_packages_npm(self): def test_scanpipe_inspect_packages_creates_packages_pypi(self): pipeline_name = "inspect_packages" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "manifests" / "python-inspector-0.10.0.zip" project1.copy_input_from(input_location) @@ -845,7 +873,7 @@ def test_scanpipe_inspect_packages_creates_packages_pypi(self): @skipIf(sys.platform == "darwin", "Not supported on macOS") def test_scanpipe_inspect_packages_with_resolved_dependencies_npm(self): pipeline_name = "inspect_packages" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "dependencies" / "resolved_dependencies_npm.zip" project1.copy_input_from(input_location) @@ -873,7 +901,7 @@ def test_scanpipe_inspect_packages_with_resolved_dependencies_npm(self): @skipIf(sys.platform == "darwin", "Not supported on macOS") def test_scanpipe_inspect_packages_with_resolved_dependencies_poetry(self): pipeline_name = "inspect_packages" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "dependencies" / "resolved_dependencies_poetry.zip" project1.copy_input_from(input_location) @@ -900,7 +928,7 @@ def test_scanpipe_inspect_packages_with_resolved_dependencies_poetry(self): def test_scanpipe_resolved_dependencies_cocoapods(self): pipeline_name = "resolve_dependencies" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = ( self.data / "dependencies" / "resolved_dependencies_cocoapods.zip" @@ -929,7 +957,7 @@ def test_scanpipe_resolved_dependencies_cocoapods(self): def test_scanpipe_resolved_dependencies_pip_inspect(self): pipeline_name = "resolve_dependencies" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "dependencies" / "resolved_dependencies_pip.zip" project1.copy_input_from(input_location) @@ -955,7 +983,7 @@ def test_scanpipe_resolved_dependencies_pip_inspect(self): def test_scanpipe_resolved_dependencies_nuget(self): pipeline_name = "resolve_dependencies" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "dependencies" / "resolved_dependencies_nuget.zip" project1.copy_input_from(input_location) @@ -982,7 +1010,7 @@ def test_scanpipe_resolved_dependencies_nuget(self): def test_scanpipe_scan_codebase_can_process_wheel(self): pipeline_name = "scan_codebase" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() filename = "daglib-0.6.0-py3-none-any.whl" input_location = self.data / "scancode" / filename @@ -1008,7 +1036,7 @@ def test_scanpipe_scan_codebase_can_process_wheel(self): @skipIf(sys.platform != "linux", "Expected results are inconsistent across OS") def test_scanpipe_docker_pipeline_alpine_integration(self): pipeline_name = "analyze_docker_image" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() filename = "alpine_3_15_4.tar.gz" input_location = self.data / "docker" / filename @@ -1031,7 +1059,7 @@ def test_scanpipe_docker_pipeline_alpine_integration(self): def test_scanpipe_docker_pipeline_does_not_report_errors_for_broken_symlinks(self): pipeline_name = "analyze_docker_image" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() filename = "minitag.tar" input_location = self.data / "image-with-symlinks" / filename @@ -1058,7 +1086,7 @@ def test_scanpipe_docker_pipeline_does_not_report_errors_for_broken_symlinks(sel @skipIf(sys.platform != "linux", "RPM related features only supported on Linux.") def test_scanpipe_docker_pipeline_rpm_integration(self): pipeline_name = "analyze_docker_image" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() filename = "centos.tar.gz" input_location = self.data / "docker" / filename @@ -1081,7 +1109,7 @@ def test_scanpipe_docker_pipeline_rpm_integration(self): def test_scanpipe_docker_pipeline_debian_integration(self): pipeline_name = "analyze_docker_image" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() filename = "debian.tar.gz" input_location = self.data / "docker" / filename @@ -1104,7 +1132,7 @@ def test_scanpipe_docker_pipeline_debian_integration(self): def test_scanpipe_docker_pipeline_distroless_debian_integration(self): pipeline_name = "analyze_docker_image" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() filename = "gcr_io_distroless_base.tar.gz" input_location = self.data / "docker" / filename @@ -1129,7 +1157,7 @@ def test_scanpipe_docker_pipeline_distroless_debian_integration(self): def test_scanpipe_rootfs_pipeline_integration(self): pipeline_name = "analyze_root_filesystem_or_vm_image" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "rootfs" / "basic-rootfs.tar.gz" project1.copy_input_from(input_location) @@ -1150,7 +1178,7 @@ def test_scanpipe_rootfs_pipeline_integration(self): def test_scanpipe_load_inventory_pipeline_integration(self): pipeline_name = "load_inventory" - project1 = Project.objects.create(name="Tool: scancode-toolkit") + project1 = make_project() input_location = self.data / "asgiref" / "asgiref-3.3.0_toolkit_scan.json" project1.copy_input_from(input_location) @@ -1172,7 +1200,7 @@ def test_scanpipe_load_inventory_pipeline_integration(self): self.assertPipelineResultEqual(expected_file, result_file) # Using the ScanCode.io JSON output as the input - project2 = Project.objects.create(name="Tool: scanpipe") + project2 = make_project() input_location = self.data / "asgiref" / "asgiref-3.3.0_scanpipe_output.json" project2.copy_input_from(input_location) @@ -1194,7 +1222,7 @@ def test_scanpipe_find_vulnerabilities_pipeline_integration( self, mock_bulk_search_by_purl, mock_is_configured, mock_is_available ): pipeline_name = "find_vulnerabilities" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() package1 = DiscoveredPackage.create_from_data(project1, package_data1) run = project1.add_pipeline(pipeline_name) @@ -1240,7 +1268,7 @@ def test_scanpipe_find_vulnerabilities_pipeline_integration( def test_scanpipe_resolve_dependencies_pipeline_integration(self): pipeline_name = "resolve_dependencies" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() selected_groups = ["DynamicResolver"] run = project1.add_pipeline( @@ -1258,7 +1286,7 @@ def test_scanpipe_resolve_dependencies_pipeline_integration(self): def test_scanpipe_resolve_dependencies_pipeline_integration_empty_manifest(self): pipeline_name = "resolve_dependencies" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() selected_groups = ["DynamicResolver"] run = project1.add_pipeline( @@ -1279,7 +1307,7 @@ def test_scanpipe_resolve_dependencies_pipeline_integration_misc( self, mock_resolve_dependencies ): pipeline_name = "resolve_dependencies" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() selected_groups = ["DynamicResolver"] input_location = self.data / "manifests" / "requirements.txt" @@ -1300,7 +1328,7 @@ def test_scanpipe_resolve_dependencies_pipeline_pypi_integration( self, mock_resolve_dependencies ): pipeline_name = "resolve_dependencies" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() selected_groups = ["DynamicResolver"] run = project1.add_pipeline( @@ -1322,7 +1350,7 @@ def test_scanpipe_resolve_dependencies_pipeline_pypi_integration( def test_scanpipe_load_sbom_pipeline_aboutfile_integration(self): pipeline_name = "load_sbom" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "manifests" / "Django-4.0.8-py3-none-any.whl.ABOUT" project1.copy_input_from(input_location) @@ -1342,7 +1370,7 @@ def test_scanpipe_load_sbom_pipeline_aboutfile_integration(self): def test_scanpipe_load_sbom_pipeline_spdx_integration(self): pipeline_name = "load_sbom" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "manifests" / "toml.spdx.json" project1.copy_input_from(input_location) @@ -1364,7 +1392,7 @@ def test_scanpipe_load_sbom_pipeline_spdx_integration(self): def test_scanpipe_load_sbom_pipeline_cyclonedx_integration(self): pipeline_name = "load_sbom" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "cyclonedx" / "nested.cdx.json" project1.copy_input_from(input_location) @@ -1439,7 +1467,7 @@ def test_scanpipe_load_sbom_pipeline_cyclonedx_integration(self): def test_scanpipe_load_sbom_pipeline_cyclonedx_with_dependencies_integration(self): pipeline_name = "load_sbom" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() input_location = self.data / "cyclonedx" / "laravel-7.12.0" / "bom.1.4.json" project1.copy_input_from(input_location) @@ -1464,7 +1492,7 @@ def test_scanpipe_deploy_to_develop_pipeline_integration( mock_uuid4.return_value = forced_uuid mock_request.return_value = None pipeline_name = "map_deploy_to_develop" - project1 = Project.objects.create(name="Analysis", uuid=forced_uuid) + project1 = make_project(name="Analysis", uuid=forced_uuid) selected_groups = ["Java"] jar_location = self.data / "d2d" / "jars" @@ -1489,7 +1517,7 @@ def test_scanpipe_deploy_to_develop_pipeline_integration( self.assertPipelineResultEqual(expected_file, result_file) def test_scanpipe_deploy_to_develop_pipeline_extract_input_files_errors(self): - project1 = Project.objects.create(name="Analysis") + project1 = make_project() run = project1.add_pipeline("map_deploy_to_develop") pipeline_instance = deploy_to_develop.DeployToDevelop(run) @@ -1528,7 +1556,7 @@ def test_scanpipe_deploy_to_develop_pipeline_with_about_file( mock_uuid4.return_value = forced_uuid mock_request.return_value = None pipeline_name = "map_deploy_to_develop" - project1 = Project.objects.create(name="Analysis", uuid=forced_uuid) + project1 = make_project(name="Analysis", uuid=forced_uuid) selected_groups = ["Java"] data_dir = self.data / "d2d" / "about_files" @@ -1567,7 +1595,7 @@ def test_scanpipe_populate_purldb_pipeline_integration( ): pipeline_name1 = "load_inventory" pipeline_name2 = "populate_purldb" - project1 = Project.objects.create(name="Utility: PurlDB") + project1 = make_project() input_location = self.data / "asgiref" / "asgiref-3.3.0_toolkit_scan.json" project1.copy_input_from(input_location) @@ -1609,7 +1637,7 @@ def test_scanpipe_populate_purldb_pipeline_integration_without_assembly( self, mock_is_available, mock_request_post ): pipeline_name = "populate_purldb" - project1 = Project.objects.create(name="Utility: PurlDB") + project1 = make_project() def mock_request_post_return(url, data, headers, timeout): payload = json.loads(data) @@ -1649,7 +1677,7 @@ def mock_request_post_return(url, data, headers, timeout): @skipIf(sys.platform == "darwin", "Not supported on macOS") def test_scanpipe_collect_symbols_ctags_pipeline_integration(self): pipeline_name = "collect_symbols_ctags" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() dir = project1.codebase_path / "codefile" dir.mkdir(parents=True) @@ -1673,7 +1701,7 @@ def test_scanpipe_collect_symbols_ctags_pipeline_integration(self): @skipIf(sys.platform != "linux", "Only supported on Linux") def test_scanpipe_collect_strings_gettext_pipeline_integration(self): pipeline_name = "collect_strings_gettext" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() dir = project1.codebase_path / "codefile" dir.mkdir(parents=True) @@ -1700,7 +1728,7 @@ def test_scanpipe_collect_strings_gettext_pipeline_integration(self): @skipIf(sys.platform == "darwin", "Not supported on macOS") def test_scanpipe_collect_symbols_pygments_pipeline_integration(self): pipeline_name = "collect_symbols_pygments" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() dir = project1.codebase_path / "codefile" dir.mkdir(parents=True) @@ -1731,7 +1759,7 @@ def test_scanpipe_collect_symbols_pygments_pipeline_integration(self): @skipIf(sys.platform == "darwin", "Not supported on macOS") def test_scanpipe_collect_symbols_tree_sitter_pipeline_integration(self): pipeline_name = "collect_symbols_tree_sitter" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() dir = project1.codebase_path / "codefile" dir.mkdir(parents=True) @@ -1766,7 +1794,7 @@ def test_scanpipe_enrich_with_purldb_pipeline_integration( self, mock_collect_data, mock_is_configured, mock_is_available ): pipeline_name = "enrich_with_purldb" - project1 = Project.objects.create(name="Analysis") + project1 = make_project() package1 = make_package(project1, package_url="pkg:npm/csvtojson@2.0.10") mock_is_configured.return_value = True From c9a2a0965e50c05cc43e7a8188b859eb7b51175c Mon Sep 17 00:00:00 2001 From: tdruez <489057+tdruez@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:58:52 +0400 Subject: [PATCH 2/6] Bump version for v34.10.0 release (#1637) Signed-off-by: tdruez Signed-off-by: Model Monster --- CHANGELOG.rst | 4 ++-- RELEASE.md | 2 +- scancodeio/__init__.py | 2 +- setup.cfg | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1d0d83fac..e61192d58 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,8 +1,8 @@ Changelog ========= -v34.9.6 (unreleased) --------------------- +v34.10.0 (2025-03-21) +--------------------- - Rename the ``docker``, ``docker_windows``, and ``root_filesystem`` modules to ``analyze_docker``, ``analyze_docker_windows``, and ``analyze_root_filesystem`` diff --git a/RELEASE.md b/RELEASE.md index 20ed80362..580738d98 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -9,7 +9,7 @@ - `CHANGELOG.rst` (set date) - Commit and push this branch - Create a PR and merge once approved -- Tag and push that tag. This will triggers the `pypi-release.yml` GitHub workflow that +- Tag and push that tag. This will trigger the `pypi-release.yml` GitHub workflow that takes care of building the dist release files and upload those to pypi: ``` VERSION=vx.x.x # <- Set the new version here diff --git a/scancodeio/__init__.py b/scancodeio/__init__.py index 1492250d7..8206c539c 100644 --- a/scancodeio/__init__.py +++ b/scancodeio/__init__.py @@ -28,7 +28,7 @@ import git -VERSION = "34.9.5" +VERSION = "34.10.0" PROJECT_DIR = Path(__file__).resolve().parent ROOT_DIR = PROJECT_DIR.parent diff --git a/setup.cfg b/setup.cfg index af35a17ea..928098303 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = scancodeio -version = 34.9.5 +version = 34.10.0 license = Apache-2.0 description = Automate software composition analysis pipelines long_description = file:README.rst @@ -159,7 +159,7 @@ scancodeio_pipelines = [bumpver] version_pattern = "MAJOR.MINOR.PATCH" -current_version = "34.9.5" +current_version = "34.10.0" [bumpver:file_patterns] setup.cfg = From 1bc14ebcc9ad09bcfd29103c321966ed85430fde Mon Sep 17 00:00:00 2001 From: tdruez Date: Fri, 21 Mar 2025 15:02:18 +0400 Subject: [PATCH 3/6] Truncate the task output in get_slack_payload to block message limit Signed-off-by: tdruez Signed-off-by: Model Monster --- scanpipe/models.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scanpipe/models.py b/scanpipe/models.py index fcdb8604a..388c8a4b2 100644 --- a/scanpipe/models.py +++ b/scanpipe/models.py @@ -4203,11 +4203,19 @@ def get_slack_payload(pipeline_run): # Adds the task output in case of run failure if pipeline_run.status == Run.Status.FAILURE and pipeline_run.task_output: + slack_text_max_length = 3000 # Slack's block message limit + output_text = pipeline_run.task_output + # Truncate the task output and add an indicator if it was cut off + if len(output_text) > slack_text_max_length: + output_text = ( + output_text[: slack_text_max_length - 30] + "\n... (truncated)" + ) + task_output_block = { "type": "section", "text": { "type": "mrkdwn", - "text": f"```{pipeline_run.task_output}```", + "text": f"```{output_text}```", }, } blocks.append(task_output_block) From e8ab39a05b8787e611e3158eb3fd915f8b0ba7cb Mon Sep 17 00:00:00 2001 From: Model Monster Date: Mon, 24 Mar 2025 14:24:44 -0500 Subject: [PATCH 4/6] Use fully qualified image names in docker-compose files Signed-off-by: Model Monster --- docker-compose-offline.yml | 6 +++--- docker-compose.yml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docker-compose-offline.yml b/docker-compose-offline.yml index 2bf7c96d3..d26d12100 100644 --- a/docker-compose-offline.yml +++ b/docker-compose-offline.yml @@ -1,13 +1,13 @@ services: db: - image: postgres:13 + image: docker.io/library/postgres:13 env_file: - docker.env volumes: - db_data:/var/lib/postgresql/data/ redis: - image: redis + image: docker.io/library/redis:latest # Enable redis data persistence using the "Append Only File" with the # default policy of fsync every second. See https://redis.io/topics/persistence command: redis-server --appendonly yes @@ -45,7 +45,7 @@ services: - web # Ensure that potential db migrations run first nginx: - image: nginx + image: docker.io/library/nginx:latest ports: - 80:80 - 443:443 diff --git a/docker-compose.yml b/docker-compose.yml index f494dd174..c05453ebb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: db: - image: postgres:13 + image: docker.io/library/postgres:13 env_file: - docker.env volumes: @@ -9,7 +9,7 @@ services: restart: always redis: - image: redis + image: docker.io/library/redis:latest # Enable redis data persistence using the "Append Only File" with the # default policy of fsync every second. See https://redis.io/topics/persistence command: redis-server --appendonly yes @@ -54,7 +54,7 @@ services: - web nginx: - image: nginx:alpine + image: docker.io/library/nginx:alpine ports: - "${NGINX_PUBLISHED_HTTP_PORT:-80}:80" - "${NGINX_PUBLISHED_HTTPS_PORT:-443}:443" @@ -67,7 +67,7 @@ services: restart: always clamav: - image: clamav/clamav + image: docker.io/clamav/clamav:latest volumes: - clamav_data:/var/lib/clamav - workspace:/var/scancodeio/workspace/ From 89a9fa2f6522fa162aceb2b95582347588e93314 Mon Sep 17 00:00:00 2001 From: Model Monster Date: Mon, 24 Mar 2025 15:20:08 -0500 Subject: [PATCH 5/6] Add name property to docker-compose files to ensure consistent container naming Signed-off-by: Model Monster --- docker-compose-offline.yml | 1 + docker-compose.dev.yml | 1 + docker-compose.purldb-scan-worker.yml | 1 + docker-compose.yml | 1 + 4 files changed, 4 insertions(+) diff --git a/docker-compose-offline.yml b/docker-compose-offline.yml index d26d12100..d64203524 100644 --- a/docker-compose-offline.yml +++ b/docker-compose-offline.yml @@ -1,3 +1,4 @@ +name: scancodeio services: db: image: docker.io/library/postgres:13 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 51bf7d4d0..597518a92 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -6,6 +6,7 @@ # $ docker compose -f docker-compose.yml -f docker-compose.dev.yml run --rm web bash # $ SCANCODEIO_TEST_FIXTURES_REGEN=1 ./manage.py test +name: scancodeio services: web: env_file: diff --git a/docker-compose.purldb-scan-worker.yml b/docker-compose.purldb-scan-worker.yml index 065eb779f..3636b26a3 100644 --- a/docker-compose.purldb-scan-worker.yml +++ b/docker-compose.purldb-scan-worker.yml @@ -1,6 +1,7 @@ include: - docker-compose.yml +name: scancodeio services: purldb_scan_worker: build: . diff --git a/docker-compose.yml b/docker-compose.yml index c05453ebb..321bc0f30 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,4 @@ +name: scancodeio services: db: image: docker.io/library/postgres:13 From 6e74f9ff22207acdaed99ae8560615f8af41efdd Mon Sep 17 00:00:00 2001 From: Model Monster Date: Mon, 24 Mar 2025 15:53:43 -0500 Subject: [PATCH 6/6] Add COMPOSE_PROJECT_NAME to .env file creation Signed-off-by: Model Monster --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index d09b2f6cc..d2ea5bf32 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,7 @@ envfile: @if test -f ${ENV_FILE}; then echo ".env file exists already"; exit 1; fi @mkdir -p $(shell dirname ${ENV_FILE}) && touch ${ENV_FILE} @echo SECRET_KEY=\"${GET_SECRET_KEY}\" > ${ENV_FILE} + @echo COMPOSE_PROJECT_NAME=\"scancodeio\" >> ${ENV_FILE} doc8: @echo "-> Run doc8 validation"