feat(cli): phase-aware scan progress and clearer verbosity flags#122
Conversation
Two coupled UX improvements that close the "I don't know what argus
is stuck on" gap surfaced during the option-A schema review.
1. Phase-aware container scan progress.
ContainerEngine accepts an optional ``progress_callback`` that's
invoked at each phase transition per image: ``pull``/``build`` on
start, ``scan`` after build completes, ``done`` on success or
failure. The CLI installs a callback that routes phase events to
the right surface based on the user's flag mix:
* Default (TTY): the spinner's status text updates in place
(``[2/4] scanner-opengrep — scan (12s)`` instead of the static
"Running container scan").
* --no-spinner OR non-TTY: phase events print as persistent
``[idx/total] name — phase (Ns)`` lines on stderr — useful for
CI logs and step-away terminals where the rolling spinner is
useless.
* --quiet: no-op (phase progress suppressed).
* --verbose / --debug: no-op (the logger already streams full
INFO/DEBUG output; the progress emitter would just duplicate it).
2. Verbosity flag re-shape — three orthogonal flags, one mental
model per flag.
The ``-v`` / ``--verbose`` / ``-vv`` ladder was confusing.
Replace with named flags whose names announce their purpose:
* (no flag) — phase-aware spinner with INFO-level engine logging.
The user sees enough to know the scan is making progress.
* --quiet / -q — suppress per-phase INFO lines and bump the
engine logger to WARNING. The spinner still draws unless
--no-spinner is also passed; final summary still prints.
* --debug — full firehose: subprocess output, DB updates, every
engine log line. ``--verbose`` is preserved as a backward-
compatible alias so existing CI scripts keep working.
The ``--quiet`` and ``--no-spinner`` flags target different
concerns and compose orthogonally:
* --quiet (log verbosity): suppress info messages
* --no-spinner (UI rendering): don't draw the animation
This matches *nix convention — git -q, pip -q, pytest -q all
suppress info but don't touch UI rendering.
New helper _configure_logger(args) maps the flag combination to
a stream-handler level: --verbose/--debug to DEBUG, --quiet to
WARNING, default to INFO. _cmd_container_scan now uses it.
Tests: 1580 passing (was 1567; ten more progress-emitter tests
covering each combination of --quiet, --verbose, --debug,
--no-spinner, and TTY-vs-non-TTY routing).
CLI reference regenerated for the new flags.
The four output-control flags (--quiet, --no-spinner, --debug, --verbose) each get individual help text in the auto-generated per- command tables, but the *compose pattern* — the orthogonality of log- verbosity (--quiet) vs UI rendering (--no-spinner) and the four UX modes that fall out — wasn't anywhere in the docs. The cli-reference header warns "Do not edit manually", so the new section is added via ``scripts/ci/gen_cli_docs.py`` so it survives every regeneration. Section sits between Global Options and Commands, displaying a four- row table with When-to-use and What-you-see columns plus a one-line note on the additional modes that fall out from composing flags.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
🔒 Argus Container Security ScanBranch: 📊 Combined Findings Summary
Scanned: 4 containers | Build Failures: 0 📦 Container Breakdown
🔍 Detailed Findings by Container🚨 cli - 29 vulnerabilities (23 unique)Image: Combined (Deduplicated)
🔷 Trivy Scanner (29 findings, 23 unique)
⚓ Grype Scanner (0 findings, 0 unique)✅ No vulnerabilities detected by Grype 🟡 scanner-bandit - 2 vulnerabilities (2 unique)Image: Combined (Deduplicated)
🔷 Trivy Scanner (2 findings, 2 unique)
⚓ Grype Scanner (0 findings, 0 unique)✅ No vulnerabilities detected by Grype
|
| 🚨 Critical | 🟡 Medium | 🔵 Low | Total | Unique | |
|---|---|---|---|---|---|
| 0 | 7 | 44 | 63 | 114 | 50 |
🔷 Trivy Scanner (114 findings, 49 unique)
| CVE | Severity | Package | Version | Fixed |
|---|---|---|---|---|
| CVE-2026-4878 | libcap2 | 1:2.75-10+b8 | N/A | |
| CVE-2025-69720 | libncursesw6 | 6.5+20250216-2 | N/A | |
| CVE-2026-29111 | libsystemd0 | 257.9-1~deb13u1 | N/A | |
| CVE-2025-69720 | libtinfo6 | 6.5+20250216-2 | N/A | |
| CVE-2026-29111 | libudev1 | 257.9-1~deb13u1 | N/A | |
| CVE-2025-69720 | ncurses-base | 6.5+20250216-2 | N/A | |
| CVE-2025-69720 | ncurses-bin | 6.5+20250216-2 | N/A | |
| CVE-2026-27456 | 🟡 MEDIUM | bsdutils | 1:2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | bsdutils | 1:2.41-5 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | libblkid1 | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | libblkid1 | 2.41-5 | N/A |
| CVE-2026-4046 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-4437 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-4438 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-5435 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-5450 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-5928 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-6238 | 🟡 MEDIUM | libc-bin | 2.41-12+deb13u2 | N/A |
| CVE-2026-4046 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-4437 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-4438 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-5435 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-5450 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-5928 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-6238 | 🟡 MEDIUM | libc6 | 2.41-12+deb13u2 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | liblastlog2-2 | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | liblastlog2-2 | 2.41-5 | N/A |
| CVE-2026-34743 | 🟡 MEDIUM | liblzma5 | 5.8.1-1 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | libmount1 | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | libmount1 | 2.41-5 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | libsmartcols1 | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | libsmartcols1 | 2.41-5 | N/A |
| CVE-2026-40225 | 🟡 MEDIUM | libsystemd0 | 257.9-1~deb13u1 | N/A |
| CVE-2026-40226 | 🟡 MEDIUM | libsystemd0 | 257.9-1~deb13u1 | N/A |
| CVE-2026-4105 | 🟡 MEDIUM | libsystemd0 | 257.9-1~deb13u1 | N/A |
| CVE-2026-40225 | 🟡 MEDIUM | libudev1 | 257.9-1~deb13u1 | N/A |
| CVE-2026-40226 | 🟡 MEDIUM | libudev1 | 257.9-1~deb13u1 | N/A |
| CVE-2026-4105 | 🟡 MEDIUM | libudev1 | 257.9-1~deb13u1 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | libuuid1 | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | libuuid1 | 2.41-5 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | login | 1:4.16.0-2+really2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | login | 1:4.16.0-2+really2.41-5 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | mount | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | mount | 2.41-5 | N/A |
| CVE-2026-5958 | 🟡 MEDIUM | sed | 4.9-2 | N/A |
| CVE-2026-5704 | 🟡 MEDIUM | tar | 1.35+dfsg-3.1 | N/A |
| CVE-2026-27456 | 🟡 MEDIUM | util-linux | 2.41-5 | N/A |
| CVE-2026-3184 | 🟡 MEDIUM | util-linux | 2.41-5 | N/A |
| CVE-2026-27171 | 🟡 MEDIUM | zlib1g | 1:1.3.dfsg+really1.3.1-1+b1 | N/A |
| CVE-2026-3219 | 🟡 MEDIUM | pip | 26.0.1 | N/A |
...and 64 more
⚓ Grype Scanner (0 findings, 0 unique)
✅ No vulnerabilities detected by Grype
⚠️ scanner-supply-chain - 9 vulnerabilities (9 unique)
Image: ghcr.io/huntridge-labs/argus/scanner-supply-chain:f5255525ef441d22932fa57272f2be0123b81d57
Combined (Deduplicated)
| 🚨 Critical | 🟡 Medium | 🔵 Low | Total | Unique | |
|---|---|---|---|---|---|
| 0 | 4 | 5 | 0 | 9 | 9 |
🔷 Trivy Scanner (9 findings, 9 unique)
| CVE | Severity | Package | Version | Fixed |
|---|---|---|---|---|
| CVE-2026-32280 | stdlib | v1.26.1 | 1.25.9, 1.26.2 | |
| CVE-2026-32281 | stdlib | v1.26.1 | 1.25.9, 1.26.2 | |
| CVE-2026-32283 | stdlib | v1.26.1 | 1.25.9, 1.26.2 | |
| CVE-2026-33810 | stdlib | v1.26.1 | 1.26.2 | |
| CVE-2026-3219 | 🟡 MEDIUM | pip | 26.0.1 | N/A |
| CVE-2026-6357 | 🟡 MEDIUM | pip | 26.0.1 | 26.1 |
| CVE-2026-32282 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.9, 1.26.2 |
| CVE-2026-32288 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.9, 1.26.2 |
| CVE-2026-32289 | 🟡 MEDIUM | stdlib | v1.26.1 | 1.25.9, 1.26.2 |
⚓ Grype Scanner (0 findings, 0 unique)
✅ No vulnerabilities detected by Grype
Generated by Argus
Description
Stacked on #121. Closes the "I don't know what argus is stuck on" gap surfaced during the option-A schema review — replaces the static "Running container scan" spinner with phase-aware progress, and re-shapes the verbosity flags into orthogonal, named flags.
Changes Made
Details
1. Phase-aware container scan progress
ContainerEngineaccepts an optionalprogress_callbackthat's invoked at each phase transition per image:pull/buildon start,scanafter build completes,doneon success or failure. The CLI installs a callback that routes phase events to the right surface based on the user's flag mix:[2/4] scanner-opengrep — scan (12s))--no-spinnerOR non-TTY[idx/total] name — phase (Ns)lines on stderr — useful for CI logs and step-away terminals--quiet--verbose/--debug2. Verbosity flag re-shape — three orthogonal flags
The old
-v/--verbose/-vvladder was confusing —--verboseand-vlooked like the same thing, then-vvwas a syntactically-similar third option meaning something different. Replaced with named flags whose names announce their purpose:--quiet/-q--no-spinneris also passed--debug--verbose--debug--no-spinner(existing)--quietand--no-spinnertarget different concerns and compose orthogonally — same convention asgit -q,pip -q,pytest -q(suppress info, don't touch UI rendering).Four discrete UX modes through three orthogonal flags:
argus scanargus scan --quietargus scan --no-spinnerargus scan --quiet --no-spinnerargus scan --debug(or--verbose)3. Documentation
docs/cli-reference.mdgets a new "Output and verbosity" section between Global Options and Commands, walking through the four most useful modes as a table and noting how compose patterns produce additional modes. Added via the doc generator (scripts/ci/gen_cli_docs.py) so it survives every regeneration._configure_logger(args)is the new central helper that maps the flag combination to a stream-handler level (DEBUG / INFO / WARNING)._cmd_container_scannow uses it instead of callingget_loggerdirectly with a verbose bool.Verification
End-to-end tested locally with all four modes:
argus scan container --no-spinneremits[1/1] scanner-bandit — pull (0s)then[1/1] scanner-bandit — scan (0s)as scrollbackargus scan container --quiet --no-spinnerruns silent except ERROR lines and the final summaryargus scan container --debugand--verboseboth stream the full firehose unchanged from todayTesting
Test Results
argus/tests/test_progress_emitter.py— 13 unit tests covering each combination of--quiet,--verbose,--debug,--no-spinner, plus TTY-vs-non-TTY routing. Validates that:--quietreturns a no-op emitter--verbose/--debugreturn no-ops (logger handles output)--no-spinnernon-quiet non-verbose: phase events print as scrollbackupdate_message--quietoverrides spinner-update pathdocs/cli-reference.mdregenerated; pre-commitCheck CLI docs are up to datepassesSecurity Considerations
AI Context Updates (.ai/)
Checklist
For New Scanners/Actions (if applicable)
Follow-ups (not in this PR)
_cmd_source_scan). Today the source-scan path also has a static "Running scanners" spinner that doesn't update per scanner. Same engine-level callback shape would work; one short follow-up PR.scan_image(trivy / grype / syft phases). Today's--debugalready shows them as logger lines; the spinner could surface them too. Worth doing if user feedback suggests image-level granularity isn't enough.