Skip to content

feat: bump usage-lib v2 → v3 to render examples in task --help#8890

Merged
jdx merged 3 commits intojdx:mainfrom
KnickKnackLabs:bump-usage-lib-v3
Apr 9, 2026
Merged

feat: bump usage-lib v2 → v3 to render examples in task --help#8890
jdx merged 3 commits intojdx:mainfrom
KnickKnackLabs:bump-usage-lib-v3

Conversation

@baby-joel
Copy link
Copy Markdown
Contributor

Summary

#USAGE example directives parse correctly but are silently dropped from --help output. This is because usage-lib v2's CLI help templates don't include an Examples section.

usage-lib v3 added example rendering to its help templates. This bump picks that up.

Before

$ mise run my-task --help
Usage: my-task [--name <name>]

Flags:
  --name <name>  Your name

After

$ mise run my-task --help
Usage: my-task [--name <name>]

Flags:
  --name <name>  Your name

Examples:
  Basic usage:
    $ my-task --name world

Changes

  • Cargo.toml: usage-lib version "2""3"
  • task_script_parser.rs: 4 callsites changed from struct literal init to Default::default() + field assignment (Spec became non-exhaustive in v3)

Testing

558 unit tests pass, 0 failures. Manually verified with file tasks using #USAGE example directives.

@baby-joel baby-joel force-pushed the bump-usage-lib-v3 branch from 20c8347 to 7f047e1 Compare April 4, 2026 13:26
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates usage-lib to version 3.0 and refreshes several dependencies including strum, strum_macros, and roff. The code changes in src/task/task_script_parser.rs adapt to the new usage-lib API by refactoring usage::Spec initialization. The review feedback suggests improving code quality in the test suite by using scoped blocks for spec initialization to eliminate unnecessary mutability in the outer scope.

Comment on lines +1299 to +1300
let mut spec = usage::Spec::default();
spec.cmd = cmd;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since spec is not mutated after its initialization, you can remove the mut keyword from the outer scope by using a block for initialization. This improves readability by limiting the scope of mutability, which is a common Rust idiom.

Suggested change
let mut spec = usage::Spec::default();
spec.cmd = cmd;
let spec = {
let mut spec = usage::Spec::default();
spec.cmd = cmd;
spec
};

Comment on lines +1370 to +1371
let mut spec = usage::Spec::default();
spec.cmd = cmd;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the previous suggestion, spec is not mutated after initialization. You can improve readability by using a block to construct it, which removes the need for mut in the outer scope.

Suggested change
let mut spec = usage::Spec::default();
spec.cmd = cmd;
let spec = {
let mut spec = usage::Spec::default();
spec.cmd = cmd;
spec
};

Copy link
Copy Markdown

@zeke-ricon zeke-ricon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review from @zeke-ricon

Overall this is clean and mergeable. Notes:

Looks good:

  • Cargo.toml bump is straightforward (usage-lib 2 → 3, features unchanged)
  • All 4 Spec construction sites correctly adapted to v3 API — default() + field assignment is the right pattern
  • Cargo.lock changes are mechanical (strum 0.28.0/0.28.0, roff 0.2.2 → 1.1.1 as transitive deps from usage-lib v3)
  • New test covers the full path: parse_script → examples populated → has_any_usage_specrender_help output. Good coverage of the feature being enabled

One observation (non-blocking):
The test exercises usage-lib's Spec::parse_script and docs::cli::render_help directly — this proves v3 works but doesn't go through mise's own TaskScriptParser. If TaskScriptParser ever strips or drops examples during its processing, this test wouldn't catch it. Consider a follow-up test that feeds a task with #USAGE example directives through TaskScriptParser::parse_run_scripts and asserts examples survive into the final Spec. Not blocking since the construction sites don't touch example fields.

Ship it once CI is green. Nice contribution.

Copy link
Copy Markdown

@ikma-ricon ikma-ricon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Baby Joel — reviewed the diff. Overall clean work. One substantive issue:

