feat: add --dry-run-code flag to exit non-zero when there is work to do#8063
feat: add --dry-run-code flag to exit non-zero when there is work to do#8063
Conversation
Summary of ChangesHello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Pull request overview
Adds a --dry-run-code flag to several CLI commands to support scripting (exit non-zero when changes are needed), and extends idiomatic version-file detection to support package.json for Node/Bun and JS package managers.
Changes:
- Add
--dry-run-codetoinstall,upgrade,prune,uninstall, anduse, causing exit code1when pending operations exist in dry-run. - Add support for reading tool versions from
package.json(devEngines/packageManager) via a newpackage_jsonmodule and backend parsing updates. - Extend e2e assertions/tests to validate new exit-code behavior and
package.jsonidiomatic-version support.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/plugins/core/node.rs | Treat package.json as an idiomatic file for Node and parse version from it. |
| src/plugins/core/bun.rs | Treat package.json as an idiomatic file for Bun and parse version from it. |
| src/package_json.rs | New parser/helpers for extracting tool versions from package.json. |
| src/main.rs | Wire in new package_json module. |
| src/config/config_file/idiomatic_version.rs | Change idiomatic parsing to skip parse errors instead of failing. |
| src/cli/use.rs | Add --dry-run-code and exit(1) when use would change config. |
| src/cli/upgrade.rs | Add --dry-run-code and exit(1) in dry-run path. |
| src/cli/uninstall.rs | Add --dry-run-code and exit(1) when uninstall work exists. |
| src/cli/prune.rs | Add --dry-run-code and exit(1) when prune work exists. |
| src/cli/install.rs | Add --dry-run-code and exit(1) when installs are needed. |
| src/backend/mod.rs | Default package.json parsing for package-manager backends. |
| registry/yarn.toml | Enable package.json as idiomatic version file for yarn. |
| registry/pnpm.toml | Enable package.json as idiomatic version file for pnpm. |
| registry/npm.toml | Enable package.json as idiomatic version file for npm. |
| e2e/core/test_package_json | New e2e test ensuring package.json idiomatic versions work. |
| e2e/cli/test_use_dry_run | Add e2e coverage for mise use --dry-run-code. |
| e2e/cli/test_install_dry_run | Add e2e coverage for mise install --dry-run-code. |
| e2e/assert.sh | Add assert_fail_contains helper used by new tests. |
Comments suppressed due to low confidence (1)
src/cli/upgrade.rs:1
--dry-run-codecurrently exits with code 1 unconditionally in the dry-run path, even when there are no upgrades/removals/config updates to perform. Track whether there is actually any work (e.g.,!to_remove.is_empty() || !to_upgrade.is_empty()and/or any config update) and onlyexit(1)when that is true; otherwise exit 0.
use std::sync::Arc;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let version = match plugin.parse_idiomatic_file(&path).await { | ||
| Ok(v) => v, | ||
| Err(e) => { | ||
| trace!("skipping {} for {}: {e}", plugin.id(), path.display()); | ||
| continue; | ||
| } | ||
| }; |
There was a problem hiding this comment.
This change swallows all errors from parse_idiomatic_file for every plugin (including IO errors, permission issues, and malformed file formats), which can silently hide real problems and make failures hard to debug. Prefer only treating a 'no version found' condition as non-fatal (e.g., have parse_idiomatic_file return Ok(\"\") when no version is present in package.json, or introduce a specific error type/marker and only continue for that case while propagating other errors).
| let version = match plugin.parse_idiomatic_file(&path).await { | |
| Ok(v) => v, | |
| Err(e) => { | |
| trace!("skipping {} for {}: {e}", plugin.id(), path.display()); | |
| continue; | |
| } | |
| }; | |
| let version = plugin.parse_idiomatic_file(&path).await?; | |
| if version.trim().is_empty() { | |
| trace!( | |
| "no idiomatic version found for {} in {}", | |
| plugin.id(), | |
| path.display() | |
| ); | |
| continue; | |
| } |
| let pm_field = self.package_manager.as_deref()?; | ||
| let (name, rest) = pm_field.split_once('@')?; | ||
| if name != tool_name { | ||
| return None; | ||
| } | ||
| // Strip +sha... suffix | ||
| let version = rest.split('+').next().unwrap_or(rest).trim(); |
There was a problem hiding this comment.
Parsing packageManager with split_once('@') breaks for scoped package manager identifiers (the string can start with @..., and there may be multiple @ characters). Use a last-split approach (e.g., split on the final @) so scoped names like @yarnpkg/cli-dist@4.1.0 are handled correctly, and then compare the full name against tool_name.
| } | ||
|
|
||
| // Upper-bound operators don't indicate a version to install | ||
| if input.starts_with('<') || input.starts_with("<=") { |
There was a problem hiding this comment.
input.starts_with('<') already matches the <= case, making the || input.starts_with(\"<=\") redundant and slightly confusing. Consider simplifying this conditional (or checking \"<=\" first if the intent is to be explicit) to make the logic clearer.
| if input.starts_with('<') || input.starts_with("<=") { | |
| if input.starts_with('<') { |
Adds a `--dry-run-code` flag to install, upgrade, prune, uninstall,
and use commands. When used, it behaves like `--dry-run` but exits
with code 1 when there are pending operations, enabling scripting
patterns like:
if ! mise install --dry-run-code --quiet; then
echo "Installing stuff using mise"
mise install
fi
Closes #8060
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces two significant features. First, it adds a --dry-run-code flag to the install, upgrade, prune, uninstall, and use commands. This new flag enhances scripting capabilities by providing a non-zero exit code when there are pending operations, while still performing a dry run. The implementation is consistent and well-integrated across all affected commands.
Second, it adds support for using package.json as an idiomatic version file for node, bun, and various JavaScript package managers. This includes parsing devEngines and packageManager fields to determine tool versions. The logic for parsing these files has also been made more resilient, which is a great improvement to avoid cascading failures.
The changes are accompanied by thorough E2E tests that cover the new functionality. Overall, the code quality is excellent, and the new features are valuable additions. I have no specific recommendations for changes.
- upgrade: exit 1 when outdated, exit 0 when up to date - prune: exit 1 when prunable, exit 0 when nothing to prune - uninstall: exit 1 when tools to uninstall, exit 0 when not installed - install (no args): exit 1 when config has missing tools, exit 0 when all installed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1b8b045 to
e8e45a4
Compare
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.7 x -- echo |
21.4 ± 0.5 | 20.2 | 23.1 | 1.00 |
mise x -- echo |
22.5 ± 0.8 | 21.1 | 28.6 | 1.05 ± 0.04 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.7 env |
20.7 ± 0.7 | 19.7 | 28.5 | 1.00 |
mise env |
21.5 ± 0.5 | 20.7 | 23.5 | 1.04 ± 0.04 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.7 hook-env |
21.2 ± 0.3 | 20.4 | 22.7 | 1.00 |
mise hook-env |
22.4 ± 0.7 | 21.4 | 25.7 | 1.06 ± 0.03 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.7 ls |
19.6 ± 0.6 | 18.5 | 25.7 | 1.00 |
mise ls |
20.3 ± 0.4 | 19.5 | 21.8 | 1.04 ± 0.04 |
xtasks/test/perf
| Command | mise-2026.2.7 | mise | Variance |
|---|---|---|---|
| install (cached) | 120ms | 119ms | +0% |
| ls (cached) | 72ms | 73ms | -1% |
| bin-paths (cached) | 75ms | 78ms | -3% |
| task-ls (cached) | 540ms | 542ms | +0% |
|
bugbot run |
### 🚀 Features - **(node)** support package.json as idiomatic version file by @jdx in [#8059](#8059) - **(ruby)** graduate precompiled ruby from experimental (gradual rollout) by @jdx in [#8052](#8052) - add --dry-run-code flag to exit non-zero when there is work to do by @jdx in [#8063](#8063) ### 🐛 Bug Fixes - **(core)** respect MISE_ARCH override in bun and erlang plugins by @jdx in [#8062](#8062) - **(hooks)** resolve 12 community-reported hooks issues by @jdx in [#8058](#8058) - accept key=value format in set/add subcommands by @jdx in [#8053](#8053) ### 📚 Documentation - bump action versions in GitHub Actions examples by @muzimuzhi in [#8065](#8065) - add opengraph meta tags by @jdx in [#8066](#8066) ### 📦️ Dependency Updates - upgrade toml to 0.9 and toml_edit to 0.24 (TOML 1.1) by @jdx in [#8057](#8057) ### 📦 Registry - add quicktype (npm:quicktype) by @zdunecki in [#8054](#8054) - use inline table for test definitions by @jdx in [#8056](#8056)
…do (jdx#8063) ## Summary - Adds a `--dry-run-code` flag to `install`, `upgrade`, `prune`, `uninstall`, and `use` commands - When used, behaves like `--dry-run` (shows what would happen without making changes) but additionally exits with code 1 when there are pending operations - Exits 0 when everything is already up-to-date - Enables scripting patterns like: ```bash if ! mise install --dry-run-code --quiet; then echo "Installing stuff using mise" mise install fi ``` Closes jdx#8060 ## Test plan - [x] `mise run test:e2e test_install_dry_run` — install dry-run tests including `--dry-run-code` - [x] `mise run test:e2e test_use_dry_run` — use dry-run tests including `--dry-run-code` - [x] `cargo test` — all 461 unit tests pass (including CLI flag sort test) - [x] `mise run lint-fix` — all linters pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes CLI exit-code behavior for multiple core commands, which may affect automation that relies on previous dry-run semantics, though the changes are localized and covered by updated e2e tests. > > **Overview** > Adds a new `--dry-run-code` flag to `install`, `use`, `upgrade`, `prune`, and `uninstall` that behaves like `--dry-run` but exits with status `1` when there is work to do (and `0` when no changes are needed). > > Refactors these commands to treat `--dry-run` and `--dry-run-code` consistently via shared `is_dry_run()` checks, skipping side effects (config writes, deletions, shim rebuilds) while optionally computing whether any operations would occur before deciding to exit non-zero. > > Updates generated CLI docs/manpages, the `mise.usage.kdl` spec, Fig completions, and expands e2e coverage (including a new `assert_fail_contains`) to validate the new exit-code semantics. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 2a305c7. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### 🚀 Features - **(node)** support package.json as idiomatic version file by @jdx in [jdx#8059](jdx#8059) - **(ruby)** graduate precompiled ruby from experimental (gradual rollout) by @jdx in [jdx#8052](jdx#8052) - add --dry-run-code flag to exit non-zero when there is work to do by @jdx in [jdx#8063](jdx#8063) ### 🐛 Bug Fixes - **(core)** respect MISE_ARCH override in bun and erlang plugins by @jdx in [jdx#8062](jdx#8062) - **(hooks)** resolve 12 community-reported hooks issues by @jdx in [jdx#8058](jdx#8058) - accept key=value format in set/add subcommands by @jdx in [jdx#8053](jdx#8053) ### 📚 Documentation - bump action versions in GitHub Actions examples by @muzimuzhi in [jdx#8065](jdx#8065) - add opengraph meta tags by @jdx in [jdx#8066](jdx#8066) ### 📦️ Dependency Updates - upgrade toml to 0.9 and toml_edit to 0.24 (TOML 1.1) by @jdx in [jdx#8057](jdx#8057) ### 📦 Registry - add quicktype (npm:quicktype) by @zdunecki in [jdx#8054](jdx#8054) - use inline table for test definitions by @jdx in [jdx#8056](jdx#8056)
Summary
--dry-run-codeflag toinstall,upgrade,prune,uninstall, andusecommands--dry-run(shows what would happen without making changes) but additionally exits with code 1 when there are pending operationsCloses #8060
Test plan
mise run test:e2e test_install_dry_run— install dry-run tests including--dry-run-codemise run test:e2e test_use_dry_run— use dry-run tests including--dry-run-codecargo test— all 461 unit tests pass (including CLI flag sort test)mise run lint-fix— all linters pass🤖 Generated with Claude Code
Note
Medium Risk
Changes CLI exit-code behavior for multiple core commands, which may affect automation that relies on previous dry-run semantics, though the changes are localized and covered by updated e2e tests.
Overview
Adds a new
--dry-run-codeflag toinstall,use,upgrade,prune, anduninstallthat behaves like--dry-runbut exits with status1when there is work to do (and0when no changes are needed).Refactors these commands to treat
--dry-runand--dry-run-codeconsistently via sharedis_dry_run()checks, skipping side effects (config writes, deletions, shim rebuilds) while optionally computing whether any operations would occur before deciding to exit non-zero.Updates generated CLI docs/manpages, the
mise.usage.kdlspec, Fig completions, and expands e2e coverage (including a newassert_fail_contains) to validate the new exit-code semantics.Written by Cursor Bugbot for commit 2a305c7. This will update automatically on new commits. Configure here.