A CLI can now claim an environment variable prefix. Any variable under that prefix that the spec doesn't declare then makes the parse fail, which catches typos and stale names in deployment config. This release also lets derive flags declare their own settings, makes flags inside clauses set their settings, and adds --help/--version to tab completion.
Added
-
(spec) New opt-in
unknown_envnode (#1609, @jdx). When it's set, a parse fails if the environment holds any variable under the prefix that nothing in the spec reads, and the error lists every such variable at once. Without the node, nothing changes.unknown_env prefix="MOQ_" { allow "MOQ_VAAPI_DEVICE" "MOQ_V4L2_ENCODER" } flag "--port <port>" env="MOQ_PORT" cmd "serve" { flag "--tls-cert <path>" env="MOQ_TLS_CERT" deprecated_env="MOQ_CERT" }
Rust derive CLIs declare it on the root struct:
#[usage(bin = "moq", unknown_env(prefix = "MOQ_", allow("MOQ_VAAPI_DEVICE")))]. WithMOQ_PROTandMOQ_TSLset, a derived CLI exits 2:error: unknown environment variables under 'MOQ_': MOQ_PROT MOQ_TSLA variable counts as known if anything in the command tree declares it, not only the command that ran. That covers
env,env_fallbackanddeprecated_envon any flag or argument, including those in subcommands, flattened groups and clauses. It also covers variables read byconfigproperties and names inallow. Other details:- The prefix only limits which variables are checked. It is never used to build a variable name, so this is not clap's
env_prefix. - Deprecated names still produce a deprecation warning rather than an error.
--helpand--versionare answered before the check runs.- On Windows, names are compared case-insensitively.
In usage-lib,
Parserchecks the map passed toParser::with_envif there is one, and the process environment otherwise. It reports aUsageErr::UnknownEnv. - The prefix only limits which variables are checked. It is never used to build a variable name, so this is not clap's
-
(go) Parsers from
usage generate gonow enforceunknown_envtoo (#1612, @jdx). Before this, generated Go ignored the node. The check runs before required flags and arguments are checked, and it reports offenders in sorted order:error: unknown environment variables under `MOQ_`: `MOQ_PROT`, `MOQ_TSL`The Go generator can't see what variables a mounted command declares, so
usage generate gofails on a spec that has bothunknown_envand amount.go/argvaddsUnknownEnv,RefuseUnknownEnv,EnvNamesand the error codeCodeUnknownEnv. -
(derive) A field with
setting = "key"now declares that setting on its own, so you no longer need a matching#[derive(Config)]field (#1610, @jdx). The registry entry takes its type from the field's type and its environment names fromenv/env_fallback/deprecated_env. List splitting comes fromdelimiter(,or:), help comes from the doc comment, and the flags are every flag bound to the key. The rootClinow exposesSETTINGS_PROPSandSETTINGS_REGISTRY, and its spec gets aconfigblock even when there is noconfig = Type.#[derive(usage_rs::Args)] struct ListenTls { /// Load the given certificate from disk. #[usage(long = "listen-tls-cert", env = "EX_LISTEN_TLS_CERT", delimiter = ',', setting = "listen.tls.cert")] cert: Vec<std::path::PathBuf>, }
If several subcommands bind the same key, they must agree on field type, environment names and delimiter. Otherwise it's a compile error. Existing CLIs don't need to change, because a key declared on the
configstruct takes precedence over the flag. Settings that need a default,merge,scopeorchoicesstill have to beConfigfields. Settings declared only by a flag are always optional and have no default.
Fixed
- (complete) Tab completion now offers
-h/--help, and-V/--versionwhere the parser accepts them (#1605, @jdx). Before this fix, CLIs compiled from the derive andusageitself never offered these flags. This applies to both the derive andusage complete-word. A spelling that a declared flag already uses (hidden flags included) is not offered twice, anddisable_help_flag/disable_version_flagare respected. Also, merging a mounted spec no longer clearsdisable_help_flag,disable_help_subcommandordisable_version_flagon the host command. For example,mise runno longer gets--helpback from the mounted root. - (derive) A flag with
setting = "key"inside a#[usage(clause)]element now sets its setting, the same way a flag in a flattened group does (#1611, @jdx). When several clause items give the same setting, list settings collect every entry in order and other settings keep the last value. A root whose only bound flags are in clauses can now use#[usage(settings)]. Before this fix, that failed to compile.
Full Changelog: v7.1.5...v7.2.0
💚 Sponsor usage
usage is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep usage fast, free, and independent.