Skip to content

Commit 6db790c

Browse files
authored
feat: move scanpipe settings to local settings.py with lazy resolution (#2187)
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent 9606cb3 commit 6db790c

27 files changed

Lines changed: 360 additions & 233 deletions

scancodeio/settings.py

Lines changed: 8 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@
6363
"SCANCODEIO_REQUIRE_AUTHENTICATION", default=False
6464
)
6565

66-
SCANCODEIO_ENABLE_ADMIN_SITE = env.bool("SCANCODEIO_ENABLE_ADMIN_SITE", default=False)
67-
6866
SECURE_CONTENT_TYPE_NOSNIFF = env.bool("SECURE_CONTENT_TYPE_NOSNIFF", default=True)
6967

7068
X_FRAME_OPTIONS = env.str("X_FRAME_OPTIONS", default="DENY")
@@ -79,102 +77,8 @@
7977

8078
# ScanCode.io
8179

82-
SCANCODEIO_WORKSPACE_LOCATION = env.str("SCANCODEIO_WORKSPACE_LOCATION", default="var")
83-
84-
SCANCODEIO_CONFIG_DIR = env.str("SCANCODEIO_CONFIG_DIR", default=".scancode")
85-
86-
SCANCODEIO_CONFIG_FILE = env.str(
87-
"SCANCODEIO_CONFIG_FILE", default="scancode-config.yml"
88-
)
89-
9080
SCANCODEIO_LOG_LEVEL = env.str("SCANCODEIO_LOG_LEVEL", "INFO")
9181

92-
# Set the number of parallel processes to use for ScanCode related scan execution.
93-
# If the SCANCODEIO_PROCESSES argument is not set, defaults to an optimal number of CPUs
94-
# available on the machine.
95-
SCANCODEIO_PROCESSES = env.int("SCANCODEIO_PROCESSES", default=None)
96-
97-
SCANCODEIO_POLICIES_FILE = env.str("SCANCODEIO_POLICIES_FILE", default="policies.yml")
98-
99-
# This setting defines the additional locations ScanCode.io will search for pipelines.
100-
# This should be set to a list of strings that contain full paths to your additional
101-
# pipelines directories.
102-
SCANCODEIO_PIPELINES_DIRS = env.list("SCANCODEIO_PIPELINES_DIRS", default=[])
103-
104-
# Maximum time allowed for a pipeline to complete.
105-
SCANCODEIO_TASK_TIMEOUT = env.str("SCANCODEIO_TASK_TIMEOUT", default="24h")
106-
107-
# Default to 2 minutes.
108-
SCANCODEIO_SCAN_FILE_TIMEOUT = env.int("SCANCODEIO_SCAN_FILE_TIMEOUT", default=120)
109-
110-
# Default to None which scans all files
111-
SCANCODEIO_SCAN_MAX_FILE_SIZE = env.int("SCANCODEIO_SCAN_MAX_FILE_SIZE", default=None)
112-
113-
# List views pagination, controls the number of items displayed per page.
114-
# Syntax in .env: SCANCODEIO_PAGINATE_BY=project=10,project_error=10
115-
SCANCODEIO_PAGINATE_BY = env.dict(
116-
"SCANCODEIO_PAGINATE_BY",
117-
default={
118-
"project": 20,
119-
"error": 50,
120-
"resource": 100,
121-
"package": 100,
122-
"dependency": 100,
123-
"license": 100,
124-
"relation": 100,
125-
},
126-
)
127-
128-
# Default limit for "most common" entries in QuerySets.
129-
SCANCODEIO_MOST_COMMON_LIMIT = env.int("SCANCODEIO_MOST_COMMON_LIMIT", default=7)
130-
131-
# The base URL (e.g., https://hostname/) of this application instance.
132-
# Required for generating URLs to reference objects within the app,
133-
# such as in webhook notifications.
134-
SCANCODEIO_SITE_URL = env.str("SCANCODEIO_SITE_URL", default="")
135-
136-
# Fetch authentication credentials
137-
138-
# SCANCODEIO_FETCH_BASIC_AUTH="host=user,password;"
139-
SCANCODEIO_FETCH_BASIC_AUTH = env.dict(
140-
"SCANCODEIO_FETCH_BASIC_AUTH",
141-
cast={"value": tuple},
142-
default={},
143-
)
144-
145-
# SCANCODEIO_FETCH_DIGEST_AUTH="host=user,password;"
146-
SCANCODEIO_FETCH_DIGEST_AUTH = env.dict(
147-
"SCANCODEIO_FETCH_DIGEST_AUTH",
148-
cast={"value": tuple},
149-
default={},
150-
)
151-
152-
# SCANCODEIO_FETCH_HEADERS="host=Header1=value,Header2=value;"
153-
SCANCODEIO_FETCH_HEADERS = {}
154-
FETCH_HEADERS_STR = env.str("SCANCODEIO_FETCH_HEADERS", default="")
155-
for entry in FETCH_HEADERS_STR.split(";"):
156-
if entry.strip():
157-
host, headers = entry.split("=", 1)
158-
SCANCODEIO_FETCH_HEADERS[host] = env.parse_value(headers, cast=dict)
159-
160-
# SCANCODEIO_NETRC_LOCATION="~/.netrc"
161-
SCANCODEIO_NETRC_LOCATION = env.str("SCANCODEIO_NETRC_LOCATION", default="")
162-
if SCANCODEIO_NETRC_LOCATION:
163-
# Propagate the location to the environ for `requests.utils.get_netrc_auth`
164-
env.ENVIRON["NETRC"] = SCANCODEIO_NETRC_LOCATION
165-
166-
# SCANCODEIO_SKOPEO_CREDENTIALS="host1=user:password,host2=user:password"
167-
SCANCODEIO_SKOPEO_CREDENTIALS = env.dict("SCANCODEIO_SKOPEO_CREDENTIALS", default={})
168-
169-
# SCANCODEIO_SKOPEO_AUTHFILE_LOCATION="/path/to/auth.json"
170-
SCANCODEIO_SKOPEO_AUTHFILE_LOCATION = env.str(
171-
"SCANCODEIO_SKOPEO_AUTHFILE_LOCATION", default=""
172-
)
173-
174-
# This webhook will be added as WebhookSubscription for each new project.
175-
# SCANCODEIO_GLOBAL_WEBHOOK=target_url=https://webhook.url,trigger_on_each_run=False,include_summary=True,include_results=False
176-
SCANCODEIO_GLOBAL_WEBHOOK = env.dict("SCANCODEIO_GLOBAL_WEBHOOK", default={})
177-
17882
# Application definition
17983

