From ca3b7781dcc73525713a0def290ff55be0579434 Mon Sep 17 00:00:00 2001 From: Mohammed Abbadi Date: Mon, 23 Mar 2026 01:00:20 +0300 Subject: [PATCH 1/5] feat: add uv subcommand for SBOM from uv.lock Implement UvBB to build CycloneDX SBOMs from pyproject.toml and uv.lock, including dependency groups and extras. Register the subcommand in the CLI, document usage in README and docs/usage.rst, and add uv to package keywords. Signed-off-by: Mohammed Abbadi --- README.md | 4 +- cyclonedx_py/_internal/cli.py | 2 + cyclonedx_py/_internal/uv.py | 797 ++++++++++++++++++++++++++++++++++ docs/usage.rst | 94 +++- pyproject.toml | 2 +- 5 files changed, 880 insertions(+), 19 deletions(-) create mode 100644 cyclonedx_py/_internal/uv.py diff --git a/README.md b/README.md index 74d211e19..900f9a088 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,7 @@ Supported data sources are: * Pip's `requirements.txt` format * `PDM` manifest and lockfile are not explicitly supported. However, PDM's Python virtual environments are fully supported. See the docs for an example. -* `uv` manifest and lockfile are not explicitly supported. - However, uv's Python virtual environments are fully supported. See the docs for an example. +* `uv` manifest and lockfile * `Conda` as a package manager is no longer supported since version 4. However, conda's Python environments are fully supported via the methods listed above. See the docs for an example. @@ -86,6 +85,7 @@ positional arguments: requirements Build an SBOM from Pip requirements pipenv Build an SBOM from Pipenv manifest poetry Build an SBOM from Poetry project + uv Build an SBOM from uv project options: -h, --help show this help message and exit diff --git a/cyclonedx_py/_internal/cli.py b/cyclonedx_py/_internal/cli.py index 6e444dc12..42b829309 100644 --- a/cyclonedx_py/_internal/cli.py +++ b/cyclonedx_py/_internal/cli.py @@ -33,6 +33,7 @@ from .pipenv import PipenvBB from .poetry import PoetryBB from .requirements import RequirementsBB +from .uv import UvBB from .utils.args import argparse_type4enum, choices4enum if TYPE_CHECKING: # pragma: no cover @@ -115,6 +116,7 @@ def make_argument_parser(cls, sco: ArgumentParser, **kwargs: Any) -> ArgumentPar (RequirementsBB, 'requirements'), (PipenvBB, 'pipenv'), (PoetryBB, 'poetry'), + (UvBB, 'uv'), ): spp = scbbc.make_argument_parser(add_help=False) sp.add_parser(sct, aliases=scta, diff --git a/cyclonedx_py/_internal/uv.py b/cyclonedx_py/_internal/uv.py new file mode 100644 index 000000000..f62a5e111 --- /dev/null +++ b/cyclonedx_py/_internal/uv.py @@ -0,0 +1,797 @@ +# 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. + +from argparse import OPTIONAL, ArgumentParser +from collections.abc import Generator, Iterable +from dataclasses import dataclass +from os.path import basename, dirname, isfile, join +from textwrap import dedent +from typing import TYPE_CHECKING, Any, Optional + +from cyclonedx.exception.model import InvalidUriException, UnknownHashTypeException +from cyclonedx.model import ExternalReference, ExternalReferenceType, HashType, Property, XsUri +from cyclonedx.model.component import Component, ComponentType +from cyclonedx.model.dependency import Dependency +from packageurl import PackageURL +from packaging.markers import Marker, default_environment +from packaging.requirements import Requirement + +from . import BomBuilder, PropertyName, PurlTypePypi +from .cli_common import add_argument_mc_type +from .utils.cdx import make_bom +from .utils.packaging import normalize_packagename +from .utils.pyproject import pyproject2component +from .utils.secret import redact_auth_from_url +from .utils.toml import toml_loads + +if TYPE_CHECKING: # pragma: no cover + from logging import Logger + + from cyclonedx.model.bom import Bom + from cyclonedx.model.component import ComponentType + + T_NameDict = dict[str, Any] + + +class ExtrasNotFoundError(ValueError): + def __init__(self, extras: Iterable[str]) -> None: + self.__extras = frozenset(extras) + + def __str__(self) -> str: + return f'Extra(s) [{",".join(sorted(self.__extras))}] not specified.' + + +class GroupsNotFoundError(ValueError): + def __init__(self, groups: Iterable[str]) -> None: + self.__groups = frozenset(groups) + + def __str__(self) -> str: + return 'Group(s) not found: ' + ', '.join(sorted(self.__groups)) + + +@dataclass +class _LockEntry: + name: str + component: Component + dependencies: frozenset[str] # keys MUST go through `normalize_packagename()` + added2bom: bool + + +class UvBB(BomBuilder): + + @staticmethod + def make_argument_parser(**kwargs: Any) -> 'ArgumentParser': + p = ArgumentParser( + description=dedent("""\ + Build an SBOM from uv project. + + This requires parsing your `pyproject.toml` and `uv.lock` file which details exact pinned versions of + dependencies. + """), + **kwargs) + p.add_argument('--group', + metavar='', + help='Include dependencies from the specified dependency group' + ' (multiple values allowed)', + action='append', + dest='groups_with', + default=[]) + p.add_argument('--no-group', + metavar='', + help='Exclude dependencies from the specified dependency group' + ' (multiple values allowed)', + action='append', + dest='groups_without', + default=[]) + og = p.add_mutually_exclusive_group() + og.add_argument('--only-group', + metavar='', + help='Only include dependencies from the specified dependency group' + ' (multiple values allowed)', + action='append', + dest='groups_only', + default=[]) + og.add_argument('--only-dev', + help='Alias for: --only-group dev', + dest='only_dev', + action='store_true') + del og + p.add_argument('--all-groups', + help='Include all dependency groups' + ' (default: %(default)s)', + dest='all_groups', + action='store_true', + default=False) + p.add_argument('--no-default-groups', + help='Ignore the default dependency groups' + ' (default: %(default)s)', + dest='no_default_groups', + action='store_true', + default=False) + p.add_argument('--no-dev', + help='Alias for: --no-group dev', + dest='no_dev', + action='store_true', + default=False) + eg = p.add_mutually_exclusive_group() + eg.add_argument('-E', '--extras', + metavar='', + help='Extra sets of dependencies to include' + ' (multiple values allowed)', + action='append', + dest='extras', + default=[]) + eg.add_argument('--all-extras', + help='Include all extra dependencies' + ' (default: %(default)s)', + action='store_true', + dest='all_extras', + default=False) + del eg + add_argument_mc_type(p) + p.add_argument('project_directory', + metavar='', + help='The project directory for uv (containing `pyproject.toml` and `uv.lock`),' + ' or a path to `uv.lock`' + ' (default: current working directory)', + nargs=OPTIONAL, + default='.') + return p + + def __init__(self, *, + logger: 'Logger', + **__: Any) -> None: + self._logger = logger + self.__marker_env_base: dict[str, str] = {} + self.__marker_env_by_extra: dict[str, dict[str, str]] = {} + self.__active_resolution_markers: Optional[frozenset[str]] = None + + def __marker_env(self, *, extra: Optional[str]) -> dict[str, str]: + extra_n = normalize_packagename(extra) if extra else '' + cached = self.__marker_env_by_extra.get(extra_n) + if cached is not None: + return cached + env = dict(self.__marker_env_base) + env['extra'] = extra_n + self.__marker_env_by_extra[extra_n] = env + return env + + def __is_marker_ok(self, marker: Any, *, extra: Optional[str] = None) -> bool: + if not marker: + return True + try: + marker_s = str(marker) + return Marker(marker_s).evaluate(self.__marker_env(extra=extra)) + except Exception as err: # pragma: no cover + self._logger.debug('failed evaluating marker %r', marker, exc_info=err) + return True + + def __select_active_resolution_markers(self, locker: 'T_NameDict') -> Optional[frozenset[str]]: + markers = locker.get('resolution-markers') + if not isinstance(markers, list) or len(markers) == 0: + return None + active = frozenset(str(m) for m in markers if self.__is_marker_ok(m)) + if len(active) == 0: + raise ValueError('uv lock has resolution-markers but none match the current environment') + if len(active) > 1: + self._logger.warning('uv lock has multiple matching resolution-markers: %s', ', '.join(sorted(active))) + return active + + def __call__(self, *, # type:ignore[override] + project_directory: str, + groups_with: list[str], + groups_without: list[str], + groups_only: list[str], + only_dev: bool, + all_groups: bool, + no_default_groups: bool, + no_dev: bool, + extras: list[str], + all_extras: bool, + mc_type: 'ComponentType', + **__: Any) -> 'Bom': + if isfile(project_directory) and basename(project_directory) == 'uv.lock': + lock_file = project_directory + project_directory = dirname(project_directory) or '.' + pyproject_file = join(project_directory, 'pyproject.toml') + else: + pyproject_file = join(project_directory, 'pyproject.toml') + lock_file = join(project_directory, 'uv.lock') + try: + pyproject_fh = open(pyproject_file, encoding='utf8', errors='replace') + except OSError as err: + raise ValueError(f'Could not open pyproject file: {pyproject_file}') from err + try: + lock_fh = open(lock_file, encoding='utf8', errors='replace') + except OSError as err: + pyproject_fh.close() + raise ValueError(f'Could not open lock file: {lock_file}') from err + + with pyproject_fh, lock_fh: + pyproject: 'T_NameDict' = toml_loads(pyproject_fh.read()) + locker: 'T_NameDict' = toml_loads(lock_fh.read()) + self.__marker_env_base = default_environment() + self.__marker_env_by_extra.clear() + self.__active_resolution_markers = self.__select_active_resolution_markers(locker) + + root_c = pyproject2component(pyproject, + ctype=mc_type, + fpath=pyproject_file, + gather_license_texts=False, + logger=self._logger) + root_c.bom_ref.value = 'root-component' + + groups_available = self.__get_dependency_groups(pyproject, locker) + groups_with_s = frozenset(map(normalize_packagename, + filter(None, ','.join(groups_with).split(',')))) + groups_without_s = frozenset(map(normalize_packagename, + filter(None, ','.join(groups_without).split(',')))) + groups_only_s = frozenset(map(normalize_packagename, + filter(None, ','.join(groups_only).split(',')))) + del groups_with, groups_without, groups_only + + if only_dev: + groups_only_s = frozenset({'dev', }) + if no_dev: + groups_without_s = groups_without_s | frozenset({'dev', }) + if only_dev and no_dev: + raise ValueError('`--only-dev` and `--no-dev` are mutually exclusive') + + all_groups_s = frozenset(groups_available) + groups_requested = groups_with_s | groups_without_s | groups_only_s + groups_unknown = groups_requested - all_groups_s + if len(groups_unknown) > 0: + groups_error = GroupsNotFoundError(groups_unknown) + self._logger.error(groups_error) + raise ValueError('some uv dependency groups are unknown') from groups_error + + include_project_deps = len(groups_only_s) == 0 + use_groups: frozenset[str] + if groups_only_s: + use_groups = groups_only_s - groups_without_s + else: + use_groups = set() + if all_groups: + use_groups.update(all_groups_s) + else: + if not no_default_groups: + use_groups.update(self.__get_default_groups(pyproject, all_groups_s)) + use_groups.update(groups_with_s) + use_groups = frozenset(use_groups - groups_without_s) + del groups_with_s, groups_without_s, groups_only_s, groups_requested, groups_unknown + + extras_s: frozenset[str] + if include_project_deps: + if all_extras: + extras_s = frozenset(self.__get_optional_dependencies(pyproject, locker)) + else: + extras_s = frozenset(map(normalize_packagename, + filter(None, ','.join(extras).split(',')))) + + optional_deps = self.__get_optional_dependencies(pyproject, locker) + extras_not_found = extras_s - optional_deps.keys() + if len(extras_not_found) > 0: + extras_error = ExtrasNotFoundError(extras_not_found) + self._logger.error(extras_error) + raise ValueError('some package extras are unknown') from extras_error + del extras_not_found + else: + # `--only-group` / `--only-dev` implies no project selection, so extras are ignored. + extras_s = frozenset() + + return self._make_bom( + root_c, + locker, + self.__get_dependency_seeds(pyproject, locker, extras_s, use_groups, include_project_deps), + extras_s, + ) + + def __get_dependency_seeds(self, pyproject: 'T_NameDict', locker: 'T_NameDict', + extras: frozenset[str], + groups: frozenset[str], + include_project_deps: bool + ) -> tuple[frozenset[str], dict[str, frozenset[str]]]: + """ + Determine which packages are included, based on `pyproject.toml` manifest. + + This mimics how `uv sync` behaves by default: base dependencies, dependencies from the default group set, + plus selected extras. + """ + dep_names: set[str] = set() + required_extras: dict[str, set[str]] = {} + + def add_req(req: Requirement, *, marker_extra: Optional[str]) -> None: + if req.marker is not None: + try: + if not req.marker.evaluate(self.__marker_env(extra=marker_extra)): + return + except Exception as err: # pragma: no cover + self._logger.debug('failed evaluating marker %r', req.marker, exc_info=err) + name = normalize_packagename(req.name) + dep_names.add(name) + if req.extras: + required_extras.setdefault(name, set()).update(map(normalize_packagename, req.extras)) + + project = pyproject.get('project') + if include_project_deps: + if isinstance(project, dict): + for dep in project.get('dependencies', ()): + try: + add_req(Requirement(dep), marker_extra=None) + except Exception as err: # pragma: no cover + self._logger.debug('failed parsing dependency %r', dep, exc_info=err) + + raw_optional = project.get('optional-dependencies', {}) + if isinstance(raw_optional, dict): + for extra_raw, dep_specs in raw_optional.items(): + extra = normalize_packagename(str(extra_raw)) + if extra not in extras: + continue + for dep_spec in dep_specs or (): + try: + add_req(Requirement(dep_spec), marker_extra=extra) + except Exception as err: # pragma: no cover + self._logger.debug('failed parsing optional dependency %r', dep_spec, exc_info=err) + else: + # best-effort fallback: rely on lockfile-only metadata + lock_root = self.__get_lock_root(locker, root_name=None) + if lock_root is not None: + dep_names.update(lock_root.dependencies) + optional_deps = self.__optional_dependencies_from_lock(lock_root.package) + for extra in extras: + dep_names.update(optional_deps.get(extra, ())) + + if groups: + for group in groups: + for dep_name, dep_extras in self.__group_dependencies(pyproject, locker, group).items(): + dep_names.add(dep_name) + if dep_extras: + required_extras.setdefault(dep_name, set()).update(dep_extras) + for dep_spec in self.__group_dependency_specs(pyproject, group): + try: + add_req(Requirement(dep_spec), marker_extra=None) + except Exception as err: # pragma: no cover + self._logger.debug('failed parsing group dependency %r', dep_spec, exc_info=err) + + return frozenset(dep_names), {k: frozenset(v) for k, v in required_extras.items()} + + def __get_optional_dependencies(self, pyproject: 'T_NameDict', locker: 'T_NameDict') -> dict[str, frozenset[str]]: + project = pyproject.get('project') + if isinstance(project, dict): + raw = project.get('optional-dependencies', {}) + if isinstance(raw, dict): + deps: dict[str, set[str]] = {} + for extra_raw, dep_specs in raw.items(): + extra = normalize_packagename(str(extra_raw)) + deps.setdefault(extra, set()) + for dep_spec in dep_specs or (): + try: + req = Requirement(dep_spec) + except Exception as err: # pragma: no cover + self._logger.debug('failed parsing optional dependency %r', dep_spec, exc_info=err) + continue + deps[extra].add(normalize_packagename(req.name)) + return {k: frozenset(v) for k, v in deps.items()} + + # best-effort fallback: lockfile may still contain this info + lock_root = self.__get_lock_root(locker, root_name=None) + if lock_root is None: + return {} + return {k: frozenset(v) for k, v in self.__optional_dependencies_from_lock(lock_root.package).items()} + + def __get_dependency_groups(self, pyproject: 'T_NameDict', locker: 'T_NameDict') -> dict[str, frozenset[str]]: + """ + Determine which dependency groups are available. + + Groups are primarily sourced from PEP 735 `dependency-groups` and uv's legacy `tool.uv.dev-dependencies`. + As a fallback, groups are also discovered from `uv.lock`. + """ + groups: dict[str, set[str]] = {} + + raw = pyproject.get('dependency-groups', {}) + if isinstance(raw, dict): + for group_raw, dep_specs in raw.items(): + group = normalize_packagename(str(group_raw)) + groups.setdefault(group, set()) + for dep_spec in dep_specs or (): + try: + req = Requirement(str(dep_spec)) + except Exception as err: # pragma: no cover + self._logger.debug('failed parsing dependency group requirement %r', dep_spec, exc_info=err) + continue + groups[group].add(normalize_packagename(req.name)) + + tool = pyproject.get('tool', {}) + if isinstance(tool, dict): + tool_uv = tool.get('uv', {}) + if isinstance(tool_uv, dict): + legacy_dev = tool_uv.get('dev-dependencies', ()) + if isinstance(legacy_dev, list): + groups.setdefault('dev', set()) + for dep_spec in legacy_dev or (): + try: + req = Requirement(str(dep_spec)) + except Exception as err: # pragma: no cover + self._logger.debug('failed parsing legacy dev dependency %r', dep_spec, exc_info=err) + continue + groups['dev'].add(normalize_packagename(req.name)) + + lock_root = self.__get_lock_root(locker, root_name=None) + if lock_root is not None: + raw_lock = lock_root.package.get('dev-dependencies', {}) + if isinstance(raw_lock, dict): + for group_raw, deps in raw_lock.items(): + group = normalize_packagename(str(group_raw)) + groups.setdefault(group, set()) + for dep in deps or (): + if isinstance(dep, dict) and 'name' in dep: + groups[group].add(normalize_packagename(str(dep['name']))) + + # uv defaults to syncing the `dev` group; treat it as existing even if empty. + groups.setdefault('dev', set()) + + return {k: frozenset(v) for k, v in groups.items()} + + def __get_default_groups(self, pyproject: 'T_NameDict', available_groups: frozenset[str]) -> frozenset[str]: + default_groups = frozenset({'dev', }) # uv default + + tool = pyproject.get('tool', {}) + if isinstance(tool, dict): + tool_uv = tool.get('uv', {}) + if isinstance(tool_uv, dict) and 'default-groups' in tool_uv: + raw = tool_uv.get('default-groups') + if raw == 'all': + default_groups = available_groups + elif isinstance(raw, list): + default_groups = frozenset( + normalize_packagename(str(g)) + for g in raw + if g + ) + elif isinstance(raw, str) and raw: + default_groups = frozenset({normalize_packagename(raw), }) + + unknown = default_groups - available_groups + if unknown: + self._logger.warning('skip unknown default groups: %s', ', '.join(sorted(unknown))) + default_groups = default_groups - unknown + + return default_groups + + def __group_dependency_specs(self, pyproject: 'T_NameDict', group: str) -> tuple[str, ...]: + group_n = normalize_packagename(group) + specs: list[str] = [] + + raw = pyproject.get('dependency-groups', {}) + if isinstance(raw, dict): + for group_raw, dep_specs in raw.items(): + if normalize_packagename(str(group_raw)) != group_n: + continue + specs.extend(str(d) for d in (dep_specs or ()) if d) + + # legacy field; included in the `dev` group + if group_n == 'dev': + tool = pyproject.get('tool', {}) + if isinstance(tool, dict): + tool_uv = tool.get('uv', {}) + if isinstance(tool_uv, dict): + legacy_dev = tool_uv.get('dev-dependencies', ()) + if isinstance(legacy_dev, list): + specs.extend(str(d) for d in legacy_dev or () if d) + + return tuple(specs) + + def __group_dependencies(self, pyproject: 'T_NameDict', locker: 'T_NameDict', group: str) -> dict[str, set[str]]: + """ + Best-effort group dependencies from `uv.lock`. + + Returns mapping of dependency-name to required extras. + """ + del pyproject # reserved for potential future use + + lock_root = self.__get_lock_root(locker, root_name=None) + if lock_root is None: + return {} + raw = lock_root.package.get('dev-dependencies', {}) + if not isinstance(raw, dict): + return {} + group_n = normalize_packagename(group) + + deps: dict[str, set[str]] = {} + for group_raw, group_deps in raw.items(): + if normalize_packagename(str(group_raw)) != group_n: + continue + for dep in group_deps or (): + if not isinstance(dep, dict) or 'name' not in dep: + continue + if not self.__is_marker_ok(dep.get('marker')): + continue + dep_name = normalize_packagename(str(dep['name'])) + dep_extras_raw = dep.get('extra', dep.get('extras')) + if isinstance(dep_extras_raw, list): + deps.setdefault(dep_name, set()).update( + normalize_packagename(str(e)) for e in dep_extras_raw if e + ) + else: + deps.setdefault(dep_name, set()) + return deps + + def __optional_dependencies_from_lock(self, lock_root_package: 'T_NameDict') -> dict[str, set[str]]: + optional_deps: dict[str, set[str]] = {} + raw = lock_root_package.get('optional-dependencies', {}) + if not isinstance(raw, dict): + return optional_deps + for extra_raw, deps in raw.items(): + extra = normalize_packagename(str(extra_raw)) + optional_deps.setdefault(extra, set()) + for dep in deps or (): + if not isinstance(dep, dict) or 'name' not in dep: + continue + if not self.__is_marker_ok(dep.get('marker'), extra=extra): + continue + optional_deps.setdefault(extra, set()).add(normalize_packagename(str(dep['name']))) + return optional_deps + + @dataclass(frozen=True) + class _LockRoot: + package: 'T_NameDict' + dependencies: frozenset[str] + + def __get_lock_root(self, locker: 'T_NameDict', *, root_name: Optional[str]) -> Optional['_LockRoot']: + """ + Best-effort lookup of the "root" package entry in `uv.lock`. + + Currently, uv includes the project as a local package, e.g. `source = { virtual = "." }` or + `source = { editable = "." }`. + """ + packages = locker.get('package', ()) + if not isinstance(packages, list): + return None + root_name_n = normalize_packagename(root_name) if root_name else None + + def is_project_source(pkg: 'T_NameDict') -> bool: + source = pkg.get('source', {}) + if not isinstance(source, dict): + return False + for k in ('virtual', 'editable', 'path', 'directory'): + if k in source and str(source.get(k)) in {'.', './'}: + return True + return False + + candidate = None + for pkg in packages: + if not isinstance(pkg, dict): + continue + if root_name_n is not None and normalize_packagename(str(pkg.get('name', ''))) != root_name_n: + continue + if is_project_source(pkg): + candidate = pkg + break + if candidate is None: + candidate = pkg + if candidate is None: + return None + + deps = frozenset( + normalize_packagename(str(d['name'])) + for d in candidate.get('dependencies', ()) + if isinstance(d, dict) and 'name' in d and self.__is_marker_ok(d.get('marker')) + ) + return self._LockRoot(candidate, deps) + + def _make_bom(self, root_c: Component, locker: 'T_NameDict', + seed_deps: tuple[frozenset[str], dict[str, frozenset[str]]], + use_extras: frozenset[str]) -> 'Bom': + bom = make_bom() + bom.metadata.component = root_c + self._logger.debug('root-component: %r', root_c) + + if use_extras: + root_c.properties.update( + Property( + name=PropertyName.PythonPackageExtra.value, + value=extra + ) for extra in use_extras + ) + + lock_data: dict[str, list[_LockEntry]] = {} + for entry in self._parse_lock(locker): + _ld = lock_data.setdefault(entry.name, []) + _ldl = len(_ld) + if _ldl > 0 and entry.component.bom_ref.value: + entry.component.bom_ref.value += f'#{_ldl}' + _ld.append(entry) + + root_name_n = normalize_packagename(root_c.name) + seed_names, required_extras = seed_deps + for dep_name, extras in required_extras.items(): + for lock_entry in lock_data.get(dep_name, ()): + lock_entry.component.properties.update( + Property( + name=PropertyName.PythonPackageExtra.value, + value=extra + ) for extra in extras + ) + + included = self.__collect_lock_entries(lock_data, seed_names, root_name_n) + + root_dep = Dependency(root_c.bom_ref) + bom.dependencies.add(root_dep) + + def deps_for_name(dep_name_n: str) -> Iterable[_LockEntry]: + if dep_name_n == root_name_n: + return () + return lock_data.get(dep_name_n, ()) + + for dep_name_n in sorted(seed_names): + entries = deps_for_name(dep_name_n) + if not entries: + self._logger.warning('skip unlocked dependency: %s', dep_name_n) + continue + for entry in entries: + root_dep.dependencies.add(Dependency(entry.component.bom_ref)) + + deps_by_ref: dict[str, Dependency] = {root_c.bom_ref.value: root_dep} + + for entry in included: + if entry.name == root_name_n: + continue + if not entry.added2bom: + entry.added2bom = True + self._logger.info('add component for package %r', entry.component.name) + self._logger.debug('add component: %r', entry.component) + bom.components.add(entry.component) + dep = deps_by_ref.get(entry.component.bom_ref.value) + if dep is None: + dep = deps_by_ref[entry.component.bom_ref.value] = Dependency(entry.component.bom_ref) + bom.dependencies.add(dep) + + for entry in included: + if entry.name == root_name_n: + continue + dep = deps_by_ref.get(entry.component.bom_ref.value) + if dep is None: + continue + for dep_name_n in sorted(entry.dependencies): + if dep_name_n == root_name_n: + dep.dependencies.add(Dependency(root_c.bom_ref)) + continue + dep_entries = lock_data.get(dep_name_n) + if dep_entries is None: + self._logger.warning('skip unlocked component: %s', dep_name_n) + continue + for dep_entry in dep_entries: + dep.dependencies.add(Dependency(dep_entry.component.bom_ref)) + + return bom + + def __collect_lock_entries(self, lock_data: dict[str, list[_LockEntry]], + seed_names: Iterable[str], + root_name: str) -> tuple[_LockEntry, ...]: + included: dict[str, _LockEntry] = {} + pending = list(sorted(seed_names)) + while pending: + name = pending.pop() + if name == root_name: + continue + entries = lock_data.get(name) + if not entries: + continue + for entry in entries: + ref = entry.component.bom_ref.value + if ref in included: + continue + included[ref] = entry + pending.extend(sorted(entry.dependencies)) + return tuple(sorted(included.values(), key=lambda e: e.component.bom_ref.value)) + + def _parse_lock(self, locker: 'T_NameDict') -> Generator[_LockEntry, None, None]: + package: 'T_NameDict' + for package in locker.get('package', []): + res_markers = package.get('resolution-markers') + if isinstance(res_markers, list) and len(res_markers) > 0: + if self.__active_resolution_markers is not None: + if not any(str(m) in self.__active_resolution_markers for m in res_markers): + if not any(self.__is_marker_ok(m) for m in res_markers): + continue + elif not any(self.__is_marker_ok(m) for m in res_markers): + continue + yield _LockEntry( + name=normalize_packagename(str(package['name'])), + component=self.__make_component4lock(package), + dependencies=frozenset( + normalize_packagename(str(d['name'])) + for d in package.get('dependencies', ()) + if isinstance(d, dict) and 'name' in d and self.__is_marker_ok(d.get('marker')) + ), + added2bom=False, + ) + + def __make_component4lock(self, package: 'T_NameDict') -> Component: + source = package.get('source', {}) + is_local = isinstance(source, dict) and ( + 'virtual' in source or 'editable' in source or 'path' in source or 'directory' in source + ) + + version = package.get('version') + bom_ref = f'{package["name"]}@{version}' if version else str(package['name']) + + return Component( + type=ComponentType.LIBRARY, + bom_ref=bom_ref, + name=package['name'], + version=version, + external_references=self.__extrefs4lock(package), + purl=PackageURL( + type=PurlTypePypi, + name=package['name'], + version=version, + qualifiers=self.__purl_qualifiers4lock(package) + ) if not is_local else None + ) + + def __purl_qualifiers4lock(self, package: 'T_NameDict') -> 'T_NameDict': + qs: 'T_NameDict' = {} + + source = package.get('source', {}) + if not isinstance(source, dict): + return qs + + if 'registry' in source: + source_url = redact_auth_from_url(str(source.get('registry', '')).rstrip('/')) + if source_url and '://pypi.org/' not in source_url: + qs['repository_url'] = source_url + elif 'url' in source: + source_url = redact_auth_from_url(str(source.get('url', ''))) + if source_url and '://files.pythonhosted.org/' not in source_url: + qs['download_url'] = source_url + elif 'git' in source: + # best-effort: uv lock format might evolve; keep this flexible. + url = redact_auth_from_url(str(source.get('git', ''))) + rev = str(source.get('rev', source.get('reference', source.get('tag', '')))) + qs['vcs_url'] = f'git+{url}@{rev}' if rev else f'git+{url}' + + return qs + + def __extrefs4lock(self, package: 'T_NameDict') -> Generator['ExternalReference', None, None]: + if isinstance(sdist := package.get('sdist'), dict) and 'url' in sdist: + try: + yield ExternalReference( + comment='sdist', + type=ExternalReferenceType.DISTRIBUTION, + url=XsUri(redact_auth_from_url(str(sdist['url']))), + hashes=[HashType.from_composite_str(str(sdist['hash']))] if 'hash' in sdist else None + ) + except (InvalidUriException, UnknownHashTypeException) as error: # pragma: nocover + self._logger.debug('skipped sdist-extRef for: %r', package.get('name'), exc_info=error) + del error + + wheels = package.get('wheels', ()) + if isinstance(wheels, list): + for wheel in wheels: + if not isinstance(wheel, dict) or 'url' not in wheel: + continue + try: + yield ExternalReference( + comment='wheel', + type=ExternalReferenceType.DISTRIBUTION, + url=XsUri(redact_auth_from_url(str(wheel['url']))), + hashes=[HashType.from_composite_str(str(wheel['hash']))] if 'hash' in wheel else None + ) + except (InvalidUriException, UnknownHashTypeException) as error: # pragma: nocover + self._logger.debug('skipped wheel-extRef for: %r', package.get('name'), exc_info=error) + del error diff --git a/docs/usage.rst b/docs/usage.rst index 2bd433744..f12bdae6c 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -348,6 +348,84 @@ The full documentation can be issued by running with ``poetry --help``: (default: silent) +For uv +------ + +**subcommand:** ``uv`` + +Support for `uv`_ dependency management and package manifest. +This requires parsing your ``pyproject.toml`` and ``uv.lock`` file which details exact pinned versions of +dependencies. +By default, dependencies from uv's default dependency groups (e.g. ``dev``) are included; +use ``--no-dev`` or ``--no-default-groups`` to exclude them. + +.. _uv: https://docs.astral.sh/uv/ + +The full documentation can be issued by running with ``uv --help``: + +.. code-block:: shell-session + + $ cyclonedx-py uv --help + usage: cyclonedx-py uv [-h] [--group ] [--no-group ] + [--only-group | --only-dev] [--all-groups] + [--no-default-groups] [--no-dev] + [-E | --all-extras] [--mc-type ] + [--short-PURLs] [--sv ] + [--output-reproducible] [--of ] [-o ] + [--validate | --no-validate] [-v] + [] + + Build an SBOM from uv project. + + This requires parsing your `pyproject.toml` and `uv.lock` file which details exact pinned versions of + dependencies. + + positional arguments: + The project directory for uv (containing + `pyproject.toml` and `uv.lock`), or a path to + `uv.lock` (default: current working directory) + + options: + -h, --help show this help message and exit + --group Include dependencies from the specified dependency + group (multiple values allowed) + --no-group Exclude dependencies from the specified dependency + group (multiple values allowed) + --only-group Only include dependencies from the specified + dependency group (multiple values allowed) + --only-dev Alias for: --only-group dev + --all-groups Include all dependency groups (default: False) + --no-default-groups Ignore the default dependency groups (default: False) + --no-dev Alias for: --no-group dev + -E, --extras + Extra sets of dependencies to include (multiple values + allowed) + --all-extras Include all extra dependencies (default: False) + --mc-type Type of the main component. {choices: application, + firmware, library} (default: application) + --short-PURLs Omit all qualifiers from PackageURLs. This causes + information loss in trade-off shorter PURLs, which + might improve ingesting these strings. + --sv, --spec-version + Which version of CycloneDX to use. {choices: 1.7, 1.6, + 1.5, 1.4, 1.3, 1.2, 1.1, 1.0} (default: 1.6) + --output-reproducible + Whether to go the extra mile and make the output + reproducible. This might result in loss of time- and + random-based values. + --of, --output-format + Which output format to use. {choices: JSON, XML} + (default: JSON) + -o, --output-file + Path to the output file. (set to "-" to output to + ) (default: -) + --validate, --no-validate + Whether to validate resulting BOM before outputting. + (default: True) + -v, --verbose Increase the verbosity of messages (multiple for more + effect) (default: silent) + + For Pip requirements -------------------- @@ -478,22 +556,6 @@ it is possible to use the functionality for Python (virtual) environments as des -For uv -------- - -Support for `uv`_ manifest and lockfile is not explicitly implemented, yet. - -However, since uv utilizes Python virtual environments under the hood, -it is possible to use the functionality for Python (virtual) environments as described above. - -.. _uv: https://docs.astral.sh/uv/ - - - -***** - - - For Conda --------- diff --git a/pyproject.toml b/pyproject.toml index 88b53aede..30925bfd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ exclude = [ keywords = [ "OWASP", "CycloneDX", "bill-of-materials", "BOM", "software-bill-of-materials", "SBOM", - "environment", "virtualenv", "venv", "Poetry", "Pipenv", "requirements", "PDM", "Conda", + "environment", "virtualenv", "venv", "Poetry", "Pipenv", "uv", "requirements", "PDM", "Conda", "SPDX", "licenses", "PURL", "package-url", "dependency-graph", ] classifiers = [ From 54df6ff84d61fd05f218b2c4fe276794bf6c6125 Mon Sep 17 00:00:00 2001 From: Mohammed Abbadi Date: Mon, 23 Mar 2026 01:04:11 +0300 Subject: [PATCH 2/5] fix(uv): satisfy mypy for Python 3.9 lowest - Build marker env base from default_environment() items as str/str pairs. - Use a mutable set when assembling dependency groups before frozenset. - Add _bom_ref_value helper for non-optional bom_ref string keys. Signed-off-by: Mohammed Abbadi --- cyclonedx_py/_internal/uv.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/cyclonedx_py/_internal/uv.py b/cyclonedx_py/_internal/uv.py index f62a5e111..5c6c5a18c 100644 --- a/cyclonedx_py/_internal/uv.py +++ b/cyclonedx_py/_internal/uv.py @@ -47,6 +47,13 @@ T_NameDict = dict[str, Any] +def _bom_ref_value(component: Component) -> str: + ref = component.bom_ref.value + if ref is None: + raise ValueError(f'component {component.name!r} is missing bom_ref') + return ref + + class ExtrasNotFoundError(ValueError): def __init__(self, extras: Iterable[str]) -> None: self.__extras = frozenset(extras) @@ -224,7 +231,8 @@ def __call__(self, *, # type:ignore[override] with pyproject_fh, lock_fh: pyproject: 'T_NameDict' = toml_loads(pyproject_fh.read()) locker: 'T_NameDict' = toml_loads(lock_fh.read()) - self.__marker_env_base = default_environment() + _marker_defaults = default_environment() + self.__marker_env_base = {str(k): str(v) for k, v in _marker_defaults.items()} self.__marker_env_by_extra.clear() self.__active_resolution_markers = self.__select_active_resolution_markers(locker) @@ -260,18 +268,17 @@ def __call__(self, *, # type:ignore[override] raise ValueError('some uv dependency groups are unknown') from groups_error include_project_deps = len(groups_only_s) == 0 - use_groups: frozenset[str] if groups_only_s: use_groups = groups_only_s - groups_without_s else: - use_groups = set() + acc: set[str] = set() if all_groups: - use_groups.update(all_groups_s) + acc.update(all_groups_s) else: if not no_default_groups: - use_groups.update(self.__get_default_groups(pyproject, all_groups_s)) - use_groups.update(groups_with_s) - use_groups = frozenset(use_groups - groups_without_s) + acc.update(self.__get_default_groups(pyproject, all_groups_s)) + acc.update(groups_with_s) + use_groups = frozenset(acc - groups_without_s) del groups_with_s, groups_without_s, groups_only_s, groups_requested, groups_unknown extras_s: frozenset[str] @@ -645,7 +652,8 @@ def deps_for_name(dep_name_n: str) -> Iterable[_LockEntry]: for entry in entries: root_dep.dependencies.add(Dependency(entry.component.bom_ref)) - deps_by_ref: dict[str, Dependency] = {root_c.bom_ref.value: root_dep} + root_bref = _bom_ref_value(root_c) + deps_by_ref: dict[str, Dependency] = {root_bref: root_dep} for entry in included: if entry.name == root_name_n: @@ -655,15 +663,16 @@ def deps_for_name(dep_name_n: str) -> Iterable[_LockEntry]: self._logger.info('add component for package %r', entry.component.name) self._logger.debug('add component: %r', entry.component) bom.components.add(entry.component) - dep = deps_by_ref.get(entry.component.bom_ref.value) + bref = _bom_ref_value(entry.component) + dep = deps_by_ref.get(bref) if dep is None: - dep = deps_by_ref[entry.component.bom_ref.value] = Dependency(entry.component.bom_ref) + dep = deps_by_ref[bref] = Dependency(entry.component.bom_ref) bom.dependencies.add(dep) for entry in included: if entry.name == root_name_n: continue - dep = deps_by_ref.get(entry.component.bom_ref.value) + dep = deps_by_ref.get(_bom_ref_value(entry.component)) if dep is None: continue for dep_name_n in sorted(entry.dependencies): @@ -692,12 +701,12 @@ def __collect_lock_entries(self, lock_data: dict[str, list[_LockEntry]], if not entries: continue for entry in entries: - ref = entry.component.bom_ref.value + ref = _bom_ref_value(entry.component) if ref in included: continue included[ref] = entry pending.extend(sorted(entry.dependencies)) - return tuple(sorted(included.values(), key=lambda e: e.component.bom_ref.value)) + return tuple(sorted(included.values(), key=lambda e: _bom_ref_value(e.component))) def _parse_lock(self, locker: 'T_NameDict') -> Generator[_LockEntry, None, None]: package: 'T_NameDict' From ffc07bfb12d4831d75c5830326021d16989b0ed3 Mon Sep 17 00:00:00 2001 From: Mohammed Abbadi Date: Mon, 23 Mar 2026 01:20:21 +0300 Subject: [PATCH 3/5] style: fix flake8 for uv CLI and imports - Sort local imports in cli.py for isort (I001). - Drop duplicate ComponentType in TYPE_CHECKING; add noqa for high-complexity uv methods; wrap long signatures (F811, C901, E501, E125). Signed-off-by: Mohammed Abbadi --- cyclonedx_py/_internal/cli.py | 2 +- cyclonedx_py/_internal/uv.py | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/cyclonedx_py/_internal/cli.py b/cyclonedx_py/_internal/cli.py index 42b829309..b2757961c 100644 --- a/cyclonedx_py/_internal/cli.py +++ b/cyclonedx_py/_internal/cli.py @@ -33,8 +33,8 @@ from .pipenv import PipenvBB from .poetry import PoetryBB from .requirements import RequirementsBB -from .uv import UvBB from .utils.args import argparse_type4enum, choices4enum +from .uv import UvBB if TYPE_CHECKING: # pragma: no cover from cyclonedx.model.bom import Bom diff --git a/cyclonedx_py/_internal/uv.py b/cyclonedx_py/_internal/uv.py index 5c6c5a18c..8c17c9c55 100644 --- a/cyclonedx_py/_internal/uv.py +++ b/cyclonedx_py/_internal/uv.py @@ -42,7 +42,6 @@ from logging import Logger from cyclonedx.model.bom import Bom - from cyclonedx.model.component import ComponentType T_NameDict = dict[str, Any] @@ -198,7 +197,7 @@ def __select_active_resolution_markers(self, locker: 'T_NameDict') -> Optional[f self._logger.warning('uv lock has multiple matching resolution-markers: %s', ', '.join(sorted(active))) return active - def __call__(self, *, # type:ignore[override] + def __call__(self, *, # type:ignore[override] # noqa: C901 project_directory: str, groups_with: list[str], groups_without: list[str], @@ -307,11 +306,12 @@ def __call__(self, *, # type:ignore[override] extras_s, ) - def __get_dependency_seeds(self, pyproject: 'T_NameDict', locker: 'T_NameDict', - extras: frozenset[str], - groups: frozenset[str], - include_project_deps: bool - ) -> tuple[frozenset[str], dict[str, frozenset[str]]]: + def __get_dependency_seeds( # noqa: C901 + self, pyproject: 'T_NameDict', locker: 'T_NameDict', + extras: frozenset[str], + groups: frozenset[str], + include_project_deps: bool + ) -> tuple[frozenset[str], dict[str, frozenset[str]]]: """ Determine which packages are included, based on `pyproject.toml` manifest. @@ -400,7 +400,9 @@ def __get_optional_dependencies(self, pyproject: 'T_NameDict', locker: 'T_NameDi return {} return {k: frozenset(v) for k, v in self.__optional_dependencies_from_lock(lock_root.package).items()} - def __get_dependency_groups(self, pyproject: 'T_NameDict', locker: 'T_NameDict') -> dict[str, frozenset[str]]: + def __get_dependency_groups( # noqa: C901 + self, pyproject: 'T_NameDict', locker: 'T_NameDict', + ) -> dict[str, frozenset[str]]: """ Determine which dependency groups are available. @@ -600,9 +602,11 @@ def is_project_source(pkg: 'T_NameDict') -> bool: ) return self._LockRoot(candidate, deps) - def _make_bom(self, root_c: Component, locker: 'T_NameDict', - seed_deps: tuple[frozenset[str], dict[str, frozenset[str]]], - use_extras: frozenset[str]) -> 'Bom': + def _make_bom( # noqa: C901 + self, root_c: Component, locker: 'T_NameDict', + seed_deps: tuple[frozenset[str], dict[str, frozenset[str]]], + use_extras: frozenset[str], + ) -> 'Bom': bom = make_bom() bom.metadata.component = root_c self._logger.debug('root-component: %r', root_c) From a2277e098b59b484b281de1e3b5c59c1e42de61b Mon Sep 17 00:00:00 2001 From: Deyaa Al-Khatib Date: Mon, 23 Mar 2026 01:50:43 +0300 Subject: [PATCH 4/5] Add uv tests and fixtures --- tests/_data/infiles/uv/via-uv/pyproject.toml | 46 ++++ tests/_data/infiles/uv/via-uv/uv.lock | 41 +++ .../uv/all-extras_via-uv_1.0.xml.bin | 17 ++ .../uv/all-extras_via-uv_1.1.xml.bin | 35 +++ .../uv/all-extras_via-uv_1.2.json.bin | 119 +++++++++ .../uv/all-extras_via-uv_1.2.xml.bin | 91 +++++++ .../uv/all-extras_via-uv_1.3.json.bin | 169 ++++++++++++ .../uv/all-extras_via-uv_1.3.xml.bin | 119 +++++++++ .../uv/all-extras_via-uv_1.4.json.bin | 204 +++++++++++++++ .../uv/all-extras_via-uv_1.4.xml.bin | 146 +++++++++++ .../uv/all-extras_via-uv_1.5.json.bin | 218 ++++++++++++++++ .../uv/all-extras_via-uv_1.5.xml.bin | 156 +++++++++++ .../uv/all-extras_via-uv_1.6.json.bin | 222 ++++++++++++++++ .../uv/all-extras_via-uv_1.6.xml.bin | 156 +++++++++++ .../uv/all-extras_via-uv_1.7.json.bin | 222 ++++++++++++++++ .../uv/all-extras_via-uv_1.7.xml.bin | 156 +++++++++++ .../snapshots/uv/plain_via-uv_1.0.xml.bin | 11 + .../snapshots/uv/plain_via-uv_1.1.xml.bin | 20 ++ .../snapshots/uv/plain_via-uv_1.2.json.bin | 96 +++++++ .../snapshots/uv/plain_via-uv_1.2.xml.bin | 74 ++++++ .../snapshots/uv/plain_via-uv_1.3.json.bin | 128 +++++++++ .../snapshots/uv/plain_via-uv_1.3.xml.bin | 93 +++++++ .../snapshots/uv/plain_via-uv_1.4.json.bin | 163 ++++++++++++ .../snapshots/uv/plain_via-uv_1.4.xml.bin | 120 +++++++++ .../snapshots/uv/plain_via-uv_1.5.json.bin | 177 +++++++++++++ .../snapshots/uv/plain_via-uv_1.5.xml.bin | 130 ++++++++++ .../snapshots/uv/plain_via-uv_1.6.json.bin | 181 +++++++++++++ .../snapshots/uv/plain_via-uv_1.6.xml.bin | 130 ++++++++++ .../snapshots/uv/plain_via-uv_1.7.json.bin | 181 +++++++++++++ .../snapshots/uv/plain_via-uv_1.7.xml.bin | 130 ++++++++++ .../uv/some-extras_via-uv_1.0.xml.bin | 17 ++ .../uv/some-extras_via-uv_1.1.xml.bin | 35 +++ .../uv/some-extras_via-uv_1.2.json.bin | 119 +++++++++ .../uv/some-extras_via-uv_1.2.xml.bin | 91 +++++++ .../uv/some-extras_via-uv_1.3.json.bin | 169 ++++++++++++ .../uv/some-extras_via-uv_1.3.xml.bin | 119 +++++++++ .../uv/some-extras_via-uv_1.4.json.bin | 204 +++++++++++++++ .../uv/some-extras_via-uv_1.4.xml.bin | 146 +++++++++++ .../uv/some-extras_via-uv_1.5.json.bin | 218 ++++++++++++++++ .../uv/some-extras_via-uv_1.5.xml.bin | 156 +++++++++++ .../uv/some-extras_via-uv_1.6.json.bin | 222 ++++++++++++++++ .../uv/some-extras_via-uv_1.6.xml.bin | 156 +++++++++++ .../uv/some-extras_via-uv_1.7.json.bin | 222 ++++++++++++++++ .../uv/some-extras_via-uv_1.7.xml.bin | 156 +++++++++++ tests/integration/test_cli_uv.py | 131 ++++++++++ tests/unit/test_uv.py | 245 ++++++++++++++++++ 46 files changed, 6157 insertions(+) create mode 100644 tests/_data/infiles/uv/via-uv/pyproject.toml create mode 100644 tests/_data/infiles/uv/via-uv/uv.lock create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.0.xml.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.1.xml.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.2.json.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.2.xml.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.3.json.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.3.xml.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.4.json.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.4.xml.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.5.json.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.5.xml.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.6.json.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.6.xml.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.7.json.bin create mode 100644 tests/_data/snapshots/uv/all-extras_via-uv_1.7.xml.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.0.xml.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.1.xml.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.2.json.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.2.xml.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.3.json.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.3.xml.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.4.json.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.4.xml.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.5.json.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.5.xml.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.6.json.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.6.xml.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.7.json.bin create mode 100644 tests/_data/snapshots/uv/plain_via-uv_1.7.xml.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.0.xml.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.1.xml.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.2.json.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.2.xml.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.3.json.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.3.xml.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.4.json.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.4.xml.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.5.json.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.5.xml.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.6.json.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.6.xml.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.7.json.bin create mode 100644 tests/_data/snapshots/uv/some-extras_via-uv_1.7.xml.bin create mode 100644 tests/integration/test_cli_uv.py create mode 100644 tests/unit/test_uv.py diff --git a/tests/_data/infiles/uv/via-uv/pyproject.toml b/tests/_data/infiles/uv/via-uv/pyproject.toml new file mode 100644 index 000000000..c3135950f --- /dev/null +++ b/tests/_data/infiles/uv/via-uv/pyproject.toml @@ -0,0 +1,46 @@ +[project] +# https://packaging.python.org/en/latest/specifications/declaring-project-metadata/#declaring-project-metadata +name = "via-uv" +version = "0.1.0" +description = "environment via uv" +license = { text = "Apache-2.0 OR MIT" } +readme = "README.md" +requires-python = ">=3.8" + +# dynamic = [] # TODO + +authors = ["Your Name ", "My Name"] +maintainers = [ + "John Smith ", + "Jane Smith ", +] + +keywords = ["packaging", "pipenv", "test"] +classifiers = [ + "License :: OSI Approved :: Apache Software License", + "License :: OSI Approved :: MIT License", + "Classifier: Development Status :: 4 - Beta", + "Intended Audience :: Developers" +] + +dependencies = [ + 'toml' +] +optional-dependencies = { 'foo' = ['ddt'] } + +# entry-point = {} # TODO + +# gui-scripts = {} # TODO +# scripts = {} # TODO + +[project.urls] +homepage = "https://oss.acme.org/my-project/" +repository = "https://oss.acme.org/my-project.git" +documentation = "https://oss.acme.org/my-project/docs/" +"Bug Tracker" = "https://oss.acme.org/my-project/bugs/" +"Funding" = "https://oss.acme.org/my-project/funding/" +"Change log" = "https://oss.acme.org/my-project/changelog/" + + +[tool.uv] +# https://docs.astral.sh/uv/reference/settings/ diff --git a/tests/_data/infiles/uv/via-uv/uv.lock b/tests/_data/infiles/uv/via-uv/uv.lock new file mode 100644 index 000000000..192664c23 --- /dev/null +++ b/tests/_data/infiles/uv/via-uv/uv.lock @@ -0,0 +1,41 @@ +version = 1 +revision = 1 +requires-python = ">=3.8" + +[[package]] +name = "ddt" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz", hash = "sha256:d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b", size = 13673 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl", hash = "sha256:6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354", size = 7065 }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, +] + +[[package]] +name = "via-uv" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "toml" }, +] + +[package.optional-dependencies] +foo = [ + { name = "ddt" }, +] + +[package.metadata] +requires-dist = [ + { name = "ddt", marker = "extra == 'foo'" }, + { name = "toml" }, +] +provides-extras = ["foo"] diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.0.xml.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.0.xml.bin new file mode 100644 index 000000000..fbb8f2699 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.0.xml.bin @@ -0,0 +1,17 @@ + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + false + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + false + + + diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.1.xml.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.1.xml.bin new file mode 100644 index 000000000..669b0112f --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.1.xml.bin @@ -0,0 +1,35 @@ + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + + + + diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.2.json.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.2.json.bin new file mode 100644 index 000000000..98a80d19f --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.2.json.bin @@ -0,0 +1,119 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "other", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "type": "application", + "version": "0.1.0" + }, + "tools": [ + { + "name": "cyclonedx-py", + "vendor": "CycloneDX", + "version": "thisVersion-testing" + }, + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "libVersion-testing" + } + ] + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.2" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.2.xml.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.2.xml.bin new file mode 100644 index 000000000..7faf48e25 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.2.xml.bin @@ -0,0 +1,91 @@ + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.3.json.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.3.json.bin new file mode 100644 index 000000000..4ce52b028 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.3.json.bin @@ -0,0 +1,169 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "id": "MIT" + } + }, + { + "license": { + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "other", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "properties": [ + { + "name": "cdx:python:package:required-extra", + "value": "foo" + } + ], + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": [ + { + "name": "cyclonedx-py", + "vendor": "CycloneDX", + "version": "thisVersion-testing" + }, + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "libVersion-testing" + } + ] + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.3" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.3.xml.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.3.xml.bin new file mode 100644 index 000000000..2c3c87896 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.3.xml.bin @@ -0,0 +1,119 @@ + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + foo + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b + + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + 6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354 + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.4.json.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.4.json.bin new file mode 100644 index 000000000..6586fc2f4 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.4.json.bin @@ -0,0 +1,204 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "id": "MIT" + } + }, + { + "license": { + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "properties": [ + { + "name": "cdx:python:package:required-extra", + "value": "foo" + } + ], + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "name": "cyclonedx-py", + "vendor": "CycloneDX", + "version": "thisVersion-testing" + }, + { + "externalReferences": [ ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "libVersion-testing" + } + ] + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.4" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.4.xml.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.4.xml.bin new file mode 100644 index 000000000..53deec66d --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.4.xml.bin @@ -0,0 +1,146 @@ + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + foo + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b + + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + 6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354 + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.5.json.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.5.json.bin new file mode 100644 index 000000000..d48665d25 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.5.json.bin @@ -0,0 +1,218 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "id": "MIT" + } + }, + { + "license": { + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "properties": [ + { + "name": "cdx:python:package:required-extra", + "value": "foo" + } + ], + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": { + "components": [ + { + "description": "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments", + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "group": "CycloneDX", + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "name": "cyclonedx-py", + "type": "application", + "version": "thisVersion-testing" + }, + { + "description": "stripped", + "externalReferences": [ ], + "group": "CycloneDX", + "licenses": [ ], + "name": "cyclonedx-python-lib", + "type": "library", + "version": "libVersion-testing" + } + ] + } + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.5" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.5.xml.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.5.xml.bin new file mode 100644 index 000000000..9aab63096 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.5.xml.bin @@ -0,0 +1,156 @@ + + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments + + + Apache-2.0 + + + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + foo + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b + + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + 6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354 + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.6.json.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.6.json.bin new file mode 100644 index 000000000..0f6a4c8c0 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.6.json.bin @@ -0,0 +1,222 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "MIT" + } + }, + { + "license": { + "acknowledgement": "declared", + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "acknowledgement": "declared", + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "properties": [ + { + "name": "cdx:python:package:required-extra", + "value": "foo" + } + ], + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": { + "components": [ + { + "description": "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments", + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "group": "CycloneDX", + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "Apache-2.0" + } + } + ], + "name": "cyclonedx-py", + "type": "application", + "version": "thisVersion-testing" + }, + { + "description": "stripped", + "externalReferences": [ ], + "group": "CycloneDX", + "licenses": [ ], + "name": "cyclonedx-python-lib", + "type": "library", + "version": "libVersion-testing" + } + ] + } + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.6.xml.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.6.xml.bin new file mode 100644 index 000000000..d733ee4e8 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.6.xml.bin @@ -0,0 +1,156 @@ + + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments + + + Apache-2.0 + + + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + foo + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b + + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + 6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354 + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.7.json.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.7.json.bin new file mode 100644 index 000000000..3ef347d66 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.7.json.bin @@ -0,0 +1,222 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "MIT" + } + }, + { + "license": { + "acknowledgement": "declared", + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "acknowledgement": "declared", + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "properties": [ + { + "name": "cdx:python:package:required-extra", + "value": "foo" + } + ], + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": { + "components": [ + { + "description": "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments", + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "group": "CycloneDX", + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "Apache-2.0" + } + } + ], + "name": "cyclonedx-py", + "type": "application", + "version": "thisVersion-testing" + }, + { + "description": "stripped", + "externalReferences": [ ], + "group": "CycloneDX", + "licenses": [ ], + "name": "cyclonedx-python-lib", + "type": "library", + "version": "libVersion-testing" + } + ] + } + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/all-extras_via-uv_1.7.xml.bin b/tests/_data/snapshots/uv/all-extras_via-uv_1.7.xml.bin new file mode 100644 index 000000000..6b4b22a49 --- /dev/null +++ b/tests/_data/snapshots/uv/all-extras_via-uv_1.7.xml.bin @@ -0,0 +1,156 @@ + + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments + + + Apache-2.0 + + + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + foo + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b + + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + 6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354 + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.0.xml.bin b/tests/_data/snapshots/uv/plain_via-uv_1.0.xml.bin new file mode 100644 index 000000000..3c181bc65 --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.0.xml.bin @@ -0,0 +1,11 @@ + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + false + + + diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.1.xml.bin b/tests/_data/snapshots/uv/plain_via-uv_1.1.xml.bin new file mode 100644 index 000000000..b66b328cb --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.1.xml.bin @@ -0,0 +1,20 @@ + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + + + + diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.2.json.bin b/tests/_data/snapshots/uv/plain_via-uv_1.2.json.bin new file mode 100644 index 000000000..19292c30e --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.2.json.bin @@ -0,0 +1,96 @@ +{ + "components": [ + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "dependsOn": [ + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "other", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "type": "application", + "version": "0.1.0" + }, + "tools": [ + { + "name": "cyclonedx-py", + "vendor": "CycloneDX", + "version": "thisVersion-testing" + }, + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "libVersion-testing" + } + ] + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.2" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.2.xml.bin b/tests/_data/snapshots/uv/plain_via-uv_1.2.xml.bin new file mode 100644 index 000000000..8501e19d0 --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.2.xml.bin @@ -0,0 +1,74 @@ + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.3.json.bin b/tests/_data/snapshots/uv/plain_via-uv_1.3.json.bin new file mode 100644 index 000000000..b90b0240b --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.3.json.bin @@ -0,0 +1,128 @@ +{ + "components": [ + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "dependsOn": [ + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "id": "MIT" + } + }, + { + "license": { + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "other", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": [ + { + "name": "cyclonedx-py", + "vendor": "CycloneDX", + "version": "thisVersion-testing" + }, + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "libVersion-testing" + } + ] + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.3" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.3.xml.bin b/tests/_data/snapshots/uv/plain_via-uv_1.3.xml.bin new file mode 100644 index 000000000..205643b35 --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.3.xml.bin @@ -0,0 +1,93 @@ + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.4.json.bin b/tests/_data/snapshots/uv/plain_via-uv_1.4.json.bin new file mode 100644 index 000000000..e252d62b3 --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.4.json.bin @@ -0,0 +1,163 @@ +{ + "components": [ + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "dependsOn": [ + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "id": "MIT" + } + }, + { + "license": { + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "name": "cyclonedx-py", + "vendor": "CycloneDX", + "version": "thisVersion-testing" + }, + { + "externalReferences": [ ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "libVersion-testing" + } + ] + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.4" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.4.xml.bin b/tests/_data/snapshots/uv/plain_via-uv_1.4.xml.bin new file mode 100644 index 000000000..a9f92f6b5 --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.4.xml.bin @@ -0,0 +1,120 @@ + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.5.json.bin b/tests/_data/snapshots/uv/plain_via-uv_1.5.json.bin new file mode 100644 index 000000000..2049fb98b --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.5.json.bin @@ -0,0 +1,177 @@ +{ + "components": [ + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "dependsOn": [ + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "id": "MIT" + } + }, + { + "license": { + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": { + "components": [ + { + "description": "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments", + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "group": "CycloneDX", + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "name": "cyclonedx-py", + "type": "application", + "version": "thisVersion-testing" + }, + { + "description": "stripped", + "externalReferences": [ ], + "group": "CycloneDX", + "licenses": [ ], + "name": "cyclonedx-python-lib", + "type": "library", + "version": "libVersion-testing" + } + ] + } + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.5" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.5.xml.bin b/tests/_data/snapshots/uv/plain_via-uv_1.5.xml.bin new file mode 100644 index 000000000..df850b052 --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.5.xml.bin @@ -0,0 +1,130 @@ + + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments + + + Apache-2.0 + + + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.6.json.bin b/tests/_data/snapshots/uv/plain_via-uv_1.6.json.bin new file mode 100644 index 000000000..b80b6543e --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.6.json.bin @@ -0,0 +1,181 @@ +{ + "components": [ + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "dependsOn": [ + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "MIT" + } + }, + { + "license": { + "acknowledgement": "declared", + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "acknowledgement": "declared", + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": { + "components": [ + { + "description": "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments", + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "group": "CycloneDX", + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "Apache-2.0" + } + } + ], + "name": "cyclonedx-py", + "type": "application", + "version": "thisVersion-testing" + }, + { + "description": "stripped", + "externalReferences": [ ], + "group": "CycloneDX", + "licenses": [ ], + "name": "cyclonedx-python-lib", + "type": "library", + "version": "libVersion-testing" + } + ] + } + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.6.xml.bin b/tests/_data/snapshots/uv/plain_via-uv_1.6.xml.bin new file mode 100644 index 000000000..4a480af97 --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.6.xml.bin @@ -0,0 +1,130 @@ + + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments + + + Apache-2.0 + + + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.7.json.bin b/tests/_data/snapshots/uv/plain_via-uv_1.7.json.bin new file mode 100644 index 000000000..7caccaf07 --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.7.json.bin @@ -0,0 +1,181 @@ +{ + "components": [ + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "dependsOn": [ + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "MIT" + } + }, + { + "license": { + "acknowledgement": "declared", + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "acknowledgement": "declared", + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": { + "components": [ + { + "description": "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments", + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "group": "CycloneDX", + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "Apache-2.0" + } + } + ], + "name": "cyclonedx-py", + "type": "application", + "version": "thisVersion-testing" + }, + { + "description": "stripped", + "externalReferences": [ ], + "group": "CycloneDX", + "licenses": [ ], + "name": "cyclonedx-python-lib", + "type": "library", + "version": "libVersion-testing" + } + ] + } + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/plain_via-uv_1.7.xml.bin b/tests/_data/snapshots/uv/plain_via-uv_1.7.xml.bin new file mode 100644 index 000000000..5fec3e8e3 --- /dev/null +++ b/tests/_data/snapshots/uv/plain_via-uv_1.7.xml.bin @@ -0,0 +1,130 @@ + + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments + + + Apache-2.0 + + + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.0.xml.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.0.xml.bin new file mode 100644 index 000000000..fbb8f2699 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.0.xml.bin @@ -0,0 +1,17 @@ + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + false + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + false + + + diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.1.xml.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.1.xml.bin new file mode 100644 index 000000000..669b0112f --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.1.xml.bin @@ -0,0 +1,35 @@ + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + + + + diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.2.json.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.2.json.bin new file mode 100644 index 000000000..98a80d19f --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.2.json.bin @@ -0,0 +1,119 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "other", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "type": "application", + "version": "0.1.0" + }, + "tools": [ + { + "name": "cyclonedx-py", + "vendor": "CycloneDX", + "version": "thisVersion-testing" + }, + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "libVersion-testing" + } + ] + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.2" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.2.xml.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.2.xml.bin new file mode 100644 index 000000000..7faf48e25 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.2.xml.bin @@ -0,0 +1,91 @@ + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.3.json.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.3.json.bin new file mode 100644 index 000000000..4ce52b028 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.3.json.bin @@ -0,0 +1,169 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "id": "MIT" + } + }, + { + "license": { + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "other", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "properties": [ + { + "name": "cdx:python:package:required-extra", + "value": "foo" + } + ], + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": [ + { + "name": "cyclonedx-py", + "vendor": "CycloneDX", + "version": "thisVersion-testing" + }, + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "libVersion-testing" + } + ] + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.3" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.3.xml.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.3.xml.bin new file mode 100644 index 000000000..2c3c87896 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.3.xml.bin @@ -0,0 +1,119 @@ + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + foo + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b + + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + 6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354 + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.4.json.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.4.json.bin new file mode 100644 index 000000000..6586fc2f4 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.4.json.bin @@ -0,0 +1,204 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "id": "MIT" + } + }, + { + "license": { + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "properties": [ + { + "name": "cdx:python:package:required-extra", + "value": "foo" + } + ], + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "name": "cyclonedx-py", + "vendor": "CycloneDX", + "version": "thisVersion-testing" + }, + { + "externalReferences": [ ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "libVersion-testing" + } + ] + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.4" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.4.xml.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.4.xml.bin new file mode 100644 index 000000000..53deec66d --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.4.xml.bin @@ -0,0 +1,146 @@ + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + foo + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b + + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + 6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354 + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.5.json.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.5.json.bin new file mode 100644 index 000000000..d48665d25 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.5.json.bin @@ -0,0 +1,218 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "id": "MIT" + } + }, + { + "license": { + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "properties": [ + { + "name": "cdx:python:package:required-extra", + "value": "foo" + } + ], + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": { + "components": [ + { + "description": "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments", + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "group": "CycloneDX", + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "name": "cyclonedx-py", + "type": "application", + "version": "thisVersion-testing" + }, + { + "description": "stripped", + "externalReferences": [ ], + "group": "CycloneDX", + "licenses": [ ], + "name": "cyclonedx-python-lib", + "type": "library", + "version": "libVersion-testing" + } + ] + } + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.5" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.5.xml.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.5.xml.bin new file mode 100644 index 000000000..9aab63096 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.5.xml.bin @@ -0,0 +1,156 @@ + + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments + + + Apache-2.0 + + + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + foo + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b + + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + 6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354 + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.6.json.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.6.json.bin new file mode 100644 index 000000000..0f6a4c8c0 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.6.json.bin @@ -0,0 +1,222 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "MIT" + } + }, + { + "license": { + "acknowledgement": "declared", + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "acknowledgement": "declared", + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "properties": [ + { + "name": "cdx:python:package:required-extra", + "value": "foo" + } + ], + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": { + "components": [ + { + "description": "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments", + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "group": "CycloneDX", + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "Apache-2.0" + } + } + ], + "name": "cyclonedx-py", + "type": "application", + "version": "thisVersion-testing" + }, + { + "description": "stripped", + "externalReferences": [ ], + "group": "CycloneDX", + "licenses": [ ], + "name": "cyclonedx-python-lib", + "type": "library", + "version": "libVersion-testing" + } + ] + } + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.6.xml.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.6.xml.bin new file mode 100644 index 000000000..d733ee4e8 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.6.xml.bin @@ -0,0 +1,156 @@ + + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments + + + Apache-2.0 + + + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + foo + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b + + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + 6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354 + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + + + diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.7.json.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.7.json.bin new file mode 100644 index 000000000..3ef347d66 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.7.json.bin @@ -0,0 +1,222 @@ +{ + "components": [ + { + "bom-ref": "ddt@1.7.2", + "externalReferences": [ + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz" + }, + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl" + } + ], + "name": "ddt", + "purl": "pkg:pypi/ddt@1.7.2", + "type": "library", + "version": "1.7.2" + }, + { + "bom-ref": "toml@0.10.2", + "externalReferences": [ + { + "comment": "wheel", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl" + }, + { + "comment": "sdist", + "hashes": [ + { + "alg": "SHA-256", + "content": "b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + } + ], + "type": "distribution", + "url": "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "ddt@1.7.2" + }, + { + "dependsOn": [ + "ddt@1.7.2", + "toml@0.10.2" + ], + "ref": "root-component" + }, + { + "ref": "toml@0.10.2" + } + ], + "metadata": { + "component": { + "bom-ref": "root-component", + "description": "environment via uv", + "evidence": { + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "MIT" + } + }, + { + "license": { + "acknowledgement": "declared", + "name": "License :: OSI Approved :: Apache Software License" + } + } + ] + }, + "externalReferences": [ + { + "comment": "from pyproject urls: documentation", + "type": "documentation", + "url": "https://oss.acme.org/my-project/docs/" + }, + { + "comment": "from pyproject urls: Bug Tracker", + "type": "issue-tracker", + "url": "https://oss.acme.org/my-project/bugs/" + }, + { + "comment": "from pyproject urls: Funding", + "type": "other", + "url": "https://oss.acme.org/my-project/funding/" + }, + { + "comment": "from pyproject urls: Change log", + "type": "release-notes", + "url": "https://oss.acme.org/my-project/changelog/" + }, + { + "comment": "from pyproject urls: repository", + "type": "vcs", + "url": "https://oss.acme.org/my-project.git" + }, + { + "comment": "from pyproject urls: homepage", + "type": "website", + "url": "https://oss.acme.org/my-project/" + } + ], + "licenses": [ + { + "acknowledgement": "declared", + "expression": "Apache-2.0 OR MIT" + } + ], + "name": "via-uv", + "properties": [ + { + "name": "cdx:python:package:required-extra", + "value": "foo" + } + ], + "type": "application", + "version": "0.1.0" + }, + "properties": [ + { + "name": "cdx:reproducible", + "value": "true" + } + ], + "tools": { + "components": [ + { + "description": "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments", + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-bom/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-bom-tool.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python/" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python/#readme" + } + ], + "group": "CycloneDX", + "licenses": [ + { + "license": { + "acknowledgement": "declared", + "id": "Apache-2.0" + } + } + ], + "name": "cyclonedx-py", + "type": "application", + "version": "thisVersion-testing" + }, + { + "description": "stripped", + "externalReferences": [ ], + "group": "CycloneDX", + "licenses": [ ], + "name": "cyclonedx-python-lib", + "type": "library", + "version": "libVersion-testing" + } + ] + } + }, + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7" +} \ No newline at end of file diff --git a/tests/_data/snapshots/uv/some-extras_via-uv_1.7.xml.bin b/tests/_data/snapshots/uv/some-extras_via-uv_1.7.xml.bin new file mode 100644 index 000000000..6b4b22a49 --- /dev/null +++ b/tests/_data/snapshots/uv/some-extras_via-uv_1.7.xml.bin @@ -0,0 +1,156 @@ + + + + + + + CycloneDX + cyclonedx-py + thisVersion-testing + CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments + + + Apache-2.0 + + + + + https://github.com/CycloneDX/cyclonedx-python/actions + + + https://pypi.org/project/cyclonedx-bom/ + + + https://cyclonedx-bom-tool.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python/issues + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python/ + + + https://github.com/CycloneDX/cyclonedx-python/#readme + + + + + CycloneDX + cyclonedx-python-lib + libVersion-testing + + + + + + + + via-uv + 0.1.0 + environment via uv + + Apache-2.0 OR MIT + + + + https://oss.acme.org/my-project/docs/ + from pyproject urls: documentation + + + https://oss.acme.org/my-project/bugs/ + from pyproject urls: Bug Tracker + + + https://oss.acme.org/my-project/funding/ + from pyproject urls: Funding + + + https://oss.acme.org/my-project/changelog/ + from pyproject urls: Change log + + + https://oss.acme.org/my-project.git + from pyproject urls: repository + + + https://oss.acme.org/my-project/ + from pyproject urls: homepage + + + + foo + + + + + MIT + + + License :: OSI Approved :: Apache Software License + + + + + + true + + + + + ddt + 1.7.2 + pkg:pypi/ddt@1.7.2 + + + https://files.pythonhosted.org/packages/51/d4/bdea45c5c1f1f0ae55844d841101b00905c9863ee1004da37d911253abb2/ddt-1.7.2.tar.gz + sdist + + d215d6b083963013c4a19b1e4dcd6a96e80e43ab77519597a6acfcf2e9a3e04b + + + + https://files.pythonhosted.org/packages/61/7c/38d1aec205833096eddefcbb3492fbb2c886e74174c72bc160da9522b2f0/ddt-1.7.2-py2.py3-none-any.whl + wheel + + 6adcfaf9785f0a36f9e73a89b91e412de9ef8649e289b750e3683bc79d5e2354 + + + + + + toml + 0.10.2 + pkg:pypi/toml@0.10.2 + + + https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl + wheel + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz + sdist + + b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + + + + + + + + + + + + + + diff --git a/tests/integration/test_cli_uv.py b/tests/integration/test_cli_uv.py new file mode 100644 index 000000000..8c836e61a --- /dev/null +++ b/tests/integration/test_cli_uv.py @@ -0,0 +1,131 @@ +# 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. + +import random +from collections.abc import Generator +from glob import glob +from os.path import basename, dirname, join +from typing import Any +from unittest import TestCase + +from cyclonedx.schema import OutputFormat, SchemaVersion +from ddt import ddt, named_data + +from tests import INFILES_DIRECTORY, SUPPORTED_OF_SV, SnapshotMixin, make_comparable +from tests.integration import run_cli + +lockfiles = glob(join(INFILES_DIRECTORY, 'uv', '*', 'uv.lock')) +projectdirs = list(dirname(lockfile) for lockfile in lockfiles) + +test_data = tuple( + (f'{basename(projectdir)}-{sv.name}-{of.name}', projectdir, sv, of) + for projectdir in projectdirs + for of, sv in SUPPORTED_OF_SV +) + + +def test_data_file_filter(s: str) -> Generator[Any, None, None]: + return ((n, d, sv, of) for n, d, sv, of in test_data if s in n) + + +@ddt +class TestCliUv(TestCase, SnapshotMixin): + + def test_help(self) -> None: + res, out, err = run_cli('uv', '--help') + self.assertEqual(0, res, '\n'.join((out, err))) + + def test_fails_with_dir_not_found(self) -> None: + _, projectdir, sv, of = random.choice(test_data) # nosec B311 + res, out, err = run_cli( + 'uv', + '-vvv', + '--sv', sv.to_version(), + '--of', of.name, + '-o=-', + 'something-that-must-not-exist.testing') + self.assertNotEqual(0, res, err) + self.assertIn('Could not open pyproject file: something-that-must-not-exist.testing', err) + + def test_fails_with_extras_not_found(self) -> None: + projectdir = random.choice(projectdirs) # nosec B311 + res, out, err = run_cli( + 'uv', + '-vvv', + '-E', 'MNE-extra-C,MNE-extra-B', + '--extras', 'MNE-extra-A', + projectdir) + self.assertNotEqual(0, res, err) + self.assertIn('Extra(s) [' + # extra names were normalized! + 'mne-extra-a,' + 'mne-extra-b,' + 'mne-extra-c' + '] not specified', err) + + @named_data(*test_data) + def test_plain_as_expected(self, projectdir: str, sv: SchemaVersion, of: OutputFormat) -> None: + res, out, err = run_cli( + 'uv', + '-vvv', + '--sv', sv.to_version(), + '--of', of.name, + '--output-reproducible', + '-o=-', + projectdir) + self.assertEqual(0, res, err) + self.assertEqualSnapshot(out, 'plain', projectdir, sv, of) + + @named_data(*test_data_file_filter('via-uv')) + def test_with_extras_as_expected(self, projectdir: str, sv: SchemaVersion, of: OutputFormat) -> None: + res, out, err = run_cli( + 'uv', + '-vvv', + '-E', 'FOO', # expected to be normalized + '--sv', sv.to_version(), + '--of', of.name, + '--output-reproducible', + '-o=-', + projectdir) + self.assertEqual(0, res, err) + self.assertEqualSnapshot(out, 'some-extras', projectdir, sv, of) + + @named_data(*test_data_file_filter('via-uv')) + def test_with_all_extras_as_expected(self, projectdir: str, sv: SchemaVersion, of: OutputFormat) -> None: + res, out, err = run_cli( + 'uv', + '-vvv', + '--all-extras', + '--sv', sv.to_version(), + '--of', of.name, + '--output-reproducible', + '-o=-', + projectdir) + self.assertEqual(0, res, err) + self.assertEqualSnapshot(out, 'all-extras', projectdir, sv, of) + + def assertEqualSnapshot(self, actual: str, # noqa:N802 + purpose: str, + projectdir: str, + sv: SchemaVersion, + of: OutputFormat + ) -> None: + super().assertEqualSnapshot( + make_comparable(actual, of), + join('uv', f'{purpose}_{basename(projectdir)}_{sv.to_version()}.{of.name.lower()}') + ) + diff --git a/tests/unit/test_uv.py b/tests/unit/test_uv.py new file mode 100644 index 000000000..e56345eff --- /dev/null +++ b/tests/unit/test_uv.py @@ -0,0 +1,245 @@ +# 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. + +import logging +from os.path import join +from tempfile import TemporaryDirectory +from unittest import TestCase + +from cyclonedx.model.component import ComponentType + +from cyclonedx_py._internal.uv import UvBB + + +class TestUv(TestCase): + + def test_filters_resolution_and_dependency_markers(self) -> None: + with TemporaryDirectory() as project_dir: + pyproject = ( + '[project]\n' + 'name = "my-project"\n' + 'version = "0.1.0"\n' + 'dependencies = ["anyio"]\n' + ) + uv_lock = ( + 'version = 1\n' + 'resolution-markers = [\n' + ' "python_full_version >= \'0\'",\n' + ' "python_full_version < \'0\'",\n' + ']\n' + '\n' + '[[package]]\n' + 'name = "anyio"\n' + 'version = "1.0.0"\n' + 'dependencies = [\n' + ' { name = "bar" },\n' + ' { name = "foo", marker = "python_full_version < \'0\'" },\n' + ']\n' + '\n' + '[[package]]\n' + 'name = "bar"\n' + 'version = "1.0.0"\n' + 'resolution-markers = ["python_full_version >= \'0\'"]\n' + '\n' + '[[package]]\n' + 'name = "bar"\n' + 'version = "2.0.0"\n' + 'resolution-markers = ["python_full_version < \'0\'"]\n' + '\n' + '[[package]]\n' + 'name = "foo"\n' + 'version = "1.0.0"\n' + ) + with open(join(project_dir, 'pyproject.toml'), 'w', encoding='utf8', newline='\n') as fh: + fh.write(pyproject) + with open(join(project_dir, 'uv.lock'), 'w', encoding='utf8', newline='\n') as fh: + fh.write(uv_lock) + + bom = UvBB(logger=logging.getLogger(__name__))( + project_directory=join(project_dir, 'uv.lock'), + groups_with=[], + groups_without=[], + groups_only=[], + only_dev=False, + all_groups=False, + no_default_groups=False, + no_dev=False, + extras=[], + all_extras=False, + mc_type=ComponentType.APPLICATION, + ) + + components = {(c.name, c.version) for c in bom.components} + self.assertIn(('anyio', '1.0.0'), components) + self.assertIn(('bar', '1.0.0'), components) + self.assertNotIn(('bar', '2.0.0'), components) + self.assertNotIn(('foo', '1.0.0'), components) + + def test_fails_when_resolution_markers_do_not_match_environment(self) -> None: + with TemporaryDirectory() as project_dir: + pyproject = ( + '[project]\n' + 'name = "my-project"\n' + 'version = "0.1.0"\n' + 'dependencies = ["anyio"]\n' + ) + uv_lock = ( + 'version = 1\n' + 'resolution-markers = ["python_full_version < \'0\'"]\n' + '\n' + '[[package]]\n' + 'name = "anyio"\n' + 'version = "1.0.0"\n' + ) + with open(join(project_dir, 'pyproject.toml'), 'w', encoding='utf8', newline='\n') as fh: + fh.write(pyproject) + with open(join(project_dir, 'uv.lock'), 'w', encoding='utf8', newline='\n') as fh: + fh.write(uv_lock) + + with self.assertRaisesRegex(ValueError, 'resolution-markers'): + UvBB(logger=logging.getLogger(__name__))( + project_directory=project_dir, + groups_with=[], + groups_without=[], + groups_only=[], + only_dev=False, + all_groups=False, + no_default_groups=False, + no_dev=False, + extras=[], + all_extras=False, + mc_type=ComponentType.APPLICATION, + ) + + def test_dependency_groups(self) -> None: + with TemporaryDirectory() as project_dir: + pyproject = ( + '[project]\n' + 'name = "my-project"\n' + 'version = "0.1.0"\n' + 'dependencies = ["requests"]\n' + '\n' + '[dependency-groups]\n' + 'dev = [\n' + ' "pytest",\n' + ' "mkdocstrings[python]",\n' + ']\n' + 'docs = ["sphinx"]\n' + ) + uv_lock = ( + 'version = 1\n' + '\n' + '[[package]]\n' + 'name = "requests"\n' + 'version = "1.0.0"\n' + '\n' + '[[package]]\n' + 'name = "pytest"\n' + 'version = "2.0.0"\n' + '\n' + '[[package]]\n' + 'name = "mkdocstrings"\n' + 'version = "3.0.0"\n' + '\n' + '[[package]]\n' + 'name = "sphinx"\n' + 'version = "4.0.0"\n' + '\n' + '[[package]]\n' + 'name = "my-project"\n' + 'version = "0.1.0"\n' + 'source = { editable = "." }\n' + 'dependencies = [\n' + ' { name = "requests" },\n' + ']\n' + '\n' + '[package.dev-dependencies]\n' + 'dev = [\n' + ' { name = "pytest" },\n' + ' { name = "mkdocstrings", extra = ["python"] },\n' + ']\n' + ) + with open(join(project_dir, 'pyproject.toml'), 'w', encoding='utf8', newline='\n') as fh: + fh.write(pyproject) + with open(join(project_dir, 'uv.lock'), 'w', encoding='utf8', newline='\n') as fh: + fh.write(uv_lock) + + components_default = {(c.name, c.version) for c in UvBB(logger=logging.getLogger(__name__))( + project_directory=project_dir, + groups_with=[], + groups_without=[], + groups_only=[], + only_dev=False, + all_groups=False, + no_default_groups=False, + no_dev=False, + extras=[], + all_extras=False, + mc_type=ComponentType.APPLICATION, + ).components} + self.assertIn(('requests', '1.0.0'), components_default) + self.assertIn(('pytest', '2.0.0'), components_default) + self.assertIn(('mkdocstrings', '3.0.0'), components_default) + self.assertNotIn(('sphinx', '4.0.0'), components_default) + + components_no_dev = {(c.name, c.version) for c in UvBB(logger=logging.getLogger(__name__))( + project_directory=project_dir, + groups_with=[], + groups_without=[], + groups_only=[], + only_dev=False, + all_groups=False, + no_default_groups=False, + no_dev=True, + extras=[], + all_extras=False, + mc_type=ComponentType.APPLICATION, + ).components} + self.assertIn(('requests', '1.0.0'), components_no_dev) + self.assertNotIn(('pytest', '2.0.0'), components_no_dev) + + components_docs_only = {(c.name, c.version) for c in UvBB(logger=logging.getLogger(__name__))( + project_directory=project_dir, + groups_with=[], + groups_without=[], + groups_only=['docs'], + only_dev=False, + all_groups=False, + no_default_groups=False, + no_dev=False, + extras=[], + all_extras=False, + mc_type=ComponentType.APPLICATION, + ).components} + self.assertIn(('sphinx', '4.0.0'), components_docs_only) + self.assertNotIn(('requests', '1.0.0'), components_docs_only) + + components_only_dev = {(c.name, c.version) for c in UvBB(logger=logging.getLogger(__name__))( + project_directory=project_dir, + groups_with=[], + groups_without=[], + groups_only=[], + only_dev=True, + all_groups=False, + no_default_groups=False, + no_dev=False, + extras=[], + all_extras=False, + mc_type=ComponentType.APPLICATION, + ).components} + self.assertIn(('pytest', '2.0.0'), components_only_dev) + self.assertNotIn(('requests', '1.0.0'), components_only_dev) From 39fc1cadf32e7aa81b6104301bbfda1cdd5f73ef Mon Sep 17 00:00:00 2001 From: Mohammed Abbadi Date: Mon, 23 Mar 2026 01:57:51 +0300 Subject: [PATCH 5/5] style(tests): remove trailing blank line in test_cli_uv Fix flake8 W391 (blank line at end of file). Signed-off-by: Mohammed Abbadi --- tests/integration/test_cli_uv.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/test_cli_uv.py b/tests/integration/test_cli_uv.py index 8c836e61a..2f1bafede 100644 --- a/tests/integration/test_cli_uv.py +++ b/tests/integration/test_cli_uv.py @@ -128,4 +128,3 @@ def assertEqualSnapshot(self, actual: str, # noqa:N802 make_comparable(actual, of), join('uv', f'{purpose}_{basename(projectdir)}_{sv.to_version()}.{of.name.lower()}') ) -