Skip to content

Commit 4ec4e6b

Browse files
committed
Refactor policies implementation to support more than licenses
Signed-off-by: tdruez <[email protected]>
1 parent b06bb87 commit 4ec4e6b

File tree

8 files changed

+66
-69
lines changed

8 files changed

+66
-69
lines changed

scancodeio/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@
292292
SCANCODEIO_WORKSPACE_LOCATION = tempfile.mkdtemp()
293293
SCANCODEIO_REQUIRE_AUTHENTICATION = True
294294
SCANCODEIO_SCAN_FILE_TIMEOUT = 120
295+
SCANCODEIO_POLICIES_FILE = None
295296
# The default password hasher is rather slow by design.
296297
# Using a faster hashing algorithm in the testing context to speed up the run.
297298
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]

scanpipe/apps.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
from licensedcode.models import load_licenses
4040

4141
from scanpipe.policies import load_policies_file
42-
from scanpipe.policies import make_license_policy_index
4342

4443
try:
4544
from importlib import metadata as importlib_metadata
@@ -61,7 +60,7 @@ def __init__(self, app_name, app_module):
6160

6261
# Mapping of registered pipeline names to pipeline classes.
6362
self._pipelines = {}
64-
self.license_policies_index = {}
63+
self.policies = {}
6564

6665
workspace_location = settings.SCANCODEIO_WORKSPACE_LOCATION
6766
self.workspace_path = Path(workspace_location).expanduser().resolve()
@@ -226,7 +225,7 @@ def get_scancode_licenses(self):
226225

227226
def set_policies(self):
228227
"""
229-
Compute and sets the `license_policies` on the app instance.
228+
Set the global app policies on the app instance.
230229
231230
If the policies file is available but not formatted properly or doesn't
232231
include the proper content, we want to raise an exception while the app
@@ -240,7 +239,7 @@ def set_policies(self):
240239
if policies_file.exists():
241240
policies = load_policies_file(policies_file)
242241
logger.debug(style.SUCCESS(f"Loaded policies from {policies_file}"))
243-
self.license_policies_index = make_license_policy_index(policies)
242+
self.policies = policies
244243
else:
245244
logger.debug(style.WARNING("Policies file not found."))
246245

scanpipe/models.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,32 +1495,35 @@ def has_single_resource(self):
14951495
"""
14961496
return self.resource_count == 1
14971497

1498-
def get_policy_index(self):
1498+
def get_policies_dict(self):
14991499
"""
1500-
Return the policy index for this project instance.
1500+
Load and return the policies from the following locations in that order:
15011501
1502-
The policies are loaded from the following locations in that order:
1503-
1. the project local settings
1504-
2. the "policies.yml" file in the project input/ directory
1505-
3. the global app settings license policies
1502+
1. project local settings (stored in the database)
1503+
2. "policies.yml" file in the project ``input/`` directory
1504+
3. global app settings policies, from SCANCODEIO_POLICIES_FILE setting
15061505
"""
15071506
if policies_from_settings := self.get_env("policies"):
15081507
policies_dict = policies_from_settings
15091508
if isinstance(policies_from_settings, str):
15101509
policies_dict = policies.load_policies_yaml(policies_from_settings)
1511-
return policies.make_license_policy_index(policies_dict)
1510+
return policies_dict
15121511

1513-
elif policies_file := self.get_input_policies_file():
1514-
policies_dict = policies.load_policies_file(policies_file)
1515-
return policies.make_license_policy_index(policies_dict)
1512+
elif project_input_policies_file := self.get_input_policies_file():
1513+
return policies.load_policies_file(project_input_policies_file)
15161514

1517-
else:
1518-
return scanpipe_app.license_policies_index
1515+
return scanpipe_app.policies
1516+
1517+
def get_license_policy_index(self):
1518+
"""Return the policy license index for this project instance."""
1519+
if policies_dict := self.get_policies_dict():
1520+
return policies.make_license_policy_index(policies_dict)
1521+
return {}
15191522

15201523
@cached_property
15211524
def policy_index(self):
1522-
"""Return the cached policy index for this project instance."""
1523-
return self.get_policy_index()
1525+
"""Return the cached license policy index for this project instance."""
1526+
return self.get_license_policy_index()
15241527

15251528
@property
15261529
def policies_enabled(self):

