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

Add argument to make_codebase_resource to make the created CodebaseResource path relative to the project work path rather than the project codebase path #378

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
14f36b7
Upgrade scancode-toolkit to latest beta release #411
tdruez May 2, 2022
e5b7696
Add a test class to regen test data #411
tdruez May 9, 2022
3519009
Upgrade container_inspector to latest 31.0.0 version #411
tdruez May 9, 2022
0110163
Handle new scan format in scancode pipes #411
JonoYang May 5, 2022
a029c26
Handle package_uids for DiscoveredPackages #411
JonoYang May 6, 2022
774c5b7
Update deprecated code #411
JonoYang May 6, 2022
a6bffc5
Regenerate asgiref 3.3.0 test data #411
JonoYang May 10, 2022
4c354f1
Add asgiref-3.3.0_scancode_scan.json #411
JonoYang May 10, 2022
4ed789f
Add asgiref-3.3.0_walk_test_fixtures.json #411
JonoYang May 10, 2022
80d6095
Signed-off-by: Jono Yang <[email protected]>
JonoYang May 10, 2022
bcceb76
Update make_results_summary() #411
JonoYang May 10, 2022
b25c2c8
Exclude system_environment from diff #411
JonoYang May 10, 2022
98127ea
Modify make_codebase_resource #180
JonoYang Dec 6, 2021
d78c895
Add argument to CodebaseResource.save #180
JonoYang Dec 7, 2021
bbe588b
Add test for make_codebase_resource #180
JonoYang Dec 8, 2021
bb8c832
Create "." directory for project codebase root #180
JonoYang Dec 14, 2021
6dc403c
Modify CodebaseResource walk to handle "." #180
JonoYang Dec 15, 2021
7bca1a2
Update expected test results #180
JonoYang Dec 15, 2021
6ececfa
Update regex in CodebaseResource.children() #180
JonoYang Dec 15, 2021
11bc9b2
Handle root creation in scancode pipes #180
JonoYang Dec 15, 2021
5d84d8e
Create replace_root_path_and_name #180
JonoYang Dec 15, 2021
d4cfac2
Update docstring for CodebaseResource #180
JonoYang Dec 15, 2021
06acd33
Use less paths for test expectations #180
JonoYang Dec 15, 2021
a9410f8
Add is_root property to CodebaseResource #180
JonoYang May 11, 2022
24902bf
Add test for Project.walk_codebase_path #180
JonoYang May 11, 2022
b3c06cd
Merge branch 'main' into 180-projectcodebase-root-workaround
JonoYang Jun 14, 2022
6831a55
Do not save package_uids in extra_data #180
JonoYang Jun 14, 2022
adb7e3f
Merge branch 'main' into 180-projectcodebase-root-workaround
JonoYang Jun 17, 2022
d73563e
Merge branch 'main' into 180-projectcodebase-root-workaround
JonoYang Jul 19, 2022
d3b985c
Update expected test results #180
JonoYang Jul 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 50 additions & 8 deletions scanpipe/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# Visit https://github.com/nexB/scancode.io for support and download.

import inspect
import itertools
import json
import logging
import re
Expand Down Expand Up @@ -73,6 +74,9 @@
scanpipe_app = apps.get_app_config("scanpipe")


ROOT_SYMBOL = "."


class RunInProgressError(Exception):
"""Run are in progress or queued on this project."""

Expand Down Expand Up @@ -672,9 +676,10 @@ def get_latest_output(self, filename):

