Skip to content

Commit aba9331

Browse files
authored
Add support for scancode-config.yml in codebase #1236 (#1243)
Signed-off-by: tdruez <tdruez@nexb.com>
1 parent ab8a50d commit aba9331

18 files changed

Lines changed: 145 additions & 67 deletions

CHANGELOG.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ v34.5.0 (unreleased)
5757
- Remove the ``scancode_license_score`` option from the Project configuration.
5858
https://github.com/nexB/scancode.io/issues/1231
5959

60+
- Remove the ``extract_recursively`` option from the Project configuration.
61+
https://github.com/nexB/scancode.io/issues/1236
62+
63+
- Add support for storing the scancode-config.yml file in codebase.
64+
The scancode-config.yml file can be provided as a project input, or can be located
65+
in the codebase/ immediate subdirectories. This allows to provide the configuration
66+
file as part of an input archive or a git clone for example.
67+
https://github.com/nexB/scancode.io/issues/1236
68+
6069
- Add support for CycloneDX SBOM component properties as generated by external tools.
6170
For example, the ``ResolvedUrl`` generated by cdxgen is now imported as the package
6271
``download_url``.

docs/custom-pipelines.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,6 @@ the file's directory in the :ref:`scancodeio_settings_pipelines_dirs`.
124124
cls.report_licenses_with_resources,
125125
)
126126
127-
# Set to True to extract recursively nested archives in archives.
128-
extract_recursively = False
129-
130127
# See https://jinja.palletsprojects.com/en/3.0.x/templates/ for documentation
131128
report_template = """
132129
{% for matched_text, paths in resources.items() -%}

docs/project-configuration.rst

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ Content of a ``scancode-config.yml`` file:
3838
3939
product_name: My Product Name
4040
product_version: '1.0'
41-
extract_recursively: yes
4241
ignored_patterns:
4342
- '*.tmp'
4443
- tests/*
@@ -66,15 +65,6 @@ product_version
6665

6766
The product version of this project, as specified within the DejaCode application.
6867

69-
extract_recursively
70-
^^^^^^^^^^^^^^^^^^^
71-
72-
Extract nested archives-in-archives recursively.
73-
**This is enabled by default on new projects.**
74-
75-
You can turn off this behavior in favor of a shallow extraction using
76-
``extract_recursively: no`` in your configuration file.
77-
7868
ignored_patterns
7969
^^^^^^^^^^^^^^^^
8070

scanpipe/forms.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -298,19 +298,11 @@ def prepare_value(self, value):
298298

299299
class ProjectSettingsForm(forms.ModelForm):
300300
settings_fields = [
301-
"extract_recursively",
302301
"ignored_patterns",
303302
"attribution_template",
304303
"product_name",
305304
"product_version",
306305
]
307-
extract_recursively = forms.BooleanField(
308-
label="Extract recursively",
309-
required=False,
310-
initial=True,
311-
help_text="Extract nested archives-in-archives recursively",
312-
widget=forms.CheckboxInput(attrs={"class": "checkbox mr-1"}),
313-
)
314306
ignored_patterns = ListTextarea(
315307
label="Ignored patterns",
316308
required=False,

scanpipe/models.py

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -753,12 +753,42 @@ def get_codebase_config_directory(self):
753753

754754
def get_input_config_file(self):
755755
"""
756-
Return the ``scancode-config.yml`` file from the input/ directory if
757-
available.
758-
"""
759-
config_file = self.input_path / settings.SCANCODEIO_CONFIG_FILE
760-
if config_file.exists():
761-
return config_file
756+
Return the ``scancode-config.yml`` file from the input/ directory
757+
or from the codebase/ immediate subdirectories.
758+
759+
Priority order:
760+
1. If a config file exists directly in the input/ directory, return it.
761+
2. If exactly one config file exists in a codebase/ immediate subdirectory,
762+
return it.
763+
3. If multiple config files are found in subdirectories, report an error.
764+
"""
765+
config_filename = settings.SCANCODEIO_CONFIG_FILE
766+
767+
# Check for the config file in the root of the input/ directory.
768+
root_config_file = self.input_path / config_filename
769+
if root_config_file.exists():
770+
return root_config_file
771+
772+
# Search for config files in immediate codebase/ subdirectories.
773+
subdir_config_files = list(self.codebase_path.glob(f"*/{config_filename}"))
774+
775+
# If exactly one config file is found in codebase/ subdirectories, return it.
776+
if len(subdir_config_files) == 1:
777+
return subdir_config_files[0]
778+
779+
# If multiple config files are found, report an error.
780+
if len(subdir_config_files) > 1:
781+
self.add_warning(
782+
f"More than one {config_filename} found. "
783+
f"Could not determine which one to use.",
784+
model="Project",
785+
details={
786+
"resources": [
787+
str(path.relative_to(self.work_path))
788+
for path in subdir_config_files
789+
]
790+
},
791+
)
762792

