Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions cyclonedx_py/_internal/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
else:
BooleanOptionalAction = None

OPTION_OUTPUT_STDOUT = '-'


class Command:
@classmethod
Expand All @@ -74,14 +76,20 @@ def make_argument_parser(cls, sco: ArgumentParser, **kwargs: Any) -> ArgumentPar
action='store_true',
dest='short_purls',
default=False)
op.add_argument('-o', '--outfile',
op.add_argument('--outfile', # DEPRECATED
metavar='<file>',
help='DEPRECATED alias for "--output-file".',
type=FileType('wt', encoding='utf8'),
dest='output_file',
default=OPTION_OUTPUT_STDOUT)
op.add_argument('-o', '--output-file',
metavar='<file>',
help='Output file path for your SBOM'
' (set to "-" to output to <stdout>)'
f' (set to "{OPTION_OUTPUT_STDOUT}" to output to <stdout>)'
' (default: %(default)s)',
type=FileType('wt', encoding='utf8'),
dest='outfile',
default='-')
dest='output_file',
default=OPTION_OUTPUT_STDOUT)
op.add_argument('--schema-version', # DEPRECATED
metavar='<version>',
help='DEPRECATED alias for option "--spec-version".',
Expand Down Expand Up @@ -159,7 +167,7 @@ def make_argument_parser(cls, sco: ArgumentParser, **kwargs: Any) -> ArgumentPar
# the arg keywords from __init__()
'logger', 'short_purls', 'output_format', 'spec_version', 'output_reproducible', 'should_validate',
# the arg keywords from __call__()
'outfile'
'output_file'
}

@classmethod
Expand Down Expand Up @@ -232,10 +240,10 @@ def _validate(self, output: str) -> bool:
self._logger.debug('result is schema-valid')
return True

def _write(self, output: str, outfile: TextIO) -> int:
self._logger.info('Writing to: %s', outfile.name)
written = outfile.write(output)
self._logger.debug('Wrote %i bytes to %s', written, outfile.name)
def _write(self, output: str, output_file: TextIO) -> int:
self._logger.info('Writing to: %s', output_file.name)
written = output_file.write(output)
self._logger.debug('Wrote %i bytes to %s', written, output_file.name)
return written

def _make_output(self, bom: 'Bom') -> str:
Expand All @@ -259,14 +267,14 @@ def _make_bom(self, **kwargs: Any) -> 'Bom':
return self._bbc(**self._clean_kwargs(kwargs))

def __call__(self,
outfile: TextIO,
output_file: TextIO,
**kwargs: Any) -> None:
bom = self._make_bom(**kwargs)
self._shorten_purls(bom)
output = self._make_output(bom)
del bom
self._validate(output)
self._write(output, outfile)
self._write(output, output_file)


def run(*, argv: Optional[Sequence[str]] = None, **kwargs: Any) -> Union[int, NoReturn]:
Expand Down
14 changes: 9 additions & 5 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Example usage: save SBOM in CycloneDX 1.6 XML format, generated from current pyt

.. code-block:: shell

cyclonedx-py environment --outfile my-sbom.xml --spec-version 1.6 --output-format XML
cyclonedx-py environment --spec-version 1.6 --output-format XML --output-file my-sbom.xml


