[18.0] [ADD] odoo_project_dependency_resolver - #157
Open
lmignon wants to merge 15 commits into
Open
Conversation
The logic mapping a module name to a project module was only reachable through the import wizard, while it is the natural entry point for any other way of feeding a project with its modules.
'_get_module_branch()' always searched the repository of the project, which is the right default but not the only case: a module pinned on a fork belongs to the repository the fork originates from, and that repository is only known by the caller. Accept an optional 'repository' argument, keeping the project repository as the default.
…y gets one '_compute_repository_branch_id' reads 'repository_id.branch_ids' but only depends on 'repository_id', so it never runs again once the repository is set. A project is commonly created before its repository has been scanned, hence before the matching branch exists: 'repository_branch_id' then stays empty for good, and 'module_not_installed_ids', computed from it, silently reports nothing. Depend on the branches of the repository so that the branch is picked up as soon as a scan creates it.
@tools.ormcache is a process-wide cache that is never invalidated by a SQL ROLLBACK. If a newly created record's id were cached from within the caller's (still uncommitted) transaction, and that transaction was later rolled back for an unrelated reason (e.g. a conflicting concurrent queue job), the cache would keep returning an id that no longer exists in the database. This commit add a new method creating and committing the record, independently of the caller's transaction. That guarantees the returned id always stays valid, whatever happens to the caller's job afterwards.
lmignon
force-pushed
the
18.0-odoo_project-from-requirements-lmi
branch
2 times, most recently
from
August 12, 2026 15:08
ef0eafd to
4030ba7
Compare
lmignon
marked this pull request as draft
August 13, 2026 06:20
Author
|
tests must be fixed... I've to figure out why some are failing only on GitHub |
lmignon
force-pushed
the
18.0-odoo_project-from-requirements-lmi
branch
3 times, most recently
from
August 13, 2026 07:35
a4bc64f to
f908a6b
Compare
GitPython 3.1.59, released on 2026-08-10, added '--separate-git-dir' to the list of unsafe options. Allow unsafe options on clone created with workaround_fs_errors=True since they are the only once using this option.
This commit fix '_get_recursive_dependencies' to properly work on a recordset
A module can be hosted by several organizations, a fork of an OCA repository being the usual case. '_find()' then picks one of them with 'fields.first()', which follows the sequence of the repositories: setting it one by one to say that OCA comes before a fork is tedious, and says nothing about the ones not listed yet. Give an organization a sequence of its own, in front of the module branch '_order' so that it decides first. The uniform default keeps the current ordering unchanged until a sequence is actually set. Add '_parse_clone_url()' and '_find_from_org_and_name()' along the way, which locating the repository a clone URL points at needs.
Expose the local clone path of a repository so callers can access files at a specific revision without having to know how the scanner stores its clones.
Separate the creation of repository scan jobs from their execution. Previously, 'action_scan' created and immediately delayed the scan jobs, making them unavailable to callers that needed to build a larger job graph around the repository scan. Expose '_create_scan_jobs()' to let callers create and access the scan jobs without delaying them. They can then orchestrate these jobs together with other jobs or processes before starting the execution. 'action_scan' keeps the existing behaviour by creating and delaying the jobs returned by '_create_scan_jobs()'.
lmignon
force-pushed
the
18.0-odoo_project-from-requirements-lmi
branch
from
August 13, 2026 08:20
f908a6b to
3aeb38f
Compare
'_check_existing_jobs()' was supposed to prevent multiple scans of the same repository from running concurrently, but its search domain could never match because 'queue.job.records' is a jsonb field containing an escaped JSON string. Search the ongoing jobs and identify their repository in Python, as the field cannot be reliably searched using a domain.
Some scanners only need the basic information required to access a repository, while '_prepare_scanner_parameters' also includes parameters specific to branch scanning. Extract the parameters needed by every repository scanner into '_prepare_base_scanner_parameters', so they can be reused by scanners with different needs.
…itory '_get_dependency_ids' took the repository branch being scanned to know where to look the dependencies of a module up. A module read outside of a scan has none: one added by a pull request not merged yet belongs to no repository at all, and its branch is only told by the module branch itself. Take the branch and the repository, which every caller has at hand.
'_prepare_module_branch_values' mixes three things: what the scan of a repository knows about a module, what its manifest declares, and what the analysis of its code counted. Only the first one is about being scanned, the other two hold for a module read from anywhere. Extract '_prepare_manifest_values' and '_prepare_code_analysis_values', so that reading a module outside of a scan does not have to spell the same mapping again.
A module is not always installed from the repository it belongs to. Freezing one built from unmerged pull requests is commonly done by pinning a revision of a fork, and that fork was nowhere in the database: its ancestry had to be asked to GitHub over and over, and nothing held the credentials a private one needs to be read. Give a fork a record of its own, an 'odoo.repository' pointing at the one it originates from through 'upstream_repository_id'. It is registered on the fly whenever the ancestry of an unknown clone URL turns out to reach a known repository, and it can be created by hand as well. A fork is never scanned, a constraint enforcing it: it hosts the very same modules as its origin, and a second scanned repository holding them would make '_find()' pick one of the two by sequence, silently corrupting the dependency graph of every project. Its credentials stay editable nonetheless, being precisely what reading a private fork needs. '_find_from_clone_url()' returns what a URL points at, fork included, while '_find_upstream_repository()' returns the repository holding the modules.
Some projects pin the versions of their dependencies in a dedicated file, such as the 'requirements.txt' produced by pip. Keeping a second, manually maintained list of these dependencies in the project can easily lead to inconsistencies when dependencies are updated. This module introduces an automatic dependency resolution mechanism for projects. Instead of maintaining this information separately, the project's dependencies are resolved from the dependency file stored in its repository at each scan. The resolution mechanism is based on a format-agnostic description of a dependency, containing the information needed to identify a module, its version and its origin. The resolution logic therefore does not depend on the format used to declare the dependencies. The dependency file parsing is implemented through pluggable components, allowing support for additional formats to be added independently. The first implementation supports 'requirements.txt', with other formats such as 'pylock.toml' or 'uv.lock' being possible in the future. This provides a single dependency resolution mechanism while allowing each project to use the dependency format that best fits its environment.
lmignon
force-pushed
the
18.0-odoo_project-from-requirements-lmi
branch
from
August 13, 2026 09:01
3aeb38f to
822d0b0
Compare
lmignon
marked this pull request as ready for review
August 13, 2026 09:05
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Some projects pin the versions of their dependencies in a dedicated file,
such as the 'requirements.txt' produced by pip. Keeping a second, manually
maintained list of these dependencies in the project can easily lead to
inconsistencies when dependencies are updated.
This module introduces an automatic dependency resolution mechanism for
projects. Instead of maintaining this information separately, the project's
dependencies are resolved from the dependency file stored in its repository at
each scan.
The resolution mechanism is based on a format-agnostic description of a
dependency, containing the information needed to identify a module, its
version and its origin. The resolution logic therefore does not depend on the
format used to declare the dependencies.
The dependency file parsing is implemented through pluggable components,
allowing support for additional formats to be added independently. The first
implementation supports 'requirements.txt', with other formats such as
'pylock.toml' or 'uv.lock' being possible in the future.
This provides a single dependency resolution mechanism while allowing each
project to use the dependency format that best fits its environment.
includes :