18084
INSTALLED_APPS = [
@@ -288,11 +192,13 @@
288192
from django.core.management.utils import get_random_secret_key
289193

290194
SECRET_KEY = get_random_secret_key()
291-
# Do not pollute the workspace while running the tests.
292-
SCANCODEIO_WORKSPACE_LOCATION = tempfile.mkdtemp()
195+
SCANPIPE = {
196+
# Do not pollute the workspace while running the tests.
197+
"WORKSPACE_LOCATION": tempfile.mkdtemp(),
198+
"SCAN_FILE_TIMEOUT": 120,
199+
"POLICIES_FILE": None,
200+
}
293201
SCANCODEIO_REQUIRE_AUTHENTICATION = True
294-
SCANCODEIO_SCAN_FILE_TIMEOUT = 120
295-
SCANCODEIO_POLICIES_FILE = None
296202
# The default password hasher is rather slow by design.
297203
# Using a faster hashing algorithm in the testing context to speed up the run.
298204
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
@@ -389,8 +295,8 @@
389295
},
390296
}
391297

392-
SCANCODEIO_ASYNC = env.bool("SCANCODEIO_ASYNC", default=False)
393-
if not SCANCODEIO_ASYNC:
298+
# Runtime async mode is accessed via scanpipe_settings.ASYNC
299+
if not env.bool("SCANCODEIO_ASYNC", default=False):
394300
for queue_config in RQ_QUEUES.values():
395301
queue_config["ASYNC"] = False
396302

scancodeio/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from scanpipe.admin import admin_site
3232
from scanpipe.api.views import ProjectViewSet
3333
from scanpipe.api.views import RunViewSet
34+
from scanpipe.settings import scanpipe_settings
3435
from scanpipe.views import AccountProfileView
3536
from scanpipe.views import GenerateAPIKeyView
3637
from scanpipe.views import RevokeAPIKeyView
@@ -67,7 +68,7 @@
6768
]
6869

6970

70-
if settings.SCANCODEIO_ENABLE_ADMIN_SITE:
71+
if scanpipe_settings.ENABLE_ADMIN_SITE:
7172
urlpatterns.append(path("admin/", admin_site.urls))
7273

7374

scanpipe/apps.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@
2323
import importlib.util
2424
import inspect
2525
import logging
26+
import os
2627
import sys
2728
import warnings
2829
from importlib.machinery import SourceFileLoader
2930
from pathlib import Path
3031

3132
from django.apps import AppConfig
32-
from django.conf import settings
3333
from django.core.exceptions import ImproperlyConfigured
3434
from django.core.management.color import color_style
3535
from django.db.models import BLANK_CHOICE_DASH
@@ -39,6 +39,7 @@
3939
from licensedcode.models import load_licenses
4040

4141
from scanpipe.policies import load_policies_file
42+
from scanpipe.settings import scanpipe_settings
4243

4344
try:
4445
from importlib import metadata as importlib_metadata
@@ -62,21 +63,24 @@ def __init__(self, app_name, app_module):
6263
self._pipelines = {}
6364
self.policies = {}
6465

