Skip to content

[18.0] [ADD] odoo_project_dependency_resolver - #157

Open
lmignon wants to merge 2 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 2 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 :

@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 5 times, most recently 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
@lmignon
lmignon force-pushed the 18.0-odoo_project-from-requirements-lmi branch from 822d0b0 to 9cddcc3 Compare August 19, 2026 10:22
@sebalix

sebalix commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

I extracted your first 10 commits there to make current PR smaller:

Thanks!

EDIT: PR merged, you can rebase

@lmignon
lmignon force-pushed the 18.0-odoo_project-from-requirements-lmi branch from 9cddcc3 to 493849c Compare August 19, 2026 13:01
@OCA-git-bot OCA-git-bot removed the mod:odoo_repository Module odoo_repository label Aug 19, 2026
@lmignon

lmignon commented Aug 19, 2026

Copy link
Copy Markdown
Author

@sebalix The branch is now rebased and the diff smaller ;-). Thank you for your work.

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.

@hparfr hparfr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi, thanks for this work.

I started a code review, and share now some thoughts without a complete read and functional tests.

It smells there is some assumtions about your workflow.

I plan to work on this review in the next weeks.

def _update_from_pinned_revision(self, data):
"""Fill this module branch with a module analysed at a pinned revision.

Meant for a module living in a pull request alone: it is in no branch

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't get it.
From my understanding a PR has always a source/branch -> target/branch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

A Git reference may point to the result of a branch created by aggregating two PRs targeting the same branch.

As part of the release process, a tag is then created from this reference to ensure that the exact source used for the release is preserved. From that point on, there is no guarantee that the branch from which the tag was created will still exist. (branch deleted, force push, ...)

Therefore, a Git tag may legitimately be orphaned from any branch, while still being the authoritative reference to the source used for a release.

"Fork the module is installed from, when it differs from the "
"repository it belongs to. Its own 'Fork Of' tells where the "
"module comes from, which is the only way to reach it for a module "
"living in the fork alone."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hard to understand.

)
source_ref = fields.Char(
string="Source Revision",
help="Revision the module is frozen on: a commit, a tag or a PR ref.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

... or a branch ?

is_pinned = fields.Boolean(
compute="_compute_is_pinned",
store=True,
help="The module is frozen on a revision instead of a published version.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

or pinned on a commit ?

@lmignon
lmignon force-pushed the 18.0-odoo_project-from-requirements-lmi branch from 493849c to 476ac07 Compare August 26, 2026 07:13
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 series:18.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants