Skip to content

Conversation

@MeteorsLiu
Copy link
Collaborator

Add module loading functionality with MVS-based dependency resolution. Includes the Load function for loading packages and resolving their dependencies, version handling, and test data for various module loading scenarios.

@gemini-code-assist
Copy link

Summary of Changes

Hello @MeteorsLiu, 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 foundational module loading system for the project, enabling the dynamic retrieval and resolution of module dependencies. By integrating the MVS algorithm, it ensures that a consistent and minimal set of module versions is selected, preventing dependency conflicts. The new Load function serves as the primary entry point for this functionality, supported by robust version handling and a mechanism to update dependency manifests. The comprehensive test suite demonstrates the system's reliability across various dependency structures and error conditions.

Highlights

  • Core Module Loading Logic: Introduced a new Load function in internal/modules/load.go that handles the loading of a main module and resolves its dependencies using the Minimal Version Selection (MVS) algorithm. This function returns a list of all modules in the computed build list, including their formulas, filesystems, paths, and versions.
  • Dependency Resolution Mechanism: Implemented resolveDeps to determine module dependencies. It prioritizes dependencies specified via an OnRequire callback within a module's formula and falls back to parsing versions.json if no dependencies are found or if versions are empty. This allows for flexible dependency declaration.
  • Version Handling and Tidy Functionality: Added latestVersion to fetch the most recent version of a module from a VCS repository. The Load function also includes an Options.Tidy flag which, when true, computes minimal dependencies using mvs.Req and updates the versions.json file, ensuring a clean and optimized dependency graph.
  • Comprehensive Test Coverage: Included an extensive suite of unit tests in internal/modules/load_test.go covering various scenarios for resolveDeps and Load. These tests validate single modules, chain dependencies, diamond dependencies (MVS version selection), module caching, error handling for missing formulas or invalid versions.json, and module synchronization failures. Integration tests for real VCS repositories are also present, though currently skipped.
  • New Test Data Modules: A wide array of test data modules and their corresponding versions.json files have been added under internal/modules/testdata/load/towner/ to thoroughly test different dependency configurations and edge cases, such as modules with no dependencies, transitive dependencies, diamond dependencies, and modules designed to trigger error conditions.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • internal/modules/load.go
    • Added Module struct to represent loaded modules with associated metadata.
    • Implemented Load function for main module loading and MVS-based dependency resolution.
    • Introduced Options struct for Load function, including Tidy flag and FormulaStore.
    • Added latestVersion function to retrieve the latest module version from a VCS repository.
    • Implemented tidy function to compute minimal dependencies and update versions.json.
    • Implemented resolveDeps function to determine module dependencies, prioritizing OnRequire callback and falling back to versions.json.
    • Integrated module caching (moduleCache) for improved performance.
  • internal/modules/load_test.go
    • Added mockVCSRepo and failingSyncRepo for isolated testing of VCS interactions.
    • Added setupTestStore and loadTestFormula helper functions for test setup.
    • Implemented comprehensive unit tests for resolveDeps covering various scenarios: no OnRequire, OnRequire with/without adding deps, empty version fallback, unknown dep dropping, and transitive dependencies.
    • Implemented extensive unit tests for Load function covering single modules, chain dependencies, module field verification, formula content, error handling (no formula for version, dependency formula not found, module sync failure, broken versions.json, main module not found), module caching, build list order, and diamond dependencies.
    • Included integration tests for Load from real VCS repositories (skipped by default) and latestVersion functionality.
  • internal/modules/testdata/load/towner/altdep/1.0.0/Altdep_llar.gox
    • Added new module formula file for altdep.
  • internal/modules/testdata/load/towner/altdep/versions.json
    • Added new versions.json for altdep module, specifying a dependency on leafmod@2.0.0.
  • internal/modules/testdata/load/towner/baddep/versions.json
    • Added new versions.json for baddep module (no dependencies).
  • internal/modules/testdata/load/towner/brokenver/1.0.0/Brokenver_llar.gox
    • Added new module formula file for brokenver.
  • internal/modules/testdata/load/towner/brokenver/versions.json
    • Added new versions.json file with invalid JSON for testing error handling.
  • internal/modules/testdata/load/towner/depbad/1.0.0/Depbad_llar.gox
    • Added new module formula file for depbad.
  • internal/modules/testdata/load/towner/depbad/versions.json
    • Added new versions.json for depbad module, specifying a dependency on baddep@1.0.0.
  • internal/modules/testdata/load/towner/depmod/1.0.0/Depmod_llar.gox
    • Added new module formula file for depmod.
  • internal/modules/testdata/load/towner/depmod/versions.json
    • Added new versions.json for depmod module, specifying a dependency on leafmod@1.0.0.
  • internal/modules/testdata/load/towner/diamond/1.0.0/Diamond_llar.gox
    • Added new module formula file for diamond.
  • internal/modules/testdata/load/towner/diamond/versions.json
    • Added new versions.json for diamond module, specifying dependencies on depmod@1.0.0 and altdep@1.0.0.
  • internal/modules/testdata/load/towner/leafmod/1.0.0/Leafmod_llar.gox
    • Added new module formula file for leafmod version 1.0.0.
  • internal/modules/testdata/load/towner/leafmod/2.0.0/Leafmod_llar.gox
    • Added new module formula file for leafmod version 2.0.0.
  • internal/modules/testdata/load/towner/leafmod/versions.json
    • Added new versions.json for leafmod module (no dependencies).
  • internal/modules/testdata/load/towner/mainmod/1.0.0/Mainmod_llar.gox
    • Added new module formula file for mainmod.
  • internal/modules/testdata/load/towner/mainmod/versions.json
    • Added new versions.json for mainmod module, specifying a dependency on depmod@1.0.0.
  • internal/modules/testdata/load/towner/reqdrop/1.0.0/Reqdrop_llar.gox
    • Added new module formula file with an OnRequire that adds an unknown dependency for testing.
  • internal/modules/testdata/load/towner/reqdrop/versions.json
    • Added new versions.json for reqdrop module.
  • internal/modules/testdata/load/towner/reqnover/1.0.0/Reqnover_llar.gox
    • Added new module formula file with an OnRequire that adds a dependency with an empty version for testing.
  • internal/modules/testdata/load/towner/reqnover/versions.json
    • Added new versions.json for reqnover module.
  • internal/modules/testdata/load/towner/standalone/1.0.0/Standalone_llar.gox
    • Added new module formula file for standalone.
  • internal/modules/testdata/load/towner/standalone/versions.json
    • Added new versions.json for standalone module (no dependencies).
  • internal/modules/testdata/load/towner/withdeps/1.0.0/Withdeps_llar.gox
    • Added new module formula file with an OnRequire that adds a dependency.
  • internal/modules/testdata/load/towner/withdeps/versions.json
    • Added new versions.json for withdeps module.
  • internal/modules/testdata/load/towner/withreq/1.0.0/Withreq_llar.gox
    • Added new module formula file with an OnRequire that echoes but doesn't add dependencies.
  • internal/modules/testdata/load/towner/withreq/versions.json
    • Added new versions.json for withreq module.
