[18.0] [ADD] odoo_project_dependency_resolver - #157
Conversation
ef0eafd to
4030ba7
Compare
|
tests must be fixed... I've to figure out why some are failing only on GitHub |
3aeb38f to
822d0b0
Compare
822d0b0 to
9cddcc3
Compare
|
I extracted your first 10 commits there to make current PR smaller: Thanks! EDIT: PR merged, you can rebase |
9cddcc3 to
493849c
Compare
|
@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
left a comment
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
I don't get it.
From my understanding a PR has always a source/branch -> target/branch.
There was a problem hiding this comment.
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." |
| ) | ||
| source_ref = fields.Char( | ||
| string="Source Revision", | ||
| help="Revision the module is frozen on: a commit, a tag or a PR ref.", |
| is_pinned = fields.Boolean( | ||
| compute="_compute_is_pinned", | ||
| store=True, | ||
| help="The module is frozen on a revision instead of a published version.", |
493849c to
476ac07
Compare
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 :