Skip to content

Commit

Permalink
feat!: migrate runtime dependency to cyclonedx-python-lib>=8<9 (#796)
Browse files Browse the repository at this point in the history
### Dependencies

* Requires `cyclonedx-python-lib>=8.0.0,<9 ` now, was
`>=7.3.0,<8.0.0,!=7.3.1`

---------

Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
  • Loading branch information
jkowalleck authored Oct 14, 2024
1 parent b234e6f commit ce8fde5
Show file tree
Hide file tree
Showing 461 changed files with 23,544 additions and 8,261 deletions.
16 changes: 16 additions & 0 deletions cyclonedx_py/_internal/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This file is part of CycloneDX Python
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.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.
#
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.
95 changes: 54 additions & 41 deletions cyclonedx_py/_internal/utils/cdx.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,54 +23,67 @@
from re import compile as re_compile
from typing import Any, Dict, Iterable

from cyclonedx.model import ExternalReference, ExternalReferenceType, Tool, XsUri
from cyclonedx.builder.this import this_component as lib_component
from cyclonedx.model import ExternalReference, ExternalReferenceType, XsUri
from cyclonedx.model.bom import Bom
from cyclonedx.model.license import License, LicenseExpression
from cyclonedx.model.component import Component, ComponentType
from cyclonedx.model.license import DisjunctiveLicense, License, LicenseAcknowledgement, LicenseExpression

from cyclonedx_py import __version__
from ... import __version__ as __THIS_VERSION # noqa:N812


def make_bom(**kwargs: Any) -> Bom:
bom = Bom(**kwargs)
bom.metadata.tools.add(Tool(
# keep in sync with `../../../pyproject.toml`
vendor='CycloneDX',
name='cyclonedx-py',
version=__version__,
external_references=[
ExternalReference(
type=ExternalReferenceType.BUILD_SYSTEM,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/actions')
bom.metadata.tools.components.update((
lib_component(),
Component(
type=ComponentType.APPLICATION,
group='CycloneDX',
# package is called 'cyclonedx-bom', but the tool is called 'cyclonedx-py'
name='cyclonedx-py',
version=__THIS_VERSION,
description='CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments',
licenses=(DisjunctiveLicense(id='Apache-2.0',
acknowledgement=LicenseAcknowledgement.DECLARED),),
external_references=(
# let's assume this is not a fork
ExternalReference(
type=ExternalReferenceType.WEBSITE,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/#readme')
),
ExternalReference(
type=ExternalReferenceType.DOCUMENTATION,
url=XsUri('https://cyclonedx-bom-tool.readthedocs.io/')
),
ExternalReference(
type=ExternalReferenceType.VCS,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/')
),
ExternalReference(
type=ExternalReferenceType.BUILD_SYSTEM,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/actions')
),
ExternalReference(
type=ExternalReferenceType.ISSUE_TRACKER,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/issues')
),
ExternalReference(
type=ExternalReferenceType.LICENSE,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE')
),
ExternalReference(
type=ExternalReferenceType.RELEASE_NOTES,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md')
),
# we cannot assert where the lib was fetched from, but we can give a hint
ExternalReference(
type=ExternalReferenceType.DISTRIBUTION,
url=XsUri('https://pypi.org/project/cyclonedx-bom/')
),
),
ExternalReference(
type=ExternalReferenceType.DISTRIBUTION,
url=XsUri('https://pypi.org/project/cyclonedx-bom/')
),
ExternalReference(
type=ExternalReferenceType.DOCUMENTATION,
url=XsUri('https://cyclonedx-bom-tool.readthedocs.io/')
),
ExternalReference(
type=ExternalReferenceType.ISSUE_TRACKER,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/issues')
),
ExternalReference(
type=ExternalReferenceType.LICENSE,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE')
),
ExternalReference(
type=ExternalReferenceType.RELEASE_NOTES,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md')
),
ExternalReference(
type=ExternalReferenceType.VCS,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/')
),
ExternalReference(
type=ExternalReferenceType.WEBSITE,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/#readme')
)
]))
# to be extended...
),
))
return bom


Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ build-backend = "poetry.core.masonry.api"


[tool.poetry]
# keep in sync with `cyclonedx_py/_internal/utils/cdx.py`
name = "cyclonedx-bom"
version = "4.6.1"
description = "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments"
Expand Down Expand Up @@ -69,7 +68,7 @@ cyclonedx-py = "cyclonedx_py._internal.cli:run"

[tool.poetry.dependencies]
python = "^3.8"
cyclonedx-python-lib = { version = "^7.3.0, !=7.3.1", extras = ["validation"] }
cyclonedx-python-lib = { version = "^8.0", extras = ["validation"] }
packageurl-python = ">=0.11, <2" # keep in sync with same dep in `cyclonedx-python-lib`
pip-requirements-parser = "^32.0"
packaging = "^22 || ^23 || ^24"
Expand All @@ -92,6 +91,7 @@ isort = "5.13.2"
autopep8 = "2.3.1"
mypy = "1.11.2"
bandit = "1.7.10"
tomli = { version = "^2.0.1", python = "<3.11" }
tox = "4.21.2"
# min version required to be able to install some dependencies
# see https://github.com/MichaelKim0407/flake8-use-fstring/issues/33
Expand Down
120 changes: 93 additions & 27 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.


import sys
from json import dumps as json_dumps
from os import getenv
from os.path import dirname, join
from os import getenv, path
from pathlib import Path
from re import sub as re_sub
from sys import stderr
from typing import Union
from typing import Any, Dict, Union
from unittest import TestCase
from xml.sax.saxutils import escape as xml_escape, quoteattr as xml_quoteattr # nosec:B406

Expand All @@ -32,16 +30,16 @@

RECREATE_SNAPSHOTS = '1' == getenv('CDX_TEST_RECREATE_SNAPSHOTS')
if RECREATE_SNAPSHOTS:
print('!!! WILL RECREATE ALL SNAPSHOTS !!!', file=stderr)
print('!!! WILL RECREATE ALL SNAPSHOTS !!!', file=sys.stderr)

INIT_TESTBEDS = '1' != getenv('CDX_TEST_SKIP_INIT_TESTBEDS')
if INIT_TESTBEDS:
print('!!! WILL INIT TESTBEDS !!!', file=stderr)
print('!!! WILL INIT TESTBEDS !!!', file=sys.stderr)

_TESTDATA_DIRECTORY = join(dirname(__file__), '_data')
_TESTDATA_DIRECTORY = path.join(path.dirname(__file__), '_data')

INFILES_DIRECTORY = join(_TESTDATA_DIRECTORY, 'infiles')
SNAPSHOTS_DIRECTORY = join(_TESTDATA_DIRECTORY, 'snapshots')
INFILES_DIRECTORY = path.join(_TESTDATA_DIRECTORY, 'infiles')
SNAPSHOTS_DIRECTORY = path.join(_TESTDATA_DIRECTORY, 'snapshots')

UNSUPPORTED_OF_SV = (
(OutputFormat.JSON, SchemaVersion.V1_1),
Expand All @@ -60,7 +58,7 @@ class SnapshotMixin:

@staticmethod
def getSnapshotFile(snapshot_name: str) -> str: # noqa: N802
return join(SNAPSHOTS_DIRECTORY, f'{snapshot_name}.bin')
return path.join(SNAPSHOTS_DIRECTORY, f'{snapshot_name}.bin')

@classmethod
def writeSnapshot(cls, snapshot_name: str, data: str) -> None: # noqa: N802
Expand Down Expand Up @@ -92,62 +90,121 @@ def assertEqualSnapshot(self: Union[TestCase, 'SnapshotMixin'], # noqa: N802
_root_file_uri_xml_attr = xml_quoteattr(_root_file_uri)[1:-1]
_root_file_uri_json = json_dumps(_root_file_uri)[1:-1]

# package is called 'cyclonedx-bom', but the tool is called 'cyclonedx-py'
EXPECTED_TOOL_NAME = 'cyclonedx-py'


def make_xml_comparable(bom: str) -> str:
bom = bom.replace(_root_file_uri_xml, 'file://.../')
bom = bom.replace(_root_file_uri_xml_attr, 'file://.../')
bom = bom.replace( # replace metadata.tools.version
bom = bom.replace( # replace this version in metadata.tools.components
' <group>CycloneDX</group>\n'
f' <name>{EXPECTED_TOOL_NAME}</name>\n'
f' <version>{__this_version}</version>',
' <group>CycloneDX</group>\n'
f' <name>{EXPECTED_TOOL_NAME}</name>\n'
' <version>thisVersion-testing</version>')
bom = bom.replace( # replace this version in metadata.tools
' <vendor>CycloneDX</vendor>\n'
' <name>cyclonedx-py</name>\n'
f' <name>{EXPECTED_TOOL_NAME}</name>\n'
f' <version>{__this_version}</version>',
' <vendor>CycloneDX</vendor>\n'
' <name>cyclonedx-py</name>\n'
f' <name>{EXPECTED_TOOL_NAME}</name>\n'
' <version>thisVersion-testing</version>')
bom = re_sub( # replace metadata.tools.version
bom = re_sub( # replace lib-dynamics in metadata.tools.components
' <group>CycloneDX</group>\n'
' <name>cyclonedx-python-lib</name>\n'
' <version>.*?</version>\n'
' <description>.*?</description>\n'
' <licenses>\n'
'(?: .*?\n)*'
' </licenses>\n'
' <externalReferences>\n'
'(?: .*?\n)*'
' </externalReferences>',
' <group>CycloneDX</group>\n'
' <name>cyclonedx-python-lib</name>\n'
' <version>libVersion-testing</version>\n'
' <description><!-- stripped --></description>\n'
' <licenses><!-- stripped --></licenses>\n'
' <externalReferences><!-- stripped --></externalReferences>',
bom)
bom = re_sub( # replace lib-dynamics version in metadata.tools[]
' <vendor>CycloneDX</vendor>\n'
' <name>cyclonedx-python-lib</name>\n'
' <version>.*?</version>',
' <vendor>CycloneDX</vendor>\n'
' <name>cyclonedx-python-lib</name>\n'
' <version>libVersion-testing</version>',
bom)
bom = re_sub( # replace metadata.tools.externalReferences
bom = re_sub( # replace lib-dynamics externalReferences in metadata.tools[]
' <vendor>CycloneDX</vendor>\n'
' <name>cyclonedx-python-lib</name>\n'
r' <version>(.*?)</version>\n'
r' <externalReferences>[\s\S]*?</externalReferences>',
' <version>(.*?)</version>\n'
' <externalReferences>\n'
'(?: .*?\n)*'
' </externalReferences>',
' <vendor>CycloneDX</vendor>\n'
' <name>cyclonedx-python-lib</name>\n'
r' <version>\1</version>''\n'
' <version>\\1</version>\n'
' <externalReferences><!-- stripped --></externalReferences>',
bom)
return bom


def make_json_comparable(bom: str) -> str:
bom = bom.replace(_root_file_uri_json, 'file://.../')
bom = bom.replace( # replace metadata.tools.version
' "name": "cyclonedx-py",\n'
bom = bom.replace( # replace this version in metadata.tools.components[]
f' "name": {json_dumps(EXPECTED_TOOL_NAME)},\n'
' "type": "application",\n'
f' "version": {json_dumps(__this_version)}',
f' "name": {json_dumps(EXPECTED_TOOL_NAME)},\n'
' "type": "application",\n'
' "version": "thisVersion-testing"')
bom = bom.replace( # replace this version in metadata.tools[]
f' "name": {json_dumps(EXPECTED_TOOL_NAME)},\n'
' "vendor": "CycloneDX",\n'
f' "version": {json_dumps(__this_version)}',
' "name": "cyclonedx-py",\n'
f' "name": {json_dumps(EXPECTED_TOOL_NAME)},\n'
' "vendor": "CycloneDX",\n'
' "version": "thisVersion-testing"')
bom = re_sub( # replace metadata.tools.version
bom = re_sub( # replace lib-dynamics in metadata.tools.components[]
' "description": ".*?",\n'
' "externalReferences": \\[\n'
'(?: .*?\n)*'
' \\],\n'
' "group": "CycloneDX",\n'
' "licenses": \\[\n'
'(?: .*?\n)*'
' \\],\n'
' "name": "cyclonedx-python-lib",\n'
' "type": "library",\n'
' "version": ".*?"',
' "description": "stripped",\n'
' "externalReferences": [ ],\n'
' "group": "CycloneDX",\n'
' "licenses": [ ],\n'
' "name": "cyclonedx-python-lib",\n'
' "type": "library",\n'
' "version": "libVersion-testing"',
bom)
bom = re_sub( # replace lib-dynamics version in metadata.tools[]
' "name": "cyclonedx-python-lib",\n'
' "vendor": "CycloneDX",\n'
' "version": ".*?"',
' "name": "cyclonedx-python-lib",\n'
' "vendor": "CycloneDX",\n'
' "version": "libVersion-testing"',
bom)
bom = re_sub( # replace metadata.tools.externalReferences
r' "externalReferences": \[[\s\S]*?\],\n'
bom = re_sub( # replace lib-dynamics externalReferences in metadata.tools[]
' "externalReferences": \\[\n'
'(?: .*?\n)*'
' \\],\n'
' "name": "cyclonedx-python-lib",\n'
' "vendor": "CycloneDX"',
' "vendor": "CycloneDX",\n',
' "externalReferences": [ ],\n'
' "name": "cyclonedx-python-lib",\n'
' "vendor": "CycloneDX"',
' "vendor": "CycloneDX",\n',
bom)
return bom

Expand All @@ -160,3 +217,12 @@ def make_comparable(bom: str, of: OutputFormat) -> str:
raise NotImplementedError(f'unknown OutputFormat: {of!r}')

# endregion reproducible test results


def load_pyproject() -> Dict[str, Any]:
if sys.version_info >= (3, 11):
from tomllib import load as toml_load
else:
from tomli import load as toml_load
with open(path.join(path.dirname(__file__), '..', 'pyproject.toml'), 'rb') as f:
return toml_load(f)

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit ce8fde5

Please sign in to comment.