Activity
  • This is a new feature pull request, introducing module loading and dependency resolution functionality. All changes are new additions to the codebase.
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

@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 module loading system with MVS-based dependency resolution. However, it contains critical security vulnerabilities: the tidy function incorrectly writes to the current working directory, potentially allowing arbitrary file overwrites, and os.MkdirTemp is susceptible to path traversal. Additionally, the code has issues with panics on recoverable errors, hardcoded values (like "github.com"), and improper context handling (context.TODO()). These issues need to be addressed to improve the tool's robustness and security.

@codecov
Copy link

codecov bot commented Feb 9, 2026

Codecov Report

❌ Patch coverage is 89.73214% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.51%. Comparing base (156cd0e) to head (124d114).

Files with missing lines Patch % Lines
internal/modules/load.go 88.88% 12 Missing and 11 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #94      +/-   ##
==========================================
+ Coverage   70.24%   73.51%   +3.27%     
==========================================
  Files          26       27       +1     
  Lines        1099     1314     +215     
==========================================
+ Hits          772      966     +194     
- Misses        259      270      +11     
- Partials       68       78      +10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

if err != nil {
return nil, err
}
current := depTable.Dependencies[mod.Version]
Copy link
Member

Choose a reason for hiding this comment

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

The current variable is named current, but in fact it represents the dependency list of a certain library under a specific version, so this variable name may not be very appropriate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done in 165b8a4

Comment on lines 146 to 165
func TestResolveDeps_TransitiveDeps(t *testing.T) {
// depmod has deps: leafmod@1.0.0
modFS := os.DirFS("testdata/load/towner/depmod").(fs.ReadFileFS)
frla := &formula.Formula{
ModPath: "towner/depmod",
FromVer: "1.0.0",
}
mod := module.Version{Path: "towner/depmod", Version: "1.0.0"}

deps, err := resolveDeps(mod, modFS, frla)
if err != nil {
t.Fatalf("resolveDeps failed: %v", err)
}
if len(deps) != 1 {
t.Fatalf("expected 1 dep, got %d: %v", len(deps), deps)
}
if deps[0].Path != "towner/leafmod" || deps[0].Version != "1.0.0" {
t.Errorf("dep = %v, want {towner/leafmod 1.0.0}", deps[0])
}
}
Copy link
Member