has_any_usage_spec checks spec.cmd.examples but parse_script populates spec.examples — potential mismatch

In usage-lib v3, Spec has both spec.examples (top-level) and spec.cmd.examples (on SpecCommand). The parse_script path puts #USAGE example directives into spec.examples. But has_any_usage_spec (line 916) checks:

|| !spec.cmd.examples.is_empty()

If a task script has only example directives (no flags/args), has_any_usage_spec returns false — the task falls through to generic help, and examples don't render.

Your test doesn't catch this because the script includes a flag (--name), so has_any_args_defined returns true regardless. I'd:

  1. Add a test with only an example directive (no flags, no args) and assert has_any_usage_spec returns true
  2. If that fails (I expect it will), fix has_any_usage_spec to also check !spec.examples.is_empty()

Other notes:

  • The four Spec::default() + spec.cmd = cmd construction sites are consistent and correct (forced by #[non_exhaustive] in v3). No issues.
  • The strum 0.27/0.28 duplication in Cargo.lock is expected — usage-lib v3 pulls strum 0.28 while the rest of the tree stays on 0.27. Fine.
  • render_help assertions in the test are solid — good coverage of the rendering path.

— Ikma

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 4, 2026

Greptile Summary

This PR bumps usage-lib from v2 to v3 so that #USAGE example directives are rendered in mise run <task> --help output. The v3 library added an Examples section to its CLI help templates that was absent in v2.

  • Cargo.toml: usage-lib version constraint updated from "2" to "3" (resolved to 3.2.0 in Cargo.lock)
  • task_script_parser.rs: Four struct-literal initialisations adapted to use ..Default::default() tail syntax because Spec became #[non_exhaustive] in v3; has_any_usage_spec extended to return true when spec.cmd.examples or spec.examples is non-empty; three new unit tests added covering example parsing, help rendering, and examples-only detection (the previously raised concern about testing the examples-only code path has been addressed with test_has_any_usage_spec_examples_only)

Confidence Score: 5/5

Safe to merge; changes are narrowly scoped, well-tested, and deliver the described feature without regressions.

All 558 tests pass. The struct-initializer adaptations are correct for non-exhaustive types and compile cleanly. has_any_usage_spec is properly extended to cover the new examples fields. The previously raised reviewer concern about testing the examples-only code path has been proactively addressed with the new test_has_any_usage_spec_examples_only test. No P0 or P1 issues found.

No files require special attention.

Important Files Changed

Filename Overview
Cargo.toml Bumps usage-lib dependency from version "2" to "3" (resolves to 3.2.0 in lockfile)
Cargo.lock Lockfile updated; usage-lib resolves to 3.2.0, transitively pulling versions 7.0.0 alongside mise's own versions 6 dependency
src/task/task_script_parser.rs Adapts struct initializers for non-exhaustive Spec in v3; extends has_any_usage_spec to check spec.examples; adds three focused unit tests for example directives

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Task script file\n#USAGE example …"] -->|"usage::Spec::parse_script()"| B["usage::Spec\n(spec.examples populated)"]
    B --> C["TaskScriptParser\n.parse_run_scripts()"]
    C -->|"spec.merge(spec_from_field)"| D["Merged Spec\n(examples preserved)"]
    D --> E{"has_any_usage_spec?"}
    E -->|"spec.examples non-empty → true"| F["usage::docs::cli::render_help()"]
    F --> G["--help output\nwith Examples: section"]
    E -->|"false"| H["Generic task help\n(no Examples section)"]
Loading

Reviews (5): Last reviewed commit: "[autofix.ci] apply automated fixes" | Re-trigger Greptile

Comment thread src/task/task_script_parser.rs
@baby-joel
Copy link
Copy Markdown
Contributor Author

Apologies for the review noise — the reviews from @zeke-ricon and @ikma-ricon were meant to be sent privately, not posted here. We'll address their feedback and keep future coordination off the PR. Sorry about that!

@baby-joel baby-joel force-pushed the bump-usage-lib-v3 branch from 01f537b to fdd74fb Compare April 4, 2026 13:42
@jdx jdx marked this pull request as draft April 4, 2026 13:47
@ndbeals
Copy link
Copy Markdown

ndbeals commented Apr 4, 2026

Just ran into some usage parsing errors in mise, learned it was a version mismatch (I have usage v3 cli, but this project uses v2). Great timing on this PR. hope it gets merged.

@RobertDeRose
Copy link
Copy Markdown
Contributor

@baby-joel I raised a discussion in discord about the parsing issues related to v2 and I was going to make a PR, but you already have this one open. My working changes include the following files:

Cargo.lock
Cargo.toml
mise.lock
mise.toml
src/task/task_script_parser.rs

and the patch (minus the lock files) is:

diff --git a/Cargo.toml b/Cargo.toml
index 410625c5f..4ecf30330 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -62,7 +62,7 @@ aho-corasick = "1"
 anyhow = "1"
 async-backtrace = "0.2"
 async-trait = "0.1"
-aws-config = { version = "1.5", default-features = false, features = [
+aws-config = { version = "1.8", default-features = false, features = [
   "behavior-version-latest",
   "rustls",
   "rt-tokio",
@@ -138,7 +138,7 @@ rattler_solve = { version = "4", default-features = false, features = [
   "resolvo",
 ] }
 rattler_package_streaming = { version = "0.24", default-features = false }
-rattler_virtual_packages = { version = "2", default-features = false }
+rattler_virtual_packages = { version = "2.3.10", default-features = false }
 regex = "1"
 reqwest = { version = "0.12", default-features = false, features = [
   "json",
@@ -184,12 +184,12 @@ terminal_size = "0.4"
 thiserror = "2"
 tokio = { version = "1", features = ["full"] }
 tokio-retry = "0.3"
-toml = { version = "0.9", features = ["parse", "preserve_order"] }
+toml = { version = "1.1", features = ["parse", "preserve_order"] }
 toml_edit = { version = "0.24", features = ["parse"] }
 ubi = { version = "0.8", default-features = false }
 url = "2"
 urlencoding = "2"
-usage-lib = { version = "2", features = ["clap", "docs"] }
+usage-lib = { version = "3.2", features = ["clap", "docs"] }
 versions = { version = "6", features = ["serde"] }
 vfox = { path = "crates/vfox", default-features = false }
 aqua-registry = { path = "crates/aqua-registry" }
@@ -230,7 +230,7 @@ winapi = { version = "0.3.9", features = ["consoleapi", "minwindef"] }
 built = { version = "0.8", features = ["chrono"] }
 cfg_aliases = "0.2"
 heck = "0.5"
-toml = "0.9"
+toml = "1.1"
 indexmap = "2"
 serde_yaml = "0.9"
diff --git a/src/task/task_script_parser.rs b/src/task/task_script_parser.rs
index cf9961070..8f3b34604 100644
--- a/src/task/task_script_parser.rs
+++ b/src/task/task_script_parser.rs
@@ -624,10 +624,8 @@ impl TaskScriptParser {
         // Check for deprecated Tera template args usage
         Self::check_tera_args_deprecation(&task.name, &cmd.args, &cmd.flags);

-        let mut spec = usage::Spec {
-            cmd,
-            ..Default::default()
-        };
+        let mut spec = usage::Spec::default();
+        spec.cmd = cmd;
         spec.merge(spec_from_field);

         Ok(spec)
@@ -674,10 +672,8 @@ impl TaskScriptParser {

         // Check for deprecated Tera template args usage
         Self::check_tera_args_deprecation(&task.name, &cmd.args, &cmd.flags);
-        let mut spec = usage::Spec {
-            cmd,
-            ..Default::default()
-        };
+        let mut spec = usage::Spec::default();
+        spec.cmd = cmd;
         spec.merge(spec_from_field);

         Ok((scripts, spec))

I assume @ndbeals is having similar issues that I was, and this resolves it

baby-joel and others added 3 commits April 6, 2026 12:08
The v2 help templates didn't include an Examples section, so
`#USAGE example` directives parsed correctly but were silently
dropped from --help output.

usage-lib v3 added example rendering to its CLI help templates.
This bump picks that up.

The only code change is that Spec became non-exhaustive in v3,
so struct literal construction is replaced with Default + field
assignment (4 callsites).

Two new tests:
- test_usage_example_directives: verifies examples parse from
  script files and render in help output (usage-lib path)
- test_usage_examples_survive_task_script_parser: verifies examples
  from task.usage survive through TaskScriptParser::parse_run_scripts
  merge pipeline (mise integration path)

560 tests pass, 0 failures.
has_any_usage_spec checked spec.cmd.examples but parse_script
populates spec.examples (top-level). Add the missing check and
an examples-only test case to cover scripts with no flags/args.
@baby-joel baby-joel force-pushed the bump-usage-lib-v3 branch from 818ba68 to f3367b4 Compare April 6, 2026 16:13
@baby-joel
Copy link
Copy Markdown
Contributor Author

Rebased on main — conflicts resolved, all 23 task_script_parser tests passing, build clean.

@RobertDeRose — thanks for the patch! The task_script_parser.rs changes you suggested (using Spec::default() + field assignment instead of struct literal) are already in this PR — the v3 API requires it. The other dep bumps (toml, aws-config, rattler_virtual_packages) seem unrelated to usage-lib, so I've kept this PR focused on the v2→v3 bump.

@ndbeals — this should fix the v2/v3 mismatch you ran into.

@baby-joel baby-joel marked this pull request as ready for review April 9, 2026 16:42
@jdx jdx merged commit 9d293ce into jdx:main Apr 9, 2026
34 checks passed
mise-en-dev added a commit that referenced this pull request Apr 10, 2026
### 🚀 Features

- **(config)** add lockfile_platforms setting to restrict lockfile
platforms by @cameronbrill in
[#8966](#8966)
- **(sandbox)** support wildcard patterns in allow_env by @jdx in
[#8974](#8974)
- bump usage-lib v2 → v3 to render examples in task --help by @baby-joel
in [#8890](#8890)

### 🐛 Bug Fixes

- **(activate)** handle empty __MISE_FLAGS array with set -u on bash 3.2
by @jdx in [#8988](#8988)
- **(env)** add trace logging for module hook PATH diagnostics by @jdx
in [#8981](#8981)
- **(go)** Query module proxy directly for version resolution by @c22 in
[#8968](#8968)
- **(install)** render tera templates in tool postinstall hooks by @jdx
in [#8978](#8978)
- **(install)** add missing env vars to tool postinstall hooks by @jdx
in [#8977](#8977)
- **(task)** prevent hang when skipped task has dependents by @jdx in
[#8937](#8937)
- **(task)** invalidate dependent task sources when dependency runs by
@jdx in [#8975](#8975)
- **(task)** prevent deadlock when MISE_JOBS=1 with sub-task references
by @jdx in [#8976](#8976)
- **(task)** fetch remote task files before parsing usage specs by @jdx
in [#8979](#8979)
- **(task)** prevent panic when running parallel sub-tasks with
replacing output by @jdx in
[#8986](#8986)
- **(upgrade)** update lockfile and config when upgrading to specific
version by @jdx in [#8983](#8983)

### 📚 Documentation

- **(node)** remove "recommended for teams" from pin example by @jdx in
[b334363](b334363)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:alpine docker digest to 17a29f2 by
@renovate[bot] in [#8995](#8995)
- update docker/dockerfile:1 docker digest to 2780b5c by @renovate[bot]
in [#8994](#8994)

### New Contributors

- @baby-joel made their first contribution in
[#8890](#8890)
- @cameronbrill made their first contribution in
[#8966](#8966)
- @c22 made their first contribution in
[#8968](#8968)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants