vulnctl 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vulnctl-0.2.1 → vulnctl-0.2.2}/CHANGELOG.md +14 -1
- {vulnctl-0.2.1 → vulnctl-0.2.2}/PKG-INFO +1 -1
- {vulnctl-0.2.1 → vulnctl-0.2.2}/pyproject.toml +1 -1
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/cli_help.py +23 -24
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_cli.py +20 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/uv.lock +1 -1
- {vulnctl-0.2.1 → vulnctl-0.2.2}/.github/workflows/ci.yml +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/.github/workflows/live-smoke.yml +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/.github/workflows/publish-testpypi.yml +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/.github/workflows/release.yml +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/.gitignore +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/.pre-commit-config.yaml +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/.python-version +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/CLAUDE.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/FRAMEWORK.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/LICENSE +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/README.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/ROADMAP.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/SPEC.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/announcement.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/case-study.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/cli.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/context.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/demo.tape +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/exit-codes.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/output.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/releasing.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/schema.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/schema.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/docs/trees.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/examples/app.cdx.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/examples/ci/vulnctl-gate.yml +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/examples/context.yaml +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/scripts/build_exploit_index.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/scripts/case_study_stats.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/__init__.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/adapters/__init__.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/adapters/base.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/adapters/epss.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/adapters/exploits.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/adapters/ghsa.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/adapters/kev.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/adapters/nvd.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/adapters/osv.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/cache.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/cli.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/commands.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/context.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/data/__init__.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/data/epss_snapshot.csv.gz +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/data/exploit_index.json.gz +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/data/kev_snapshot.json.gz +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/ingest/__init__.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/ingest/cve_list.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/ingest/cyclonedx.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/ingest/grype.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/models.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/output/__init__.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/output/explain.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/output/json_out.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/output/markdown.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/output/progress.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/output/render.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/output/sarif.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/output/table.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/pipeline.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/py.typed +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/ssvc/__init__.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/ssvc/engine.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/ssvc/tree.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/src/vulnctl/ssvc/trees/cisa-deployer-v1.yaml +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/conftest.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/epss/batch.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/epss/malformed.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/epss/missing.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/ghsa/advisory-by-cve.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/ghsa/advisory-by-ghsa-id.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/ghsa/malformed.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/ghsa/not-found-empty-list.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/ghsa/rate-limited.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/golden/enrich.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/golden/enrich.md +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/grype/duplicate-layers.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/grype/ghsa-only.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/grype/npm-app.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/kev/catalog.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/nvd/cve-2021-44228.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/nvd/multiple-cvss.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/nvd/not-found.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/nvd/rejected.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/nvd/v2-only.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/osv/cve-2021-44228.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/osv/malformed.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/osv/not-found.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/osv/querybatch-npm-app.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/osv/querybatch.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/osv/vuln-cve-record.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/osv/vuln-ghsa-no-cve.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/osv/vuln-ghsa-with-cve.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/osv/vuln-go.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/osv/vuln-pypi.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/sarif/sarif-schema-2.1.0.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/sbom/npm-app-1.6.cdx.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/sbom/npm-app.cdx.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/sbom/py-app.cdx.json +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/fixtures/trees/toy.yaml +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/live/test_live_smoke.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_adapter_epss.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_adapter_exploits.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_adapter_ghsa.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_adapter_kev.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_adapter_nvd.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_adapter_osv.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_adapters_base.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_cache.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_context.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_ingest_cve_list.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_ingest_cyclonedx.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_ingest_grype.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_models.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_output_explain.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_output_json.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_output_markdown.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_output_progress.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_output_sarif.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_output_table.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_pipeline.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_ssvc_cisa_tree.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_ssvc_engine.py +0 -0
- {vulnctl-0.2.1 → vulnctl-0.2.2}/tests/test_ssvc_tree.py +0 -0
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.2] - 2026-08-01
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Root help type column now matches Typer's own spelling.** The generated
|
|
15
|
+
"Options by command" panel derived its type column itself (`PATH`, `TEXT`),
|
|
16
|
+
which disagreed with the per-command panels on Typer 0.27+ — what a fresh
|
|
17
|
+
`pipx install vulnctl` resolves — where Typer prints `<path>`, `<str>`. It
|
|
18
|
+
now delegates to Click's metavar, so both spellings track the installed
|
|
19
|
+
version. Arguments drop the column entirely: their metavar already names
|
|
20
|
+
what they take.
|
|
21
|
+
|
|
10
22
|
## [0.2.1] - 2026-08-01
|
|
11
23
|
|
|
12
24
|
### Added
|
|
@@ -130,7 +142,8 @@ that produced it.
|
|
|
130
142
|
sign with keyless cosign (OIDC), and publish to PyPI via trusted publishing
|
|
131
143
|
(no stored token).
|
|
132
144
|
|
|
133
|
-
[Unreleased]: https://github.com/NokiGuard/vulnctl/compare/v0.2.
|
|
145
|
+
[Unreleased]: https://github.com/NokiGuard/vulnctl/compare/v0.2.2...HEAD
|
|
146
|
+
[0.2.2]: https://github.com/NokiGuard/vulnctl/compare/v0.2.1...v0.2.2
|
|
134
147
|
[0.2.1]: https://github.com/NokiGuard/vulnctl/compare/v0.2.0...v0.2.1
|
|
135
148
|
[0.2.0]: https://github.com/NokiGuard/vulnctl/compare/v0.1.0...v0.2.0
|
|
136
149
|
[0.1.0]: https://github.com/NokiGuard/vulnctl/releases/tag/v0.1.0
|
|
@@ -57,21 +57,18 @@ _EXAMPLES: tuple[tuple[str, str], ...] = (
|
|
|
57
57
|
)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def
|
|
61
|
-
"""
|
|
62
|
-
if getattr(param, "is_flag", False):
|
|
63
|
-
return ""
|
|
64
|
-
if param.metavar:
|
|
65
|
-
return str(param.metavar)
|
|
66
|
-
choices = getattr(param.type, "choices", None)
|
|
67
|
-
if choices:
|
|
68
|
-
return "[" + "|".join(str(choice) for choice in choices) + "]"
|
|
69
|
-
return str(param.type.name).upper()
|
|
60
|
+
def _metavar(param: Any, ctx: Any) -> str:
|
|
61
|
+
"""What a parameter takes, spelled the way Typer's own help spells it.
|
|
70
62
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
Delegates to Click rather than deriving it, so the appended panel agrees
|
|
64
|
+
with the per-command panels on whatever version is installed — Typer 0.26
|
|
65
|
+
renders ``PATH``, 0.27 renders ``<path>``. Click gained the ``ctx``
|
|
66
|
+
argument in 8.2; older versions take none.
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
return str(param.make_metavar(ctx=ctx))
|
|
70
|
+
except TypeError:
|
|
71
|
+
return str(param.make_metavar())
|
|
75
72
|
|
|
76
73
|
|
|
77
74
|
def _visible_params(cmd: Any, kind: str) -> list[Any]:
|
|
@@ -82,28 +79,30 @@ def _visible_params(cmd: Any, kind: str) -> list[Any]:
|
|
|
82
79
|
]
|
|
83
80
|
|
|
84
81
|
|
|
85
|
-
def _usage(path: str, cmd: Any) -> str:
|
|
82
|
+
def _usage(path: str, cmd: Any, ctx: Any) -> str:
|
|
86
83
|
"""The one-line invocation shape, e.g. ``vulnctl enrich [OPTIONS] [VULN_ID...]``."""
|
|
87
84
|
parts = [path]
|
|
88
85
|
if _visible_params(cmd, "option"):
|
|
89
86
|
parts.append("[OPTIONS]")
|
|
90
|
-
parts.extend(
|
|
87
|
+
parts.extend(_metavar(param, ctx) for param in _visible_params(cmd, "argument"))
|
|
91
88
|
return " ".join(parts)
|
|
92
89
|
|
|
93
90
|
|
|
94
|
-
def _param_rows(cmd: Any) -> Iterator[tuple[Text, Text, str]]:
|
|
91
|
+
def _param_rows(cmd: Any, ctx: Any) -> Iterator[tuple[Text, Text, str]]:
|
|
95
92
|
"""Yield ``(name, value hint, help)`` for one command's arguments and options."""
|
|
96
93
|
for param in _visible_params(cmd, "argument"):
|
|
94
|
+
# No type column: an argument's metavar already names what it takes.
|
|
97
95
|
yield (
|
|
98
|
-
Text(f" {
|
|
99
|
-
Text(
|
|
96
|
+
Text(f" {_metavar(param, ctx)}", style=_STYLE_OPTION),
|
|
97
|
+
Text(""),
|
|
100
98
|
str(getattr(param, "help", "") or ""),
|
|
101
99
|
)
|
|
102
100
|
for param in _visible_params(cmd, "option"):
|
|
103
101
|
switch = bool(getattr(param, "is_flag", False))
|
|
102
|
+
hint = "" if switch else _metavar(param, ctx)
|
|
104
103
|
yield (
|
|
105
104
|
Text(" " + " ".join(param.opts), style=_STYLE_SWITCH if switch else _STYLE_OPTION),
|
|
106
|
-
Text(
|
|
105
|
+
Text(hint, style=_STYLE_TYPES),
|
|
107
106
|
str(getattr(param, "help", "") or ""),
|
|
108
107
|
)
|
|
109
108
|
|
|
@@ -127,7 +126,7 @@ def _leaf_commands(group: Any, prefix: str) -> Iterator[tuple[str, str, Any]]:
|
|
|
127
126
|
yield label, name, cmd
|
|
128
127
|
|
|
129
128
|
|
|
130
|
-
def _flags_panel(group: Any, prog: str) -> Panel:
|
|
129
|
+
def _flags_panel(group: Any, prog: str, ctx: Any) -> Panel:
|
|
131
130
|
"""Every command's parameters, generated from the registered Click objects."""
|
|
132
131
|
table = Table(box=None, show_header=False, pad_edge=False, padding=(0, 1))
|
|
133
132
|
table.add_column(no_wrap=True) # command / flag
|
|
@@ -140,9 +139,9 @@ def _flags_panel(group: Any, prog: str) -> Panel:
|
|
|
140
139
|
table.add_row(
|
|
141
140
|
Text(label, style="bold"),
|
|
142
141
|
"",
|
|
143
|
-
Text(_usage(f"{prog} {label}", cmd), style="dim"),
|
|
142
|
+
Text(_usage(f"{prog} {label}", cmd, ctx), style="dim"),
|
|
144
143
|
)
|
|
145
|
-
for name, hint, help_text in _param_rows(cmd):
|
|
144
|
+
for name, hint, help_text in _param_rows(cmd, ctx):
|
|
146
145
|
table.add_row(name, hint, help_text)
|
|
147
146
|
|
|
148
147
|
return Panel(
|
|
@@ -178,7 +177,7 @@ class HelpfulGroup(TyperGroup):
|
|
|
178
177
|
super().format_help(ctx, formatter)
|
|
179
178
|
prog = str(ctx.find_root().info_name or "vulnctl")
|
|
180
179
|
console = Console()
|
|
181
|
-
console.print(_flags_panel(self, prog))
|
|
180
|
+
console.print(_flags_panel(self, prog, ctx))
|
|
182
181
|
console.print(_examples_panel())
|
|
183
182
|
console.print(
|
|
184
183
|
Padding(
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
+
import re
|
|
6
7
|
from importlib.metadata import version as pkg_version
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
|
|
@@ -77,6 +78,25 @@ def test_root_help_shows_usage_shapes_and_examples() -> None:
|
|
|
77
78
|
assert "vulnctl COMMAND --help" in result.output # pointer to per-command detail
|
|
78
79
|
|
|
79
80
|
|
|
81
|
+
def test_root_help_type_column_matches_per_command_help() -> None:
|
|
82
|
+
# The type column is Click's own metavar, not a hand-derived one, so it
|
|
83
|
+
# tracks whatever spelling the installed Typer uses (0.26 prints `PATH`,
|
|
84
|
+
# 0.27 prints `<path>`) instead of disagreeing with the panels above it.
|
|
85
|
+
def plain(text: str) -> str:
|
|
86
|
+
return re.sub(r"\x1b\[[0-9;]*m", "", text) # CI forces colour on Typer's console
|
|
87
|
+
|
|
88
|
+
def option_row(output: str) -> str:
|
|
89
|
+
# The flag also occurs inside other params' help text; take the row it
|
|
90
|
+
# heads, panel border and indent stripped.
|
|
91
|
+
rows = [ln.lstrip("│ ") for ln in plain(output).splitlines()]
|
|
92
|
+
(row,) = [ln for ln in rows if ln.startswith("--sbom")]
|
|
93
|
+
return row
|
|
94
|
+
|
|
95
|
+
metavar = option_row(runner.invoke(app, ["enrich", "--help"]).output).split()[1]
|
|
96
|
+
assert metavar.strip("<>").lower() == "path"
|
|
97
|
+
assert metavar in option_row(runner.invoke(app, ["--help"]).output)
|
|
98
|
+
|
|
99
|
+
|
|
80
100
|
def test_help_examples_use_real_flags() -> None:
|
|
81
101
|
"""Worked examples rot silently; assert every flag in them still exists."""
|
|
82
102
|
known = _subcommand_flags()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|