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 pipeline to publish scan to federatedcode #1400

Merged
merged 19 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ v34.9.0 (unreleased)
"policies.yml" files, or global app settings.
https://github.com/aboutcode-org/scancode.io/issues/386

- Add a new ``PublishToFederatedCode`` pipeline (addon) to push scan result
to FederatedCode.
https://github.com/nexB/scancode.io/pull/1400

- Add new ``purl`` field to project model. https://github.com/nexB/scancode.io/pull/1400

v34.8.3 (2024-10-30)
--------------------

Expand Down
Empty file removed aboutcode/__init__.py
Empty file.
22 changes: 22 additions & 0 deletions docs/application-settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,28 @@ API key using ``MATCHCODEIO_API_KEY``::

MATCHCODEIO_API_KEY=insert_your_api_key_here

.. _scancodeio_settings_federatedcode:

FEDERATEDCODE
^^^^^^^^^^^^^

FederatedCode is decentralized and federated metadata for software applications
stored in Git repositories.


To configure your local environment, set the following in your ``.env`` file::

FEDERATEDCODE_GIT_ACCOUNT_URL=https://<Address to your git account>/

FEDERATEDCODE_GIT_SERVICE_TOKEN=insert_your_git_api_key_here

Also provide the name and email that will be used to sign off on commits to Git repositories::

FEDERATEDCODE_GIT_SERVICE_NAME=insert_name_here

FEDERATEDCODE_GIT_SERVICE_EMAIL=insert_email_here


.. _scancodeio_settings_fetch_authentication:

Fetch Authentication
Expand Down
14 changes: 14 additions & 0 deletions docs/built-in-pipelines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,20 @@ Populate PurlDB (addon)
:members:
:member-order: bysource

.. _pipeline_publish_to_federatedcode:

Publish To FederatedCode (addon)
--------------------------------

.. warning::
This pipeline requires access to a FederatedCode service.
Refer to :ref:`scancodeio_settings_federatedcode` to configure access to
FederatedCode in your ScanCode.io instance.

.. autoclass:: scanpipe.pipelines.publish_to_federatedcode.PublishToFederatedCode()
:members:
:member-order: bysource

.. _pipeline_scan_codebase:

Scan Codebase
Expand Down
7 changes: 7 additions & 0 deletions scancodeio/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,10 @@
MATCHCODEIO_USER = env.str("MATCHCODEIO_USER", default="")
MATCHCODEIO_PASSWORD = env.str("MATCHCODEIO_PASSWORD", default="")
MATCHCODEIO_API_KEY = env.str("MATCHCODEIO_API_KEY", default="")

# FederatedCode integration

FEDERATEDCODE_GIT_ACCOUNT_URL = env.str("FEDERATEDCODE_GIT_ACCOUNT_URL", default="")
FEDERATEDCODE_GIT_SERVICE_TOKEN = env.str("FEDERATEDCODE_GIT_SERVICE_TOKEN", default="")
FEDERATEDCODE_GIT_SERVICE_NAME = env.str("FEDERATEDCODE_GIT_SERVICE_NAME", default="")
FEDERATEDCODE_GIT_SERVICE_EMAIL = env.str("FEDERATEDCODE_GIT_SERVICE_EMAIL", default="")
1 change: 1 addition & 0 deletions scanpipe/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ class Meta:
"name",
"url",
"uuid",
"purl",
"upload_file",
"upload_file_tag",
"input_urls",
Expand Down
20 changes: 20 additions & 0 deletions scanpipe/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ValidationError

from packageurl import PackageURL
from taggit.forms import TagField
from taggit.forms import TagWidget

Expand Down Expand Up @@ -480,12 +481,31 @@ class Meta:
fields = [
"name",
"notes",
"purl",
]
widgets = {
"name": forms.TextInput(attrs={"class": "input"}),
"notes": forms.Textarea(attrs={"rows": 3, "class": "textarea is-dynamic"}),
"purl": forms.TextInput(
attrs={
"class": "input",
"placeholder": "pkg:npm/[email protected]",
}
),
}

def clean_purl(self):
"""Validate the Project PURL."""
purl = self.cleaned_data.get("purl")

if purl:
try:
PackageURL.from_string(purl)
except ValueError:
raise forms.ValidationError("PURL must be a valid PackageURL")

