Skip to content

Commit

Permalink
feat: subcommand environment got alias env/venv
Browse files Browse the repository at this point in the history
Signed-off-by: Jan Kowalleck <[email protected]>
  • Loading branch information
jkowalleck committed Feb 20, 2025
1 parent 12cc59b commit 16566df
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 21 deletions.
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,21 @@ python3 -m cyclonedx_py # call python module CLI

```shellSession
$ cyclonedx-py --help
usage: cyclonedx-py [-h] [--version] command ...
usage: cyclonedx-py [-h] [--version] <command> ...

Creates CycloneDX Software Bill of Materials (SBOM) from Python projects and environments.

positional arguments:
command
environment Build an SBOM from Python (virtual) environment
requirements Build an SBOM from Pip requirements
pipenv Build an SBOM from Pipenv manifest
poetry Build an SBOM from Poetry project
<command>
environment (env, venv)
Build an SBOM from Python (virtual) environment
requirements Build an SBOM from Pip requirements
pipenv Build an SBOM from Pipenv manifest
poetry Build an SBOM from Poetry project

options:
-h, --help show this help message and exit
--version show program's version number and exit
-h, --help show this help message and exit
--version show program's version number and exit
```

### Advanced usage and details
Expand Down
20 changes: 13 additions & 7 deletions cyclonedx_py/_internal/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import sys
from argparse import ArgumentParser, FileType, RawDescriptionHelpFormatter
from itertools import chain
from typing import TYPE_CHECKING, Any, Dict, NoReturn, Optional, Sequence, TextIO, Type, Union
from typing import TYPE_CHECKING, Any, Dict, List, NoReturn, Optional, Sequence, TextIO, Type, Union

from cyclonedx.model import Property
from cyclonedx.output import make_outputter
Expand Down Expand Up @@ -53,6 +53,7 @@
class Command:
@classmethod
def make_argument_parser(cls, sco: ArgumentParser, **kwargs: Any) -> ArgumentParser:
# region Command
p = ArgumentParser(
description='Creates CycloneDX Software Bill of Materials (SBOM) from Python projects and environments.',
formatter_class=RawDescriptionHelpFormatter,
Expand All @@ -62,7 +63,9 @@ def make_argument_parser(cls, sco: ArgumentParser, **kwargs: Any) -> ArgumentPar
sp = p.add_subparsers(metavar='<command>', dest='command',
# not required. if omitted: show help and exit
required=False)
# region Command

# region SubCOmmand
op = ArgumentParser(add_help=False)
op.add_argument('--short-PURLs',
help='Omit all qualifiers from PackageURLs.\n'
Expand Down Expand Up @@ -124,21 +127,24 @@ def make_argument_parser(cls, sco: ArgumentParser, **kwargs: Any) -> ArgumentPar
action='store_false')

scbbc: Type['BomBuilder']
for sct, scbbc in (
('environment', EnvironmentBB),
('requirements', RequirementsBB),
('pipenv', PipenvBB),
('poetry', PoetryBB),
sct: str
scta: List[str]
for scbbc, sct, *scta in (
(EnvironmentBB, 'environment', 'env', 'venv'),
(RequirementsBB, 'requirements'),
(PipenvBB, 'pipenv'),
(PoetryBB, 'poetry'),
):
spp = scbbc.make_argument_parser(add_help=False)
sp.add_parser(sct,
sp.add_parser(sct, aliases=scta,
help=(spp.description or '').split('\n')[0].strip('. '),
description=spp.description,
epilog=spp.epilog,
parents=[spp, op, sco],
formatter_class=p.formatter_class,
allow_abbrev=p.allow_abbrev,
).set_defaults(_bbc=scbbc)
# endregion SubCommand

return p

Expand Down
2 changes: 1 addition & 1 deletion cyclonedx_py/_internal/poetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ def __purl_qualifiers4lock(self, package: 'T_NameDict') -> 'T_NameDict':
# > For version-controlled files, the VCS location syntax is similar to a URL and has the:
# > `<vcs_tool>+<transport>://<host_name>[/<path_to_repository>][@<revision_tag_or_branch>][#<sub_path>]`
qs['vcs_url'] = f'{source_type}+{redact_auth_from_url(source["url"])}@' + \
source.get('resolved_reference', source.get('reference', ''))
source.get('resolved_reference', source.get('reference', ''))
elif source_type == 'url':
if '://files.pythonhosted.org/' not in source['url']:
# skip PURL bloat, do not add implicit information
Expand Down
6 changes: 4 additions & 2 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ For Python (virtual) environment

**subcommand:** ``environment``

**aliases:** ``env``, ``venv``

.. TODO: describe what an environment is...
This will produce the most accurate and complete CycloneDX BOM as it analyses the actually installed packages.
It will include metadata, licenses, dependency graph, and more in the generated CycloneDX SBOM.
By analyzing the actually installed packages, this will produce the most accurate and complete CycloneDX BOM.
The generated CycloneDX SBOM will include metadata, licenses, dependency graph, and more.

The full documentation can be issued by running with ``environment --help``:

Expand Down
10 changes: 7 additions & 3 deletions tests/integration/test_cli_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from unittest import TestCase, skipIf

from cyclonedx.schema import OutputFormat, SchemaVersion
from ddt import ddt, named_data
from ddt import data, ddt, named_data

from tests import INFILES_DIRECTORY, INIT_TESTBEDS, SUPPORTED_OF_SV, SnapshotMixin, make_comparable
from tests.integration import run_cli
Expand All @@ -45,8 +45,12 @@ def test_data_file_filter(s: str) -> Generator[Any, None, None]:
@ddt
class TestCliEnvironment(TestCase, SnapshotMixin):

def test_help(self) -> None:
res, out, err = run_cli('environment', '--help')
@data(
'environment',
'env', 'venv' # aliases
)
def test_help(self, subcommand: str) -> None:
res, out, err = run_cli(subcommand, '--help')
self.assertEqual(0, res, '\n'.join((out, err)))

@classmethod
Expand Down

0 comments on commit 16566df

Please sign in to comment.