65-
workspace_location = settings.SCANCODEIO_WORKSPACE_LOCATION
66+
workspace_location = scanpipe_settings.WORKSPACE_LOCATION
6667
self.workspace_path = Path(workspace_location).expanduser().resolve()
6768

6869
def ready(self):
69-
logger.debug(f"Read environment variables from {settings.ENV_FILE}")
7070
self.load_pipelines()
7171
self.set_policies()
7272

73+
if netrc_location := scanpipe_settings.NETRC_LOCATION:
74+
# Propagate the location to the environ for `requests.utils.get_netrc_auth`
75+
os.environ["NETRC"] = netrc_location
76+
7377
# In SYNC mode, the Run instances cleanup is triggered on app.ready()
7478
# only when the app is started through "runserver".
7579
# This cleanup is required if a running pipeline process gets killed and
7680
# since KeyboardInterrupt cannot be captured to properly update the Run instance
7781
# before its running process death.
7882
# In ASYNC mode, the cleanup is handled by the "ScanCodeIOWorker" worker.
79-
if not settings.SCANCODEIO_ASYNC and "runserver" in sys.argv:
83+
if not scanpipe_settings.ASYNC and "runserver" in sys.argv:
8084
warnings.filterwarnings(
8185
"ignore",
8286
message="Accessing the database during app initialization",
@@ -96,7 +100,7 @@ def load_pipelines(self):
96100
for entry_point in sorted(pipeline_entry_points):
97101
self.register_pipeline(name=entry_point.name, cls=entry_point.load())
98102

99-
pipelines_dirs = getattr(settings, "SCANCODEIO_PIPELINES_DIRS", [])
103+
pipelines_dirs = scanpipe_settings.PIPELINES_DIRS
100104
logger.debug(f"Load user provided pipelines from {pipelines_dirs}")
101105

102106
for pipelines_dir in pipelines_dirs:
@@ -231,7 +235,7 @@ def set_policies(self):
231235
include the proper content, we want to raise an exception while the app
232236
is loading to warn system admins about the issue.
233237
"""
234-
policies_file_setting = getattr(settings, "SCANCODEIO_POLICIES_FILE", None)
238+
policies_file_setting = scanpipe_settings.POLICIES_FILE
235239
if not policies_file_setting:
236240
return
237241

@@ -259,5 +263,5 @@ def sync_runs_and_jobs(self):
259263

260264
@property
261265
def site_url(self):
262-
if site_url := settings.SCANCODEIO_SITE_URL:
266+
if site_url := scanpipe_settings.SITE_URL:
263267
return site_url.rstrip("/")

scanpipe/management/commands/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from pathlib import Path
2626

2727
from django.apps import apps
28-
from django.conf import settings
2928
from django.core.exceptions import ObjectDoesNotExist
3029
from django.core.exceptions import ValidationError
3130
from django.core.management.base import BaseCommand
@@ -39,6 +38,7 @@
3938
from scanpipe.models import ProjectMessage
4039
from scanpipe.pipes import count_group_by
4140
from scanpipe.pipes.fetch import set_project_purl_from_input_url
41+
from scanpipe.settings import scanpipe_settings
4242

4343
scanpipe_app = apps.get_app_config("scanpipe")
4444

@@ -405,7 +405,7 @@ def execute_project(project, run_async=False, command=None): # noqa: C901
405405
raise CommandError(f"No pipelines to run on project {project}")
406406

407407
if run_async:
408-
if not settings.SCANCODEIO_ASYNC:
408+
if not scanpipe_settings.ASYNC:
409409
msg = "SCANCODEIO_ASYNC=False is not compatible with --async option."
410410
raise CommandError(msg)
411411

scanpipe/migrations/0031_scancode_toolkit_v32_data_updates.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def compute_package_declared_license_expression_spdx(apps, schema_editor):
1616
"""
1717
from licensedcode.cache import build_spdx_license_expression
1818

19-
if settings.IS_TESTS:
19+
if getattr(settings, "IS_TESTS", False):
2020
return
2121

2222
DiscoveredPackage = apps.get_model("scanpipe", "DiscoveredPackage")
@@ -55,7 +55,7 @@ def compute_resource_detected_license_expression(apps, schema_editor):
5555
from license_expression import combine_expressions
5656
from licensedcode.cache import build_spdx_license_expression
5757

58-
if settings.IS_TESTS:
58+
if getattr(settings, "IS_TESTS", False):
5959
return
6060

6161
CodebaseResource = apps.get_model("scanpipe", "CodebaseResource")
@@ -161,7 +161,7 @@ def _convert_matches_to_detections(license_matches):
161161

162162
def compute_resource_license_detections(apps, schema_editor):
163163
"""Compute CodebaseResource `license_detections` from old `licenses` field."""
164-
if settings.IS_TESTS:
164+
if getattr(settings, "IS_TESTS", False):
165165
return
166166

167167
CodebaseResource = apps.get_model("scanpipe", "CodebaseResource")

0 commit comments

Comments
 (0)