feat(prepare): add git-submodule built-in provider#8407
Conversation
Add a built-in prepare provider for git submodules that detects .gitmodules and runs `git submodule update --init --recursive` when submodule directories are stale. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello, 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 new built-in 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.
Code Review
This pull request introduces a new built-in git-submodule prepare provider, which is a great addition for projects using git submodules. The implementation is straightforward and well-integrated with the existing provider system. The end-to-end tests are also a good inclusion, covering the basic success and failure cases.
My main feedback is on the parsing of the .gitmodules file, which is currently a bit too simplistic and could lead to incorrect submodule path detection in some cases. I've left a specific comment with a suggestion to make it more robust.
| fn submodule_paths(&self) -> Vec<PathBuf> { | ||
| let gitmodules = self.base.project_root.join(".gitmodules"); | ||
| let Ok(content) = std::fs::read_to_string(&gitmodules) else { | ||
| return vec![]; | ||
| }; | ||
|
|
||
| content | ||
| .lines() | ||
| .filter_map(|line| { | ||
| let line = line.trim(); | ||
| if let Some(value) = line.strip_prefix("path") { | ||
| let value = value.trim_start(); | ||
| if let Some(value) = value.strip_prefix('=') { | ||
| Some(self.base.project_root.join(value.trim())) | ||
| } else { | ||
| None | ||
| } | ||
| } else { | ||
| None | ||
| } | ||
| }) | ||
| .collect() | ||
| } |
There was a problem hiding this comment.
The current implementation for parsing .gitmodules is too simplistic and may lead to incorrect behavior. It scans for any line starting with path = without considering the INI-style sections (e.g., [submodule "name"]) or comments. This could cause it to pick up paths from other sections or from comments.
For example, it would incorrectly parse path from:
# A comment that happens to mention a path
# path = /some/commented/out/path
[not-a-submodule]
path = /some/other/pathA more robust approach would be to use a proper git-config file parser. The gix-config crate from the gitoxide project is well-suited for this. If adding a new dependency is acceptable, I'd recommend that approach.
If you prefer to avoid a new dependency, the parser can be made more robust manually by handling sections and comments. Here is a suggestion for that:
/// Parse submodule paths from .gitmodules file
fn submodule_paths(&self) -> Vec<PathBuf> {
let gitmodules = self.base.project_root.join(".gitmodules");
let Ok(content) = std::fs::read_to_string(&gitmodules) else {
return vec![];
};
let mut in_submodule_section = false;
content
.lines()
.filter_map(|line| {
let line = line.trim();
if line.starts_with('#') || line.starts_with(';') {
return None; // Ignore comments
}
if line.starts_with("[submodule") {
in_submodule_section = true;
return None;
}
if line.starts_with('[') {
in_submodule_section = false;
return None;
}
if !in_submodule_section {
return None;
}
if let Some(value) = line.strip_prefix("path") {
let value = value.trim_start();
if let Some(value) = value.strip_prefix('=') {
Some(self.base.project_root.join(value.trim()))
} else {
None
}
} else {
None
}
})
.collect()
}
Greptile SummaryThis PR introduces a new built-in Key changes:
Implementation notes:
Confidence Score: 5/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[PrepareEngine detects providers] --> B{.gitmodules exists?}
B -->|No| C[Skip git-submodule provider]
B -->|Yes| D{.gitmodules size > 0?}
D -->|No| C
D -->|Yes| E[Provider is applicable]
E --> F[Parse .gitmodules for submodule paths]
F --> G[Extract lines with 'path = ...']
G --> H[Return submodule directories as outputs]
H --> I{Submodule dirs stale?}
I -->|Yes| J[Run: git submodule update --init --recursive]
I -->|No| K[Skip execution]
J --> L[Submodules initialized]
Last reviewed commit: e2a28ab |
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.24 x -- echo |
29.2 ± 0.7 | 28.0 | 35.1 | 1.19 ± 0.05 |
mise x -- echo |
24.5 ± 0.9 | 22.8 | 32.7 | 1.00 |
✅ Performance improvement for x -- echo is 19% |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.24 env |
28.5 ± 0.7 | 27.0 | 31.0 | 1.20 ± 0.07 |
mise env |
23.8 ± 1.3 | 22.3 | 47.3 | 1.00 |
✅ Performance improvement for env is 20% |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.24 hook-env |
29.2 ± 1.1 | 27.7 | 40.2 | 1.18 ± 0.06 |
mise hook-env |
24.7 ± 0.8 | 23.0 | 26.8 | 1.00 |
✅ Performance improvement for hook-env is 18% |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.24 ls |
23.4 ± 0.6 | 22.1 | 26.4 | 1.07 ± 0.04 |
mise ls |
21.9 ± 0.6 | 20.8 | 27.9 | 1.00 |
xtasks/test/perf
| Command | mise-2026.2.24 | mise | Variance |
|---|---|---|---|
| install (cached) | 162ms | 148ms | +9% |
| ls (cached) | 89ms | ✅ 80ms | +11% |
| bin-paths (cached) | 98ms | ✅ 86ms | +13% |
| task-ls (cached) | 868ms | 845ms | +2% |
✅ Performance improvement: ls cached is 11%
✅ Performance improvement: bin-paths cached is 13%
…ror handling (#8412) ## Summary - Parse `.gitmodules` with INI section awareness: only extract `path` values from `[submodule "..."]` sections, skip comments (`#`, `;`) and non-submodule sections. Previously the parser would pick up `path =` lines from any section or even comments. - Change `check_staleness()` `unwrap_or(true)` to `unwrap_or(false)` so freshness check errors default to fresh rather than stale, preventing spurious warnings. Follow-up to #8407 which was merged before these fixes landed. ## Test plan - [ ] Existing `test_prepare` e2e tests pass - [ ] `mise run build` compiles cleanly 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk, localized change to `.gitmodules` parsing to avoid false-positive submodule paths; main impact is potentially ignoring non-standard/malformed entries that previously (incorrectly) matched. > > **Overview** > Tightens `.gitmodules` parsing in `GitSubmodulePrepareProvider::submodule_paths()` to be INI-aware: it now skips comment lines, tracks when it is inside a `[submodule "..."]` section, and only extracts `path = ...` entries from those sections (ignoring `path` keys in other sections). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit c011531. 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>
### 🚀 Features - **(hooks)** add task references to hooks and watch_files by @jdx in [#8400](#8400) - **(prepare)** add git-submodule built-in provider by @jdx in [#8407](#8407) - **(prepare)** add human-readable stale reasons to prepare output by @jdx in [#8408](#8408) - **(prepare)** add dependency ordering to prepare steps by @jdx in [#8401](#8401) - **(prepare)** add --explain flag for provider diagnostics by @jdx in [#8409](#8409) - **(prepare)** add per-provider timeout support by @jdx in [#8405](#8405) - **(prepare)** add blake3 content-hash freshness checking by @jdx in [#8404](#8404) - **(tasks)** monorepo vars and per-task vars by @halms in [#8248](#8248) ### 🐛 Bug Fixes - **(aqua)** restore bin_paths disk cache with fresh_file invalidation by @jdx in [#8398](#8398) - **(idiomatic)** use generic parser for idiomatic files by @risu729 in [#8171](#8171) - **(install)** apply precompiled options to all platforms in lockfile by @jdx in [#8396](#8396) - **(install)** normalize "v" prefix when matching lockfile versions by @jdx in [#8413](#8413) - **(prepare)** improve git submodule parser and fix check_staleness error handling by @jdx in [#8412](#8412) - **(python)** respect precompiled settings in lock file generation by @jdx in [#8399](#8399) - **(python)** clarify uv_venv_auto docs + prevent uv shim recursion in venv creation by @halms in [#8402](#8402) - **(task)** remove deprecated `# mise` task header syntax by @jdx in [#8403](#8403) - **(vfox)** avoid eager metadata loading during config file detection by @jdx in [#8397](#8397) - clarify GitHub attestations to be artifact ones by @scop in [#8394](#8394) - ignore comments in idiomatic version files by @iloveitaly in [#7682](#7682) ### 🚜 Refactor - unify archive detection by @risu729 in [#8137](#8137) ### 📚 Documentation - remove duplicated docs for npm.package_manager by @risu729 in [#8414](#8414)
Summary
git-submoduleprepare provider that detects.gitmodulesand runsgit submodule update --init --recursivewhen submodule directories are stalepath = ...lines from.gitmodulesto determine output directories.gitmodulesexists and is non-emptyTest plan
.gitmoduleshas submodule entries.gitmodulesas source and submodule paths as outputs.gitmodulesis not applicable🤖 Generated with Claude Code
Note
Medium Risk
Adds a new built-in prepare provider that shells out to
git submodule update, which can modify working tree state and may behave differently across environments. Logic is small and isolated, but incorrect.gitmodulesparsing or output detection could cause unnecessary or missed prepares.Overview
Adds a new built-in
git-submoduleprepare provider that becomes applicable when a non-empty.gitmodulesfile exists, treating.gitmodulesas the source and parsed submodulepath = ...entries as outputs.When stale, the provider runs
git submodule update --init --recursiveby default (or an overriddenruncommand), and it’s wired into provider discovery/detection viaBUILTIN_PROVIDERS, the prepare engine, and provider exports. E2E coverage is extended to assert listing of sources/outputs and that an empty.gitmodulesdoes not enable the provider.Written by Cursor Bugbot for commit 845fe91. This will update automatically on new commits. Configure here.