Skip to content

Commit

Permalink
feat: populate properties cdx:python:package:source:vcs:... (#790)
Browse files Browse the repository at this point in the history
populate the newly added/fixed CycloneDX properties
`cdx:python:package:source:vcs:...` in accordance with
<CycloneDX/cyclonedx-property-taxonomy#96> and
<CycloneDX/cyclonedx-property-taxonomy#98>.

the deprecated properties are still used, so no breaking changes exist.

fixes #789

---------

Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
  • Loading branch information
jkowalleck committed Sep 20, 2024
1 parent 39c426b commit b08e1bb
Show file tree
Hide file tree
Showing 46 changed files with 333 additions and 24 deletions.
24 changes: 17 additions & 7 deletions cyclonedx_py/_internal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,29 +45,39 @@ def __call__(self, **kwargs: Any) -> 'Bom': # pragma: no cover
...


class PropertyName(Enum):
class PropertyValue(Enum):
# region general
# see https://github.com/CycloneDX/cyclonedx-property-taxonomy/blob/main/cdx.md
BooleanTrue = 'true'
BooleanFalse = 'false'
# endregion general


class PropertyName(Enum):
# region general
# see https://github.com/CycloneDX/cyclonedx-property-taxonomy/blob/main/cdx.md
Reproducible = 'cdx:reproducible'
# endregion general

# region python
# see https://github.com/CycloneDX/cyclonedx-property-taxonomy/blob/main/cdx/python.md
PackageExtra = 'cdx:python:package:required-extra'
PackageSourceSubdirectory = 'cdx:python:package:source:subdirectory'
PackageSourceVcsRequestedRevision = 'cdx:poetry:package:source:vcs:requested_revision'
PackageSourceVcsCommitId = 'cdx:poetry:package:source:vcs:commit_id'
PackageSourceLocalEditable = 'cdx:python:package:source:local:editable'
PythonPackageExtra = 'cdx:python:package:required-extra'
PythonPackageSourceSubdirectory = 'cdx:python:package:source:subdirectory'
PythonPackageSourceVcsRequestedRevision = 'cdx:python:package:source:vcs:requested_revision'
PythonPackageSourceVcsCommitId = 'cdx:python:package:source:vcs:commit_id'
PythonPackageSourceLocalEditable = 'cdx:python:package:source:local:editable'
# endregion python

# region poetry
# see https://github.com/CycloneDX/cyclonedx-property-taxonomy/blob/main/cdx/poetry.md
PoetryGroup = 'cdx:poetry:group'
PoetryPackageSourceReference = 'cdx:poetry:source:package:reference'
# region poetry-deprecated
# the following property names are deprecated
PoetryPackageSourceReference_misspelled = 'cdx:poetry:source:package:reference'
PoetryPackageSourceResolvedReference = 'cdx:poetry:package:source:resolved_reference'
PoetryPackageSourceVcsRequestedRevision = 'cdx:poetry:package:source:vcs:requested_revision'
PoetryPackageSourceVcsCommitId = 'cdx:poetry:package:source:vcs:commit_id'
# endregion poetry-deprecated
# endregion poetry

# region pipenv
Expand Down
4 changes: 2 additions & 2 deletions cyclonedx_py/_internal/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from cyclonedx.validation import make_schemabased_validator

from .. import __version__
from . import PropertyName
from . import PropertyName, PropertyValue
from .environment import EnvironmentBB
from .pipenv import PipenvBB
from .poetry import PoetryBB
Expand Down Expand Up @@ -230,7 +230,7 @@ def _make_output(self, bom: 'Bom') -> str:

if self._output_reproducible:
bom.metadata.properties.add(Property(name=PropertyName.Reproducible.value,
value=PropertyName.BooleanTrue.value))
value=PropertyValue.BooleanTrue.value))
# dirty hacks to remove these mandatory properties
bom.serial_number = None # type:ignore[assignment]
bom.metadata.timestamp = None # type:ignore[assignment]
Expand Down
23 changes: 16 additions & 7 deletions cyclonedx_py/_internal/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ def __finalize_dependencies(self, bom: 'Bom', all_components: 'T_AllComponents')
component_deps.append(req_component)
req_component.properties.update(
Property(
name=PropertyName.PackageExtra.value,
name=PropertyName.PythonPackageExtra.value,
value=normalize_packagename(extra)
) for extra in req.extras
)
Expand All @@ -236,16 +236,25 @@ def __component_add_extref_and_purl(self, component: 'Component',
purl_subpath = None
if packagesource is not None:
if packagesource.subdirectory:
component.properties.add(Property(name=PropertyName.PackageSourceSubdirectory.value,
value=packagesource.subdirectory))
component.properties.add(Property(
name=PropertyName.PythonPackageSourceSubdirectory.value,
value=packagesource.subdirectory))
purl_subpath = packagesource.subdirectory
if isinstance(packagesource, PackageSourceVcs):
purl_qs['vcs_url'] = f'{packagesource.vcs}+{packagesource.url}@{packagesource.commit_id}'
component.properties.add(Property(name=PropertyName.PackageSourceVcsCommitId.value,
value=packagesource.commit_id))
component.properties.add(Property(
name=PropertyName.PythonPackageSourceVcsCommitId.value,
value=packagesource.commit_id))
component.properties.add(Property(
name=PropertyName.PoetryPackageSourceVcsCommitId.value, # deprecated
value=packagesource.commit_id))
if packagesource.requested_revision:
component.properties.add(Property(name=PropertyName.PackageSourceVcsRequestedRevision.value,
value=packagesource.requested_revision))
component.properties.add(Property(
name=PropertyName.PythonPackageSourceVcsRequestedRevision.value,
value=packagesource.requested_revision))
component.properties.add(Property(
name=PropertyName.PoetryPackageSourceVcsRequestedRevision.value, # deprecated
value=packagesource.requested_revision))
elif isinstance(packagesource, PackageSourceArchive):
if '://files.pythonhosted.org/' not in packagesource.url:
# skip PURL bloat, do not add implicit information
Expand Down
2 changes: 1 addition & 1 deletion cyclonedx_py/_internal/pipenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def _make_bom(self, root_c: Optional['Component'],
))
component.properties.update(
Property(
name=PropertyName.PackageExtra.value,
name=PropertyName.PythonPackageExtra.value,
value=normalize_packagename(package_extra)
) for package_extra in package_data.get('extras', ())
)
Expand Down
22 changes: 16 additions & 6 deletions cyclonedx_py/_internal/poetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def _make_bom(self, project: 'T_NameDict', locker: 'T_NameDict',
root_c.bom_ref.value = root_c.name
root_c.properties.update(
Property(
name=PropertyName.PackageExtra.value,
name=PropertyName.PythonPackageExtra.value,
value=extra
) for extra in use_extras
)
Expand Down Expand Up @@ -344,7 +344,7 @@ def __add_dep(self, bom: 'Bom', lock_entry: _LockEntry, use_extras: Iterable[str
use_extras = frozenset(map(normalize_packagename, use_extras))
lock_entry.component.properties.update(
Property(
name=PropertyName.PackageExtra.value,
name=PropertyName.PythonPackageExtra.value,
value=extra
) for extra in use_extras
)
Expand Down Expand Up @@ -403,20 +403,30 @@ def __make_component4lock(self, package: 'T_NameDict') -> 'Component':
description=package.get('description'),
scope=ComponentScope.OPTIONAL if package.get('optional') else None,
external_references=self.__extrefs4lock(package),
properties=filter(lambda p: p and p.value, [ # type: ignore[arg-type]
properties=filter(lambda p: p and p.value, ( # type: ignore[arg-type]
Property(
name=PropertyName.PythonPackageSourceVcsRequestedRevision.value,
value=source['reference']
) if is_vcs and 'reference' in source else None,
Property(
name=PropertyName.PythonPackageSourceVcsCommitId.value,
value=source['resolved_reference']
) if is_vcs and 'resolved_reference' in source else None,
Property( # for backwards compatibility: category -> group
name=PropertyName.PoetryGroup.value,
value=package['category']
) if 'category' in package else None,
# region deprecated
Property(
name=PropertyName.PoetryPackageSourceReference.value,
name=PropertyName.PoetryPackageSourceReference_misspelled.value, # deprecated
value=source['reference']
) if is_vcs and 'reference' in source else None,
Property(
name=PropertyName.PoetryPackageSourceResolvedReference.value,
name=PropertyName.PoetryPackageSourceResolvedReference.value, # deprecated
value=source['resolved_reference']
) if is_vcs and 'resolved_reference' in source else None,
]),
# endregion deprecated
)),
purl=PackageURL(
type=PurlTypePypi,
name=package['name'],
Expand Down
2 changes: 1 addition & 1 deletion cyclonedx_py/_internal/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ def _make_component(self, req: 'InstallRequirement',
) if not is_local and name else None,
external_references=external_references,
properties=(Property(
name=PropertyName.PackageExtra.value,
name=PropertyName.PythonPackageExtra.value,
value=normalize_packagename(extra)
) for extra in req.extras)
)

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

2 changes: 2 additions & 0 deletions tests/_data/snapshots/environment/plain_with-urls_1.3.xml.bin

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

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

2 changes: 2 additions & 0 deletions tests/_data/snapshots/environment/plain_with-urls_1.4.xml.bin

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

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

2 changes: 2 additions & 0 deletions tests/_data/snapshots/environment/plain_with-urls_1.5.xml.bin

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

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

2 changes: 2 additions & 0 deletions tests/_data/snapshots/environment/plain_with-urls_1.6.xml.bin

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

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

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

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

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

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

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

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

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

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

Loading

0 comments on commit b08e1bb

Please sign in to comment.