763793
def get_settings_as_yml(self):
764794
"""Return the ``settings`` file content as yml, suitable for a config file."""
@@ -774,17 +804,23 @@ def get_enabled_settings(self):
774804

775805
def get_env(self, field_name=None):
776806
"""
777-
Return the project environment loaded from the ``.scancode/config.yml`` config
778-
file, when available, and overriden by the ``settings`` model field.
807+
Return the project environment loaded from the ``scancode-config.yml`` config
808+
file, when available, and overridden by the ``settings`` model field.
779809
780810
``field_name`` can be provided to get a single entry from the env.
781811
"""
782812
env = {}
783813

784814
# 1. Load settings from config file when available.
785815
if config_file := self.get_input_config_file():
786-
with suppress(saneyaml.YAMLError):
816+
logger.info(f"Loading env from {config_file.relative_to(self.work_path)}")
817+
try:
787818
env = saneyaml.load(config_file.read_text())
819+
except saneyaml.YAMLError:
820+
self.add_error(
821+
f'Failed to load configuration from "{config_file}". '
822+
f"The file format is invalid."
823+
)
788824

789825
# 2. Update with defined values from the Project ``settings`` field.
790826
env.update(self.get_enabled_settings())

scanpipe/pipelines/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,12 +295,16 @@ def extract_archives(self):
295295

296296
extract_errors = scancode.extract_archives(
297297
location=self.project.codebase_path,
298-
recurse=self.env.get("extract_recursively", True),
298+
recurse=True,
299299
)
300300

301301
if extract_errors:
302302
self.add_error("\n".join(extract_errors))
303303

304+
# Reload the project env post-extraction as the scancode-config.yml file
305+
# may be located in one of the extracted archives.
306+
self.env = self.project.get_env()
307+
304308

305309
def is_pipeline(obj):
306310
"""

scanpipe/pipelines/deploy_to_develop.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ def extract_inputs_to_codebase_directory(self):
135135
if errors:
136136
self.add_error("\n".join(errors))
137137

138+
# Reload the project env post-extraction as the scancode-config.yml file
139+
# may be located in one of the extracted archives.
140+
self.env = self.project.get_env()
141+
138142
def collect_and_create_codebase_resources(self):
139143
"""Collect and create codebase resources."""
140144
pipes.collect_and_create_codebase_resources(self.project)

scanpipe/pipelines/root_filesystem.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ def extract_input_files_to_codebase_directory(self):
6363
if errors:
6464
self.add_error("\n".join(errors))
6565

66+
# Reload the project env post-extraction as the scancode-config.yml file
67+
# may be located in one of the extracted archives.
68+
self.env = self.project.get_env()
69+
6670
def find_root_filesystems(self):
6771
"""Find root filesystems in the project's codebase/."""
6872
self.root_filesystems = list(rootfs.RootFs.from_project_codebase(self.project))

scanpipe/pipelines/scan_single_package.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ def extract_input_to_codebase_directory(self):
101101
if extract_errors:
102102
self.add_error("\n".join(extract_errors))
103103

104+
# Reload the project env post-extraction as the scancode-config.yml file
105+
# may be located in one of the extracted archives.
106+
self.env = self.project.get_env()
107+
104108
def run_scan(self):
105109
"""Scan extracted codebase/ content."""
106110
scan_output_path = self.project.get_output_file_path("scancode", "json")

scanpipe/templates/scanpipe/includes/project_settings_menu.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@
55
<i class="fa-solid fa-gear mr-2"></i>General
66
</a>
77
</li>
8-
<li>
9-
<a href="#extraction">
10-
<i class="fa-solid fa-file-zipper mr-2"></i>Extraction
11-
</a>
12-
</li>
138
<li>
149
<a href="#ignored">
1510
<i class="fa-solid fa-forward mr-2"></i>Ignored

0 commit comments

Comments
 (0)