Choose a reason for hiding this comment

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

seem same with TestResolveDeps_NoOnRequire_DepsFromVersionsJson

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done in d25dd79

@luoliwoshang
Copy link
Member

/review

MeteorsLiu and others added 5 commits February 11, 2026 11:53
Add module loading functionality with MVS-based dependency resolution. Includes the Load function for loading packages and resolving their dependencies, version handling, and test data for various module loading scenarios.
Add concurrency safety to the modules package by:

- Replace map-based moduleCache with sync.Map for thread-safe access
- Introduce loadMu mutex to serialize ixgo interpreter loading to prevent
  internal race conditions during concurrent operations
- Add mutex protection (m.mu) for formula map access within formulaModule
- Refactor comparator loading using sync.OnceValues for lazy initialization
- Extract loadOrDefaultComparator function for cleaner comparator handling

These changes prevent data races when Load() and formula.LoadFS are called
concurrently, as the ixgo interpreter has internal race conditions that
require serialized access.
- Replace context.TODO() with the provided ctx parameter to respect the caller's context
- Remove hardcoded context.WithTimeout wrapper that was constraining operations to 10 minutes
- Remove unused time import
- Simplify context propagation throughout the Load function
- Add documentation for exported Module struct
- Fix Options comment: LoadPackages -> Load
- Fix FormulaStore field comment: FormulaRepo -> FormulaStore
- Fix Load function comment: LoadPackages -> Load, formulas -> modules

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: MeteorsLiu <17515813+MeteorsLiu@users.noreply.github.com>
@MeteorsLiu
Copy link
Collaborator Author

/review

@xgopilot
Copy link
Contributor

xgopilot bot commented Feb 11, 2026

Code Review Summary

The module loading and dependency resolution implementation is well-structured with good test coverage (including diamond dependency and edge case tests). The use of sync.Map + sync.OnceValues for caching is a solid pattern.

Key concerns:

  • panic in MVS comparator (load.go:119-124) — program crashes on module loading errors instead of propagating them
  • tidy() writes to CWD (load.go:257) — fileStat.Name() returns only the base name, not the original path; this is a functional bug
  • O(N^2) dep filling (load.go:191-207) — mvs.Req re-traverses the graph for each non-main module
  • Minor: unnecessary VCS repo creation, context.TODO() instead of propagating context, nested lock ordering undocumented

…commit

The moduleOf closure uses FormulaStore.ModuleFS which syncs with an
empty ref, meaning formulas are always fetched from the latest commit
on the default branch of the formula repository.

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com>
Move the comment directly onto the ModuleFS call as a simple inline
note indicating it always fetches from the latest commit.

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: MeteorsLiu <17515813+MeteorsLiu@users.noreply.github.com>
Replace O(n log n) sort + first element with O(n) slices.MaxFunc
to find the latest version tag.

