Skip to content

[18.0] [ADD] odoo_project_dependency_resolver - #157

Open
lmignon wants to merge 15 commits into
OCA:18.0from
acsone:18.0-odoo_project-from-requirements-lmi
Open

[18.0] [ADD] odoo_project_dependency_resolver#157
lmignon wants to merge 15 commits into
OCA:18.0from
acsone:18.0-odoo_project-from-requirements-lmi

Conversation

@lmignon

@lmignon lmignon commented Aug 12, 2026

Copy link
Copy Markdown

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 :

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.
@OCA-git-bot OCA-git-bot added series:18.0 mod:odoo_repository Module odoo_repository mod:odoo_project_migration Module odoo_project_migration mod:odoo_project Module odoo_project mod:odoo_repository_fork Module odoo_repository_fork mod:odoo_project_dependency_resolver Module odoo_project_dependency_resolver labels Aug 12, 2026
@lmignon
lmignon force-pushed the 18.0-odoo_project-from-requirements-lmi branch 2 times, most recently from ef0eafd to 4030ba7 Compare August 12, 2026 15:08
@lmignon
lmignon marked this pull request as draft August 13, 2026 06:20
@lmignon

lmignon commented Aug 13, 2026

Copy link
Copy Markdown
Author

tests must be fixed... I've to figure out why some are failing only on GitHub

@lmignon
lmignon force-pushed the 18.0-odoo_project-from-requirements-lmi branch 3 times, most recently from a4bc64f to f908a6b Compare August 13, 2026 07:35
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
lmignon force-pushed the 18.0-odoo_project-from-requirements-lmi branch from f908a6b to 3aeb38f Compare August 13, 2026 08:20
'_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
lmignon force-pushed the 18.0-odoo_project-from-requirements-lmi branch from 3aeb38f to 822d0b0 Compare August 13, 2026 09:01
@lmignon
lmignon marked this pull request as ready for review August 13, 2026 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mod:odoo_project_dependency_resolver Module odoo_project_dependency_resolver mod:odoo_project_migration Module odoo_project_migration mod:odoo_project Module odoo_project mod:odoo_repository_fork Module odoo_repository_fork mod:odoo_repository Module odoo_repository series:18.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants