Skip to content

feat(prepare): add git-submodule built-in provider#8407

Merged
jdx merged 3 commits intomainfrom
feat/prepare-git-submodule
Mar 2, 2026
Merged

feat(prepare): add git-submodule built-in provider#8407
jdx merged 3 commits intomainfrom
feat/prepare-git-submodule

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Mar 2, 2026

Summary

  • Add a built-in git-submodule prepare provider that detects .gitmodules and runs git submodule update --init --recursive when submodule directories are stale
  • Parses path = ... lines from .gitmodules to determine output directories
  • Provider is only applicable when .gitmodules exists and is non-empty

Test plan

  • E2E test: provider detected when .gitmodules has submodule entries
  • E2E test: provider lists .gitmodules as source and submodule paths as outputs
  • E2E test: empty .gitmodules is not applicable
  • All existing prepare e2e tests pass

🤖 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 .gitmodules parsing or output detection could cause unnecessary or missed prepares.

Overview
Adds a new built-in git-submodule prepare provider that becomes applicable when a non-empty .gitmodules file exists, treating .gitmodules as the source and parsed submodule path = ... entries as outputs.

When stale, the provider runs git submodule update --init --recursive by default (or an overridden run command), and it’s wired into provider discovery/detection via BUILTIN_PROVIDERS, the prepare engine, and provider exports. E2E coverage is extended to assert listing of sources/outputs and that an empty .gitmodules does not enable the provider.

Written by Cursor Bugbot for commit 845fe91. This will update automatically on new commits. Configure here.

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>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, 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 git-submodule prepare provider to the system. This enhancement automates the management of Git submodules within projects, ensuring that they are correctly initialized and updated based on the .gitmodules configuration. It streamlines the development workflow by handling a common dependency management task without manual intervention.

Highlights

  • New git-submodule prepare provider: Introduced a built-in git-submodule prepare provider that automatically detects .gitmodules files.
  • Automatic submodule updates: The provider runs git submodule update --init --recursive when submodule directories are detected as stale.
  • Submodule path parsing: Implemented logic to parse path = ... lines from .gitmodules to identify and track submodule output directories.
  • Conditional applicability: The provider is only active if a non-empty .gitmodules file exists in the project root.
  • E2E test coverage: Added comprehensive end-to-end tests to verify the correct detection, source/output listing, and applicability of the new provider.
Changelog
  • e2e/cli/test_prepare
    • Added new E2E tests to validate the functionality of the git-submodule prepare provider, including its detection, source/output identification, and handling of empty .gitmodules files.
  • src/prepare/engine.rs
    • Imported the GitSubmodulePrepareProvider.
    • Integrated the GitSubmodulePrepareProvider into the PrepareEngine's get_provider method, allowing it to be instantiated when requested.
  • src/prepare/mod.rs
    • Added the GitSubmodulePrepareProvider to the list of providers checked by detect_applicable_providers, enabling automatic detection.
  • src/prepare/providers/git_submodule.rs
    • Created a new file defining the GitSubmodulePrepareProvider struct.
    • Implemented the PrepareProvider trait for GitSubmodulePrepareProvider, including methods for sources, outputs, prepare_command, and is_applicable.
    • Added logic to parse .gitmodules for submodule paths and to construct the git submodule update --init --recursive command.
  • src/prepare/providers/mod.rs
    • Declared the git_submodule module.
    • Exported GitSubmodulePrepareProvider for external use.
  • src/prepare/rule.rs
    • Added "git-submodule" to the BUILTIN_PROVIDERS constant array.
Activity
  • The pull request includes new E2E tests specifically designed to validate the git-submodule provider's behavior, which have all passed.
  • The changes were generated with the assistance of Claude Code.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
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 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.

Comment on lines +24 to +46
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()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

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/path

A 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-apps
Copy link

greptile-apps bot commented Mar 2, 2026

Greptile Summary

This PR introduces a new built-in git-submodule provider for the prepare system that automatically detects and initializes git submodules when their directories are stale.

Key changes:

  • Implemented GitSubmodulePrepareProvider that parses .gitmodules to extract submodule paths
  • Provider runs git submodule update --init --recursive when submodule directories need updating
  • Properly integrated into the prepare system (engine, detection, and built-in providers list)
  • Added comprehensive E2E tests covering detection, source/output listing, and edge cases

Implementation notes:

  • The .gitmodules parsing correctly handles standard formatting with whitespace around = signs
  • Provider is only applicable when .gitmodules exists and is non-empty
  • Follows the same architectural pattern as existing providers (npm, composer, etc.)
  • Allows users to override the default command via custom run configuration

Confidence Score: 5/5

  • This PR is safe to merge with no identified issues
  • The implementation is clean, well-tested, and follows existing patterns. The parsing logic correctly handles standard .gitmodules formats, integration is complete across all required files, and E2E tests cover the main functionality including edge cases. No bugs or security issues identified.
  • No files require special attention

Important Files Changed

Filename Overview
src/prepare/providers/git_submodule.rs New git-submodule provider implementation with proper parsing of .gitmodules paths and command construction
src/prepare/providers/mod.rs Added export for GitSubmodulePrepareProvider
src/prepare/engine.rs Integrated git-submodule provider into engine's provider instantiation logic
src/prepare/mod.rs Added git-submodule to provider detection checks
src/prepare/rule.rs Added git-submodule to BUILTIN_PROVIDERS list with improved formatting
e2e/cli/test_prepare Added comprehensive tests for git-submodule provider covering detection, source/output listing, and empty file handling

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]
Loading

Last reviewed commit: e2a28ab

@jdx jdx merged commit f7d3c60 into main Mar 2, 2026
33 checks passed
@jdx jdx deleted the feat/prepare-git-submodule branch March 2, 2026 01:58
@github-actions
Copy link

github-actions bot commented Mar 2, 2026

Hyperfine Performance

mise x -- echo

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%

jdx added a commit that referenced this pull request Mar 2, 2026
…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>
jdx pushed a commit that referenced this pull request Mar 2, 2026
### 🚀 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)
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.

1 participant