blob: bc92175a84704594b122d581a48e1537c6d28ece [file] [log] [blame]
# SPDX-FileCopyrightText: 2023 spdx contributors
#
# SPDX-License-Identifier: Apache-2.0
import logging
from datetime import datetime
from typing import List
from spdx_tools.common.spdx_licensing import spdx_licensing
from spdx_tools.spdx.model import (
Actor,
ActorType,
Checksum,
ChecksumAlgorithm,
CreationInfo,
Document,
ExternalPackageRef,
ExternalPackageRefCategory,
File,
FileType,
Package,
PackagePurpose,
PackageVerificationCode,
Relationship,
RelationshipType,
)
from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
from spdx_tools.spdx.validation.validation_message import ValidationMessage
from spdx_tools.spdx.writer.write_anything import write_file
# This example shows how to use the spdx-tools to create an SPDX document from scratch,
# validate it and write it to a file.
# First up, we need general information about the creation of the document, summarised by the CreationInfo class.
creation_info = CreationInfo(
spdx_version="SPDX-2.3",
spdx_id="SPDXRef-DOCUMENT",
name="document name",
data_license="CC0-1.0",
document_namespace="https://some.namespace",
creators=[Actor(ActorType.PERSON, "Jane Doe", "jane.doe@example.com")],
created=datetime(2022, 1, 1),
)
# creation_info is the only required property of the Document class (have a look there!), the rest are optional lists.
# So, we are set up to create a new document instance.
document = Document(creation_info)
# The document currently does not describe anything. Let's create a package that we can add to it.
# The Package class has quite a few properties (have a look there!),
# but only name, spdx_id and download_location are mandatory in SPDX v2.3.
package = Package(
name="package name",
spdx_id="SPDXRef-Package",
download_location="https://download.com",
version="2.2.1",
file_name="./foo.bar",
supplier=Actor(ActorType.PERSON, "Jane Doe", "jane.doe@example.com"),
originator=Actor(ActorType.ORGANIZATION, "some organization", "contact@example.com"),
files_analyzed=True,
verification_code=PackageVerificationCode(
value="d6a770ba38583ed4bb4525bd96e50461655d2758", excluded_files=["./some.file"]
),
checksums=[
Checksum(ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2758"),
Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24"),
],
license_concluded=spdx_licensing.parse("GPL-2.0-only OR MIT"),
license_info_from_files=[spdx_licensing.parse("GPL-2.0-only"), spdx_licensing.parse("MIT")],
license_declared=spdx_licensing.parse("GPL-2.0-only AND MIT"),
license_comment="license comment",
copyright_text="Copyright 2022 Jane Doe",
description="package description",
attribution_texts=["package attribution"],
primary_package_purpose=PackagePurpose.LIBRARY,
release_date=datetime(2015, 1, 1),
external_references=[
ExternalPackageRef(
category=ExternalPackageRefCategory.OTHER,
reference_type="http://reference.type",
locator="reference/locator",
comment="external reference comment",
)
],
)
# Now that we have a package defined, we can add it to the document's package property.
document.packages = [package]
# A DESCRIBES relationship asserts that the document indeed describes the package.
describes_relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-Package")
document.relationships = [describes_relationship]
# Let's add two files. Have a look at the file class for all possible properties a file can have.
file1 = File(
name="./package/file1.py",
spdx_id="SPDXRef-File1",
file_types=[FileType.SOURCE],
checksums=[
Checksum(ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2758"),
Checksum(ChecksumAlgorithm.MD5, "624c1abb3664f4b35547e7c73864ad24"),
],
license_concluded=spdx_licensing.parse("MIT"),
license_info_in_file=[spdx_licensing.parse("MIT")],
copyright_text="Copyright 2022 Jane Doe",
)
file2 = File(
name="./package/file2.py",
spdx_id="SPDXRef-File2",
checksums=[
Checksum(ChecksumAlgorithm.SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2759"),
],
license_concluded=spdx_licensing.parse("GPL-2.0-only"),
)
# Assuming the package contains those two files, we create two CONTAINS relationships.
contains_relationship1 = Relationship("SPDXRef-Package", RelationshipType.CONTAINS, "SPDXRef-File1")
contains_relationship2 = Relationship("SPDXRef-Package", RelationshipType.CONTAINS, "SPDXRef-File2")
# This library uses run-time type checks when assigning properties.
# Because in-place alterations like .append() circumvent these checks, we don't use them here.
document.relationships += [contains_relationship1, contains_relationship2]
document.files += [file1, file2]
# We now have created a document with basic creation information, describing a package that contains two files.
# You can also add Annotations, Snippets and ExtractedLicensingInfo to the document in an analogous manner to the above.
# Have a look at their respective classes if you are unsure about their properties.
# This library provides comprehensive validation against the SPDX specification.
# Note that details of the validation depend on the SPDX version of the document.
validation_messages: List[ValidationMessage] = validate_full_spdx_document(document)
# You can have a look at each entry's message and context (like spdx_id, parent_id, full_element)
# which will help you pinpoint the location of the invalidity.
for message in validation_messages:
logging.warning(message.validation_message)
logging.warning(message.context)
# If the document is valid, validation_messages will be empty.
assert validation_messages == []
# Finally, we can serialize the document to any of the five supported formats.
# Using the write_file() method from the write_anything module,
# the format will be determined by the file ending: .spdx (tag-value), .json, .xml, .yaml. or .rdf (or .rdf.xml)
write_file(document, "my_spdx_document.spdx.json")