For Python (virtual) environment
Expand Down Expand Up @@ -81,7 +81,8 @@ The full documentation can be issued by running with ``environment --help``:
(default: application)
--short-PURLs Omit all qualifiers from PackageURLs.
This causes information loss in trade-off shorter PURLs, which might improve ingesting these strings.
-o <file>, --outfile <file>
--outfile <file> DEPRECATED alias for "--output-file".
-o <file>, --output-file <file>
Output file path for your SBOM
(set to "-" to output to STDOUT)
(default: -)
Expand Down Expand Up @@ -254,7 +255,8 @@ The full documentation can be issued by running with ``pipenv --help``:
(default: application)
--short-PURLs Omit all qualifiers from PackageURLs.
This causes information loss in trade-off shorter PURLs, which might improve ingesting these strings.
-o <file>, --outfile <file>
--outfile <file> DEPRECATED alias for "--output-file".
-o <file>, --output-file <file>
Output file path for your SBOM
(set to "-" to output to <stdout>)
(default: -)
Expand Down Expand Up @@ -331,7 +333,8 @@ The full documentation can be issued by running with ``poetry --help``:
(default: application)
--short-PURLs Omit all qualifiers from PackageURLs.
This causes information loss in trade-off shorter PURLs, which might improve ingesting these strings.
-o <file>, --outfile <file>
--outfile <file> DEPRECATED alias for "--output-file".
-o <file>, --output-file <file>
Output file path for your SBOM
(set to "-" to output to <stdout>)
(default: -)
Expand Down Expand Up @@ -404,7 +407,8 @@ The full documentation can be issued by running with ``requirements --help``:
(default: application)
--short-PURLs Omit all qualifiers from PackageURLs.
This causes information loss in trade-off shorter PURLs, which might improve ingesting these strings.
-o <file>, --outfile <file>
--outfile <file> DEPRECATED alias for "--output-file".
-o <file>, --output-file <file>
Output file path for your SBOM
(set to "-" to output to <stdout>)
(default: -)
Expand Down
18 changes: 9 additions & 9 deletions tests/integration/test_cli_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def test_fails_with_python_not_found(self, wrong_python: str, expected_error: st
'-vvv',
f'--sv={sv.to_version()}',
f'--of={of.name}',
'--outfile=-',
'-o=-',
wrong_python)
self.assertNotEqual(0, res, err)
self.assertIn(expected_error, err)
Expand All @@ -96,7 +96,7 @@ def test_fails_with_python_unexpected(self, wrong_python: str, expected_error: s
'-vvv',
f'--sv={sv.to_version()}',
f'--of={of.name}',
'--outfile=-',
'-o=-',
wrong_python)
self.assertNotEqual(0, res, err)
self.assertIn(expected_error, err)
Expand All @@ -108,7 +108,7 @@ def test_with_pyproject_not_found(self) -> None:
'-vvv',
'--sv', sv.to_version(),
'--of', of.name,
'--outfile=-',
'-o=-',
'--pyproject=something-that-must-not-exist.testing',
projectdir
)
Expand All @@ -124,7 +124,7 @@ def test_with_current_python(self) -> None:
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
# no project dir -> search in current python
)
self.assertEqual(0, res, err)
Expand All @@ -134,7 +134,7 @@ def test_with_current_python(self) -> None:
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
executable # explicitly current python
)
self.assertEqual(0, res, err)
Expand All @@ -151,7 +151,7 @@ def test_plain_as_expected(self, projectdir: str, sv: SchemaVersion, of: OutputF
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
'--pyproject', join(projectdir, 'pyproject.toml'),
join(projectdir, '.venv'))
self.assertEqual(0, res, err)
Expand All @@ -165,7 +165,7 @@ def test_pep639_as_expected(self, projectdir: str, sv: SchemaVersion, of: Output
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
'--pyproject', join(projectdir, 'pyproject.toml'),
'--PEP-639',
join(projectdir, '.venv'))
Expand All @@ -180,7 +180,7 @@ def test_pep639_texts_as_expected(self, projectdir: str, sv: SchemaVersion, of:
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
'--pyproject', join(projectdir, 'pyproject.toml'),
'--PEP-639',
'--gather-license-texts',
Expand All @@ -196,7 +196,7 @@ def test_texts_as_expected(self, projectdir: str, sv: SchemaVersion, of: OutputF
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
'--pyproject', join(projectdir, 'pyproject.toml'),
'--gather-license-texts',
join(projectdir, '.venv'))
Expand Down
12 changes: 6 additions & 6 deletions tests/integration/test_cli_pipenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def test_fails_with_dir_not_found(self) -> None:
'-vvv',
'--sv', sv.to_version(),
'--of', of.name,
'--outfile=-',
'-o=-',
'something-that-must-not-exist.testing')
self.assertNotEqual(0, res, err)
self.assertIn('Could not open lock file: something-that-must-not-exist.testing', err)
Expand All @@ -68,7 +68,7 @@ def test_with_pyproject_not_found(self) -> None:
'-vvv',
'--sv', sv.to_version(),
'--of', of.name,
'--outfile=-',
'-o=-',
'--pyproject=something-that-must-not-exist.testing',
projectdir)
self.assertNotEqual(0, res, err)
Expand All @@ -82,7 +82,7 @@ def test_plain_as_expected(self, projectdir: str, sv: SchemaVersion, of: OutputF
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
'--pyproject', join(projectdir, 'pyproject.toml'),
projectdir)
self.assertEqual(0, res, err)
Expand All @@ -96,7 +96,7 @@ def test_with_categories_as_expected(self, projectdir: str, sv: SchemaVersion, o
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
'--categories', 'categoryB,groupA packages,dev-packages',
projectdir)
self.assertEqual(0, res, err)
Expand All @@ -110,7 +110,7 @@ def test_with_dev_as_expected(self, projectdir: str, sv: SchemaVersion, of: Outp
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
'--dev',
projectdir)
self.assertEqual(0, res, err)
Expand All @@ -124,7 +124,7 @@ def test_with_pypi_mirror_as_expected(self, projectdir: str, sv: SchemaVersion,
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
'--pypi-mirror', 'https://user:password@pypy-mirror.testing.acme.org/simple/',
projectdir)
self.assertEqual(0, res, err)
Expand Down
14 changes: 7 additions & 7 deletions tests/integration/test_cli_poetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def test_fails_with_dir_not_found(self) -> None:
'-vvv',
'--sv', sv.to_version(),
'--of', of.name,
'--outfile=-',
'-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)
Expand Down Expand Up @@ -109,7 +109,7 @@ def test_plain_as_expected(self, projectdir: str, sv: SchemaVersion, of: OutputF
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
projectdir)
self.assertEqual(0, res, err)
self.assertEqualSnapshot(out, 'plain', projectdir, sv, of)
Expand All @@ -124,7 +124,7 @@ def test_with_groups_as_expected(self, projectdir: str, sv: SchemaVersion, of: O
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
projectdir)
self.assertEqual(0, res, err)
self.assertEqualSnapshot(out, 'some-groups', projectdir, sv, of)
Expand All @@ -138,7 +138,7 @@ def test_only_groups_as_expected(self, projectdir: str, sv: SchemaVersion, of: O
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
projectdir)
self.assertEqual(0, res, err)
self.assertEqualSnapshot(out, 'only-groups', projectdir, sv, of)
Expand All @@ -153,7 +153,7 @@ def test_nodev_as_expected(self, projectdir: str, sv: SchemaVersion, of: OutputF
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
projectdir)
self.assertEqual(0, res, err)
self.assertEqualSnapshot(out, 'no-dev', projectdir, sv, of)
Expand All @@ -167,7 +167,7 @@ def test_with_extras_as_expected(self, projectdir: str, sv: SchemaVersion, of: O
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
projectdir)
self.assertEqual(0, res, err)
self.assertEqualSnapshot(out, 'some-extras', projectdir, sv, of)
Expand All @@ -181,7 +181,7 @@ def test_with_all_extras_as_expected(self, projectdir: str, sv: SchemaVersion, o
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
projectdir)
self.assertEqual(0, res, err)
self.assertEqualSnapshot(out, 'all-extras', projectdir, sv, of)
Expand Down
10 changes: 5 additions & 5 deletions tests/integration/test_cli_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def test_with_file_not_found(self) -> None:
'-vvv',
'--sv', sv.to_version(),
'--of', of.name,
'--outfile=-',
'-o=-',
'something-that-must-not-exist.testing')
self.assertNotEqual(0, res, err)
self.assertIn('Could not open requirements file: something-that-must-not-exist.testing', err)
Expand All @@ -79,7 +79,7 @@ def test_with_pyproject_not_found(self) -> None:
'-vvv',
'--sv', sv.to_version(),
'--of', of.name,
'--outfile=-',
'-o=-',
'--pyproject=something-that-must-not-exist.testing',
infile
)
Expand All @@ -94,7 +94,7 @@ def test_with_file_as_expected(self, infile: str, sv: SchemaVersion, of: OutputF
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
'--pyproject', pyproject_file,
infile)
self.assertEqual(0, res, err)
Expand All @@ -109,7 +109,7 @@ def test_with_stream_as_expected(self, infile: str, sv: SchemaVersion, of: Outpu
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
# no pyproject for this case
'-',
inp=inp)
Expand All @@ -127,7 +127,7 @@ def test_with_index_auth(self, infile: str, sv: SchemaVersion, of: OutputFormat)
'--sv', sv.to_version(),
'--of', of.name,
'--output-reproducible',
'--outfile=-',
'-o=-',
'--pyproject', pyproject_file,
infile)
self.assertEqual(0, res, err)
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def __new__(cls, *args: Any, **kwargs: Any) -> BomBuilder:
output_reproducible=True,
_bbc=MyBBC
)
command(outfile=outs)
command(output_file=outs)

out = outs.getvalue()

Expand All @@ -109,7 +109,7 @@ def __new__(cls, *args: Any, **kwargs: Any) -> BomBuilder:
command._make_output = Mock(return_value=r'["invalid to CDX schema"]')

with self.assertRaisesRegex(ValueError, 'is schema-invalid'):
command(outfile=outs)
command(output_file=outs)

def test_validation_skip_with_invalid(self) -> None:
class MyBBC(BomBuilder):
Expand All @@ -131,7 +131,7 @@ def __new__(cls, *args: Any, **kwargs: Any) -> BomBuilder:
)
command._make_output = Mock(return_value=r'["invalid to CDX schema"]')

command(outfile=outs)
command(output_file=outs)

log = logs.getvalue()
out = outs.getvalue()
Expand Down