Generated with [codeagent](https://github.com/qbox/codeagent)

Co-authored-by: xgopilot <noreply@goplus.org>
Co-authored-by: MeteorsLiu <17515813+MeteorsLiu@users.noreply.github.com>
if mod.Path == main.Path && mod.Version == main.Version {
deps = modules[1:]
} else {
reqs, err := mvs.Req(module.Version{mod.Path, mod.Version}, []string{}, reqs)
Copy link
Member

@luoliwoshang luoliwoshang Feb 11, 2026

Choose a reason for hiding this comment

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

There is a correctness issue in the fill the deps logic.

BuildList already represents the final version selection after MVS resolution (i.e., the global minimal version selection result). However, when rebuilding each module’s dependencies using mvs.Req(mod, ..., reqs), we are retrieving the module’s direct requirements as declared in its own module, not the versions selected in the final BuildList.

As a result, the reconstructed Deps may contain versions that differ from (and are lower than) the versions chosen by MVS in the final build list, leading to an inconsistent dependency graph.

For example:

Declared dependencies:

A -> B@1.0, C@1.0
B@1.0 -> Z@1.0
C@1.0 -> Z@1.1

Final MVS result (BuildList):

A
├─ B@1.0
├─ C@1.0
└─ Z@1.1

Reconstructed deps:

A
├─ B@1.0
│  └─ Z@1.0   <- obtained from Req(B@1.0), not aligned with BuildList
└─ C@1.0
   └─ Z@1.1

Here, B’s dependency on Z is filled as Z@1.0, even though the final selected version in BuildList is Z@1.1.

If Module.Deps is intended to represent the actual resolved dependency graph, then the dependencies returned by Req() should be normalized/projected to the versions selected in BuildList. Otherwise, the constructed graph does not accurately reflect the resolved build state.

Copy link
Member

Choose a reason for hiding this comment

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

zzy0 versions.json

{
    "path": "towner/zzyA",
    "deps": {
        "1.0.0": [
            {
                "path": "towner/zzyA",
                "version": "1.0.0"
            },
            {
                "path": "towner/zzyB",
                "version": "1.0.0"
            }
        ]
    }
}

zzyA versions.json

{
    "path": "towner/zzyA",
    "deps": {
        "1.0.0": [
            {
                "path": "towner/leafmod",
                "version": "1.0.0"
            }
        ]
    }
}

zzyB version.json

{
    "path": "towner/zzyB",
    "deps": {
        "1.0.0": [
            {
                "path": "towner/leafmod",
                "version": "2.0.0"
            }
        ]
    }
}

the buildlist is
image

and the zzyA 's depend is towner/leafmod v1.0.0 when fill the deps
image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, that's a problem. When we try to fill the dependencies of a sub-dependency, using the sub-dependency as the root and calling mvs.Reqs, we might end up with the dependency graph of the sub-dependency. However, what we actually need is a pruned version of the full dependency graph, with the sub-dependency as the root.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done in 89607c5

Copy link
Member

Choose a reason for hiding this comment

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

LGTM

MeteorsLiu and others added 7 commits February 11, 2026 17:48
…ph and add conflict tests

- Introduce an mvs.Graph in Load and sync dependency edges via graph.Require during onLoad
- Replace non-main module Deps reconstruction with BFS from the module root
- Normalize RequiredBy edges to computed versions using graph.Selected(path)
- Reverse collected results to keep the current sub-dependency output order contract
- Strengthen Diamond assertions to verify depmod/altdep both resolve to leafmod@2.0.0
- Add deep conflict fixtures and tests (deepmain/deepb/deepd/deepc/deepe) to cover convergence to deepc@1.1.0 and deepe@1.3.0
- Switch dependency assertions to ordered checks with slices.Equal
ensures
version consistency across the dependency tree.

Key changes:
- Initialize MVS graph with comparator and main dependencies
- Populate graph in onLoad callback with thread-safe locking
- Replace mvs.Req() with BFS-based RequiredBy() and Selected() traversal
- Fix latestVersion to use MaxFunc instead of SortFunc
- Add test helpers and enhance diamond dependency test coverage
…Require panics

- Add `depLoadCache` in `Load` to memoize per-module dependency results inside `onLoad`
- Return cached deps when a module is requested again (e.g. during `tidy -> mvs.Req -> BuildList`)
- Keep `graph.Require` execution to the first load of each module, preventing:
  `panic: requirements of {<mod> <ver>} have already been set`
- Preserve existing dependency resolution and graph traversal behavior

Context:
- `mvs.Graph.Require` is single-write per node; repeated calls for the same module panic by design.
- `Load` may trigger multiple MVS traversals in one call path when `Tidy` is enabled.
This commit:
- Adds new tests for CMakeLists.txt parsing via OnRequire callback
- Includes tests for successful dependency extraction and missing CMakeLists.txt handling
- Removes obsolete TestResolveDeps_TransitiveDeps test
- Consolidates TestLoad_ModuleFields into TestLoad_FormulaContent
- Adds module path sorting verification in TestLoad_ChainDeps
- Imports classfile package for Project type used in new tests
Improve code clarity by using a more descriptive variable name that
explicitly indicates the variable contains versioned dependencies from
the dependency table. This makes the code more maintainable and easier
to understand at a glance.
…text.TODO()

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: MeteorsLiu <17515813+MeteorsLiu@users.noreply.github.com>
Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: MeteorsLiu <17515813+MeteorsLiu@users.noreply.github.com>
Track visited modules to prevent cycles in BFS traversal and ensure
the dependency graph uses the MVS-selected version of each module
rather than the originally required version. This prevents missing
dependencies that may only appear in higher selected versions.

Add comprehensive test case demonstrating the scenario where MVS
selects a higher version with different dependencies than originally
required.
}
var deps classfile.ModuleDeps

frla.OnRequire(proj, &deps)
Copy link
Member

Choose a reason for hiding this comment

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

maybe we can test the Load work expect at this case

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done in 656a9f5, but there's a bug caused by git sparse-checkout, try to fix it in #98

Add TestIntegration_LoadCMakeListsDeps to verify that modules can correctly parse CMakeLists.txt files through OnRequire handlers, resolve find_package dependencies, and load them into the build list. This integration test ensures that the CMake-based dependency discovery workflow functions as expected when loading real module formulas.
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.

3 participants