def walk_codebase_path(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a unit test for this method.

"""
Returns all files and directories path of the codebase/ directory recursively.
Returns all files and directories path of the codebase/ directory recursively,
including the codebase/ directory itself.
"""
return self.codebase_path.rglob("*")
return itertools.chain((self.codebase_path,), self.codebase_path.rglob("*"))

@cached_property
def can_add_input(self):
Expand Down Expand Up @@ -1372,6 +1377,9 @@ class CodebaseResource(
"""
A project Codebase Resources are records of its code files and directories.
Each record is identified by its path under the project workspace.

If a CodebaseResource has a path of ".", then it is considered the root
CodebaseResource of a codebase.
"""

rootfs_path = models.CharField(
Expand Down Expand Up @@ -1488,11 +1496,14 @@ def from_db(cls, db, field_names, values):

return new

def save(self, *args, **kwargs):
def save(self, codebase=None, *args, **kwargs):
"""
Saves the current resource instance.
Injects policies—if the feature is enabled—when the `licenses` field value is
changed.

`codebase` is not used in this context but required for compatibility
with the commoncode.resource.Codebase class API.
"""
if scanpipe_app.policies_enabled:
loaded_licenses = getattr(self, "loaded_licenses", [])
Expand Down Expand Up @@ -1547,6 +1558,16 @@ def is_symlink(self):
"""
return self.type == self.Type.SYMLINK

@property
def is_root(self):
"""
Returns True, if the resource is the root of the codebase.

We consider the root resource of the codebase to be the resource whose
path is equal to ROOT_SYMBOL.
"""
return self.path == ROOT_SYMBOL

def compute_compliance_alert(self):
"""
Computes and returns the compliance_alert value from the `licenses` policies.
Expand Down Expand Up @@ -1587,8 +1608,21 @@ def descendants(self):
Returns a QuerySet of descendant CodebaseResource objects using a
database query on the current CodebaseResource `path`.
The current CodebaseResource is not included.

In the case where we are at the root CodebaseResource of a codebase, we
return all CodebaseResources, excluding the root CodebaseResource, whose
path is equal to ROOT_SYMBOL.
"""
return self.project.codebaseresources.filter(path__startswith=f"{self.path}/")
if self.is_root:
# We use a different query in the case we are at the root
# CodebaseResource because its path is equal to ROOT_SYMBOL, and no
# other CodebaseResource start with ROOT_SYMBOL. Using the other query
# would result in no CodebaseResources being returned.
return self.project.codebaseresources.exclude(path=ROOT_SYMBOL)
else:
return self.project.codebaseresources.filter(
path__startswith=f"{self.path}/"
)

def children(self, codebase=None):
"""
Expand All @@ -1600,10 +1634,18 @@ def children(self, codebase=None):
https://github.com/nexB/commoncode/blob/main/src/commoncode/resource.py

`codebase` is not used in this context but required for compatibility
with the commoncode.resource.VirtualCodebase class API.
"""
exactly_one_sub_directory = "[^/]+$"
children_regex = rf"^{self.path}/{exactly_one_sub_directory}"
with the commoncode.resource.Codebase class API.
"""
if self.is_root:
# The root CodebaseResource has a path of ROOT_SYMBOL, and no other
# CodebaseResource paths starts with ROOT_SYMBOL. Because of this,
# we cannot use the path value of the root as part of the query, as
# no other CodebaseResources would be found.
unnested_file_or_directory = "^[^/]+$"
children_regex = rf"{unnested_file_or_directory}"
else:
exactly_one_sub_directory = "[^/]+$"
children_regex = rf"^{self.path}/{exactly_one_sub_directory}"
return (
self.descendants()
.filter(path__regex=children_regex)
Expand Down
15 changes: 14 additions & 1 deletion scanpipe/pipes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from django.db.models import Count

from scanpipe.models import ROOT_SYMBOL
from scanpipe.models import CodebaseResource
from scanpipe.models import DiscoveredPackage
from scanpipe.pipes import scancode
Expand Down Expand Up @@ -61,10 +62,22 @@ def make_codebase_resource(project, location, **extra_fields):
If a CodebaseResource already exists in the `project` with the same path,
the error raised on save() is not stored in the database and the creation is
skipped.

If `location` is the same as `project.codebase_path`, then the CodebaseResource
created will have a name of ".", and a path of ".".
"""
relative_path = Path(location).relative_to(project.codebase_path)
location_path = Path(location)
relative_path = location_path.relative_to(project.codebase_path)
resource_data = scancode.get_resource_info(location=location)

if location_path == project.codebase_path:
# If we are making a CodebaseResource for the project codebase/
# directory, `relative_path` will be ROOT_SYMBOL. However, when we scan
# `location`, the `name` field will be "codebase". We have to overwrite
# the `name` field with ROOT_SYMBOL to be consistant with
# `relative_path`
resource_data["name"] = ROOT_SYMBOL

if extra_fields:
resource_data.update(**extra_fields)

Expand Down
3 changes: 2 additions & 1 deletion scanpipe/pipes/codebase.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from django.core.exceptions import ObjectDoesNotExist

from scanpipe.models import ROOT_SYMBOL
from scanpipe.models import Project


Expand Down Expand Up @@ -70,7 +71,7 @@ def __init__(self, project):
@property
def root(self):
try:
return self.project.codebaseresources.get(path="codebase")
return self.project.codebaseresources.get(path=ROOT_SYMBOL)
except ObjectDoesNotExist:
raise AttributeError("Codebase root cannot be determined.")

Expand Down
26 changes: 24 additions & 2 deletions scanpipe/pipes/scancode.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ def create_codebase_resources(project, scanned_codebase):
CodebaseResource objects as the existing objects (based on the `path`) will be
skipped.
"""
for scanned_resource in scanned_codebase.walk(skip_root=True):
for scanned_resource in scanned_codebase.walk():
resource_data = {}

for field in CodebaseResource._meta.fields:
Expand All @@ -383,7 +383,14 @@ def create_codebase_resources(project, scanned_codebase):

resource_type = "FILE" if scanned_resource.is_file else "DIRECTORY"
resource_data["type"] = CodebaseResource.Type[resource_type]
resource_path = scanned_resource.get_path(strip_root=True)

if scanned_resource.is_root:
# In ScanCode.io, we represent the root CodebaseResource using "."
# as its name and path
resource_path = "."
resource_data["name"] = "."
else:
resource_path = scanned_resource.get_path(strip_root=True)

codebase_resource, _ = CodebaseResource.objects.get_or_create(
project=project,
Expand Down Expand Up @@ -494,6 +501,21 @@ def make_results_summary(project, scan_results_location):
return summary


def replace_root_path_and_name(scanned_codebase):
"""
Set the name and path of the root Resource of `scanned_codebase` to "."
and strip the root prefix from the paths of the remaining Resources.
"""
for scanned_resource in scanned_codebase.walk():
if scanned_resource.is_root:
scanned_resource.path = "."
scanned_resource.name = "."
else:
scanned_resource.path = scanned_resource.get_path(strip_root=True)
scanned_resource.save(scanned_codebase)
return scanned_codebase


def create_inventory_from_scan(project, input_location):
"""
Create CodebaseResource and DiscoveredPackage instances loaded from the scan
Expand Down
Loading