scanpipe/tests/pipes/test_scancode.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,8 @@ def test_scanpipe_max_file_size_works(self):
467467
self.assertEqual(resource1.status, flag.IGNORED_BY_MAX_FILE_SIZE)
468468

469469
def test_scanpipe_pipes_scancode_make_results_summary(self, regen=FIXTURES_REGEN):
470-
# Ensure the policies index is empty to avoid any side effect on results
471-
scanpipe_app.license_policies_index = None
470+
# Ensure the policies are empty to avoid any side effect on results
471+
scanpipe_app.policies = None
472472
# Run the scan_single_package pipeline to have a proper DB and local files setup
473473
pipeline_name = "scan_single_package"
474474
project1 = Project.objects.create(name="Analysis")

scanpipe/tests/test_apps.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import uuid
2424
from pathlib import Path
2525
from unittest import mock
26+
from unittest.mock import patch
2627

2728
from django.apps import apps
2829
from django.core.exceptions import ImproperlyConfigured
@@ -33,7 +34,7 @@
3334
from scanpipe.models import Project
3435
from scanpipe.models import Run
3536
from scanpipe.tests import filter_warnings
36-
from scanpipe.tests import license_policies_index
37+
from scanpipe.tests import global_policies
3738
from scanpipe.tests.pipelines.register_from_file import RegisterFromFile
3839

3940
scanpipe_app = apps.get_app_config("scanpipe")
@@ -43,26 +44,23 @@ class ScanPipeAppsTest(TestCase):
4344
data = Path(__file__).parent / "data"
4445
pipelines_location = Path(__file__).parent / "pipelines"
4546

46-
def test_scanpipe_apps_set_policies(self):
47-
scanpipe_app.license_policies_index = {}
48-
policies_files = None
49-
with override_settings(SCANCODEIO_POLICIES_FILE=policies_files):
47+
@patch.object(scanpipe_app, "policies", new_callable=dict)
48+
def test_scanpipe_apps_set_policies(self, mock_policies):
49+
# Case 1: No file set
50+
with override_settings(SCANCODEIO_POLICIES_FILE=None):
5051
scanpipe_app.set_policies()
51-
self.assertEqual({}, scanpipe_app.license_policies_index)
52+
self.assertEqual({}, scanpipe_app.policies)
5253

53-
scanpipe_app.license_policies_index = {}
54-
policies_files = "not_existing"
55-
with override_settings(SCANCODEIO_POLICIES_FILE=policies_files):
54+
# Case 2: Non-existing file
55+
with override_settings(SCANCODEIO_POLICIES_FILE="not_existing"):
5656
scanpipe_app.set_policies()
57-
self.assertEqual({}, scanpipe_app.license_policies_index)
57+
self.assertEqual({}, scanpipe_app.policies)
5858

59-
scanpipe_app.license_policies_index = {}
59+
# Case 3: Valid file
6060
policies_files = self.data / "policies" / "policies.yml"
6161
with override_settings(SCANCODEIO_POLICIES_FILE=str(policies_files)):
6262
scanpipe_app.set_policies()
63-
self.assertEqual(
64-
license_policies_index, scanpipe_app.license_policies_index
65-
)
63+
self.assertEqual(global_policies, scanpipe_app.policies)
6664

6765
def test_scanpipe_apps_register_pipeline_from_file(self):
6866
path = self.pipelines_location / "do_nothing.py"

scanpipe/tests/test_forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def test_scanpipe_forms_project_settings_form_policies(self):
230230
self.assertTrue(form.is_valid())
231231
project = form.save()
232232
self.assertEqual(policies_as_yaml.strip(), project.settings["policies"])
233-
self.assertEqual(license_policies_index, project.get_policy_index())
233+
self.assertEqual(license_policies_index, project.get_license_policy_index())
234234