return purl
keshav-space marked this conversation as resolved.
Show resolved Hide resolved

def __init__(self, *args, **kwargs):
"""Load initial values from Project ``settings`` field."""
super().__init__(*args, **kwargs)
Expand Down
18 changes: 18 additions & 0 deletions scanpipe/migrations/0069_project_purl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2024-11-08 12:47

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('scanpipe', '0068_rename_discovered_dependencies_attribute'),
]

operations = [
migrations.AddField(
model_name='project',
name='purl',
field=models.CharField(blank=True, help_text='Package URL for the project, used for pushing project scan result to FederatedCode. This should be the PURL of the input.', max_length=2048),
),
]
9 changes: 9 additions & 0 deletions scanpipe/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,14 @@ class Project(UUIDPKModel, ExtraDataFieldMixin, UpdateMixin, models.Model):
notes = models.TextField(blank=True)
settings = models.JSONField(default=dict, blank=True)
labels = TaggableManager(through=UUIDTaggedItem)
purl = models.CharField(
max_length=2048,
blank=True,
help_text=_(
"Package URL for the project, used for pushing project scan result to "
"FederatedCode. This should be the PURL of the input."
keshav-space marked this conversation as resolved.
Show resolved Hide resolved
),
)

objects = ProjectQuerySet.as_manager()

Expand Down Expand Up @@ -705,6 +713,7 @@ def clone(
"""Clone this project using the provided ``clone_name`` as new project name."""
new_project = Project.objects.create(
name=clone_name,
purl=self.purl,
settings=self.settings if copy_settings else {},
)

Expand Down
97 changes: 97 additions & 0 deletions scanpipe/pipelines/publish_to_federatedcode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# SPDX-License-Identifier: Apache-2.0
#
# http://nexb.com and https://github.com/aboutcode-org/scancode.io
# The ScanCode.io software is licensed under the Apache License version 2.0.
# Data generated with ScanCode.io is provided as-is without warranties.
# ScanCode is a trademark of nexB Inc.
#
# You may not use this software except in compliance with the License.
# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Data Generated with ScanCode.io is provided on an "AS IS" BASIS, WITHOUT WARRANTIES
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
# ScanCode.io should be considered or used as legal advice. Consult an Attorney
# for any legal advice.
#
# ScanCode.io is a free software code scanning tool from nexB Inc. and others.
# Visit https://github.com/aboutcode-org/scancode.io for support and download.


from scanpipe.pipelines import Pipeline
from scanpipe.pipes import federatedcode


class PublishToFederatedCode(Pipeline):
"""
Publish package scan to FederatedCode.

This pipeline commits the project scan result in FederatedCode Git repository.
It uses ``Project PURL`` to determine the Git repository and the
exact directory path where the scan should be stored.
"""

download_inputs = False
is_addon = True

@classmethod
def steps(cls):
return (
cls.check_federatedcode_eligibility,
cls.get_package_repository,
cls.clone_repository,
cls.add_scan_result,
cls.commit_and_push_changes,
cls.delete_local_clone,
)

def check_federatedcode_eligibility(self):
"""
Check if the project fulfills the following criteria for
pushing the project result to FederatedCode.
"""
federatedcode.check_federatedcode_eligibility(project=self.project)

def get_package_repository(self):
"""Get the Git repository URL and scan path for a given package."""
self.package_git_repo, self.package_scan_file = (
federatedcode.get_package_repository(
project_purl=self.project.purl, logger=self.log
)
)

def clone_repository(self):
"""Clone repository to local_path."""
self.repo = federatedcode.clone_repository(
repo_url=self.package_git_repo,
logger=self.log,
)

def add_scan_result(self):
"""Add package scan result to the local Git repository."""
self.relative_file_path = federatedcode.add_scan_result(
project=self.project,
repo=self.repo,
package_scan_file=self.package_scan_file,
logger=self.log,
)

def commit_and_push_changes(self):
"""Commit and push changes to remote repository."""
federatedcode.commit_and_push_changes(
repo=self.repo,
file_to_commit=str(self.relative_file_path),
purl=self.project.purl,
logger=self.log,
)
self.log(
f"Scan result for '{self.project.purl}' "
f"pushed to '{self.package_git_repo}'"
)

def delete_local_clone(self):
"""Remove local clone."""
federatedcode.delete_local_clone(repo=self.repo)
Loading