From 080c711bc733c2d47c5d48aad7e79e52521efef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Fri, 29 May 2026 09:24:35 +0200 Subject: [PATCH] docs: add CLI output and verbosity page Closes #205 --- client_reference/output_and_verbosity.md | 70 ++++++++++++++++++++++++ client_reference/overview.md | 4 ++ config/navigation.json | 6 ++ scripts/update-cli-nav.py | 42 ++++++++------ troubleshooting/subshell_stderr.md | 4 ++ 5 files changed, 110 insertions(+), 16 deletions(-) create mode 100644 client_reference/output_and_verbosity.md diff --git a/client_reference/output_and_verbosity.md b/client_reference/output_and_verbosity.md new file mode 100644 index 0000000..a3367ea --- /dev/null +++ b/client_reference/output_and_verbosity.md @@ -0,0 +1,70 @@ +--- +title: "Output and verbosity" +description: "How the Kosli CLI writes output, what warnings mean, and how to control verbosity." +--- + +This page explains where the Kosli CLI writes its output, what `[warning]` messages mean, and how to control verbosity in scripts and CI/CD pipelines. + +## Where output goes + +The CLI splits its output across two streams: + +- **stdout** — Command results: created object IDs, JSON, tables, and any value intended to be piped or captured. +- **stderr** — Warnings, debug lines, and the "new version available" update notice. + +The **exit code** is the authoritative success or failure signal. A non-zero exit code means the command failed; zero means it succeeded, regardless of what appeared on stderr. + +## Warning messages + +Warnings are written in the form `[warning] ` and indicate a non-fatal condition that the CLI worked around. Examples: + +```text +[warning] failed to get git repo info.
+[warning] Repo URL will not be reported,
+[warning] failed to remove evidence file :
+``` + +Warnings: + +- Are always written to **stderr**. +- **Never** affect the exit code. +- Are not errors — the command completed successfully. + +If you see a warning in a pipeline that you have already validated end-to-end, it is safe to suppress it (see below). If a warning is new or unexpected, read the message and address the underlying cause. + +## Verbosity controls + +| Flag | Effect | +|---|---| +| _(default)_ | Errors and `[warning]` lines to stderr. Results to stdout. | +| `-q`, `--quiet` | Suppresses `[warning]` lines. Errors still print. | +| `--debug` | Adds `[debug]` lines to stderr. Overrides `--quiet`. | +| `--debug=false` | Explicit-off form. Useful when a parent process or env var has enabled debug and you want to disable it for one command. | + +When both `--quiet` and `--debug` are set, `--debug` wins and a debug notice is printed explaining the override. + +Both flags have equivalent environment variables, following the standard `KOSLI_` prefix: + +- `KOSLI_QUIET=true` +- `KOSLI_DEBUG=true` + +## Warnings in CI/CD pipelines + +CI systems including Jenkins, Azure DevOps, Bitbucket Pipelines, and GitHub Actions multiplex stdout and stderr into a single build log. Some render stderr lines in red or with error-level styling. This makes non-fatal `[warning]` lines look alarming to pipeline reviewers even though they have no effect on the build. + +Once you have validated a workflow end-to-end, add `--quiet` (or set `KOSLI_QUIET=true`) to the Kosli CLI invocations to keep build logs clean: + +```shell +kosli attest generic --quiet ... +``` + + +Avoid redirecting all of stderr to `/dev/null` (for example, `kosli ... 2>/dev/null`). That hides genuine error messages and makes failures much harder to diagnose. Prefer `--quiet`, which suppresses only `[warning]` lines and keeps real errors visible. + + +If you are capturing CLI output in a shell subshell and seeing debug or warning lines mixed into the captured value, see [CLI in subshell captures stderr](/troubleshooting/subshell_stderr). + +## See also + +- [kosli root command and global flags](/client_reference/kosli) +- [CLI in subshell captures stderr](/troubleshooting/subshell_stderr) diff --git a/client_reference/overview.md b/client_reference/overview.md index 1f56e5f..f3c378c 100644 --- a/client_reference/overview.md +++ b/client_reference/overview.md @@ -14,3 +14,7 @@ For installation instructions, see [Install the Kosli CLI](/getting_started/inst ## Commands Browse the CLI commands using the sidebar navigation, or start with the [kosli](/client_reference/kosli) root command to see global flags and environment variable configuration. + +## Output and verbosity + +See [Output and verbosity](/client_reference/output_and_verbosity) for how the CLI uses stdout and stderr, what `[warning]` messages mean, and how to control output in scripts and CI/CD pipelines with `--quiet` and `--debug`. diff --git a/config/navigation.json b/config/navigation.json index 739d4db..e4190ac 100644 --- a/config/navigation.json +++ b/config/navigation.json @@ -219,6 +219,12 @@ "group": "General", "pages": [ "client_reference/overview", + "client_reference/output_and_verbosity" + ] + }, + { + "group": "Top-level commands", + "pages": [ "client_reference/kosli", "client_reference/kosli_attach-policy", "client_reference/kosli_completion", diff --git a/scripts/update-cli-nav.py b/scripts/update-cli-nav.py index cc251f5..aafd59b 100644 --- a/scripts/update-cli-nav.py +++ b/scripts/update-cli-nav.py @@ -54,13 +54,13 @@ def get_command_group(title): cmd = title.replace('kosli ', '').strip() if cmd == '': - return 'General' + return 'Top-level commands' # Top-level commands (no subcommand) parts = cmd.split(' ') if len(parts) == 1: # Single-word commands like 'search', 'version', 'status', etc. - return 'General' + return 'Top-level commands' # Group by first word for multi-word commands group_word = parts[0] @@ -72,8 +72,8 @@ def get_command_group(title): 'attach': 'attach / detach', 'attest': 'attest', 'begin': 'begin', - 'completion': 'General', - 'config': 'General', + 'completion': 'Top-level commands', + 'config': 'Top-level commands', 'create': 'create', 'detach': 'attach / detach', 'diff': 'diff', @@ -87,9 +87,9 @@ def get_command_group(title): 'rename': 'rename', 'report': 'report', 'request': 'request', - 'search': 'General', + 'search': 'Top-level commands', 'snapshot': 'snapshot', - 'tag': 'General', + 'tag': 'Top-level commands', } return group_map.get(group_word, group_word) @@ -102,9 +102,9 @@ def build_nav_groups(docs_dir): for filename in sorted(os.listdir(docs_dir)): if not filename.endswith('.md'): continue - # Skip the overview page — it's manually maintained and always - # inserted as the first page in the General group below. - if filename == 'overview.md': + # Skip manually-maintained intro pages; they are inserted into the + # General group below in a fixed order. + if filename in ('overview.md', 'output_and_verbosity.md'): continue filepath = os.path.join(docs_dir, filename) @@ -132,14 +132,24 @@ def build_nav_groups(docs_dir): groups_dict[group] = [] groups_dict[group].append(cmd['page']) - # Build ordered navigation groups - # Put General first, then alphabetical - nav_groups = [] - - if 'General' in groups_dict: - nav_groups.append({ + # Build ordered navigation groups. + # 1. General — manually-maintained intro pages, fixed order. + # 2. Top-level commands — auto-generated single-word kosli commands. + # 3. Remaining command families in alphabetical order, prefixed 'kosli '. + nav_groups = [ + { 'group': 'General', - 'pages': ['client_reference/overview'] + groups_dict.pop('General'), + 'pages': [ + 'client_reference/overview', + 'client_reference/output_and_verbosity', + ], + } + ] + + if 'Top-level commands' in groups_dict: + nav_groups.append({ + 'group': 'Top-level commands', + 'pages': groups_dict.pop('Top-level commands'), }) for group_name in sorted(groups_dict.keys()): diff --git a/troubleshooting/subshell_stderr.md b/troubleshooting/subshell_stderr.md index 8bb8b39..e7cf9cf 100644 --- a/troubleshooting/subshell_stderr.md +++ b/troubleshooting/subshell_stderr.md @@ -26,3 +26,7 @@ DIGEST="$(kosli fingerprint "${IMAGE_NAME}" --artifact-type=docker --debug=false ## Context The Kosli CLI writes debug information to `stderr` and all other output to `stdout`. In a local terminal, a `$(subshell)` captures only `stdout`. However, in many CI workflows (including GitHub and GitLab), `stdout` and `stderr` are multiplexed together, causing debug output to leak into captured variables. + +## See also + +- [Output and verbosity](/client_reference/output_and_verbosity) — full reference for the CLI's stdout/stderr behavior, `[warning]` messages, and the `--quiet` and `--debug` flags.