feat: add --outdated flag to mise plugins ls#8360
Conversation
Add a new `--outdated` flag that checks remote refs via `git ls-remote` and only displays plugins where the local SHA differs from the remote. Shows a table with plugin name, URL, ref, local SHA, and remote SHA. Closes #8319 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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 introduces a significant enhancement to the 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
|
Greptile SummaryAdded
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant PluginsLs
participant Plugin
participant Git
participant Remote
User->>PluginsLs: mise plugins ls --outdated
PluginsLs->>PluginsLs: Filter installed plugins
loop For each installed plugin
PluginsLs->>Plugin: current_sha_short()
Plugin->>Git: current_sha_short()
Git-->>Plugin: local SHA (7 chars)
Plugin-->>PluginsLs: local SHA
PluginsLs->>Plugin: remote_sha()
Plugin->>Git: current_branch()
Git-->>Plugin: branch name
Plugin->>Git: remote_sha(branch)
Git->>Remote: git ls-remote origin <branch>
Remote-->>Git: remote SHA (40 chars)
Git-->>Plugin: remote SHA
Plugin-->>PluginsLs: remote SHA
alt Remote SHA starts with local SHA
PluginsLs->>PluginsLs: Filter out (up to date)
else Different SHAs
PluginsLs->>Plugin: get_remote_url()
Plugin-->>PluginsLs: URL
PluginsLs->>Plugin: current_abbrev_ref()
Plugin-->>PluginsLs: ref
PluginsLs->>PluginsLs: Add to outdated list
end
end
alt No outdated plugins
PluginsLs->>User: "All plugins are up to date"
else Has outdated plugins
PluginsLs->>User: Display table with plugin info
end
Last reviewed commit: d50cf53 |
src/cli/plugins/ls.rs
Outdated
| url: remote_url.unwrap_or_default(), | ||
| ref_: abbrev_ref.unwrap_or_default(), | ||
| local: local_sha, | ||
| remote: remote_sha[..7].to_string(), |
There was a problem hiding this comment.
Code Review
This pull request introduces a new --outdated flag to mise plugins ls for checking outdated plugins. The implementation correctly uses git ls-remote to compare local and remote SHAs. The changes are well-structured across the plugin system.
My review includes two main points:
- The e2e test for this new feature could be improved by adding a case that verifies the detection of an outdated plugin.
- The implementation for checking outdated plugins performs blocking I/O sequentially in an async context. I've suggested a refactoring to parallelize these checks using
tokio::task::spawn_blockingfor better performance and to avoid blocking the async runtime.
Overall, this is a great feature addition. Addressing these points will make it more robust and performant.
| if self.outdated { | ||
| let data = plugins | ||
| .into_iter() | ||
| .filter_map(|(name, p)| { | ||
| if !p.is_installed() { | ||
| return None; | ||
| } | ||
| let local_sha = p.current_sha_short().unwrap_or_else(|e| { | ||
| warn!("{name}: {e:?}"); | ||
| None | ||
| })?; | ||
| let remote_sha = p.remote_sha().unwrap_or_else(|e| { | ||
| warn!("{name}: {e:?}"); | ||
| None | ||
| })?; | ||
| if remote_sha.starts_with(&local_sha) { | ||
| return None; | ||
| } | ||
| let remote_url = p.get_remote_url().unwrap_or_else(|e| { | ||
| warn!("{name}: {e:?}"); | ||
| None | ||
| }); | ||
| let abbrev_ref = p.current_abbrev_ref().unwrap_or_else(|e| { | ||
| warn!("{name}: {e:?}"); | ||
| None | ||
| }); | ||
| Some(OutdatedRow { | ||
| plugin: name, | ||
| url: remote_url.unwrap_or_default(), | ||
| ref_: abbrev_ref.unwrap_or_default(), | ||
| local: local_sha, | ||
| remote: remote_sha[..7].to_string(), | ||
| }) | ||
| }) | ||
| .collect::<Vec<_>>(); | ||
| if data.is_empty() { | ||
| info!("All plugins are up to date"); | ||
| } else { | ||
| let mut table = Table::new(data); | ||
| table::default_style(&mut table, false); | ||
| miseprintln!("{table}"); | ||
| } |
There was a problem hiding this comment.
The current implementation for checking outdated plugins is sequential and uses blocking I/O within an async function. This can be inefficient, especially with many plugins, and it blocks the Tokio runtime worker thread. To improve performance and follow async best practices, these checks should be parallelized and blocking calls should be moved to a blocking thread pool using tokio::task::spawn_blocking.
if self.outdated {
let tasks: Vec<_> = plugins
.into_iter()
.map(|(name, p)| {
tokio::spawn(async move {
if !p.is_installed() {
return None;
}
let p_task = p.clone();
let name_task = name.clone();
tokio::task::spawn_blocking(move || {
let local_sha = p_task.current_sha_short().unwrap_or_else(|e| {
warn!("{}: {e:?}", name_task);
None
})?;
let remote_sha = p_task.remote_sha().unwrap_or_else(|e| {
warn!("{}: {e:?}", name_task);
None
})?;
if remote_sha.starts_with(&local_sha) {
return None;
}
let remote_url = p_task.get_remote_url().unwrap_or_else(|e| {
warn!("{}: {e:?}", name_task);
None
});
let abbrev_ref = p_task.current_abbrev_ref().unwrap_or_else(|e| {
warn!("{}: {e:?}", name_task);
None
});
Some(OutdatedRow {
plugin: name_task,
url: remote_url.unwrap_or_default(),
ref_: abbrev_ref.unwrap_or_default(),
local: local_sha,
remote: remote_sha[..7].to_string(),
})
})
.await
.ok()
.flatten()
})
})
.collect();
let data = futures::future::join_all(tasks)
.await
.into_iter()
.filter_map(|r| r.ok().flatten())
.collect::<Vec<_>>();
if data.is_empty() {
info!("All plugins are up to date");
} else {
let mut table = Table::new(data);
table::default_style(&mut table, false);
miseprintln!("{table}");
}| #!/usr/bin/env bash | ||
|
|
||
| mise plugin install tiny https://github.com/mise-plugins/rtx-tiny.git | ||
|
|
||
| # freshly installed plugin should not appear as outdated | ||
| assert_not_contains "mise plugins ls --outdated" "tiny" | ||
|
|
||
| # verify --outdated flag works without error | ||
| assert_succeed "mise plugins ls --outdated" | ||
|
|
||
| # verify -o short flag works | ||
| assert_succeed "mise plugins ls -o" |
There was a problem hiding this comment.
The test only covers the case where no plugins are outdated. It would be more robust to also test the case where a plugin is outdated. This could be done by installing a plugin, then using git reset in the plugin's directory to move it to an older commit, and then asserting that mise plugins ls --outdated lists the plugin.
#!/usr/bin/env bash
# setup
mise plugin install tiny https://github.com/mise-plugins/rtx-tiny.git
# freshly installed plugin should not appear as outdated
assert_not_contains "mise plugins ls --outdated" "tiny"
# verify --outdated flag works without error
assert_succeed "mise plugins ls --outdated"
# verify -o short flag works
assert_succeed "mise plugins ls -o"
# verify outdated detection works
(cd "$MISE_DATA_DIR/plugins/tiny" && git reset --hard HEAD~1)
assert_contains "mise plugins ls --outdated" "tiny"
- Reorder struct fields so short flags are alphabetical (-a, -c, -o, -u) fixing the test_subcommands_are_sorted test - Use .chars().take(7).collect() instead of [..7] to avoid potential panic - Add outdated detection test case using git reset HEAD~1 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use parallel::parallel with spawn_blocking to check all plugins concurrently instead of sequentially, improving performance when many plugins are installed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.21 x -- echo |
27.6 ± 0.4 | 27.0 | 31.9 | 1.00 |
mise x -- echo |
28.1 ± 0.9 | 27.0 | 35.3 | 1.02 ± 0.04 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.21 env |
27.7 ± 0.5 | 26.6 | 30.1 | 1.00 |
mise env |
28.0 ± 1.4 | 26.8 | 56.9 | 1.01 ± 0.05 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.21 hook-env |
28.1 ± 0.5 | 27.1 | 30.7 | 1.01 ± 0.03 |
mise hook-env |
27.8 ± 0.7 | 27.0 | 39.2 | 1.00 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.21 ls |
25.9 ± 0.3 | 25.4 | 27.2 | 1.00 ± 0.01 |
mise ls |
25.9 ± 0.3 | 25.3 | 27.6 | 1.00 |
xtasks/test/perf
| Command | mise-2026.2.21 | mise | Variance |
|---|---|---|---|
| install (cached) | 169ms | 167ms | +1% |
| ls (cached) | 95ms | 94ms | +1% |
| bin-paths (cached) | 101ms | 100ms | +1% |
| task-ls (cached) | 836ms | 840ms | +0% |
--outdated flag to mise plugins ls--outdated flag to mise plugins ls
### 🚀 Features - add `--outdated` flag to `mise plugins ls` by @jdx in [#8360](#8360) ### 🐛 Bug Fixes - **(github)** resolve rename_exe search dir for archives with bin/ subdirectory by @jdx in [#8358](#8358) - **(install)** skip tools=true env directives during backend installation by @jdx in [#8356](#8356) - **(ruby)** resolve correct Windows checksums in lockfile by @jdx in [#8357](#8357) ### 📦 Registry - switch terradozer backend to github fork by @chenrui333 in [#8365](#8365) ### Chore - **(release)** fix duplicated version prefix in release title by @jdx in [#8359](#8359) ### New Contributors - @chenrui333 made their first contribution in [#8365](#8365) ## 📦 Aqua Registry Updates #### New Packages (1) - [`huseyinbabal/taws`](https://github.com/huseyinbabal/taws) #### Updated Packages (2) - [`block/goose`](https://github.com/block/goose) - [`pre-commit/pre-commit`](https://github.com/pre-commit/pre-commit)
## Summary - Adds `--outdated` (`-o`) flag to `mise plugins ls` that checks remote refs via `git ls-remote` and only displays plugins where the local SHA differs from the remote - Shows a table with plugin name, URL, ref, local SHA, and remote SHA columns - Prints "All plugins are up to date" when no plugins are outdated Closes jdx#8319 ## Test plan - [x] `mise run build` compiles cleanly - [x] `mise run lint-fix` passes - [x] `mise plugins ls --outdated` works with installed plugins - [x] `mise plugins ls -o` short flag works - [x] e2e test added and passing (`test_plugins_outdated`) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Adds remote git lookups to a common CLI command path, which can introduce latency, network-related failures, and output behavior changes despite being behind a new flag. > > **Overview** > Adds `-o/--outdated` to `mise plugins ls` to show only plugins with available updates by comparing local vs remote git SHAs and printing a table of outdated entries (or logging that everything is up to date). > > Implements plugin remote SHA fetching via a new `Git::remote_sha()` helper and a `Plugin::remote_sha()` hook (implemented for asdf/vfox plugins), runs checks in parallel, and updates generated docs/completions/manpage plus an e2e test covering the new flag. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit b61ac6e. 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 - add `--outdated` flag to `mise plugins ls` by @jdx in [jdx#8360](jdx#8360) ### 🐛 Bug Fixes - **(github)** resolve rename_exe search dir for archives with bin/ subdirectory by @jdx in [jdx#8358](jdx#8358) - **(install)** skip tools=true env directives during backend installation by @jdx in [jdx#8356](jdx#8356) - **(ruby)** resolve correct Windows checksums in lockfile by @jdx in [jdx#8357](jdx#8357) ### 📦 Registry - switch terradozer backend to github fork by @chenrui333 in [jdx#8365](jdx#8365) ### Chore - **(release)** fix duplicated version prefix in release title by @jdx in [jdx#8359](jdx#8359) ### New Contributors - @chenrui333 made their first contribution in [jdx#8365](jdx#8365) ## 📦 Aqua Registry Updates #### New Packages (1) - [`huseyinbabal/taws`](https://github.com/huseyinbabal/taws) #### Updated Packages (2) - [`block/goose`](https://github.com/block/goose) - [`pre-commit/pre-commit`](https://github.com/pre-commit/pre-commit)
Summary
--outdated(-o) flag tomise plugins lsthat checks remote refs viagit ls-remoteand only displays plugins where the local SHA differs from the remoteCloses #8319
Test plan
mise run buildcompiles cleanlymise run lint-fixpassesmise plugins ls --outdatedworks with installed pluginsmise plugins ls -oshort flag workstest_plugins_outdated)🤖 Generated with Claude Code
Note
Medium Risk
Adds remote git lookups to a common CLI command path, which can introduce latency, network-related failures, and output behavior changes despite being behind a new flag.
Overview
Adds
-o/--outdatedtomise plugins lsto show only plugins with available updates by comparing local vs remote git SHAs and printing a table of outdated entries (or logging that everything is up to date).Implements plugin remote SHA fetching via a new
Git::remote_sha()helper and aPlugin::remote_sha()hook (implemented for asdf/vfox plugins), runs checks in parallel, and updates generated docs/completions/manpage plus an e2e test covering the new flag.Written by Cursor Bugbot for commit b61ac6e. This will update automatically on new commits. Configure here.