235235
def test_scanpipe_forms_project_settings_form_purl(self):
236236
data_invalid_purl = {

scanpipe/tests/test_models.py

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
from scanpipe.pipes.input import copy_input
7676
from scanpipe.tests import dependency_data1
7777
from scanpipe.tests import dependency_data2
78+
from scanpipe.tests import global_policies
7879
from scanpipe.tests import license_policies_index
7980
from scanpipe.tests import make_dependency
8081
from scanpipe.tests import make_message
@@ -697,21 +698,21 @@ def test_scanpipe_project_get_input_policies_file(self):
697698
policies_file_location = str(self.project1.get_input_policies_file())
698699
self.assertTrue(policies_file_location.endswith("input/policies.yml"))
699700

700-
def test_scanpipe_project_model_get_policy_index(self):
701-
scanpipe_app.license_policies_index = None
702-
self.assertFalse(self.project1.policies_enabled)
703-
704-
policies_from_app_settings = {"from": "scanpipe_app"}
705-
scanpipe_app.license_policies_index = policies_from_app_settings
706-
self.assertEqual(policies_from_app_settings, self.project1.get_policy_index())
701+
@patch.object(scanpipe_app, "policies", new=global_policies)
702+
def test_scanpipe_project_model_get_license_policy_index(self):
703+
self.assertEqual(
704+
license_policies_index, self.project1.get_license_policy_index()
705+
)
707706

708707
policies_from_input_dir = {"license_policies": [{"license_key": "input_dir"}]}
709708
policies_file = self.project1.input_path / "policies.yml"
710709
policies_file.touch()
711710
policies_as_yaml = saneyaml.dump(policies_from_input_dir)
712711
policies_file.write_text(policies_as_yaml)
713712
expected_index_from_input = {"input_dir": {"license_key": "input_dir"}}
714-
self.assertEqual(expected_index_from_input, self.project1.get_policy_index())
713+
self.assertEqual(
714+
expected_index_from_input, self.project1.get_license_policy_index()
715+
)
715716
# Refresh the instance to bypass the cached_property cache.
716717
self.project1 = Project.objects.get(uuid=self.project1.uuid)
717718
self.assertTrue(self.project1.policies_enabled)
@@ -723,10 +724,9 @@ def test_scanpipe_project_model_get_policy_index(self):
723724
self.project1.settings = config
724725
self.project1.save()
725726
expected_index_from_env = {"project_env": {"license_key": "project_env"}}
726-
self.assertEqual(expected_index_from_env, self.project1.get_policy_index())
727-
728-
# Reset the index value
729-
scanpipe_app.license_policies_index = None
727+
self.assertEqual(
728+
expected_index_from_env, self.project1.get_license_policy_index()
729+
)
730730

731731
def test_scanpipe_project_get_settings_as_yml(self):
732732
self.assertEqual("{}\n", self.project1.get_settings_as_yml())
@@ -1547,28 +1547,31 @@ def test_scanpipe_codebase_resource_model_commoncode_methods_extracted_to_from(
15471547
self.assertEqual(extracted_dir_resource, archive_resource.extracted_to())
15481548
self.assertEqual(archive_resource, extracted_dir_resource.extracted_from())
15491549

1550+
@patch.object(scanpipe_app, "policies", new=global_policies)
15501551
def test_scanpipe_codebase_resource_model_compliance_alert(self):
1551-
scanpipe_app.license_policies_index = license_policies_index
1552+
project_license_policies_index = self.project1.policy_index
1553+
self.assertEqual(license_policies_index, project_license_policies_index)
1554+
15521555
resource = CodebaseResource.objects.create(project=self.project1, path="file")
15531556
self.assertEqual("", resource.compliance_alert)
15541557

15551558
license_expression = "bsd-new"
1556-
self.assertNotIn(license_expression, scanpipe_app.license_policies_index)
1559+
self.assertNotIn(license_expression, project_license_policies_index)
15571560
resource.update(detected_license_expression=license_expression)
15581561
self.assertEqual("missing", resource.compliance_alert)
15591562

15601563
license_expression = "apache-2.0"
1561-
self.assertIn(license_expression, scanpipe_app.license_policies_index)
1564+
self.assertIn(license_expression, project_license_policies_index)
15621565
resource.update(detected_license_expression=license_expression)
15631566
self.assertEqual("ok", resource.compliance_alert)
15641567

15651568
license_expression = "mpl-2.0"
1566-
self.assertIn(license_expression, scanpipe_app.license_policies_index)
1569+
self.assertIn(license_expression, project_license_policies_index)
15671570
resource.update(detected_license_expression=license_expression)
15681571
self.assertEqual("warning", resource.compliance_alert)
15691572

15701573
license_expression = "gpl-3.0"
1571-
self.assertIn(license_expression, scanpipe_app.license_policies_index)
1574+
self.assertIn(license_expression, project_license_policies_index)
15721575
resource.update(detected_license_expression=license_expression)
15731576
self.assertEqual("error", resource.compliance_alert)
15741577

@@ -1584,11 +1587,8 @@ def test_scanpipe_codebase_resource_model_compliance_alert(self):
15841587
resource.update(detected_license_expression=license_expression)
15851588
self.assertEqual("warning", resource.compliance_alert)
15861589

1587-
# Reset the index value
1588-
scanpipe_app.license_policies_index = None
1589-
1590+
@patch.object(scanpipe_app, "policies", new=global_policies)
15901591
def test_scanpipe_codebase_resource_model_compliance_alert_update_fields(self):
1591-
scanpipe_app.license_policies_index = license_policies_index
15921592
resource = CodebaseResource.objects.create(project=self.project1, path="file")
15931593
self.assertEqual("", resource.compliance_alert)
15941594

@@ -1598,9 +1598,6 @@ def test_scanpipe_codebase_resource_model_compliance_alert_update_fields(self):
15981598
resource.refresh_from_db()
15991599
self.assertEqual("ok", resource.compliance_alert)
16001600

1601-
# Reset the index value
1602-
scanpipe_app.license_policies_index = None
1603-
16041601
def test_scanpipe_scan_fields_model_mixin_methods(self):
16051602
expected = [
16061603
"detected_license_expression",
@@ -2422,30 +2419,27 @@ def test_scanpipe_discovered_package_model_as_cyclonedx(self):
24222419
cyclonedx_component.evidence.licenses[0].value,
24232420
)
24242421

2422+
@patch.object(scanpipe_app, "policies", new=global_policies)
24252423
def test_scanpipe_discovered_package_model_compliance_alert(self):
2426-
scanpipe_app.license_policies_index = license_policies_index
24272424
package_data = package_data1.copy()
24282425
package_data["declared_license_expression"] = ""
24292426
package = DiscoveredPackage.create_from_data(self.project1, package_data)
24302427
self.assertEqual("", package.compliance_alert)
24312428

24322429
license_expression = "bsd-new"
2433-
self.assertNotIn(license_expression, scanpipe_app.license_policies_index)
2430+
self.assertNotIn(license_expression, self.project1.policy_index)
24342431
package.update(declared_license_expression=license_expression)
24352432
self.assertEqual("missing", package.compliance_alert)
24362433

24372434
license_expression = "apache-2.0"
2438-
self.assertIn(license_expression, scanpipe_app.license_policies_index)
2435+
self.assertIn(license_expression, self.project1.policy_index)
24392436
package.update(declared_license_expression=license_expression)
24402437
self.assertEqual("ok", package.compliance_alert)
24412438

24422439
license_expression = "apache-2.0 AND mpl-2.0 OR gpl-3.0"
24432440
package.update(declared_license_expression=license_expression)
24442441
self.assertEqual("error", package.compliance_alert)
24452442

2446-
# Reset the index value
2447-
scanpipe_app.license_policies_index = None
2448-
24492443
def test_scanpipe_discovered_package_model_spdx_id(self):
24502444
package1 = make_package(self.project1, "pkg:type/a")
24512445
expected = f"SPDXRef-scancodeio-discoveredpackage-{package1.uuid}"

scanpipe/tests/test_policies.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,12 @@ def test_scanpipe_policies_scan_codebase_pipeline_integration(self):
9898
pipeline = run.make_pipeline_instance()
9999

100100
# Capture the real method's return value
101-
real_get_policy_index = project1.get_policy_index
101+
real_get_license_policy_index = project1.get_license_policy_index
102102

103-
with mock.patch("scanpipe.models.Project.get_policy_index") as mock_get_index:
104-
mock_get_index.side_effect = real_get_policy_index
103+
with mock.patch(
104+
"scanpipe.models.Project.get_license_policy_index"
105+
) as mock_get_index:
106+
mock_get_index.side_effect = real_get_license_policy_index
105107
exitcode, out = pipeline.execute()
106108
mock_get_index.assert_called_once()
107109

@@ -132,7 +134,7 @@ def test_scanpipe_policies_scan_codebase_pipeline_integration(self):
132134
"apache-2.0": {"license_key": "apache-2.0", "compliance_alert": ""},
133135
"gpl-2.0": {"license_key": "gpl-2.0", "compliance_alert": "error"},
134136
}
135-
self.assertEqual(expected_index, project1.get_policy_index())
137+
self.assertEqual(expected_index, project1.get_license_policy_index())
136138

137139
def test_scanpipe_policies_through_scancode_config_file(self):
138140
project1 = make_project()

0 commit comments

Comments
 (0)