From 30b76a87344070c12d3c05f8ecf8fe2d74cffba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 31 Aug 2023 12:25:20 +0200 Subject: [PATCH 01/65] Add 'odoo_repository_migration' Module to manage data regarding the migration of modules from one branch to another: - what has to be migrated - migration PR that could be reviewed - commits/PRs present on source branch but not on target branch --- odoo_repository_migration/__init__.py | 2 + odoo_repository_migration/__manifest__.py | 29 ++++ odoo_repository_migration/data/queue_job.xml | 18 +++ odoo_repository_migration/hooks.py | 14 ++ odoo_repository_migration/models/__init__.py | 4 + .../models/odoo_migration_path.py | 40 +++++ .../models/odoo_module_branch.py | 14 ++ .../models/odoo_module_branch_migration.py | 142 ++++++++++++++++++ .../models/odoo_repository.py | 71 +++++++++ .../security/ir.model.access.csv | 4 + odoo_repository_migration/utils/__init__.py | 0 odoo_repository_migration/utils/scanner.py | 79 ++++++++++ .../views/odoo_migration_path.xml | 43 ++++++ .../views/odoo_module_branch.xml | 39 +++++ .../views/odoo_module_branch_migration.xml | 100 ++++++++++++ .../views/odoo_repository.xml | 17 +++ 16 files changed, 616 insertions(+) create mode 100644 odoo_repository_migration/__init__.py create mode 100644 odoo_repository_migration/__manifest__.py create mode 100644 odoo_repository_migration/data/queue_job.xml create mode 100644 odoo_repository_migration/hooks.py create mode 100644 odoo_repository_migration/models/__init__.py create mode 100644 odoo_repository_migration/models/odoo_migration_path.py create mode 100644 odoo_repository_migration/models/odoo_module_branch.py create mode 100644 odoo_repository_migration/models/odoo_module_branch_migration.py create mode 100644 odoo_repository_migration/models/odoo_repository.py create mode 100644 odoo_repository_migration/security/ir.model.access.csv create mode 100644 odoo_repository_migration/utils/__init__.py create mode 100644 odoo_repository_migration/utils/scanner.py create mode 100644 odoo_repository_migration/views/odoo_migration_path.xml create mode 100644 odoo_repository_migration/views/odoo_module_branch.xml create mode 100644 odoo_repository_migration/views/odoo_module_branch_migration.xml create mode 100644 odoo_repository_migration/views/odoo_repository.xml diff --git a/odoo_repository_migration/__init__.py b/odoo_repository_migration/__init__.py new file mode 100644 index 00000000..4e97e1b8 --- /dev/null +++ b/odoo_repository_migration/__init__.py @@ -0,0 +1,2 @@ +from . import models +from .hooks import update_oca_repositories diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py new file mode 100644 index 00000000..e82f6c4d --- /dev/null +++ b/odoo_repository_migration/__manifest__.py @@ -0,0 +1,29 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +{ + "name": "Odoo Repository Migration Data", + "summary": "Collect modules migration data for Odoo Repositories.", + "version": "16.0.1.0.0", + "category": "Tools", + "author": "Camptocamp, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/TODO", + "data": [ + "security/ir.model.access.csv", + "data/queue_job.xml", + "views/odoo_migration_path.xml", + "views/odoo_module_branch.xml", + "views/odoo_module_branch_migration.xml", + "views/odoo_repository.xml", + ], + "installable": True, + "depends": [ + "odoo_repository", + ], + "external_dependencies": { + "python": [ + "oca-port", + ], + }, + "license": "AGPL-3", + "post_init_hook": "update_oca_repositories", +} diff --git a/odoo_repository_migration/data/queue_job.xml b/odoo_repository_migration/data/queue_job.xml new file mode 100644 index 00000000..1d4200da --- /dev/null +++ b/odoo_repository_migration/data/queue_job.xml @@ -0,0 +1,18 @@ + + + + + + odoo_repository_scan_migration + + + + + + _scan_migration_data + + + + + diff --git a/odoo_repository_migration/hooks.py b/odoo_repository_migration/hooks.py new file mode 100644 index 00000000..d95987f0 --- /dev/null +++ b/odoo_repository_migration/hooks.py @@ -0,0 +1,14 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import api, SUPERUSER_ID + + +def update_oca_repositories(cr, registry): + """Configure OCA repositories to collect migration data.""" + env = api.Environment(cr, SUPERUSER_ID, {}) + org = env["odoo.repository.org"].search([("name", "=", "OCA")]) + if org: + repositories = env["odoo.repository"].with_context( + active_test=False).search([("org_id", "=", org.id)]) + repositories.write({"collect_migration_data": True}) diff --git a/odoo_repository_migration/models/__init__.py b/odoo_repository_migration/models/__init__.py new file mode 100644 index 00000000..db7baee9 --- /dev/null +++ b/odoo_repository_migration/models/__init__.py @@ -0,0 +1,4 @@ +from . import odoo_migration_path +from . import odoo_module_branch_migration +from . import odoo_module_branch +from . import odoo_repository diff --git a/odoo_repository_migration/models/odoo_migration_path.py b/odoo_repository_migration/models/odoo_migration_path.py new file mode 100644 index 00000000..34bfbe0d --- /dev/null +++ b/odoo_repository_migration/models/odoo_migration_path.py @@ -0,0 +1,40 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import api, fields, models + + +class OdooMigrationPath(models.Model): + _name = "odoo.migration.path" + _description = "Define a migration path (from one branch to another)" + _order = "name" + + name = fields.Char(compute="_compute_name", store=True) + active = fields.Boolean(default=True) + source_branch_id = fields.Many2one( + comodel_name="odoo.branch", + ondelete="cascade", + domain=[("odoo_version", "=", True)], + required=True, + ) + target_branch_id = fields.Many2one( + comodel_name="odoo.branch", + ondelete="cascade", + domain=[("odoo_version", "=", True)], + required=True, + ) + + _sql_constraints = [ + ( + "migration_path_uniq", + "UNIQUE (source_branch_id, target_branch_id)", + "This migration path already exists." + ), + ] + + @api.depends("source_branch_id.name", "target_branch_id.name") + def _compute_name(self): + for rec in self: + rec.name = ( + f"{rec.source_branch_id.name} -> {rec.target_branch_id.name}" + ) diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py new file mode 100644 index 00000000..18dd9ca7 --- /dev/null +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -0,0 +1,14 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import fields, models + + +class OdooModuleBranch(models.Model): + _inherit = "odoo.module.branch" + + migration_ids = fields.One2many( + comodel_name="odoo.module.branch.migration", + inverse_name="module_branch_id", + string="Migrations", + ) diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py new file mode 100644 index 00000000..85d3e978 --- /dev/null +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -0,0 +1,142 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import pprint + +from odoo import api, fields, models + + +class OdooModuleBranchMigration(models.Model): + _name = "odoo.module.branch.migration" + _description = "Migration data for a module of a given branch." + _order = "display_name" + + display_name = fields.Char(compute="_compute_display_name", store=True) + module_branch_id = fields.Many2one( + comodel_name="odoo.module.branch", + ondelete="cascade", + string="Module", + required=True, + index=True, + ) + module_id = fields.Many2one( + related="module_branch_id.module_id", + store=True, + index=True, + ) + org_id = fields.Many2one(related="module_branch_id.org_id", store=True) + repository_id = fields.Many2one( + related="module_branch_id.repository_id", + store=True, + ondelete="cascade", + ) + migration_path_id = fields.Many2one( + comodel_name="odoo.migration.path", + ondelete="cascade", + required=True, + index=True, + ) + source_branch_id = fields.Many2one( + related="migration_path_id.source_branch_id", + store=True, + index=True, + ) + target_branch_id = fields.Many2one( + related="migration_path_id.target_branch_id", + store=True, + index=True, + ) + author_ids = fields.Many2many(related="module_branch_id.author_ids") + maintainer_ids = fields.Many2many(related="module_branch_id.maintainer_ids") + process = fields.Char(index=True) + state = fields.Selection( + selection=[ + ("fully_ported", "Fully Ported"), + ("migrate", "To migrate"), + ("port_commits", "Commits to port"), + ("review_migration", "Migration to review"), + ], + string="Migration Status", + compute="_compute_state", + store=True, + index=True, + ) + pr_url = fields.Char( + string="PR URL", + compute="_compute_pr_url", + store=True, + ) + results = fields.Serialized() + results_text = fields.Text(compute="_compute_results_text") + last_source_scanned_commit = fields.Char() + last_target_scanned_commit = fields.Char() + + _sql_constraints = [ + ( + "module_migration_path_uniq", + "UNIQUE (module_branch_id, migration_path_id)", + "This module migration path already exists." + ), + ] + + @api.depends( + "module_branch_id.module_id", + "source_branch_id.name", + "target_branch_id.name", + ) + def _compute_display_name(self): + for rec in self: + rec.display_name = ( + f"{rec.module_branch_id.module_id.name}: " + f"{rec.source_branch_id.name} -> {rec.target_branch_id.name}" + ) + + @api.depends("process", "pr_url") + def _compute_state(self): + for rec in self: + rec.state = rec.process or "fully_ported" + if rec.process == "migrate" and rec.pr_url: + rec.state = "review_migration" + + @api.depends("results") + def _compute_pr_url(self): + for rec in self: + rec.pr_url = rec.results.get("existing_pr", {}).get("url") + + @api.depends("results") + def _compute_results_text(self): + for rec in self: + rec.results_text = pprint.pformat(rec.results) + + @api.model + @api.returns("odoo.module.branch.migration") + def push_scanned_data(self, module_branch_id, data): + migration_path = self.env["odoo.migration.path"].search( + [ + ("source_branch_id", "=", data["source_branch"]), + ("target_branch_id", "=", data["target_branch"]), + ] + ) + values = { + "module_branch_id": module_branch_id, + "migration_path_id": migration_path.id, + "last_source_scanned_commit": data["source_commit"], + "last_target_scanned_commit": data["target_commit"], + } + for key in ("process", "results"): + if data.get(key): + values[key] = data[key] + return self._create_or_update(module_branch_id, migration_path, values) + + def _create_or_update(self, module_branch_id, migration_path, values): + args = [ + ("module_branch_id", "=", module_branch_id), + ("source_branch_id", "=", migration_path.source_branch_id.id), + ("target_branch_id", "=", migration_path.target_branch_id.id), + ] + migration = self.search(args) + if migration: + migration.sudo().write(values) + else: + migration = self.sudo().create(values) + return migration diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py new file mode 100644 index 00000000..161d8560 --- /dev/null +++ b/odoo_repository_migration/models/odoo_repository.py @@ -0,0 +1,71 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import fields, models + +from odoo.addons.queue_job.job import identity_exact + +from ..utils.scanner import MigrationScannerOdooEnv + + +class OdooRepository(models.Model): + _inherit = "odoo.repository" + + collect_migration_data = fields.Boolean( + string="Collect migration data", + help=( + "Collect migration data based on the configured migration paths." + ), + default=False, + ) + + def _reset_scanned_commits(self): + res = super()._reset_scanned_commits() + self.branch_ids.module_ids.migration_ids.sudo().write( + { + "last_source_scanned_commit": False, + "last_target_scanned_commit": False, + } + ) + return res + + def _create_jobs(self, branches): + jobs = super()._create_jobs(branches) + # Check if the addons_paths are compatible with 'oca_port' + if not self.collect_migration_data: + return jobs + # Override to run the MigrationScanner once all branches are scanned + migration_paths = self.env["odoo.migration.path"].search([]) + for rec in migration_paths: + migration_path = ( + rec.source_branch_id.name, + rec.target_branch_id.name + ) + delayable = self.delayable( + description=( + f"Collect {self.display_name} " + f"{' > '.join(migration_path)} migration data" + ), + identity_key=identity_exact + ) + job = delayable._scan_migration_data(migration_path) + jobs.append(job) + return jobs + + def _scan_migration_data(self, migration_path): + """Scan repository branches to collect modules migration data.""" + params = self._prepare_migration_scanner_parameters(migration_path) + scanner = MigrationScannerOdooEnv(**params) + return scanner.scan() + + def _prepare_migration_scanner_parameters(self, migration_path): + key = self._repositories_path_key + repositories_path = self.env["ir.config_parameter"].get_param(key) + return { + "org": self.org_id.name, + "name": self.name, + "clone_url": self.clone_url, + "migration_paths": [migration_path], + "repositories_path": repositories_path, + "env": self.env + } diff --git a/odoo_repository_migration/security/ir.model.access.csv b/odoo_repository_migration/security/ir.model.access.csv new file mode 100644 index 00000000..41e6e372 --- /dev/null +++ b/odoo_repository_migration/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_odoo_migration_path_user,odoo_migration_path_user,model_odoo_migration_path,base.group_user,1,0,0,0 +access_odoo_migration_path_manager,odoo_migration_path_manager,model_odoo_migration_path,base.group_system,1,0,1,1 +access_odoo_module_branch_migration_user,odoo_module_branch_migration_user,model_odoo_module_branch_migration,base.group_user,1,0,0,0 diff --git a/odoo_repository_migration/utils/__init__.py b/odoo_repository_migration/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/odoo_repository_migration/utils/scanner.py b/odoo_repository_migration/utils/scanner.py new file mode 100644 index 00000000..c9e11485 --- /dev/null +++ b/odoo_repository_migration/utils/scanner.py @@ -0,0 +1,79 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.addons.odoo_repository.lib.scanner import MigrationScanner + + +class MigrationScannerOdooEnv(MigrationScanner): + """MigrationScanner running on the same server than Odoo. + + This class takes an additional `env` parameter (`odoo.api.Environment`) + used to request Odoo, and implement required methods to use it. + """ + + def __init__(self, *args, **kwargs): + if kwargs.get("env"): + self.env = kwargs.pop("env") + super().__init__(*args, **kwargs) + + def _get_odoo_repository_id(self) -> int: + return self.env["odoo.repository"].search( + [("name", "=", self.name), ("org_id", "=", self.org)] + ).id + + def _get_odoo_repository_branches(self, repo_id) -> list[str]: + args = [ + ("repository_id", "=", repo_id), + ("branch_id", "in", self.branches), + ] + repo_branches = self.env["odoo.repository.branch"].search(args) + return repo_branches.mapped("branch_id.name") + + def _get_odoo_migration_paths(self, branches: list[str]) -> list[tuple[str]]: + args = [ + ("source_branch_id", "in", branches), + ("target_branch_id", "in", branches), + ] + migration_paths = self.env["odoo.migration.path"].search(args) + return [ + (mp.source_branch_id.name, mp.target_branch_id.name) + for mp in migration_paths + ] + + def _get_odoo_module_branch_id(self, module: str, branch: str) -> int: + args = [ + ("module_id", "=", module), + ("branch_id", "=", branch), + ] + return self.env["odoo.module.branch"].search(args).id + + def _get_odoo_module_branch_migration_id( + self, module_branch_id: int, source_branch: str, target_branch: str) -> int: + args = [ + ("module_branch_id", "=", module_branch_id), + ("source_branch_id", "=", source_branch), + ("target_branch_id", "=", target_branch), + ] + migration = self.env["odoo.module.branch.migration"].search(args) + if migration: + return migration.id + + def _get_odoo_module_branch_migration_data( + self, module: str, source_branch: str, target_branch: str) -> dict: + args = [ + ("module_id", "=", module), + ("source_branch_id", "=", source_branch), + ("target_branch_id", "=", target_branch), + ] + migration = self.env["odoo.module.branch.migration"].search(args) + if migration: + return migration.read()[0] + return {} + + def _push_scanned_data(self, module_branch_id: int, data: dict): + res = self.env["odoo.module.branch.migration"].push_scanned_data( + module_branch_id, data + ) + # Commit after each scan + self.env.cr.commit() + return res diff --git a/odoo_repository_migration/views/odoo_migration_path.xml b/odoo_repository_migration/views/odoo_migration_path.xml new file mode 100644 index 00000000..fc49e7e1 --- /dev/null +++ b/odoo_repository_migration/views/odoo_migration_path.xml @@ -0,0 +1,43 @@ + + + + + + odoo.migration.path.form + odoo.migration.path + +
+ + + + + + +
+
+
+ + + odoo.migration.path.tree + odoo.migration.path + + + + + + + + + + Migration Paths + ir.actions.act_window + odoo.migration.path + + + + + +
diff --git a/odoo_repository_migration/views/odoo_module_branch.xml b/odoo_repository_migration/views/odoo_module_branch.xml new file mode 100644 index 00000000..9e6ca821 --- /dev/null +++ b/odoo_repository_migration/views/odoo_module_branch.xml @@ -0,0 +1,39 @@ + + + + + + odoo.module.branch.form.inherit + odoo.module.branch + + + + + + + + + + + + + + + + + + odoo.module.branch.search.inherit + odoo.module.branch + + + + + + + + + + diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml new file mode 100644 index 00000000..dc2d529b --- /dev/null +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -0,0 +1,100 @@ + + + + + + odoo.module.branch.migration.form + odoo.module.branch.migration + +
+ +
+

+
+ + + + + + + + + + + + +
+
+
+
+ + + odoo.module.branch.migration.tree + odoo.module.branch.migration + + + + + + + + + + + + + odoo.module.branch.migration.search + odoo.module.branch.migration + search + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Migrations + ir.actions.act_window + odoo.module.branch.migration + + + + + +
diff --git a/odoo_repository_migration/views/odoo_repository.xml b/odoo_repository_migration/views/odoo_repository.xml new file mode 100644 index 00000000..8a2c0482 --- /dev/null +++ b/odoo_repository_migration/views/odoo_repository.xml @@ -0,0 +1,17 @@ + + + + + + odoo.repository.form.inherit + odoo.repository + + + + + + + + + From 8e9b17666651ef20d305b50959a52d8084de8a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Tue, 10 Oct 2023 18:39:28 +0200 Subject: [PATCH 02/65] odoo_repository: add settings panel And handle GitHub token. --- odoo_repository_migration/models/odoo_repository.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 161d8560..b338d382 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -1,6 +1,8 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import os + from odoo import fields, models from odoo.addons.queue_job.job import identity_exact @@ -59,13 +61,19 @@ def _scan_migration_data(self, migration_path): return scanner.scan() def _prepare_migration_scanner_parameters(self, migration_path): - key = self._repositories_path_key - repositories_path = self.env["ir.config_parameter"].get_param(key) + ir_config = self.env["ir.config_parameter"] + repositories_path = ir_config.get_param(self._repositories_path_key) + github_token = ir_config.get_param( + "odoo_repository_github_token", + os.environ.get("GITHUB_TOKEN") + ) return { "org": self.org_id.name, "name": self.name, "clone_url": self.clone_url, "migration_paths": [migration_path], "repositories_path": repositories_path, + "ssh_key": self.ssh_key_id.private_key, + "github_token": github_token, "env": self.env } From 6bdc85ad24066f6e645776c300e57d127182b78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Fri, 3 Nov 2023 12:13:31 +0100 Subject: [PATCH 03/65] Apply pre-commit --- odoo_repository_migration/README.rst | 61 +++ odoo_repository_migration/__manifest__.py | 2 +- odoo_repository_migration/data/queue_job.xml | 7 +- odoo_repository_migration/hooks.py | 9 +- .../models/odoo_migration_path.py | 6 +- .../models/odoo_module_branch_migration.py | 2 +- .../models/odoo_repository.py | 16 +- .../readme/CONTRIBUTORS.rst | 2 + .../readme/DESCRIPTION.rst | 1 + .../static/description/index.html | 417 ++++++++++++++++++ odoo_repository_migration/utils/scanner.py | 16 +- .../views/odoo_migration_path.xml | 20 +- .../views/odoo_module_branch.xml | 26 +- .../views/odoo_module_branch_migration.xml | 135 +++--- .../views/odoo_repository.xml | 6 +- 15 files changed, 626 insertions(+), 100 deletions(-) create mode 100644 odoo_repository_migration/README.rst create mode 100644 odoo_repository_migration/readme/CONTRIBUTORS.rst create mode 100644 odoo_repository_migration/readme/DESCRIPTION.rst create mode 100644 odoo_repository_migration/static/description/index.html diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst new file mode 100644 index 00000000..cafdf9b9 --- /dev/null +++ b/odoo_repository_migration/README.rst @@ -0,0 +1,61 @@ +============================== +Odoo Repository Migration Data +============================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:4705947f581f1953f9c146b61253c9ebd83975867442c894ece00ac3c5f142e0 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-camptocamp%2Fodoo--repository-lightgray.png?logo=github + :target: https://github.com/camptocamp/odoo-repository/tree/16.0/odoo_repository_migration + :alt: camptocamp/odoo-repository + +|badge1| |badge2| |badge3| + +This module collects modules migration data from Odoo repositories. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Camptocamp + +Contributors +~~~~~~~~~~~~ + +* Camptocamp + * Sébastien Alix + +Maintainers +~~~~~~~~~~~ + +This module is part of the `camptocamp/odoo-repository `_ project on GitHub. + +You are welcome to contribute. diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index e82f6c4d..46cbf98a 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -6,7 +6,7 @@ "version": "16.0.1.0.0", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", - "website": "https://github.com/OCA/TODO", + "website": "https://github.com/camptocamp/odoo-repository", "data": [ "security/ir.model.access.csv", "data/queue_job.xml", diff --git a/odoo_repository_migration/data/queue_job.xml b/odoo_repository_migration/data/queue_job.xml index 1d4200da..d3c25e06 100644 --- a/odoo_repository_migration/data/queue_job.xml +++ b/odoo_repository_migration/data/queue_job.xml @@ -1,4 +1,4 @@ - + @@ -8,7 +8,10 @@ - + _scan_migration_data diff --git a/odoo_repository_migration/hooks.py b/odoo_repository_migration/hooks.py index d95987f0..ee035c26 100644 --- a/odoo_repository_migration/hooks.py +++ b/odoo_repository_migration/hooks.py @@ -1,7 +1,7 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import api, SUPERUSER_ID +from odoo import SUPERUSER_ID, api def update_oca_repositories(cr, registry): @@ -9,6 +9,9 @@ def update_oca_repositories(cr, registry): env = api.Environment(cr, SUPERUSER_ID, {}) org = env["odoo.repository.org"].search([("name", "=", "OCA")]) if org: - repositories = env["odoo.repository"].with_context( - active_test=False).search([("org_id", "=", org.id)]) + repositories = ( + env["odoo.repository"] + .with_context(active_test=False) + .search([("org_id", "=", org.id)]) + ) repositories.write({"collect_migration_data": True}) diff --git a/odoo_repository_migration/models/odoo_migration_path.py b/odoo_repository_migration/models/odoo_migration_path.py index 34bfbe0d..2f15433c 100644 --- a/odoo_repository_migration/models/odoo_migration_path.py +++ b/odoo_repository_migration/models/odoo_migration_path.py @@ -28,13 +28,11 @@ class OdooMigrationPath(models.Model): ( "migration_path_uniq", "UNIQUE (source_branch_id, target_branch_id)", - "This migration path already exists." + "This migration path already exists.", ), ] @api.depends("source_branch_id.name", "target_branch_id.name") def _compute_name(self): for rec in self: - rec.name = ( - f"{rec.source_branch_id.name} -> {rec.target_branch_id.name}" - ) + rec.name = f"{rec.source_branch_id.name} -> {rec.target_branch_id.name}" diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index 85d3e978..b5819583 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -75,7 +75,7 @@ class OdooModuleBranchMigration(models.Model): ( "module_migration_path_uniq", "UNIQUE (module_branch_id, migration_path_id)", - "This module migration path already exists." + "This module migration path already exists.", ), ] diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index b338d382..deee9437 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -15,9 +15,7 @@ class OdooRepository(models.Model): collect_migration_data = fields.Boolean( string="Collect migration data", - help=( - "Collect migration data based on the configured migration paths." - ), + help=("Collect migration data based on the configured migration paths."), default=False, ) @@ -39,16 +37,13 @@ def _create_jobs(self, branches): # Override to run the MigrationScanner once all branches are scanned migration_paths = self.env["odoo.migration.path"].search([]) for rec in migration_paths: - migration_path = ( - rec.source_branch_id.name, - rec.target_branch_id.name - ) + migration_path = (rec.source_branch_id.name, rec.target_branch_id.name) delayable = self.delayable( description=( f"Collect {self.display_name} " f"{' > '.join(migration_path)} migration data" ), - identity_key=identity_exact + identity_key=identity_exact, ) job = delayable._scan_migration_data(migration_path) jobs.append(job) @@ -64,8 +59,7 @@ def _prepare_migration_scanner_parameters(self, migration_path): ir_config = self.env["ir.config_parameter"] repositories_path = ir_config.get_param(self._repositories_path_key) github_token = ir_config.get_param( - "odoo_repository_github_token", - os.environ.get("GITHUB_TOKEN") + "odoo_repository_github_token", os.environ.get("GITHUB_TOKEN") ) return { "org": self.org_id.name, @@ -75,5 +69,5 @@ def _prepare_migration_scanner_parameters(self, migration_path): "repositories_path": repositories_path, "ssh_key": self.ssh_key_id.private_key, "github_token": github_token, - "env": self.env + "env": self.env, } diff --git a/odoo_repository_migration/readme/CONTRIBUTORS.rst b/odoo_repository_migration/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..a0c91e35 --- /dev/null +++ b/odoo_repository_migration/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Camptocamp + * Sébastien Alix diff --git a/odoo_repository_migration/readme/DESCRIPTION.rst b/odoo_repository_migration/readme/DESCRIPTION.rst new file mode 100644 index 00000000..fa0b3fab --- /dev/null +++ b/odoo_repository_migration/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module collects modules migration data from Odoo repositories. diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html new file mode 100644 index 00000000..cc60c0b7 --- /dev/null +++ b/odoo_repository_migration/static/description/index.html @@ -0,0 +1,417 @@ + + + + + + +Odoo Repository Migration Data + + + +
+

Odoo Repository Migration Data

+ + +

Beta License: AGPL-3 camptocamp/odoo-repository

+

This module collects modules migration data from Odoo repositories.

+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the camptocamp/odoo-repository project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/odoo_repository_migration/utils/scanner.py b/odoo_repository_migration/utils/scanner.py index c9e11485..e95a4dd3 100644 --- a/odoo_repository_migration/utils/scanner.py +++ b/odoo_repository_migration/utils/scanner.py @@ -17,9 +17,11 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _get_odoo_repository_id(self) -> int: - return self.env["odoo.repository"].search( - [("name", "=", self.name), ("org_id", "=", self.org)] - ).id + return ( + self.env["odoo.repository"] + .search([("name", "=", self.name), ("org_id", "=", self.org)]) + .id + ) def _get_odoo_repository_branches(self, repo_id) -> list[str]: args = [ @@ -48,7 +50,8 @@ def _get_odoo_module_branch_id(self, module: str, branch: str) -> int: return self.env["odoo.module.branch"].search(args).id def _get_odoo_module_branch_migration_id( - self, module_branch_id: int, source_branch: str, target_branch: str) -> int: + self, module_branch_id: int, source_branch: str, target_branch: str + ) -> int: args = [ ("module_branch_id", "=", module_branch_id), ("source_branch_id", "=", source_branch), @@ -59,7 +62,8 @@ def _get_odoo_module_branch_migration_id( return migration.id def _get_odoo_module_branch_migration_data( - self, module: str, source_branch: str, target_branch: str) -> dict: + self, module: str, source_branch: str, target_branch: str + ) -> dict: args = [ ("module_id", "=", module), ("source_branch_id", "=", source_branch), @@ -75,5 +79,5 @@ def _push_scanned_data(self, module_branch_id: int, data: dict): module_branch_id, data ) # Commit after each scan - self.env.cr.commit() + self.env.cr.commit() # pylint: disable=invalid-commit return res diff --git a/odoo_repository_migration/views/odoo_migration_path.xml b/odoo_repository_migration/views/odoo_migration_path.xml index fc49e7e1..ff8691ad 100644 --- a/odoo_repository_migration/views/odoo_migration_path.xml +++ b/odoo_repository_migration/views/odoo_migration_path.xml @@ -1,4 +1,4 @@ - + @@ -10,8 +10,8 @@
- - + +
@@ -23,8 +23,8 @@ odoo.migration.path - - + +
@@ -33,11 +33,13 @@ Migration Paths ir.actions.act_window odoo.migration.path - +
- +
diff --git a/odoo_repository_migration/views/odoo_module_branch.xml b/odoo_repository_migration/views/odoo_module_branch.xml index 9e6ca821..d0558fc4 100644 --- a/odoo_repository_migration/views/odoo_module_branch.xml +++ b/odoo_repository_migration/views/odoo_module_branch.xml @@ -1,4 +1,4 @@ - + @@ -6,15 +6,15 @@ odoo.module.branch.form.inherit odoo.module.branch - + - - - + + + @@ -25,13 +25,19 @@ odoo.module.branch.search.inherit odoo.module.branch - + - - + + diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index dc2d529b..e2ac202f 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -1,4 +1,4 @@ - + @@ -10,19 +10,19 @@
-

+

- - - - - + + + + + - - - + + +
@@ -34,11 +34,11 @@ odoo.module.branch.migration - - - - - + + + + +
@@ -49,38 +49,71 @@ search - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - + + + + + + + @@ -90,11 +123,13 @@ Migrations ir.actions.act_window odoo.module.branch.migration - + - +
diff --git a/odoo_repository_migration/views/odoo_repository.xml b/odoo_repository_migration/views/odoo_repository.xml index 8a2c0482..a8961598 100644 --- a/odoo_repository_migration/views/odoo_repository.xml +++ b/odoo_repository_migration/views/odoo_repository.xml @@ -1,4 +1,4 @@ - + @@ -6,10 +6,10 @@ odoo.repository.form.inherit odoo.repository - + - + From e7ba0e8b29b116f26f9990ceea8a0b172c615f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Sat, 4 Nov 2023 11:42:50 +0100 Subject: [PATCH 04/65] odoo_repository_migration: sync data from main node --- .../models/odoo_module_branch.py | 8 ++++ .../models/odoo_module_branch_migration.py | 11 +++++ .../models/odoo_repository.py | 46 ++++++++++++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index 18dd9ca7..78699a98 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -12,3 +12,11 @@ class OdooModuleBranch(models.Model): inverse_name="module_branch_id", string="Migrations", ) + + def _to_dict(self): + # Add the migrations data + data = super()._to_dict() + data["migrations"] = [] + for migration in self.migration_ids: + data["migrations"].append(migration._to_dict()) + return data diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index b5819583..8a9f1cba 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -140,3 +140,14 @@ def _create_or_update(self, module_branch_id, migration_path, values): else: migration = self.sudo().create(values) return migration + + def _to_dict(self): + self.ensure_one() + return { + "source_branch": self.source_branch_id.name, + "target_branch": self.target_branch_id.name, + "process": self.process, + "results": self.results, + "last_source_scanned_commit": self.last_source_scanned_commit, + "last_target_scanned_commit": self.last_target_scanned_commit, + } diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index deee9437..3e61eb9e 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -3,7 +3,7 @@ import os -from odoo import fields, models +from odoo import fields, models, tools from odoo.addons.queue_job.job import identity_exact @@ -71,3 +71,47 @@ def _prepare_migration_scanner_parameters(self, migration_path): "github_token": github_token, "env": self.env, } + + def _prepare_module_branch_values(self, data): + # Handle migration data + values = super()._prepare_module_branch_values(data) + migrations = data.get("migrations", []) + for mig in migrations: + source_branch = self.env["odoo.branch"].search( + [("odoo_version", "=", True), ("name", "=", mig["source_branch"])] + ) + target_branch = self.env["odoo.branch"].search( + [("odoo_version", "=", True), ("name", "=", mig["target_branch"])] + ) + if not source_branch or not target_branch: + # Such branches are not configured on this instance, skip + continue + migration_path = self._get_migration_path( + source_branch.id, target_branch.id + ) + mig_values = { + "migration_path_id": migration_path.id, + "process": mig["process"], + "results": mig["results"], + "last_source_scanned_commit": mig["last_source_scanned_commit"], + "last_target_scanned_commit": mig["last_target_scanned_commit"], + } + values["migration_ids"] = [(0, 0, mig_values)] + return values + + @tools.ormcache("source_branch_id", "target_branch_id") + def _get_migration_path(self, source_branch_id, target_branch_id): + rec = self.env["odoo.migration.path"].search( + [ + ("source_branch_id", "=", source_branch_id), + ("target_branch_id", "=", target_branch_id), + ], + limit=1, + ) + values = { + "source_branch_id": source_branch_id, + "target_branch_id": target_branch_id, + } + if not rec: + rec = self.env["odoo.migration.path"].sudo().create(values) + return rec From ad553015dbb63297b728cf12111459062170898b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Wed, 8 Nov 2023 12:58:20 +0100 Subject: [PATCH 05/65] o_r_migration: default filters for 'Migrations' menu --- .../views/odoo_module_branch_migration.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index e2ac202f..cad1e2ae 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -124,6 +124,9 @@ ir.actions.act_window odoo.module.branch.migration + {'search_default_group_by_migration_path_id': 1, 'search_default_group_by_state': 2} Date: Wed, 22 Nov 2023 13:29:27 +0100 Subject: [PATCH 06/65] odoo_repository_migration: fix sync data from main node --- .../models/odoo_repository.py | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 3e61eb9e..cc86ec12 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -72,10 +72,12 @@ def _prepare_migration_scanner_parameters(self, migration_path): "env": self.env, } - def _prepare_module_branch_values(self, data): + def _pre_create_or_update_module_branch(self, rec, values, raw_data): # Handle migration data - values = super()._prepare_module_branch_values(data) - migrations = data.get("migrations", []) + values = super()._pre_create_or_update_module_branch(rec, values, raw_data) + mig_model = self.env["odoo.module.branch.migration"] + migrations = raw_data.get("migrations", []) + values["migration_ids"] = [] for mig in migrations: source_branch = self.env["odoo.branch"].search( [("odoo_version", "=", True), ("name", "=", mig["source_branch"])] @@ -96,7 +98,20 @@ def _prepare_module_branch_values(self, data): "last_source_scanned_commit": mig["last_source_scanned_commit"], "last_target_scanned_commit": mig["last_target_scanned_commit"], } - values["migration_ids"] = [(0, 0, mig_values)] + # Check if this migration data exists to update it, otherwise create it + mig_rec = None + if rec: + mig_rec = mig_model.search( + [ + ("migration_path_id", "=", migration_path.id), + ("module_branch_id", "=", rec.id), + ], + ) + if mig_rec: + mig_values_ = fields.Command.update(mig_rec.id, mig_values) + else: + mig_values_ = fields.Command.create(mig_values) + values["migration_ids"].append(mig_values_) return values @tools.ormcache("source_branch_id", "target_branch_id") From 01c3d0764fa38436001ab59ddba8a852d45d0bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Sun, 26 Nov 2023 11:39:32 +0100 Subject: [PATCH 07/65] odoo_repository: limit the scan/force-scan to selected branches --- .../models/odoo_repository.py | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index cc86ec12..74ce291e 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -19,9 +19,16 @@ class OdooRepository(models.Model): default=False, ) - def _reset_scanned_commits(self): - res = super()._reset_scanned_commits() - self.branch_ids.module_ids.migration_ids.sudo().write( + def _reset_scanned_commits(self, branches=None): + res = super()._reset_scanned_commits(branches) + if branches is None: + branches = [] + branches_ = ( + self.branch_ids.filtered(lambda br: br.branch_id.name in branches) + if branches + else self.branch_ids + ) + branches_.module_ids.migration_ids.sudo().write( { "last_source_scanned_commit": False, "last_target_scanned_commit": False, @@ -34,8 +41,15 @@ def _create_jobs(self, branches): # Check if the addons_paths are compatible with 'oca_port' if not self.collect_migration_data: return jobs - # Override to run the MigrationScanner once all branches are scanned - migration_paths = self.env["odoo.migration.path"].search([]) + # Override to run the MigrationScanner once branches are scanned + args = [] + if branches: + args = [ + "|", + ("source_branch_id", "in", branches), + ("target_branch_id", "in", branches), + ] + migration_paths = self.env["odoo.migration.path"].search(args) for rec in migration_paths: migration_path = (rec.source_branch_id.name, rec.target_branch_id.name) delayable = self.delayable( From fe2a6799e469e476f28ef6a1ececac669484d9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 27 Nov 2023 08:18:04 +0100 Subject: [PATCH 08/65] odoo_repository_migration: ctx key to disable MigrationScanner run --- odoo_repository_migration/models/odoo_repository.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 74ce291e..34842d00 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -39,7 +39,8 @@ def _reset_scanned_commits(self, branches=None): def _create_jobs(self, branches): jobs = super()._create_jobs(branches) # Check if the addons_paths are compatible with 'oca_port' - if not self.collect_migration_data: + disable_collect = self.env.context.get("disable_collect_migration_data") + if not self.collect_migration_data or disable_collect: return jobs # Override to run the MigrationScanner once branches are scanned args = [] From aa3be31003cc9622025f7829c8d7bcd1a54b5968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 2 May 2024 15:15:43 +0200 Subject: [PATCH 09/65] odoo_repository: handle git authentication with tokens --- odoo_repository_migration/models/odoo_repository.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 34842d00..7244e5f0 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -1,7 +1,6 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -import os from odoo import fields, models, tools @@ -73,17 +72,15 @@ def _scan_migration_data(self, migration_path): def _prepare_migration_scanner_parameters(self, migration_path): ir_config = self.env["ir.config_parameter"] repositories_path = ir_config.get_param(self._repositories_path_key) - github_token = ir_config.get_param( - "odoo_repository_github_token", os.environ.get("GITHUB_TOKEN") - ) return { "org": self.org_id.name, "name": self.name, "clone_url": self.clone_url, "migration_paths": [migration_path], "repositories_path": repositories_path, + "repo_type": self.repo_type, "ssh_key": self.ssh_key_id.private_key, - "github_token": github_token, + "token": self._get_token(), "env": self.env, } From fcee9f289f1cb88bbd9d31d4273d222f41a038b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Fri, 10 May 2024 09:51:23 +0200 Subject: [PATCH 10/65] odoo_repository: handle blacklisted modules We do not want to scan some modules and attach them to a repository as while they have the same name among different projects, they do not share the same code and are specific to a project. Such modules are for instance: - studio_customization (generated by Studio in Odoo Enterprise) - server_environment_files (created manually by developers to leverage server_environment module features) --- odoo_repository_migration/utils/scanner.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/odoo_repository_migration/utils/scanner.py b/odoo_repository_migration/utils/scanner.py index e95a4dd3..deddc1d9 100644 --- a/odoo_repository_migration/utils/scanner.py +++ b/odoo_repository_migration/utils/scanner.py @@ -42,6 +42,13 @@ def _get_odoo_migration_paths(self, branches: list[str]) -> list[tuple[str]]: for mp in migration_paths ] + def _is_module_blacklisted(self, module): + return bool( + self.env["odoo.module"].search_count( + [("name", "=", module), ("blacklisted", "=", True)] + ) + ) + def _get_odoo_module_branch_id(self, module: str, branch: str) -> int: args = [ ("module_id", "=", module), From 54ad0fad4eae2042c4c841bf02edcb99f3e5f9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 23 May 2024 18:03:54 +0200 Subject: [PATCH 11/65] odoo_repository_migration: force scan when a migration path is created --- .../models/odoo_migration_path.py | 19 +++++++++++++++++++ .../views/odoo_migration_path.xml | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/odoo_repository_migration/models/odoo_migration_path.py b/odoo_repository_migration/models/odoo_migration_path.py index 2f15433c..83cd5190 100644 --- a/odoo_repository_migration/models/odoo_migration_path.py +++ b/odoo_repository_migration/models/odoo_migration_path.py @@ -36,3 +36,22 @@ class OdooMigrationPath(models.Model): def _compute_name(self): for rec in self: rec.name = f"{rec.source_branch_id.name} -> {rec.target_branch_id.name}" + + @api.model_create_multi + def create(self, vals_list): + records = super().create(vals_list) + # Automatically launch a scan on all relevant repositories when a + # migration path is created + if not self.env.context.get("disable_force_scan"): + records.action_force_scan() + return records + + def action_force_scan(self): + """Force the scan of the source branch. + + Scan is done on all related repositories configured to collect migration data. + """ + branches = self.source_branch_id | self.target_branch_id + return branches.repository_branch_ids.filtered( + lambda o: o.repository_id.collect_migration_data + ).action_force_scan() diff --git a/odoo_repository_migration/views/odoo_migration_path.xml b/odoo_repository_migration/views/odoo_migration_path.xml index ff8691ad..b5e52e54 100644 --- a/odoo_repository_migration/views/odoo_migration_path.xml +++ b/odoo_repository_migration/views/odoo_migration_path.xml @@ -8,6 +8,15 @@ odoo.migration.path
+
+
From 42fe1d85a212b93294dbf142ea4e7415d3d73394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Wed, 18 Sep 2024 15:36:24 +0200 Subject: [PATCH 12/65] odoo_repository_migration: limit strictly the scan of migration paths through a context key --- odoo_repository_migration/models/odoo_repository.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 7244e5f0..9231cdc0 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -44,8 +44,12 @@ def _create_jobs(self, branches): # Override to run the MigrationScanner once branches are scanned args = [] if branches: + # A strict scan of branches avoids unwanted migration scans + # For instance if we are interested only by 14.0 and 17.0 branches, + # this avoids to scan other migration paths like 15.0 -> 17.0 + strict_scan = self.env.context.get("strict_branches_scan") args = [ - "|", + "&" if strict_scan else "|", ("source_branch_id", "in", branches), ("target_branch_id", "in", branches), ] From 3756f74e2263178a35dce52b29ff1f6ec975af50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Sun, 22 Sep 2024 17:34:50 +0200 Subject: [PATCH 13/65] odoo_repository: split RepositoryScanner jobs in smaller jobs RepositoryScanner jobs could be quite long to execute on a repository (like 'odoo/odoo'), and depending on your Odoo config or your monitoring tools, such job could get killed before it ends up its work. While there is no issue to get the RepositoryScanner job killed and restarted later, it'll re-analyze the branch to scan to detect again the modules it has to scan, and this process itself could take ages before it re-starts the scan of the modules themselves. This commit is splitting these two tasks: - one job to detect modules to scan for a given repository/branch - one job created for each module to scan All of these jobs are depending on each other so we ensure that they are run sequentially (one branch after another, and one module after another) so only one job is running for a given repository at a time. We also ensure that no more than one job can be spawned by the user from UI. This results in a higher number of jobs, but scanning can be recovered easily. --- .../models/odoo_repository.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 9231cdc0..39d74253 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -1,7 +1,6 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) - from odoo import fields, models, tools from odoo.addons.queue_job.job import identity_exact @@ -35,23 +34,29 @@ def _reset_scanned_commits(self, branches=None): ) return res - def _create_jobs(self, branches): - jobs = super()._create_jobs(branches) + def _create_subsequent_jobs(self, branch, next_branches, all_branches, data): + jobs = super()._create_subsequent_jobs( + branch, next_branches, all_branches, data + ) + # Prepare migration scan jobs when its the last repository scan + last_scan = not next_branches + if not last_scan: + return jobs # Check if the addons_paths are compatible with 'oca_port' disable_collect = self.env.context.get("disable_collect_migration_data") if not self.collect_migration_data or disable_collect: return jobs # Override to run the MigrationScanner once branches are scanned args = [] - if branches: + if all_branches: # A strict scan of branches avoids unwanted migration scans # For instance if we are interested only by 14.0 and 17.0 branches, # this avoids to scan other migration paths like 15.0 -> 17.0 strict_scan = self.env.context.get("strict_branches_scan") args = [ "&" if strict_scan else "|", - ("source_branch_id", "in", branches), - ("target_branch_id", "in", branches), + ("source_branch_id", "in", all_branches), + ("target_branch_id", "in", all_branches), ] migration_paths = self.env["odoo.migration.path"].search(args) for rec in migration_paths: From a004a39001ed2d8c4f49f9ca8a5f8766275aa2ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 23 Sep 2024 14:43:24 +0200 Subject: [PATCH 14/65] odoo_repository_migration: split MigrationScanner jobs in smaller jobs MigrationScanner jobs could be quite long to execute on some repositories, and depending on your Odoo config or your monitoring tools, such job could get killed before it ends up its work. This commit is splitting these two tasks: - one job to scan a migration path for a given repository (that will create one job per module needing a migration scan) - one job created for each module to scan Migration scan jobs depends on the last repository scan job, as we need this one to be finished to know the list of modules that would need a migration scan. --- odoo_repository_migration/data/queue_job.xml | 14 ++- .../models/odoo_migration_path.py | 10 +++ .../models/odoo_module_branch.py | 39 +++++++- .../models/odoo_repository.py | 88 ++++++++++++++++--- 4 files changed, 138 insertions(+), 13 deletions(-) diff --git a/odoo_repository_migration/data/queue_job.xml b/odoo_repository_migration/data/queue_job.xml index d3c25e06..448affa1 100644 --- a/odoo_repository_migration/data/queue_job.xml +++ b/odoo_repository_migration/data/queue_job.xml @@ -9,11 +9,21 @@ - _scan_migration_data + _scan_migration_paths + + + + + + + _scan_migration_module diff --git a/odoo_repository_migration/models/odoo_migration_path.py b/odoo_repository_migration/models/odoo_migration_path.py index 83cd5190..605fc423 100644 --- a/odoo_repository_migration/models/odoo_migration_path.py +++ b/odoo_repository_migration/models/odoo_migration_path.py @@ -40,6 +40,16 @@ def _compute_name(self): @api.model_create_multi def create(self, vals_list): records = super().create(vals_list) + # Recompute 'migration_scan' flag on relevant modules + modules = self.env["odoo.module.branch"].search( + [ + "|", + ("branch_id", "in", records.source_branch_id.ids), + ("branch_id", "in", records.target_branch_id.ids), + ] + ) + modules.modified(["last_scanned_commit"]) + modules.flush_recordset(["migration_scan"]) # Automatically launch a scan on all relevant repositories when a # migration path is created if not self.env.context.get("disable_force_scan"): diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index 78699a98..544ea666 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -1,7 +1,7 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import fields, models +from odoo import api, fields, models class OdooModuleBranch(models.Model): @@ -13,6 +13,43 @@ class OdooModuleBranch(models.Model): string="Migrations", ) + migration_scan = fields.Boolean( + compute="_compute_migration_scan", + store=True, + help="Technical field telling if this module is elligible for a migration scan.", + ) + + @api.depends( + "last_scanned_commit", + "migration_ids.last_source_scanned_commit", + ) + def _compute_migration_scan(self): + for rec in self: + # Default repository migration scan policy + rec.migration_scan = rec.repository_id.collect_migration_data + if not rec.migration_scan: + continue + # Repository scan has to be performed first + if not rec.last_scanned_commit: + continue + # Migration scan to do as soon as a migration path is missing + # among existing scans + available_migration_paths = self.env["odoo.migration.path"].search( + [("source_branch_id", "=", rec.branch_id.id)] + ) + scanned_migration_paths = rec.migration_ids.migration_path_id + if available_migration_paths != scanned_migration_paths: + rec.migration_scan = True + continue + # Migration scan to do if last scanned commit doesn't match the last + # migration scan + for migration in rec.migration_ids: + if migration.last_source_scanned_commit != rec.last_scanned_commit: + rec.migration_scan = True + break + # Reaching this point means no scan is required + rec.migration_scan = False + def _to_dict(self): # Add the migrations data data = super()._to_dict() diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 39d74253..a0c51213 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -3,6 +3,8 @@ from odoo import fields, models, tools +from odoo.addons.queue_job.delay import chain +from odoo.addons.queue_job.exception import RetryableJobError from odoo.addons.queue_job.job import identity_exact from ..utils.scanner import MigrationScannerOdooEnv @@ -59,33 +61,99 @@ def _create_subsequent_jobs(self, branch, next_branches, all_branches, data): ("target_branch_id", "in", all_branches), ] migration_paths = self.env["odoo.migration.path"].search(args) - for rec in migration_paths: - migration_path = (rec.source_branch_id.name, rec.target_branch_id.name) + # Launch one job for all migration_paths + if migration_paths: + delayable = self.delayable( + description=f"Collect {self.display_name} migration data", + identity_key=identity_exact, + ) + job = delayable._scan_migration_paths(migration_paths.ids) + jobs.append(job) + return jobs + + def _scan_migration_paths(self, migration_path_ids): + """Scan repository branches to collect modules migration data. + + Spawn one job per module to scan. + """ + self.ensure_one() + jobs = [] + migration_paths = ( + self.env["odoo.migration.path"].browse(migration_path_ids).exists() + ) + for migration_path in migration_paths: + modules_to_scan = self._migration_get_modules_to_scan(migration_path) + if modules_to_scan: + jobs.extend( + self._migration_create_jobs_scan_module( + migration_path, modules_to_scan + ) + ) + if jobs: + chain(*jobs).delay() + return True + + def _migration_create_jobs_scan_module(self, migration_path, modules_to_scan): + jobs = [] + mig_path = ( + migration_path.source_branch_id.name, + migration_path.target_branch_id.name, + ) + for module in modules_to_scan: delayable = self.delayable( description=( - f"Collect {self.display_name} " - f"{' > '.join(migration_path)} migration data" + f"Collect {module.name} migration data " f"({' > '.join(mig_path)})" ), identity_key=identity_exact, ) - job = delayable._scan_migration_data(migration_path) + job = delayable._scan_migration_module( + migration_path.id, module.module_id.name + ) jobs.append(job) return jobs - def _scan_migration_data(self, migration_path): - """Scan repository branches to collect modules migration data.""" + def _scan_migration_module(self, migration_path_id, module_name): + """Scan migration path for `module_name`.""" + migration_path = ( + self.env["odoo.migration.path"].browse(migration_path_id).exists() + ) params = self._prepare_migration_scanner_parameters(migration_path) - scanner = MigrationScannerOdooEnv(**params) - return scanner.scan() + try: + scanner = MigrationScannerOdooEnv(**params) + return scanner.scan(modules=[module_name]) + except Exception as exc: + raise RetryableJobError("Scanner error") from exc + + def _migration_get_modules_to_scan(self, migration_path): + """Return `odoo.module.branch` records that need a migration scan.""" + self.ensure_one() + return self.env["odoo.module.branch"].search( + [ + ( + "repository_id", + "=", + self.id, + ), + ("branch_id", "=", migration_path.source_branch_id.id), + ("migration_scan", "=", True), + # Do not scan removed or pending (in PR) modules + ("removed", "=", False), + ("last_scanned_commit", "!=", False), + ] + ) def _prepare_migration_scanner_parameters(self, migration_path): ir_config = self.env["ir.config_parameter"] repositories_path = ir_config.get_param(self._repositories_path_key) + mig_path = ( + migration_path.source_branch_id.name, + migration_path.target_branch_id.name, + ) return { "org": self.org_id.name, "name": self.name, "clone_url": self.clone_url, - "migration_paths": [migration_path], + "migration_path": mig_path, "repositories_path": repositories_path, "repo_type": self.repo_type, "ssh_key": self.ssh_key_id.private_key, From 7aaab04b2e1e0313616190bf4b995070a028d67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Fri, 11 Oct 2024 09:15:23 +0200 Subject: [PATCH 15/65] odoo_repository_migration: fix 'push_scanned_data' Once a module is fully ported/migrated on the target branch, the `results` key is empty, as such the state has to be updated to 'Fully Ported'. --- .../models/odoo_module_branch_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index 8a9f1cba..b12e2c9a 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -124,7 +124,7 @@ def push_scanned_data(self, module_branch_id, data): "last_target_scanned_commit": data["target_commit"], } for key in ("process", "results"): - if data.get(key): + if key in data: values[key] = data[key] return self._create_or_update(module_branch_id, migration_path, values) From 648c55f07be96924f51bee02b5ee90e98e3ac479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 14 Oct 2024 16:17:23 +0200 Subject: [PATCH 16/65] odoo_repository_migration: fix 'migration_scan' computation --- odoo_repository_migration/models/odoo_module_branch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index 544ea666..a3e54a6a 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -37,7 +37,9 @@ def _compute_migration_scan(self): available_migration_paths = self.env["odoo.migration.path"].search( [("source_branch_id", "=", rec.branch_id.id)] ) - scanned_migration_paths = rec.migration_ids.migration_path_id + scanned_migration_paths = rec.migration_ids.migration_path_id.filtered( + "active" + ) if available_migration_paths != scanned_migration_paths: rec.migration_scan = True continue From bbe55b74dcd07ec986de8682d90c2e10b9a46d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 17 Oct 2024 19:43:45 +0200 Subject: [PATCH 17/65] odoo_repository: add User and Manager user groups --- odoo_repository_migration/security/ir.model.access.csv | 6 +++--- odoo_repository_migration/views/odoo_migration_path.xml | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/odoo_repository_migration/security/ir.model.access.csv b/odoo_repository_migration/security/ir.model.access.csv index 41e6e372..7b021329 100644 --- a/odoo_repository_migration/security/ir.model.access.csv +++ b/odoo_repository_migration/security/ir.model.access.csv @@ -1,4 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_odoo_migration_path_user,odoo_migration_path_user,model_odoo_migration_path,base.group_user,1,0,0,0 -access_odoo_migration_path_manager,odoo_migration_path_manager,model_odoo_migration_path,base.group_system,1,0,1,1 -access_odoo_module_branch_migration_user,odoo_module_branch_migration_user,model_odoo_module_branch_migration,base.group_user,1,0,0,0 +access_odoo_migration_path_user,odoo_migration_path_user,model_odoo_migration_path,odoo_repository.group_odoo_repository_user,1,0,0,0 +access_odoo_migration_path_manager,odoo_migration_path_manager,model_odoo_migration_path,odoo_repository.group_odoo_repository_manager,1,0,1,1 +access_odoo_module_branch_migration_user,odoo_module_branch_migration_user,model_odoo_module_branch_migration,odoo_repository.group_odoo_repository_user,1,0,0,0 diff --git a/odoo_repository_migration/views/odoo_migration_path.xml b/odoo_repository_migration/views/odoo_migration_path.xml index b5e52e54..151c9c79 100644 --- a/odoo_repository_migration/views/odoo_migration_path.xml +++ b/odoo_repository_migration/views/odoo_migration_path.xml @@ -15,6 +15,7 @@ string="Force Scan" class="btn-secondary" confirm="This operation will restart the scan on all relevant repositories. Are you sure?" + groups="odoo_repository.group_odoo_repository_manager" /> From 4fca55f4db4934c6a2611ef3309bb2195a1fbb43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Tue, 29 Oct 2024 17:24:58 +0100 Subject: [PATCH 18/65] odoo_repository: option to force cloned repository name --- odoo_repository_migration/models/odoo_repository.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index a0c51213..411af9ff 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -158,6 +158,10 @@ def _prepare_migration_scanner_parameters(self, migration_path): "repo_type": self.repo_type, "ssh_key": self.ssh_key_id.private_key, "token": self._get_token(), + "workaround_fs_errors": ( + self.env.company.config_odoo_repository_workaround_fs_errors + ), + "clone_name": self.clone_name, "env": self.env, } From 49c3f166a487e289023c8f616abd987f50f6065b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Fri, 18 Oct 2024 14:35:41 +0200 Subject: [PATCH 19/65] [REF] Allow same module name across different repositories This change allows to get a module `x` present in different repositories (unicity constraint on `odoo.module.branch` has changed accordingly). This is useful in case two projects have `x` installed but the code of these modules (that can be different) are hosted in their own project repository. Therefore, the scan of repositories and project modules import have been adapted to fullfil this new possibility. Features: - allows multiple instances of `` records sharing the same module technical name and branch but belonging to different repositories, - repositories have now a new `specific` flag, propagated to their modules.. By default they host generic modules, but project repositories will host specific modules that cannot be shared to other projects, - a generic module cannot depend on a specific module - when importing modules in a project, specific modules of other projects cannot be mapped, - once a specific module is scanned in a project repository, relevant orphaned modules installed in a project will be re-mapped to this newly created specific module, - detect PR only on generic modules (unmerged/pending modules are generic only, specific modules have to be found in project repositories). --- odoo_repository_migration/utils/scanner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/odoo_repository_migration/utils/scanner.py b/odoo_repository_migration/utils/scanner.py index deddc1d9..994add61 100644 --- a/odoo_repository_migration/utils/scanner.py +++ b/odoo_repository_migration/utils/scanner.py @@ -49,8 +49,9 @@ def _is_module_blacklisted(self, module): ) ) - def _get_odoo_module_branch_id(self, module: str, branch: str) -> int: + def _get_odoo_module_branch_id(self, repo_id: int, module: str, branch: str) -> int: args = [ + ("repository_id", "=", repo_id), ("module_id", "=", module), ("branch_id", "=", branch), ] From e48eb0d21f9a2a150155b047e668a85c8d56ecdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 31 Oct 2024 09:05:28 +0100 Subject: [PATCH 20/65] odoo_repository_*: rename some filters --- odoo_repository_migration/views/odoo_module_branch.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odoo_repository_migration/views/odoo_module_branch.xml b/odoo_repository_migration/views/odoo_module_branch.xml index d0558fc4..5ee36f58 100644 --- a/odoo_repository_migration/views/odoo_module_branch.xml +++ b/odoo_repository_migration/views/odoo_module_branch.xml @@ -27,7 +27,7 @@ odoo.module.branch - + Date: Wed, 13 Nov 2024 09:00:30 +0100 Subject: [PATCH 21/65] fixup! MigrationScanner: upgrade oca-port --- .../models/odoo_repository.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 411af9ff..4e100192 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -106,21 +106,23 @@ def _migration_create_jobs_scan_module(self, migration_path, modules_to_scan): ), identity_key=identity_exact, ) - job = delayable._scan_migration_module( - migration_path.id, module.module_id.name - ) + job = delayable._scan_migration_module(migration_path.id, module.id) jobs.append(job) return jobs - def _scan_migration_module(self, migration_path_id, module_name): - """Scan migration path for `module_name`.""" + def _scan_migration_module(self, migration_path_id, module_branch_id): + """Scan migration path for `module_branch_id`.""" + module = self.env["odoo.module.branch"].browse(module_branch_id).exists() + module.ensure_one() migration_path = ( self.env["odoo.migration.path"].browse(migration_path_id).exists() ) params = self._prepare_migration_scanner_parameters(migration_path) try: scanner = MigrationScannerOdooEnv(**params) - return scanner.scan(modules=[module_name]) + return scanner.scan( + addons_path=module.addons_path, module_names=[module.module_id.name] + ) except Exception as exc: raise RetryableJobError("Scanner error") from exc From c82716f448b2a2de2f99f01dbc5781352b812337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Tue, 19 Nov 2024 16:15:05 +0100 Subject: [PATCH 22/65] odoo_repository[_migration]: fix access to 'ir.config_parameter' --- odoo_repository_migration/models/odoo_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 4e100192..065282d2 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -146,7 +146,7 @@ def _migration_get_modules_to_scan(self, migration_path): def _prepare_migration_scanner_parameters(self, migration_path): ir_config = self.env["ir.config_parameter"] - repositories_path = ir_config.get_param(self._repositories_path_key) + repositories_path = ir_config.sudo().get_param(self._repositories_path_key) mig_path = ( migration_path.source_branch_id.name, migration_path.target_branch_id.name, From ff93e657e3b4cd6db537c4894bafd47b3f46f648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 17 Oct 2024 14:34:28 +0200 Subject: [PATCH 23/65] odoo_repository_migration: auto-archive 'odoo.module.branch.migration' --- odoo_repository_migration/models/odoo_module_branch_migration.py | 1 + 1 file changed, 1 insertion(+) diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index b12e2c9a..88af9062 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -70,6 +70,7 @@ class OdooModuleBranchMigration(models.Model): results_text = fields.Text(compute="_compute_results_text") last_source_scanned_commit = fields.Char() last_target_scanned_commit = fields.Char() + active = fields.Boolean(related="migration_path_id.active", store=True) _sql_constraints = [ ( From 7f711372fba38d69398a7d93b49345caa27d622d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 17 Oct 2024 14:35:09 +0200 Subject: [PATCH 24/65] odoo_repository_migration: fix 'migration_scan' flag Remove migration path that doesn't match branches scanned in the repository (e.g. 18.0 branch could be missing in a repo while a migration path 16.0 -> 18.0 is configured, so no need to do a migration scan in this case). --- odoo_repository_migration/__manifest__.py | 2 +- .../migrations/16.0.1.0.1/post-migration.py | 22 +++++++++++++++++++ .../models/odoo_module_branch.py | 22 ++++++++++++++----- .../models/odoo_repository.py | 3 --- 4 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 odoo_repository_migration/migrations/16.0.1.0.1/post-migration.py diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index 46cbf98a..f1d65302 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "16.0.1.0.0", + "version": "16.0.1.0.1", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/camptocamp/odoo-repository", diff --git a/odoo_repository_migration/migrations/16.0.1.0.1/post-migration.py b/odoo_repository_migration/migrations/16.0.1.0.1/post-migration.py new file mode 100644 index 00000000..8262205f --- /dev/null +++ b/odoo_repository_migration/migrations/16.0.1.0.1/post-migration.py @@ -0,0 +1,22 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import logging + +from odoo import SUPERUSER_ID, api + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + if not version: + return + env = api.Environment(cr, SUPERUSER_ID, {}) + recompute_migration_scan(env) + + +def recompute_migration_scan(env): + _logger.info("Recompute '.migration_scan' field...") + mods = env["odoo.module.branch"].search([]) + mods.modified(["last_scanned_commit"]) + mods.flush_recordset(["migration_scan"]) diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index a3e54a6a..c8ececda 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -20,11 +20,18 @@ class OdooModuleBranch(models.Model): ) @api.depends( + "removed", + "pr_url", "last_scanned_commit", "migration_ids.last_source_scanned_commit", + "repository_id.collect_migration_data", ) def _compute_migration_scan(self): for rec in self: + # Do not scan removed or pending (in PR) modules + if rec.removed or rec.pr_url: + rec.migration_scan = False + continue # Default repository migration scan policy rec.migration_scan = rec.repository_id.collect_migration_data if not rec.migration_scan: @@ -33,13 +40,18 @@ def _compute_migration_scan(self): if not rec.last_scanned_commit: continue # Migration scan to do as soon as a migration path is missing - # among existing scans + # among existing scans. However, we remove migration path that doesn't + # match branches scanned in the repository (e.g. 18.0 branch could + # be missing in a repo while a migration path 16.0 -> 18.0 is + # configured, so no need to do a migration scan in this case). + available_repo_branches = rec.repository_id.branch_ids.branch_id available_migration_paths = self.env["odoo.migration.path"].search( - [("source_branch_id", "=", rec.branch_id.id)] - ) - scanned_migration_paths = rec.migration_ids.migration_path_id.filtered( - "active" + [ + ("source_branch_id", "=", rec.branch_id.id), + ("target_branch_id", "in", available_repo_branches.ids), + ] ) + scanned_migration_paths = rec.migration_ids.migration_path_id if available_migration_paths != scanned_migration_paths: rec.migration_scan = True continue diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 065282d2..521100b6 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -138,9 +138,6 @@ def _migration_get_modules_to_scan(self, migration_path): ), ("branch_id", "=", migration_path.source_branch_id.id), ("migration_scan", "=", True), - # Do not scan removed or pending (in PR) modules - ("removed", "=", False), - ("last_scanned_commit", "!=", False), ] ) From 989df06a0cb607c07c5bf0e3a4bd8052dc0822cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 17 Oct 2024 14:52:35 +0200 Subject: [PATCH 25/65] odoo_repository_migration: do not force scan when creating a migration path And change the method/button 'action_force_scan' to 'action_scan'. Thanks to the new way jobs are spawned (PR #53) and the new `.migration_scan` flag, there is no need to reset the last scanned commits to perform a migration scan on relevant modules, preserving a lot of computation time and resources. --- .../models/odoo_migration_path.py | 10 +++------- .../views/odoo_migration_path.xml | 6 +++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/odoo_repository_migration/models/odoo_migration_path.py b/odoo_repository_migration/models/odoo_migration_path.py index 605fc423..7dd314be 100644 --- a/odoo_repository_migration/models/odoo_migration_path.py +++ b/odoo_repository_migration/models/odoo_migration_path.py @@ -50,18 +50,14 @@ def create(self, vals_list): ) modules.modified(["last_scanned_commit"]) modules.flush_recordset(["migration_scan"]) - # Automatically launch a scan on all relevant repositories when a - # migration path is created - if not self.env.context.get("disable_force_scan"): - records.action_force_scan() return records - def action_force_scan(self): - """Force the scan of the source branch. + def action_scan(self): + """Scan the source+target branches. Scan is done on all related repositories configured to collect migration data. """ branches = self.source_branch_id | self.target_branch_id return branches.repository_branch_ids.filtered( lambda o: o.repository_id.collect_migration_data - ).action_force_scan() + ).action_scan() diff --git a/odoo_repository_migration/views/odoo_migration_path.xml b/odoo_repository_migration/views/odoo_migration_path.xml index 151c9c79..a2c5c716 100644 --- a/odoo_repository_migration/views/odoo_migration_path.xml +++ b/odoo_repository_migration/views/odoo_migration_path.xml @@ -10,11 +10,11 @@
From 5299e6cfe39d417752fc979bdaf1e08974d7ffd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Tue, 19 Nov 2024 17:05:36 +0100 Subject: [PATCH 26/65] odoo_repository_migration: add tests on 'migration_scan' flag --- odoo_repository_migration/tests/__init__.py | 1 + .../tests/test_odoo_module_branch.py | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 odoo_repository_migration/tests/__init__.py create mode 100644 odoo_repository_migration/tests/test_odoo_module_branch.py diff --git a/odoo_repository_migration/tests/__init__.py b/odoo_repository_migration/tests/__init__.py new file mode 100644 index 00000000..39bb08d7 --- /dev/null +++ b/odoo_repository_migration/tests/__init__.py @@ -0,0 +1 @@ +from . import test_odoo_module_branch diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py new file mode 100644 index 00000000..0d7076b5 --- /dev/null +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -0,0 +1,68 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.addons.odoo_repository.tests import common + + +class TestOdooModuleBranch(common.Common): + def setUp(self): + super().setUp() + self.module = self._create_odoo_module("my_module") + self.repo_branch = self._create_odoo_repository_branch( + self.odoo_repository, self.branch + ) + self.repo_branch2 = self._create_odoo_repository_branch( + self.odoo_repository, self.branch2 + ) + self.module_branch = self._create_odoo_module_branch( + self.module, + self.branch, + specific=False, + repository_branch_id=self.repo_branch.id, + last_scanned_commit="sha", + ) + + def test_migration_scan_removed(self): + self.module_branch.removed = True + self.assertFalse(self.module_branch.migration_scan) + + def test_migration_scan_pr_url(self): + self.module_branch.pr_url = "https://my/pr" + self.assertFalse(self.module_branch.migration_scan) + + def test_migration_scan_repo_collect_migration_data(self): + self.assertFalse(self.module_branch.migration_scan) + self.odoo_repository.collect_migration_data = True + # It's not enough to flag the module as there is no available + # migration path to scan + self.assertFalse(self.module_branch.migration_scan) + + def test_migration_scan_never_scanned(self): + self.module_branch.last_scanned_commit = False + self.assertFalse(self.module_branch.migration_scan) + self.odoo_repository.collect_migration_data = True + self.assertTrue(self.module_branch.migration_scan) + + def test_migration_scan_missing_migration_path(self): + self.odoo_repository.collect_migration_data = True + self.assertFalse(self.module_branch.migration_scan) + mig_path = self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": self.branch2.id, + } + ) + self.assertTrue(self.module_branch.migration_scan) + # Once we collected migration data for the expected branch+commit + # the module doesn't require a migration scan anymore + self.module_branch.migration_ids |= ( + # Simulate migration data addition + self.env["odoo.module.branch.migration"].create( + { + "module_branch_id": self.module_branch.id, + "migration_path_id": mig_path.id, + "last_source_scanned_commit": self.module_branch.last_scanned_commit, + } + ) + ) + self.assertFalse(self.module_branch.migration_scan) From 3af82568aeca5119c95745ef124997f3afdd5ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 9 Dec 2024 10:02:13 +0100 Subject: [PATCH 27/65] odoo_repository_migration: fix migration data computation --- odoo_repository_migration/__manifest__.py | 2 +- .../migrations/16.0.1.1.0/post-migration.py | 26 ++++ .../models/odoo_module_branch.py | 39 ++++-- .../models/odoo_module_branch_migration.py | 59 ++++++++- .../tests/test_odoo_module_branch.py | 120 ++++++++++++++++-- odoo_repository_migration/utils/scanner.py | 13 +- .../views/odoo_module_branch.xml | 1 + .../views/odoo_module_branch_migration.xml | 3 + 8 files changed, 237 insertions(+), 26 deletions(-) create mode 100644 odoo_repository_migration/migrations/16.0.1.1.0/post-migration.py diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index f1d65302..f6f6d967 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "16.0.1.0.1", + "version": "16.0.1.1.0", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/camptocamp/odoo-repository", diff --git a/odoo_repository_migration/migrations/16.0.1.1.0/post-migration.py b/odoo_repository_migration/migrations/16.0.1.1.0/post-migration.py new file mode 100644 index 00000000..7de23bfe --- /dev/null +++ b/odoo_repository_migration/migrations/16.0.1.1.0/post-migration.py @@ -0,0 +1,26 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import logging + +from odoo import SUPERUSER_ID, api + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + if not version: + return + env = api.Environment(cr, SUPERUSER_ID, {}) + recompute_migration_scan(env) + + +def recompute_migration_scan(env): + _logger.info("Recompute '.migration_scan' field...") + mb_model = env["odoo.module.branch"] + mods = mb_model.search([]) + mods.modified(["last_scanned_commit"]) + mods.flush_recordset(["migration_scan"]) + # Reset last target scanned commit on those that need a migration scan + mods_to_scan = mb_model.search([("migration_scan", "=", True)]) + mods_to_scan.migration_ids.write({"last_target_scanned_commit": False}) diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index c8ececda..36dfce56 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -23,7 +23,7 @@ class OdooModuleBranch(models.Model): "removed", "pr_url", "last_scanned_commit", - "migration_ids.last_source_scanned_commit", + "migration_ids.migration_scan", "repository_id.collect_migration_data", ) def _compute_migration_scan(self): @@ -55,14 +55,8 @@ def _compute_migration_scan(self): if available_migration_paths != scanned_migration_paths: rec.migration_scan = True continue - # Migration scan to do if last scanned commit doesn't match the last - # migration scan - for migration in rec.migration_ids: - if migration.last_source_scanned_commit != rec.last_scanned_commit: - rec.migration_scan = True - break - # Reaching this point means no scan is required - rec.migration_scan = False + # Migration scan to do if any of the migration path requires one + rec.migration_scan = any(rec.migration_ids.mapped("migration_scan")) def _to_dict(self): # Add the migrations data @@ -71,3 +65,30 @@ def _to_dict(self): for migration in self.migration_ids: data["migrations"].append(migration._to_dict()) return data + + @api.model_create_multi + def create(self, vals_list): + recs = super().create(vals_list) + recs._update_migration_target_module_id() + return recs + + def write(self, vals): + res = super().write(vals) + # When 'pr_url' is set or unset, this means the module has been found + # in a PR or has been merged upstream. We want to recompute the target + # module in migration data in such case. + if "pr_url" in vals: + self._update_migration_target_module_id() + return res + + def _update_migration_target_module_id(self): + """Update `target_module_id` field on relevant module migration records.""" + for rec in self: + migrations = self.env["odoo.module.branch.migration"].search( + [ + ("module_id", "=", rec.module_id.id), + ("target_branch_id", "=", rec.branch_id.id), + ] + ) + # Recompute 'target_module_id' field + migrations._compute_target_module_branch_id() diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index 88af9062..b678a04d 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -15,7 +15,7 @@ class OdooModuleBranchMigration(models.Model): module_branch_id = fields.Many2one( comodel_name="odoo.module.branch", ondelete="cascade", - string="Module", + string="Source", required=True, index=True, ) @@ -46,6 +46,14 @@ class OdooModuleBranchMigration(models.Model): store=True, index=True, ) + target_module_branch_id = fields.Many2one( + comodel_name="odoo.module.branch", + ondelete="cascade", + string="Target", + compute="_compute_target_module_branch_id", + store=True, + index=True, + ) author_ids = fields.Many2many(related="module_branch_id.author_ids") maintainer_ids = fields.Many2many(related="module_branch_id.maintainer_ids") process = fields.Char(index=True) @@ -71,6 +79,11 @@ class OdooModuleBranchMigration(models.Model): last_source_scanned_commit = fields.Char() last_target_scanned_commit = fields.Char() active = fields.Boolean(related="migration_path_id.active", store=True) + migration_scan = fields.Boolean( + compute="_compute_migration_scan", + store=True, + help="Technical field telling if this migration path needs a migration scan.", + ) _sql_constraints = [ ( @@ -92,6 +105,17 @@ def _compute_display_name(self): f"{rec.source_branch_id.name} -> {rec.target_branch_id.name}" ) + @api.depends("module_branch_id", "migration_path_id") + def _compute_target_module_branch_id(self): + module_branch_model = self.env["odoo.module.branch"] + for rec in self: + rec.target_module_branch_id = module_branch_model._find( + rec.migration_path_id.target_branch_id, + rec.module_branch_id.module_id, + rec.module_branch_id.repository_id, + domain=[("installable", "=", True)], + ) + @api.depends("process", "pr_url") def _compute_state(self): for rec in self: @@ -109,6 +133,32 @@ def _compute_results_text(self): for rec in self: rec.results_text = pprint.pformat(rec.results) + @api.depends( + "last_source_scanned_commit", + "last_target_scanned_commit", + "pr_url", + "target_module_branch_id.pr_url", + "target_module_branch_id.last_scanned_commit", + ) + def _compute_migration_scan(self): + # Migration scan to do if last scanned commit doesn't match the last + # migration scan, both for source and target modules. + for rec in self: + rec.migration_scan = False + if ( + rec.last_source_scanned_commit + != rec.module_branch_id.last_scanned_commit + ): + rec.migration_scan = True + elif ( + rec.target_module_branch_id.last_scanned_commit + and rec.last_target_scanned_commit + != rec.target_module_branch_id.last_scanned_commit + ): + rec.migration_scan = True + elif rec.target_module_branch_id.pr_url != rec.pr_url: + rec.migration_scan = True + @api.model @api.returns("odoo.module.branch.migration") def push_scanned_data(self, module_branch_id, data): @@ -124,9 +174,10 @@ def push_scanned_data(self, module_branch_id, data): "last_source_scanned_commit": data["source_commit"], "last_target_scanned_commit": data["target_commit"], } - for key in ("process", "results"): - if key in data: - values[key] = data[key] + # Update migration data only if a migration scan occured + if "report" in data: + values["process"] = data["report"].get("process", False) + values["results"] = data["report"].get("results", {}) return self._create_or_update(module_branch_id, migration_path, values) def _create_or_update(self, module_branch_id, migration_path, values): diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index 0d7076b5..ebfa7254 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -22,6 +22,22 @@ def setUp(self): last_scanned_commit="sha", ) + def _simulate_migration_scan(self, target_commit, report=None): + """Helper method that pushes scanned migration data.""" + data = { + "module": self.module_branch.module_name, + "source_branch": self.branch.name, + "target_branch": self.branch2.name, + "source_commit": self.module_branch.last_scanned_commit, + "target_commit": target_commit, + } + if report is not None: + data["report"] = report + return self.env["odoo.module.branch.migration"].push_scanned_data( + self.module_branch.id, + data, + ) + def test_migration_scan_removed(self): self.module_branch.removed = True self.assertFalse(self.module_branch.migration_scan) @@ -39,30 +55,114 @@ def test_migration_scan_repo_collect_migration_data(self): def test_migration_scan_never_scanned(self): self.module_branch.last_scanned_commit = False + self.assertFalse(self.module_branch.migration_ids) self.assertFalse(self.module_branch.migration_scan) self.odoo_repository.collect_migration_data = True + self.assertFalse(self.module_branch.migration_ids) self.assertTrue(self.module_branch.migration_scan) def test_migration_scan_missing_migration_path(self): self.odoo_repository.collect_migration_data = True + self.assertFalse(self.module_branch.migration_ids) self.assertFalse(self.module_branch.migration_scan) - mig_path = self.env["odoo.migration.path"].create( + self.env["odoo.migration.path"].create( { "source_branch_id": self.branch.id, "target_branch_id": self.branch2.id, } ) + self.assertFalse(self.module_branch.migration_ids) self.assertTrue(self.module_branch.migration_scan) # Once we collected migration data for the expected branch+commit # the module doesn't require a migration scan anymore - self.module_branch.migration_ids |= ( - # Simulate migration data addition - self.env["odoo.module.branch.migration"].create( - { - "module_branch_id": self.module_branch.id, - "migration_path_id": mig_path.id, - "last_source_scanned_commit": self.module_branch.last_scanned_commit, - } - ) + self._simulate_migration_scan( + "target_commit1", report={"process": "migrate", "results": {}} ) + self.assertTrue(self.module_branch.migration_ids) + self.assertFalse(self.module_branch.migration_scan) + + def test_migration_scan_target_module_in_review_then_merged(self): + """Test full flow of the migration of a module. + + 1) At first, the module of the source branch needs a migration scan + because the migration data are missing for the target branch. + 2) Once the migration is done (and migration data available), the migration + scan is not needed anymore. + 3) Then the target module could be found in a PR to review, but this + doesn't + """ + self.odoo_repository.collect_migration_data = True + # Simulate a scan of a given migration path while the target module is + # not yet migrated/available in a repository + self.assertFalse(self.module_branch.migration_ids) + self.assertFalse(self.module_branch.migration_scan) + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": self.branch2.id, + } + ) + self.assertFalse(self.module_branch.migration_ids) + self.assertTrue(self.module_branch.migration_scan) + self._simulate_migration_scan( + "target_commit1", report={"process": "migrate", "results": {}} + ) + self.assertTrue(self.module_branch.migration_ids) + self.assertFalse(self.module_branch.migration_ids.migration_scan) + self.assertFalse(self.module_branch.migration_scan) + self.assertEqual(self.module_branch.migration_ids.state, "migrate") + # Make the module available for targeted branch in review (available in a PR). + # The source module now needs a migration scan as the target module is + # available in a PR, the migration status has to be updated. + target_module_branch = self._create_odoo_module_branch( + self.module, + self.branch2, + specific=False, + repository_branch_id=self.repo_branch.id, + # Module available in a PR + pr_url="https://my/pr", + ) + self.assertEqual( + self.module_branch.migration_ids.target_module_branch_id, + target_module_branch, + ) + self.assertEqual(self.module_branch.migration_ids.state, "migrate") + self.assertTrue(self.module_branch.migration_ids.migration_scan) + self.assertTrue(self.module_branch.migration_scan) + # Simulate the migration scan. + # The source module doesn't need a migration scan anymore. + self._simulate_migration_scan( + "target_commit1", + report={ + "process": "migrate", + "results": {"existing_pr": {"url": target_module_branch.pr_url}}, + }, + ) + self.assertEqual(self.module_branch.migration_ids.state, "review_migration") + self.assertFalse(self.module_branch.migration_ids.migration_scan) + self.assertFalse(self.module_branch.migration_scan) + # Merge the module in the upstream repository. + # The source module now needs a migration scan (to check if there is + # something to port, or to set the module as fully ported...). + target_module_branch.write( + { + "last_scanned_commit": "target_commit2", + # When 'pr_url' is unset, this means the module has been merged + "pr_url": False, + } + ) + self.module_branch.migration_ids.last_target_scanned_commit = ( + target_module_branch.last_scanned_commit + ) + self.assertEqual( + self.module_branch.migration_ids.target_module_branch_id, + target_module_branch, + ) + self.assertTrue(self.module_branch.migration_ids.migration_scan) + self.assertTrue(self.module_branch.migration_scan) + # Simulate the migration scan. + # The source module is fully ported and doesn't need a migration scan afterwards. + self._simulate_migration_scan("target_commit2", report={"results": {}}) + self.assertEqual(self.module_branch.migration_ids.state, "fully_ported") + self.assertFalse(self.module_branch.migration_ids.migration_scan) self.assertFalse(self.module_branch.migration_scan) diff --git a/odoo_repository_migration/utils/scanner.py b/odoo_repository_migration/utils/scanner.py index 994add61..c4f3eb81 100644 --- a/odoo_repository_migration/utils/scanner.py +++ b/odoo_repository_migration/utils/scanner.py @@ -70,16 +70,25 @@ def _get_odoo_module_branch_migration_id( return migration.id def _get_odoo_module_branch_migration_data( - self, module: str, source_branch: str, target_branch: str + self, repo_id: int, module: str, source_branch: str, target_branch: str ) -> dict: args = [ + ("module_branch_id.repository_id", "=", repo_id), ("module_id", "=", module), ("source_branch_id", "=", source_branch), ("target_branch_id", "=", target_branch), ] migration = self.env["odoo.module.branch.migration"].search(args) if migration: - return migration.read()[0] + data = { + "last_source_scanned_commit": migration.module_branch_id.last_scanned_commit, + "last_target_scanned_commit": ( + migration.target_module_branch_id.last_scanned_commit + ), + "last_source_mig_scanned_commit": migration.last_source_scanned_commit, + "last_target_mig_scanned_commit": migration.last_target_scanned_commit, + } + return data return {} def _push_scanned_data(self, module_branch_id: int, data: dict): diff --git a/odoo_repository_migration/views/odoo_module_branch.xml b/odoo_repository_migration/views/odoo_module_branch.xml index 5ee36f58..a0cd9738 100644 --- a/odoo_repository_migration/views/odoo_module_branch.xml +++ b/odoo_repository_migration/views/odoo_module_branch.xml @@ -15,6 +15,7 @@ + diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index cad1e2ae..7872c8d3 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -14,6 +14,7 @@ + @@ -36,8 +37,10 @@ + + From 0fe1408d8b53ed2e2d3cc95d8b6def328c62a9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Fri, 10 Jan 2025 16:12:08 +0100 Subject: [PATCH 28/65] odoo_project_migration: handle projects in migration data E.g this allows to cross modules common to different projects, to share the costs between them regarding a migration. --- odoo_repository_migration/views/odoo_module_branch_migration.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index 7872c8d3..d14ed39b 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -19,6 +19,7 @@ + From dbb879c9bc9c1febec0ba6a424a2cb0d5c32f531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Fri, 28 Feb 2025 11:20:26 +0100 Subject: [PATCH 29/65] odoo_repository_migration: hide columns by default in 'Migrations' menu --- .../views/odoo_module_branch_migration.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index d14ed39b..17eb9973 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -37,9 +37,9 @@ - - - + + + From 13afecf9ec72ebd680626cae3fe4e15e00460d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 13 Mar 2025 16:52:53 +0100 Subject: [PATCH 30/65] odoo_repository_migration: support migration scan between two repositories This supports the migration scan for modules that have been moved to another repository. This commit also adds new technical flags and migration states to improve module qualification during a migration: - Moved to standard - Moved to OCA - Moved to generic repo (specific modules moved to generic repo) Migration with such states won't trigger a migration scan as modules could be different. E.g. 'l10n_eu_oss' Odoo module is not the same than OCA one starting from 15.0, and OCA renamed its module 'l10n_eu_oss_oca' from this version, either to complete std implementation, or to propose another one. So these states will help integrators to identify such modules. Later we could add a feature to set a given module as renamed from a given version, like 'l10n_eu_oss' renamed to 'l10n_eu_oss_oca', so the module won't be qualified with 'Moved to standard' state as it's just a renaming. But such feature will require to also improve 'oca-port' to handle such case to perform a migration scan. --- odoo_repository_migration/__manifest__.py | 2 +- .../migrations/16.0.1.2.0/post-migration.py | 56 ++++++++ .../models/odoo_module_branch_migration.py | 90 ++++++++++++- .../models/odoo_repository.py | 30 ++++- .../tests/test_odoo_module_branch.py | 122 ++++++++++++++++++ 5 files changed, 295 insertions(+), 5 deletions(-) create mode 100644 odoo_repository_migration/migrations/16.0.1.2.0/post-migration.py diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index f6f6d967..9625b7aa 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "16.0.1.1.0", + "version": "16.0.1.2.0", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/camptocamp/odoo-repository", diff --git a/odoo_repository_migration/migrations/16.0.1.2.0/post-migration.py b/odoo_repository_migration/migrations/16.0.1.2.0/post-migration.py new file mode 100644 index 00000000..471bdb3e --- /dev/null +++ b/odoo_repository_migration/migrations/16.0.1.2.0/post-migration.py @@ -0,0 +1,56 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import logging + +from odoo import SUPERUSER_ID, api + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + if not version: + return + env = api.Environment(cr, SUPERUSER_ID, {}) + fix_migration_states(env) + + +def fix_migration_states(env): + _logger.info("Plan a scan to fix '.state' field...") + query = """ + SELECT mig.id + FROM odoo_module_branch_migration mig + JOIN odoo_module_branch AS source + ON mig.module_branch_id=source.id + JOIN odoo_module_branch AS target + ON mig.target_module_branch_id=target.id + WHERE source.repository_branch_id IS NOT NULL + AND target.repository_branch_id IS NOT NULL + AND source.repository_id != target.repository_id; + """ + env.cr.execute(query) + mig_ids = [row[0] for row in env.cr.fetchall()] + # Reset 'last_target_scanned_commit' to recompute 'migration_scan' + if mig_ids: + query = """ + UPDATE odoo_module_branch_migration + SET last_target_scanned_commit=NULL + WHERE id IN %s; + """ + args = (tuple(mig_ids),) + env.cr.execute(query, args) + # Reset collected migration data on modules that shouldn't have any + mbm_model = env["odoo.module.branch.migration"] + migs = mbm_model.search( + [ + ("results", "!=", False), + ("repository_id.collect_migration_data", "=", False), + ] + ) + migs.results = False + # Recompute migration state/flag, especially for modules moved to another repo + # that won't trigger a migration scan. + migs = mbm_model.search([("id", "in", mig_ids)]) + env.add_to_compute(migs._fields["state"], migs) + env.add_to_compute(migs._fields["migration_scan"], migs) + migs.modified(["state", "migration_scan"]) diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index b678a04d..aea3d234 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -57,12 +57,43 @@ class OdooModuleBranchMigration(models.Model): author_ids = fields.Many2many(related="module_branch_id.author_ids") maintainer_ids = fields.Many2many(related="module_branch_id.maintainer_ids") process = fields.Char(index=True) + moved_to_standard = fields.Boolean( + compute="_compute_moved_to_standard", + store=True, + help=( + "Module now available in Odoo standard code. " + "This module is maybe not exactly the same, and doesn't have the " + "same scope so it deserves a check during a migration." + ), + ) + moved_to_oca = fields.Boolean( + compute="_compute_moved_to_oca", + store=True, + help=( + "Module now available in OCA. " + "This module is maybe not exactly the same, and doesn't have the " + "same scope so it deserves a check during a migration." + ), + ) + moved_to_generic = fields.Boolean( + compute="_compute_moved_to_generic", + store=True, + string="Now generic", + help=( + "Specific module now available in a generic repository. " + "This module is maybe not exactly the same, and doesn't have the " + "same scope so it deserves a check during a migration." + ), + ) state = fields.Selection( selection=[ ("fully_ported", "Fully Ported"), ("migrate", "To migrate"), ("port_commits", "Commits to port"), ("review_migration", "Migration to review"), + ("moved_to_standard", "Moved to standard"), + ("moved_to_oca", "Moved to OCA"), + ("moved_to_generic", "Moved to generic repo"), ], string="Migration Status", compute="_compute_state", @@ -116,9 +147,58 @@ def _compute_target_module_branch_id(self): domain=[("installable", "=", True)], ) - @api.depends("process", "pr_url") + @api.depends("module_branch_id.is_standard", "target_module_branch_id.is_standard") + def _compute_moved_to_standard(self): + for rec in self: + rec.moved_to_standard = ( + not rec.module_branch_id.is_standard + and rec.target_module_branch_id.is_standard + ) + + @api.depends("org_id", "target_module_branch_id.org_id") + def _compute_moved_to_oca(self): + org_oca = self.env.ref( + "odoo_repository.odoo_repository_org_oca", raise_if_not_found=False + ) + for rec in self: + rec.moved_to_oca = False + if not org_oca: + continue + rec.moved_to_oca = ( + rec.org_id != org_oca and rec.target_module_branch_id.org_id == org_oca + ) + + @api.depends( + "repository_id.specific", "target_module_branch_id.repository_id.specific" + ) + def _compute_moved_to_generic(self): + for rec in self: + rec.moved_to_generic = ( + rec.repository_id.specific + and rec.target_module_branch_id.repository_id + and not rec.target_module_branch_id.repository_id.specific + ) + + @api.depends( + "process", "pr_url", "moved_to_standard", "moved_to_oca", "moved_to_generic" + ) def _compute_state(self): for rec in self: + if rec.moved_to_standard: + # Module moved to a standard repository (likely from OCA to + # odoo/odoo, like 'l10n_eu_oss', 'knowledge', ...). + # E.g. this could tell integrators that a module like + # 'l10n_eu_oss_oca' should now be used instead. + rec.state = "moved_to_standard" + continue + if rec.moved_to_oca: + # Module moved to an OCA repository + rec.state = "moved_to_oca" + continue + if rec.moved_to_generic: + # Specific module moved to a generic repository (public or private) + rec.state = "moved_to_generic" + continue rec.state = rec.process or "fully_ported" if rec.process == "migrate" and rec.pr_url: rec.state = "review_migration" @@ -134,17 +214,25 @@ def _compute_results_text(self): rec.results_text = pprint.pformat(rec.results) @api.depends( + "repository_id.collect_migration_data", "last_source_scanned_commit", "last_target_scanned_commit", "pr_url", "target_module_branch_id.pr_url", "target_module_branch_id.last_scanned_commit", + "state", ) def _compute_migration_scan(self): # Migration scan to do if last scanned commit doesn't match the last # migration scan, both for source and target modules. for rec in self: rec.migration_scan = False + # No migration scan if repository is not configured to do it + if not rec.repository_id.collect_migration_data: + continue + # No migration scan for modules moved to Odoo/OCA/generic repo + if rec.state and rec.state.startswith("moved_to"): + continue if ( rec.last_source_scanned_commit != rec.module_branch_id.last_scanned_commit diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 521100b6..b2879e2a 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -117,7 +117,25 @@ def _scan_migration_module(self, migration_path_id, module_branch_id): migration_path = ( self.env["odoo.migration.path"].browse(migration_path_id).exists() ) - params = self._prepare_migration_scanner_parameters(migration_path) + # Check if module has already been migrated on target version but in a + # different repository. If so, tune the scanner parameters to perform + # the scan from current repo to new one. + target_repository = None + mig = module.migration_ids.filtered( + lambda mig: mig.migration_path_id.id == migration_path_id + ) + target_module = mig.target_module_branch_id + if target_module.repository_branch_id: + target_repository = target_module.repository_id + if not target_repository.collect_migration_data: + return ( + "Cannot collect migration data on repository " + f"{target_repository.display_name}." + ) + params = self._prepare_migration_scanner_parameters( + migration_path, target_repository + ) + # Run the migration scan try: scanner = MigrationScannerOdooEnv(**params) return scanner.scan( @@ -141,14 +159,16 @@ def _migration_get_modules_to_scan(self, migration_path): ] ) - def _prepare_migration_scanner_parameters(self, migration_path): + def _prepare_migration_scanner_parameters( + self, migration_path, target_repository=None + ): ir_config = self.env["ir.config_parameter"] repositories_path = ir_config.sudo().get_param(self._repositories_path_key) mig_path = ( migration_path.source_branch_id.name, migration_path.target_branch_id.name, ) - return { + params = { "org": self.org_id.name, "name": self.name, "clone_url": self.clone_url, @@ -163,6 +183,10 @@ def _prepare_migration_scanner_parameters(self, migration_path): "clone_name": self.clone_name, "env": self.env, } + if target_repository and target_repository != self: + params["new_repo_name"] = target_repository.name + params["new_repo_url"] = target_repository.clone_url + return params def _pre_create_or_update_module_branch(self, rec, values, raw_data): # Handle migration data diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index ebfa7254..6efabeab 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -21,6 +21,18 @@ def setUp(self): repository_branch_id=self.repo_branch.id, last_scanned_commit="sha", ) + self.std_repository = self.env.ref("odoo_repository.odoo_repository_odoo_odoo") + self.oca_repository = self.env.ref("odoo_repository.repo_oca_server_tools") + self.gen_repository = self.env["odoo.repository"].create( + { + "name": "new_repo", + "org_id": self.odoo_repository.org_id.id, + "repo_url": "http://example.net/new_repo", + "specific": False, + "to_scan": False, + } + ) + self.gen_repository.addons_path_ids = self.odoo_repository.addons_path_ids def _simulate_migration_scan(self, target_commit, report=None): """Helper method that pushes scanned migration data.""" @@ -166,3 +178,113 @@ def test_migration_scan_target_module_in_review_then_merged(self): self.assertEqual(self.module_branch.migration_ids.state, "fully_ported") self.assertFalse(self.module_branch.migration_ids.migration_scan) self.assertFalse(self.module_branch.migration_scan) + + def test_migration_scan_target_module_moved_to_standard(self): + """Module moved into a standard repository.""" + # Simulate a scan of a given migration path while the target module is + # not yet migrated/available in a repository + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": self.branch2.id, + } + ) + self._simulate_migration_scan( + "target_commit1", report={"process": "migrate", "results": {}} + ) + self.assertTrue(self.module_branch.migration_ids) + mig = self.module_branch.migration_ids + self.assertFalse(mig.target_module_branch_id) + self.assertFalse(mig.migration_scan) + self.assertFalse(self.module_branch.migration_scan) + self.assertEqual(mig.state, "migrate") + # Then the module is discovered in a std repository + std_repo_branch = self._create_odoo_repository_branch( + self.std_repository, self.branch2 + ) + target_module_branch = self._create_odoo_module_branch( + self.module, + self.branch2, + specific=False, + is_standard=True, + repository_branch_id=std_repo_branch.id, + ) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertTrue(mig.moved_to_standard) + self.assertFalse(mig.moved_to_oca) + self.assertFalse(mig.moved_to_generic) + self.assertEqual(mig.state, "moved_to_standard") + self.assertFalse(mig.migration_scan) + + def test_migration_scan_target_module_moved_to_oca(self): + """Module moved into an OCA repository.""" + # Simulate a scan of a given migration path while the target module is + # not yet migrated/available in a repository + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": self.branch2.id, + } + ) + self._simulate_migration_scan( + "target_commit1", report={"process": "migrate", "results": {}} + ) + self.assertTrue(self.module_branch.migration_ids) + mig = self.module_branch.migration_ids + self.assertFalse(mig.target_module_branch_id) + self.assertFalse(mig.migration_scan) + self.assertFalse(self.module_branch.migration_scan) + self.assertEqual(mig.state, "migrate") + # Then the module is discovered in an OCA repository + oca_repo_branch = self._create_odoo_repository_branch( + self.oca_repository, self.branch2 + ) + target_module_branch = self._create_odoo_module_branch( + self.module, + self.branch2, + specific=False, + repository_branch_id=oca_repo_branch.id, + ) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertFalse(mig.moved_to_standard) + self.assertTrue(mig.moved_to_oca) + self.assertFalse(mig.moved_to_generic) + self.assertEqual(mig.state, "moved_to_oca") + self.assertFalse(mig.migration_scan) + + def test_migration_scan_target_module_moved_to_generic(self): + """Specific module moved into a generic repository (that is not std or OCA).""" + self.odoo_repository.specific = True + # Simulate a scan of a given migration path while the target module is + # not yet migrated/available in a repository + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": self.branch2.id, + } + ) + self._simulate_migration_scan( + "target_commit1", report={"process": "migrate", "results": {}} + ) + self.assertTrue(self.module_branch.migration_ids) + mig = self.module_branch.migration_ids + self.assertFalse(mig.target_module_branch_id) + self.assertFalse(mig.migration_scan) + self.assertFalse(self.module_branch.migration_scan) + self.assertEqual(mig.state, "migrate") + # Then the module is discovered in an OCA repository + gen_repo_branch = self._create_odoo_repository_branch( + self.gen_repository, self.branch2 + ) + target_module_branch = self._create_odoo_module_branch( + self.module, + self.branch2, + specific=False, + repository_branch_id=gen_repo_branch.id, + ) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertFalse(mig.moved_to_standard) + self.assertFalse(mig.moved_to_oca) + self.assertTrue(mig.moved_to_generic) + self.assertEqual(mig.state, "moved_to_generic") + self.assertFalse(mig.migration_scan) From 18f5b925d3185ba495a9af0c7567e9528e085863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Fri, 11 Apr 2025 15:32:02 +0200 Subject: [PATCH 31/65] odoo_repository_migration: able to qualify a module as renamed or replaced for next Odoo version Adapt odoo_project_migration accordingly, and show this info in CSV migration report. --- .../models/odoo_module_branch.py | 176 +++++++++++++++++- .../models/odoo_module_branch_migration.py | 78 +++++++- .../models/odoo_repository.py | 29 ++- .../tests/test_odoo_module_branch.py | 100 ++++++++++ .../views/odoo_module_branch.xml | 37 ++++ .../views/odoo_module_branch_migration.xml | 24 ++- 6 files changed, 427 insertions(+), 17 deletions(-) diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index 36dfce56..a33a66c3 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -1,24 +1,188 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import api, fields, models +from odoo import _, api, fields, models class OdooModuleBranch(models.Model): _inherit = "odoo.module.branch" + next_odoo_version_id = fields.Many2one( + comodel_name="odoo.branch", + compute="_compute_next_odoo_version_id", + ) + next_odoo_version_state = fields.Selection( + selection=[ + ("same", "is named the same"), + ("renamed", "has been renamed to"), + ("replaced", "has been replaced by"), + ], + inverse="_inverse_next_odoo_version_fields", + default="same", + required=True, + index=True, + ) + next_odoo_version_module_id = fields.Many2one( + comodel_name="odoo.module", + ondelete="restrict", + inverse="_inverse_next_odoo_version_fields", + string="Next Odoo Version Module", + index=True, + ) + next_odoo_version_module_branch_id = fields.Many2one( + comodel_name="odoo.module.branch", + compute="_compute_next_odoo_version_module_branch_id", + string="Next Odoo Version Module Branch", + ) migration_ids = fields.One2many( comodel_name="odoo.module.branch.migration", inverse_name="module_branch_id", string="Migrations", ) - migration_scan = fields.Boolean( compute="_compute_migration_scan", store=True, help="Technical field telling if this module is elligible for a migration scan.", ) + @api.depends("branch_id") + def _compute_next_odoo_version_id(self): + for rec in self: + next_odoo_version = False + if rec.branch_id: + next_odoo_version = self.env["odoo.branch"].search( + [ + ("odoo_version", "=", True), + ("sequence", ">", rec.branch_id.sequence), + ], + limit=1, + ) + rec.next_odoo_version_id = next_odoo_version + + @api.depends("next_odoo_version_id", "next_odoo_version_module_id") + def _compute_next_odoo_version_module_branch_id(self): + for rec in self: + rec.next_odoo_version_module_branch_id = False + # Stop there if no next version + if not rec.next_odoo_version_id: + continue + # Look for the next available version for this module name + rec.next_odoo_version_module_branch_id = self.search( + [ + ("branch_id", "=", rec.next_odoo_version_id.id), + ("module_id", "=", rec.module_id.id), + ], + limit=1, + ) + # Stop there if no renaming/relacement + if not rec.next_odoo_version_module_id: + continue + rec.next_odoo_version_module_branch_id = self.search( + [ + ("branch_id", "=", rec.next_odoo_version_id.id), + ("module_id", "=", rec.next_odoo_version_module_id.id), + ], + limit=1, + ) + + def _inverse_next_odoo_version_fields(self): + # Reset selected module if the module name doesn't change with next version + if self.next_odoo_version_state == "same": + self.next_odoo_version_module_id = False + # If module is renamed or replaced in next Odoo version, we reset the + # last target scan commits on all impacted migration paths. + # E.g. + # if a module on 17.0 is set as renamed starting from 18.0, + # all migration paths of this module targetting versions >= 18.0 + # should re-trigger a migration scan. + else: + migrations = ( + self.env["odoo.module.branch.migration"] + .search( + [ + ("module_id", "=", self.module_id.id), + ( + "target_branch_id.sequence", + ">=", + self.next_odoo_version_id.sequence, + ), + ] + ) + .sudo() + ) + migrations.last_target_scanned_commit = False + migrations._compute_renamed_to_module_id() + migrations._compute_replaced_by_module_id() + migrations._compute_state() + + def _replaced_by_module_in_target_version(self, target_branch): + """Return the module replacing current one in last module versions. + + Look for current + next modules as the migration scan could do a jump + 14.0 -> 18.0, while a module has been replaced starting from 18.0 (and + therefore flagged as replaced in 17.0 module data). + + We give the priority to last modules while checking them. + """ + self.ensure_one() + modules = self._get_next_versions(target_branch) + for module in modules.sorted( + key=lambda mod: mod.branch_id.sequence, reverse=True + ): + if module.next_odoo_version_state == "replaced": + return module.next_odoo_version_module_id + return self.env["odoo.module"] + + def _renamed_to_module_in_target_version(self, target_branch): + """Return the new module technical name in last module versions. + + Look for current + next modules as the migration scan could do a jump + 14.0 -> 18.0, while a module has been renamed starting from 18.0 (and + therefore flagged as renamed in 17.0 module data). + + We give the priority to last modules while checking them. + """ + self.ensure_one() + modules = self._get_next_versions(target_branch) + for module in modules.sorted( + key=lambda mod: mod.branch_id.sequence, reverse=True + ): + if module.next_odoo_version_state == "renamed": + return module.next_odoo_version_module_id + return self.env["odoo.module"] + + def _get_next_versions(self, target_branch): + self.ensure_one() + return self.env["odoo.module.branch"].search( + [ + ("module_id", "=", self.module_id.id), + ("branch_id.sequence", ">=", self.branch_id.sequence), + ("branch_id.sequence", "<", target_branch.sequence), + ] + ) + + def _get_next_module_branches(self, target_branch=None): + """Return all modules in the right version order starting from current one. + + This is taking into account module renamed or replaced in intermediate versions. + """ + if not self: + return self.browse() + self.ensure_one() + if target_branch: + assert self.branch_id.sequence < target_branch.sequence + next_module_branch = self.next_odoo_version_module_branch_id + next_module_branch_ids = [] + while next_module_branch: + if ( + target_branch + and next_module_branch.branch_id.sequence > target_branch.sequence + ): + break + next_module_branch_ids.append(next_module_branch.id) + next_module_branch = next_module_branch.next_odoo_version_module_branch_id + return self.browse(next_module_branch_ids) + @api.depends( "removed", "pr_url", @@ -92,3 +256,11 @@ def _update_migration_target_module_id(self): ) # Recompute 'target_module_id' field migrations._compute_target_module_branch_id() + + def open_next_module_branches(self): + self.ensure_one() + xml_id = "odoo_repository.odoo_module_branch_action" + action = self.env["ir.actions.actions"]._for_xml_id(xml_id) + action["name"] = _("Next versions") + action["domain"] = [("id", "in", self._get_next_module_branches().ids)] + return action diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index aea3d234..720f4706 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -85,13 +85,28 @@ class OdooModuleBranchMigration(models.Model): "same scope so it deserves a check during a migration." ), ) + renamed_to_module_id = fields.Many2one( + comodel_name="odoo.module", + compute="_compute_renamed_to_module_id", + string="Renamed to", + store=True, + index=True, + ) + replaced_by_module_id = fields.Many2one( + comodel_name="odoo.module", + compute="_compute_replaced_by_module_id", + string="Replaced by", + store=True, + index=True, + ) state = fields.Selection( selection=[ ("fully_ported", "Fully Ported"), ("migrate", "To migrate"), - ("port_commits", "Commits to port"), - ("review_migration", "Migration to review"), - ("moved_to_standard", "Moved to standard"), + ("port_commits", "Ported (missing commits?)"), + ("review_migration", "To review"), + ("replaced", "Replaced"), + ("moved_to_standard", "Moved to standard?"), ("moved_to_oca", "Moved to OCA"), ("moved_to_generic", "Moved to generic repo"), ], @@ -136,13 +151,24 @@ def _compute_display_name(self): f"{rec.source_branch_id.name} -> {rec.target_branch_id.name}" ) - @api.depends("module_branch_id", "migration_path_id") + @api.depends( + "module_branch_id", + "migration_path_id", + "replaced_by_module_id", + "renamed_to_module_id", + ) def _compute_target_module_branch_id(self): module_branch_model = self.env["odoo.module.branch"] for rec in self: + # Look for the right module technical name + module = ( + rec.replaced_by_module_id + or rec.renamed_to_module_id + or rec.module_branch_id.module_id + ) rec.target_module_branch_id = module_branch_model._find( rec.migration_path_id.target_branch_id, - rec.module_branch_id.module_id, + module, rec.module_branch_id.repository_id, domain=[("installable", "=", True)], ) @@ -180,10 +206,45 @@ def _compute_moved_to_generic(self): ) @api.depends( - "process", "pr_url", "moved_to_standard", "moved_to_oca", "moved_to_generic" + "module_branch_id.next_odoo_version_state", + "module_branch_id.next_odoo_version_module_id", + "target_branch_id", + ) + def _compute_renamed_to_module_id(self): + for rec in self: + rec.renamed_to_module_id = ( + rec.module_branch_id._renamed_to_module_in_target_version( + rec.target_branch_id + ) + ) + + @api.depends( + "module_branch_id.next_odoo_version_state", + "module_branch_id.next_odoo_version_module_id", + "target_branch_id", + ) + def _compute_replaced_by_module_id(self): + for rec in self: + rec.replaced_by_module_id = ( + rec.module_branch_id._replaced_by_module_in_target_version( + rec.target_branch_id + ) + ) + + @api.depends( + "replaced_by_module_id", + "process", + "pr_url", + "moved_to_standard", + "moved_to_oca", + "moved_to_generic", ) def _compute_state(self): for rec in self: + if rec.replaced_by_module_id: + # Module replaced by another one + rec.state = "replaced" + continue if rec.moved_to_standard: # Module moved to a standard repository (likely from OCA to # odoo/odoo, like 'l10n_eu_oss', 'knowledge', ...). @@ -214,6 +275,8 @@ def _compute_results_text(self): rec.results_text = pprint.pformat(rec.results) @api.depends( + "module_branch_id.last_scanned_commit", + "replaced_by_module_id", "repository_id.collect_migration_data", "last_source_scanned_commit", "last_target_scanned_commit", @@ -233,6 +296,9 @@ def _compute_migration_scan(self): # No migration scan for modules moved to Odoo/OCA/generic repo if rec.state and rec.state.startswith("moved_to"): continue + # No migration scan for modules replaced by another module + if rec.replaced_by_module_id: + continue if ( rec.last_source_scanned_commit != rec.module_branch_id.last_scanned_commit diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index b2879e2a..96d2ed75 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -111,12 +111,33 @@ def _migration_create_jobs_scan_module(self, migration_path, modules_to_scan): return jobs def _scan_migration_module(self, migration_path_id, module_branch_id): - """Scan migration path for `module_branch_id`.""" + """Scan migration path for `module_branch_id`. + + The migration scan can only occur if: + - target module doesn't exist (and can be migrated) + - source and target modules share the same commits histories (able to + collect migration data) + + Also, a target module could have been renamed while sharing the commits history. + + But a module that has been replaced (different name, different commits + history, but providing the same feature) in next versions cannot be scanned. + Such module will get a migration status "Replaced". + """ module = self.env["odoo.module.branch"].browse(module_branch_id).exists() module.ensure_one() migration_path = ( self.env["odoo.migration.path"].browse(migration_path_id).exists() ) + # Skip migration scan if module is replaced in next versions + if module._replaced_by_module_in_target_version( + migration_path.target_branch_id + ): + return ( + f"{module.name} is now replaced by " + f"{module.next_odoo_version_module_id.name}, no need to collect " + "migration data." + ) # Check if module has already been migrated on target version but in a # different repository. If so, tune the scanner parameters to perform # the scan from current repo to new one. @@ -135,11 +156,15 @@ def _scan_migration_module(self, migration_path_id, module_branch_id): params = self._prepare_migration_scanner_parameters( migration_path, target_repository ) + module_names = [module.module_id.name] + if target_module: + if module.module_id != target_module.module_id: + module_names = [(module.module_id.name, target_module.module_id.name)] # Run the migration scan try: scanner = MigrationScannerOdooEnv(**params) return scanner.scan( - addons_path=module.addons_path, module_names=[module.module_id.name] + addons_path=module.addons_path, module_names=module_names ) except Exception as exc: raise RetryableJobError("Scanner error") from exc diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index 6efabeab..09bf8eb8 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -288,3 +288,103 @@ def test_migration_scan_target_module_moved_to_generic(self): self.assertTrue(mig.moved_to_generic) self.assertEqual(mig.state, "moved_to_generic") self.assertFalse(mig.migration_scan) + + def test_renamed_to_module_in_target_version(self): + self.odoo_repository.collect_migration_data = True + # Next version is 16.0 + next_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 1)] + ) + # Create the target module + new_module = self.module.copy({"name": "new_module"}) + target_module_branch = self._create_odoo_module_branch( + new_module, + next_branch, + specific=False, + repository_branch_id=self.repo_branch.id, + last_scanned_commit="sha", + ) + # Generate migration data records + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": next_branch.id, + } + ) + self._simulate_migration_scan( + "target_commit1", report={"process": "migrate", "results": {}} + ) + self.assertEqual(self.module_branch.next_odoo_version_id, next_branch) + # Module has been renamed starting from 16.0 + self.module_branch.next_odoo_version_state = "renamed" + self.module_branch.next_odoo_version_module_id = new_module + renamed_to_module = self.module_branch._renamed_to_module_in_target_version( + self.module_branch.next_odoo_version_id + ) + self.assertEqual(renamed_to_module, new_module) + # We target 17.0 to check if intermediate data in 16.0 is found + target_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 2)] + ) + renamed_to_module = self.module_branch._renamed_to_module_in_target_version( + target_branch + ) + self.assertEqual(renamed_to_module, new_module) + # Check migration data + mig = self.module_branch.migration_ids + self.assertEqual(mig.renamed_to_module_id, new_module) + self.assertFalse(mig.replaced_by_module_id) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertFalse(mig.last_target_scanned_commit) + self.assertEqual(mig.state, "migrate") + self.assertTrue(mig.migration_scan) + + def test_replaced_by_module_in_target_version(self): + self.odoo_repository.collect_migration_data = True + # Next version is 16.0 + next_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 1)] + ) + # Create the target module + new_module = self.module.copy({"name": "new_module"}) + target_module_branch = self._create_odoo_module_branch( + new_module, + next_branch, + specific=False, + repository_branch_id=self.repo_branch.id, + last_scanned_commit="sha", + ) + # Generate migration data records + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": next_branch.id, + } + ) + self._simulate_migration_scan( + "target_commit1", report={"process": "migrate", "results": {}} + ) + self.assertEqual(self.module_branch.next_odoo_version_id, next_branch) + # New module is replacing current one starting from 16.0 + self.module_branch.next_odoo_version_state = "replaced" + self.module_branch.next_odoo_version_module_id = new_module + replaced_by_module = self.module_branch._replaced_by_module_in_target_version( + self.module_branch.next_odoo_version_id + ) + self.assertEqual(replaced_by_module, new_module) + # We target 17.0 to check if intermediate data in 16.0 is found + target_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 2)] + ) + replaced_by_module = self.module_branch._replaced_by_module_in_target_version( + target_branch + ) + self.assertEqual(replaced_by_module, new_module) + # Check migration data + mig = self.module_branch.migration_ids + self.assertEqual(mig.replaced_by_module_id, new_module) + self.assertFalse(mig.renamed_to_module_id) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertFalse(mig.last_target_scanned_commit) + self.assertEqual(mig.state, "replaced") + self.assertFalse(mig.migration_scan) diff --git a/odoo_repository_migration/views/odoo_module_branch.xml b/odoo_repository_migration/views/odoo_module_branch.xml index a0cd9738..914d7dc9 100644 --- a/odoo_repository_migration/views/odoo_module_branch.xml +++ b/odoo_repository_migration/views/odoo_module_branch.xml @@ -8,8 +8,45 @@ odoo.module.branch +
+ +
+
+ Starting from version , + this module + + + + +
diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index 17eb9973..868b338d 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -13,13 +13,23 @@

- - - - - - - + + + + + + + + + + + From 2cad9458e019e72e634a89b1fd6de3e634cef57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Wed, 28 May 2025 13:54:38 +0200 Subject: [PATCH 32/65] odoo_repository_migration: collect migration data on specific repositories Now that multiple Odoo versions/branches can be set on a specific repository, we are able to collect the migration data of specific modules hosted in it. --- .../models/odoo_migration_path.py | 2 - .../models/odoo_module_branch.py | 5 +- .../models/odoo_module_branch_migration.py | 4 +- .../models/odoo_repository.py | 137 +++++++++++++----- .../tests/test_odoo_module_branch.py | 2 + odoo_repository_migration/utils/scanner.py | 6 +- 6 files changed, 109 insertions(+), 47 deletions(-) diff --git a/odoo_repository_migration/models/odoo_migration_path.py b/odoo_repository_migration/models/odoo_migration_path.py index 7dd314be..b3db3de6 100644 --- a/odoo_repository_migration/models/odoo_migration_path.py +++ b/odoo_repository_migration/models/odoo_migration_path.py @@ -14,13 +14,11 @@ class OdooMigrationPath(models.Model): source_branch_id = fields.Many2one( comodel_name="odoo.branch", ondelete="cascade", - domain=[("odoo_version", "=", True)], required=True, ) target_branch_id = fields.Many2one( comodel_name="odoo.branch", ondelete="cascade", - domain=[("odoo_version", "=", True)], required=True, ) diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index a33a66c3..bf13371b 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -51,10 +51,7 @@ def _compute_next_odoo_version_id(self): next_odoo_version = False if rec.branch_id: next_odoo_version = self.env["odoo.branch"].search( - [ - ("odoo_version", "=", True), - ("sequence", ">", rec.branch_id.sequence), - ], + [("sequence", ">", rec.branch_id.sequence)], limit=1, ) rec.next_odoo_version_id = next_odoo_version diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index 720f4706..515b0b05 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -318,8 +318,8 @@ def _compute_migration_scan(self): def push_scanned_data(self, module_branch_id, data): migration_path = self.env["odoo.migration.path"].search( [ - ("source_branch_id", "=", data["source_branch"]), - ("target_branch_id", "=", data["target_branch"]), + ("source_branch_id", "=", data["source_version"]), + ("target_branch_id", "=", data["target_version"]), ] ) values = { diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 96d2ed75..b3a23203 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -19,16 +19,25 @@ class OdooRepository(models.Model): default=False, ) - def _reset_scanned_commits(self, branches=None): - res = super()._reset_scanned_commits(branches) - if branches is None: - branches = [] - branches_ = ( - self.branch_ids.filtered(lambda br: br.branch_id.name in branches) - if branches - else self.branch_ids + def action_scan(self, branch_ids=None, force=False, raise_exc=True): + for rec in self: + # Scan only relevant branches regarding migration paths + rec_ctx = rec + if rec.specific: + rec_ctx = rec.with_context(strict_branches_scan=True) + super(OdooRepository, rec_ctx).action_scan( + branch_ids=branch_ids, force=force, raise_exc=raise_exc + ) + return True + + def _reset_scanned_commits(self, branch_ids=None): + res = super()._reset_scanned_commits(branch_ids=branch_ids) + if branch_ids is None: + branch_ids = self.branch_ids.branch_id.ids + repo_branches = self.branch_ids.filtered( + lambda rb: rb.branch_id.id in branch_ids ) - branches_.module_ids.migration_ids.sudo().write( + repo_branches.module_ids.migration_ids.sudo().write( { "last_source_scanned_commit": False, "last_target_scanned_commit": False, @@ -36,12 +45,14 @@ def _reset_scanned_commits(self, branches=None): ) return res - def _create_subsequent_jobs(self, branch, next_branches, all_branches, data): + def _create_subsequent_jobs( + self, version_branch, next_versions_branches, all_versions_branches, data + ): jobs = super()._create_subsequent_jobs( - branch, next_branches, all_branches, data + version_branch, next_versions_branches, all_versions_branches, data ) # Prepare migration scan jobs when its the last repository scan - last_scan = not next_branches + last_scan = not next_versions_branches if not last_scan: return jobs # Check if the addons_paths are compatible with 'oca_port' @@ -50,50 +61,91 @@ def _create_subsequent_jobs(self, branch, next_branches, all_branches, data): return jobs # Override to run the MigrationScanner once branches are scanned args = [] - if all_branches: + if all_versions_branches: + all_versions = [vb[0] for vb in all_versions_branches] # A strict scan of branches avoids unwanted migration scans # For instance if we are interested only by 14.0 and 17.0 branches, # this avoids to scan other migration paths like 15.0 -> 17.0 - strict_scan = self.env.context.get("strict_branches_scan") + # NOTE: a strict scan always occurs on specific repositories + strict_scan = self.env.context.get("strict_branches_scan") or self.specific args = [ "&" if strict_scan else "|", - ("source_branch_id", "in", all_branches), - ("target_branch_id", "in", all_branches), + ("source_branch_id", "in", all_versions), + ("target_branch_id", "in", all_versions), ] migration_paths = self.env["odoo.migration.path"].search(args) # Launch one job for all migration_paths if migration_paths: + # Migration paths parameter containing the migration path ID + + # the Odoo versions and branches to scan. + # E.g. {MIG_PATH_ID: [('14.0', 'master'), ('18.0', '18.0-mig')], ...} + migration_paths_param = {} + for migration_path in migration_paths: + source_rb = self.branch_ids.filtered( + lambda rb: rb.branch_id == migration_path.source_branch_id + ) + target_rb = self.branch_ids.filtered( + lambda rb: rb.branch_id == migration_path.target_branch_id + ) + # Need the two Odoo versions of the migration path available + # in the scanned repository + if not source_rb or not target_rb: + continue + # Build list of tuples (Odoo version, branch name) corresponding + # to the migration path + versions_branches = [ + ( + source_rb.branch_id.name, + source_rb.cloned_branch or source_rb.branch_id.name, + ), + ( + target_rb.branch_id.name, + target_rb.cloned_branch or target_rb.branch_id.name, + ), + ] + migration_paths_param[migration_path.id] = versions_branches + delayable = self.delayable( description=f"Collect {self.display_name} migration data", identity_key=identity_exact, ) - job = delayable._scan_migration_paths(migration_paths.ids) + job = delayable._scan_migration_paths(migration_paths_param) jobs.append(job) return jobs - def _scan_migration_paths(self, migration_path_ids): + def _scan_migration_paths(self, migration_paths_param): """Scan repository branches to collect modules migration data. Spawn one job per module to scan. """ self.ensure_one() jobs = [] - migration_paths = ( - self.env["odoo.migration.path"].browse(migration_path_ids).exists() - ) - for migration_path in migration_paths: + for migration_path_id in migration_paths_param: + versions_branches = migration_paths_param[migration_path_id] + migration_path = ( + self.env["odoo.migration.path"] + .browse( + # Job encodes dict key as string => convert it to integer + int(migration_path_id) + ) + .exists() + ) + if not migration_path: + continue modules_to_scan = self._migration_get_modules_to_scan(migration_path) if modules_to_scan: jobs.extend( self._migration_create_jobs_scan_module( - migration_path, modules_to_scan + migration_path, versions_branches, modules_to_scan ) ) if jobs: chain(*jobs).delay() return True - def _migration_create_jobs_scan_module(self, migration_path, modules_to_scan): + def _migration_create_jobs_scan_module( + self, migration_path, versions_branches, modules_to_scan + ): jobs = [] mig_path = ( migration_path.source_branch_id.name, @@ -106,11 +158,15 @@ def _migration_create_jobs_scan_module(self, migration_path, modules_to_scan): ), identity_key=identity_exact, ) - job = delayable._scan_migration_module(migration_path.id, module.id) + job = delayable._scan_migration_module( + migration_path.id, versions_branches, module.id + ) jobs.append(job) return jobs - def _scan_migration_module(self, migration_path_id, module_branch_id): + def _scan_migration_module( + self, migration_path_id, versions_branches, module_branch_id + ): """Scan migration path for `module_branch_id`. The migration scan can only occur if: @@ -154,7 +210,7 @@ def _scan_migration_module(self, migration_path_id, module_branch_id): f"{target_repository.display_name}." ) params = self._prepare_migration_scanner_parameters( - migration_path, target_repository + versions_branches, target_repository ) module_names = [module.module_id.name] if target_module: @@ -164,7 +220,9 @@ def _scan_migration_module(self, migration_path_id, module_branch_id): try: scanner = MigrationScannerOdooEnv(**params) return scanner.scan( - addons_path=module.addons_path, module_names=module_names + addons_path=module.addons_path, + target_addons_path=target_module.addons_path or module.addons_path, + module_names=module_names, ) except Exception as exc: raise RetryableJobError("Scanner error") from exc @@ -172,7 +230,8 @@ def _scan_migration_module(self, migration_path_id, module_branch_id): def _migration_get_modules_to_scan(self, migration_path): """Return `odoo.module.branch` records that need a migration scan.""" self.ensure_one() - return self.env["odoo.module.branch"].search( + mb_model = self.env["odoo.module.branch"] + modules = mb_model.search( [ ( "repository_id", @@ -183,21 +242,27 @@ def _migration_get_modules_to_scan(self, migration_path): ("migration_scan", "=", True), ] ) + module_ids = [] + for module in modules: + migration = module.migration_ids.filtered( + lambda mig: mig.migration_path_id == migration_path + ) + if migration and not migration.migration_scan: + # Skip module that do not need a scan for the given migration path + continue + module_ids.append(module.id) + return mb_model.browse(module_ids) def _prepare_migration_scanner_parameters( self, migration_path, target_repository=None ): ir_config = self.env["ir.config_parameter"] repositories_path = ir_config.sudo().get_param(self._repositories_path_key) - mig_path = ( - migration_path.source_branch_id.name, - migration_path.target_branch_id.name, - ) params = { "org": self.org_id.name, "name": self.name, "clone_url": self.clone_url, - "migration_path": mig_path, + "migration_path": migration_path, "repositories_path": repositories_path, "repo_type": self.repo_type, "ssh_key": self.ssh_key_id.private_key, @@ -221,10 +286,10 @@ def _pre_create_or_update_module_branch(self, rec, values, raw_data): values["migration_ids"] = [] for mig in migrations: source_branch = self.env["odoo.branch"].search( - [("odoo_version", "=", True), ("name", "=", mig["source_branch"])] + [("name", "=", mig["source_branch"])] ) target_branch = self.env["odoo.branch"].search( - [("odoo_version", "=", True), ("name", "=", mig["target_branch"])] + [("name", "=", mig["target_branch"])] ) if not source_branch or not target_branch: # Such branches are not configured on this instance, skip diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index 09bf8eb8..0797d32f 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -38,7 +38,9 @@ def _simulate_migration_scan(self, target_commit, report=None): """Helper method that pushes scanned migration data.""" data = { "module": self.module_branch.module_name, + "source_version": self.branch.name, "source_branch": self.branch.name, + "target_version": self.branch2.name, "target_branch": self.branch2.name, "source_commit": self.module_branch.last_scanned_commit, "target_commit": target_commit, diff --git a/odoo_repository_migration/utils/scanner.py b/odoo_repository_migration/utils/scanner.py index c4f3eb81..a5734fcc 100644 --- a/odoo_repository_migration/utils/scanner.py +++ b/odoo_repository_migration/utils/scanner.py @@ -70,13 +70,13 @@ def _get_odoo_module_branch_migration_id( return migration.id def _get_odoo_module_branch_migration_data( - self, repo_id: int, module: str, source_branch: str, target_branch: str + self, repo_id: int, module: str, source_version: str, target_version: str ) -> dict: args = [ ("module_branch_id.repository_id", "=", repo_id), ("module_id", "=", module), - ("source_branch_id", "=", source_branch), - ("target_branch_id", "=", target_branch), + ("source_branch_id", "=", source_version), + ("target_branch_id", "=", target_version), ] migration = self.env["odoo.module.branch.migration"].search(args) if migration: From da8337e6044626740a583202a52d199e059d6694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Sun, 14 Sep 2025 11:17:45 +0200 Subject: [PATCH 33/65] odoo_repository_migration: move replaced/renamed modules data in their own data model --- odoo_repository_migration/__manifest__.py | 3 +- .../migrations/16.0.1.3.0/post-migration.py | 35 ++++++ odoo_repository_migration/models/__init__.py | 1 + .../models/odoo_module_branch.py | 88 +++------------ .../models/odoo_module_branch_migration.py | 8 +- .../models/odoo_module_branch_timeline.py | 102 ++++++++++++++++++ .../security/ir.model.access.csv | 2 + .../tests/test_odoo_module_branch.py | 26 +++-- .../views/odoo_module_branch.xml | 29 +---- .../views/odoo_module_branch_migration.xml | 3 +- .../views/odoo_module_branch_timeline.xml | 102 ++++++++++++++++++ 11 files changed, 286 insertions(+), 113 deletions(-) create mode 100644 odoo_repository_migration/migrations/16.0.1.3.0/post-migration.py create mode 100644 odoo_repository_migration/models/odoo_module_branch_timeline.py create mode 100644 odoo_repository_migration/views/odoo_module_branch_timeline.xml diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index 9625b7aa..26385d82 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "16.0.1.2.0", + "version": "16.0.1.3.0", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/camptocamp/odoo-repository", @@ -13,6 +13,7 @@ "views/odoo_migration_path.xml", "views/odoo_module_branch.xml", "views/odoo_module_branch_migration.xml", + "views/odoo_module_branch_timeline.xml", "views/odoo_repository.xml", ], "installable": True, diff --git a/odoo_repository_migration/migrations/16.0.1.3.0/post-migration.py b/odoo_repository_migration/migrations/16.0.1.3.0/post-migration.py new file mode 100644 index 00000000..af988af2 --- /dev/null +++ b/odoo_repository_migration/migrations/16.0.1.3.0/post-migration.py @@ -0,0 +1,35 @@ +# Copyright 2025 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import logging + +from odoo import SUPERUSER_ID, api + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + if not version: + return + env = api.Environment(cr, SUPERUSER_ID, {}) + migrate_odoo_module_branch_timelines(env) + + +def migrate_odoo_module_branch_timelines(env): + _logger.info("Migrate Odoo module timelines to 'odoo.module.branch.timeline'...") + query = """ + SELECT id, next_odoo_version_state, next_odoo_version_module_id + FROM odoo_module_branch + WHERE next_odoo_version_state != 'same'; + """ + env.cr.execute(query) + rows = env.cr.dictfetchall() + vals_list = [] + for row in rows: + vals = { + "module_branch_id": row["id"], + "state": row["next_odoo_version_state"], + "next_module_id": row["next_odoo_version_module_id"], + } + vals_list.append(vals) + env["odoo.module.branch.timeline"].create(vals_list) diff --git a/odoo_repository_migration/models/__init__.py b/odoo_repository_migration/models/__init__.py index db7baee9..75bb5828 100644 --- a/odoo_repository_migration/models/__init__.py +++ b/odoo_repository_migration/models/__init__.py @@ -1,4 +1,5 @@ from . import odoo_migration_path from . import odoo_module_branch_migration +from . import odoo_module_branch_timeline from . import odoo_module_branch from . import odoo_repository diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index bf13371b..62e40d81 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -7,27 +7,10 @@ class OdooModuleBranch(models.Model): _inherit = "odoo.module.branch" - next_odoo_version_id = fields.Many2one( - comodel_name="odoo.branch", - compute="_compute_next_odoo_version_id", - ) - next_odoo_version_state = fields.Selection( - selection=[ - ("same", "is named the same"), - ("renamed", "has been renamed to"), - ("replaced", "has been replaced by"), - ], - inverse="_inverse_next_odoo_version_fields", - default="same", - required=True, - index=True, - ) - next_odoo_version_module_id = fields.Many2one( - comodel_name="odoo.module", - ondelete="restrict", - inverse="_inverse_next_odoo_version_fields", - string="Next Odoo Version Module", - index=True, + timeline_ids = fields.One2many( + comodel_name="odoo.module.branch.timeline", + inverse_name="module_branch_id", + string="Timeline", ) next_odoo_version_module_branch_id = fields.Many2one( comodel_name="odoo.module.branch", @@ -45,73 +28,34 @@ class OdooModuleBranch(models.Model): help="Technical field telling if this module is elligible for a migration scan.", ) - @api.depends("branch_id") - def _compute_next_odoo_version_id(self): - for rec in self: - next_odoo_version = False - if rec.branch_id: - next_odoo_version = self.env["odoo.branch"].search( - [("sequence", ">", rec.branch_id.sequence)], - limit=1, - ) - rec.next_odoo_version_id = next_odoo_version - - @api.depends("next_odoo_version_id", "next_odoo_version_module_id") + @api.depends("branch_id.next_id", "timeline_ids") def _compute_next_odoo_version_module_branch_id(self): for rec in self: rec.next_odoo_version_module_branch_id = False # Stop there if no next version - if not rec.next_odoo_version_id: + if not rec.branch_id.next_id: continue # Look for the next available version for this module name rec.next_odoo_version_module_branch_id = self.search( [ - ("branch_id", "=", rec.next_odoo_version_id.id), + ("branch_sequence", ">=", rec.branch_id.next_id.sequence), ("module_id", "=", rec.module_id.id), ], + order="branch_sequence", limit=1, ) # Stop there if no renaming/relacement - if not rec.next_odoo_version_module_id: + if not rec.timeline_ids: continue rec.next_odoo_version_module_branch_id = self.search( [ - ("branch_id", "=", rec.next_odoo_version_id.id), - ("module_id", "=", rec.next_odoo_version_module_id.id), + ("branch_sequence", ">=", rec.branch_id.next_id.sequence), + ("module_id", "=", rec.timeline_ids.next_module_id.id), ], + order="branch_sequence", limit=1, ) - def _inverse_next_odoo_version_fields(self): - # Reset selected module if the module name doesn't change with next version - if self.next_odoo_version_state == "same": - self.next_odoo_version_module_id = False - # If module is renamed or replaced in next Odoo version, we reset the - # last target scan commits on all impacted migration paths. - # E.g. - # if a module on 17.0 is set as renamed starting from 18.0, - # all migration paths of this module targetting versions >= 18.0 - # should re-trigger a migration scan. - else: - migrations = ( - self.env["odoo.module.branch.migration"] - .search( - [ - ("module_id", "=", self.module_id.id), - ( - "target_branch_id.sequence", - ">=", - self.next_odoo_version_id.sequence, - ), - ] - ) - .sudo() - ) - migrations.last_target_scanned_commit = False - migrations._compute_renamed_to_module_id() - migrations._compute_replaced_by_module_id() - migrations._compute_state() - def _replaced_by_module_in_target_version(self, target_branch): """Return the module replacing current one in last module versions. @@ -126,8 +70,8 @@ def _replaced_by_module_in_target_version(self, target_branch): for module in modules.sorted( key=lambda mod: mod.branch_id.sequence, reverse=True ): - if module.next_odoo_version_state == "replaced": - return module.next_odoo_version_module_id + if module.timeline_ids.state == "replaced": + return module.timeline_ids.next_module_id return self.env["odoo.module"] def _renamed_to_module_in_target_version(self, target_branch): @@ -144,8 +88,8 @@ def _renamed_to_module_in_target_version(self, target_branch): for module in modules.sorted( key=lambda mod: mod.branch_id.sequence, reverse=True ): - if module.next_odoo_version_state == "renamed": - return module.next_odoo_version_module_id + if module.timeline_ids.state == "renamed": + return module.timeline_ids.next_module_id return self.env["odoo.module"] def _get_next_versions(self, target_branch): diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index 515b0b05..24980f0e 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -206,8 +206,8 @@ def _compute_moved_to_generic(self): ) @api.depends( - "module_branch_id.next_odoo_version_state", - "module_branch_id.next_odoo_version_module_id", + "module_branch_id.timeline_ids.state", + "module_branch_id.timeline_ids.next_module_id", "target_branch_id", ) def _compute_renamed_to_module_id(self): @@ -219,8 +219,8 @@ def _compute_renamed_to_module_id(self): ) @api.depends( - "module_branch_id.next_odoo_version_state", - "module_branch_id.next_odoo_version_module_id", + "module_branch_id.timeline_ids.state", + "module_branch_id.timeline_ids.next_module_id", "target_branch_id", ) def _compute_replaced_by_module_id(self): diff --git a/odoo_repository_migration/models/odoo_module_branch_timeline.py b/odoo_repository_migration/models/odoo_module_branch_timeline.py new file mode 100644 index 00000000..b924a991 --- /dev/null +++ b/odoo_repository_migration/models/odoo_module_branch_timeline.py @@ -0,0 +1,102 @@ +# Copyright 2025 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import api, fields, models + + +class OdooModuleBranchTimeline(models.Model): + _name = "odoo.module.branch.timeline" + _description = "Odoo Module Timeline (renaming/replacement)" + _order = "odoo_version_sequence" + + module_branch_id = fields.Many2one( + string="Module", + comodel_name="odoo.module.branch", + required=True, + index=True, + ) + org_id = fields.Many2one( + string="Organization", + related="module_branch_id.org_id", + store=True, + index=True, + ) + repository_id = fields.Many2one( + string="Repository", + related="module_branch_id.repository_id", + store=True, + index=True, + ) + odoo_version_id = fields.Many2one( + string="Starting from version", + related="module_branch_id.branch_id.next_id", + store=True, + index=True, + ) + odoo_version_sequence = fields.Integer( + related="odoo_version_id.sequence", + store=True, + ) + state = fields.Selection( + selection=[ + ("renamed", "has been renamed to"), + ("replaced", "has been replaced by"), + ], + inverse="_inverse_next_fields", + default="renamed", + required=True, + index=True, + help=( + "Renamed: modules still share the same Git history (it allows to " + "check commits that could be ported)\n" + "Replaced: module has been replaced (or merged) by another one that " + "fulfill the same feature" + ), + ) + next_module_id = fields.Many2one( + string="New module name", + comodel_name="odoo.module", + inverse="_inverse_next_fields", + ondelete="restrict", + index=True, + ) + next_module_branch_id = fields.Many2one( + string="New module", + related="module_branch_id.next_odoo_version_module_branch_id", + ) + note = fields.Html() + + @api.depends("odoo_version_id", "module_branch_id", "next_module_id") + def _compute_display_name(self): + for rec in self: + rec.display_name = ( + f"[{rec.odoo_version_id.name}] " + f"{rec.module_branch_id.module_id.name} > {rec.next_module_id.name}" + ) + + def _inverse_next_fields(self): + # When a module is renamed or replaced, we reset the + # last target scan commits on all impacted migration paths. + # E.g. + # if a module on 17.0 is set as renamed starting from 18.0, + # all migration paths of this module targetting versions >= 18.0 + # should re-trigger a migration scan. + for rec in self: + migrations = ( + self.env["odoo.module.branch.migration"] + .search( + [ + ("module_id", "=", rec.module_branch_id.module_id.id), + ( + "target_branch_id.sequence", + ">=", + rec.odoo_version_id.sequence, + ), + ] + ) + .sudo() + ) + migrations.last_target_scanned_commit = False + migrations._compute_renamed_to_module_id() + migrations._compute_replaced_by_module_id() + migrations._compute_state() diff --git a/odoo_repository_migration/security/ir.model.access.csv b/odoo_repository_migration/security/ir.model.access.csv index 7b021329..fd58e05f 100644 --- a/odoo_repository_migration/security/ir.model.access.csv +++ b/odoo_repository_migration/security/ir.model.access.csv @@ -2,3 +2,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_odoo_migration_path_user,odoo_migration_path_user,model_odoo_migration_path,odoo_repository.group_odoo_repository_user,1,0,0,0 access_odoo_migration_path_manager,odoo_migration_path_manager,model_odoo_migration_path,odoo_repository.group_odoo_repository_manager,1,0,1,1 access_odoo_module_branch_migration_user,odoo_module_branch_migration_user,model_odoo_module_branch_migration,odoo_repository.group_odoo_repository_user,1,0,0,0 +access_odoo_module_branch_timeline_user,odoo_module_branch_timeline_user,model_odoo_module_branch_timeline,odoo_repository.group_odoo_repository_user,1,0,0,0 +access_odoo_module_branch_timeline_manager,odoo_module_branch_timeline_manager,model_odoo_module_branch_timeline,odoo_repository.group_odoo_repository_manager,1,1,1,1 diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index 0797d32f..b5b38356 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -297,6 +297,7 @@ def test_renamed_to_module_in_target_version(self): next_branch = self.env["odoo.branch"].search( [("sequence", "=", self.branch.sequence + 1)] ) + self.assertEqual(self.branch.next_id, next_branch) # Create the target module new_module = self.module.copy({"name": "new_module"}) target_module_branch = self._create_odoo_module_branch( @@ -316,12 +317,16 @@ def test_renamed_to_module_in_target_version(self): self._simulate_migration_scan( "target_commit1", report={"process": "migrate", "results": {}} ) - self.assertEqual(self.module_branch.next_odoo_version_id, next_branch) # Module has been renamed starting from 16.0 - self.module_branch.next_odoo_version_state = "renamed" - self.module_branch.next_odoo_version_module_id = new_module + self.module_branch.timeline_ids.create( + { + "module_branch_id": self.module_branch.id, + "state": "renamed", + "next_module_id": new_module.id, + } + ) renamed_to_module = self.module_branch._renamed_to_module_in_target_version( - self.module_branch.next_odoo_version_id + next_branch ) self.assertEqual(renamed_to_module, new_module) # We target 17.0 to check if intermediate data in 16.0 is found @@ -347,6 +352,7 @@ def test_replaced_by_module_in_target_version(self): next_branch = self.env["odoo.branch"].search( [("sequence", "=", self.branch.sequence + 1)] ) + self.assertEqual(self.branch.next_id, next_branch) # Create the target module new_module = self.module.copy({"name": "new_module"}) target_module_branch = self._create_odoo_module_branch( @@ -366,12 +372,16 @@ def test_replaced_by_module_in_target_version(self): self._simulate_migration_scan( "target_commit1", report={"process": "migrate", "results": {}} ) - self.assertEqual(self.module_branch.next_odoo_version_id, next_branch) # New module is replacing current one starting from 16.0 - self.module_branch.next_odoo_version_state = "replaced" - self.module_branch.next_odoo_version_module_id = new_module + self.module_branch.timeline_ids.create( + { + "module_branch_id": self.module_branch.id, + "state": "replaced", + "next_module_id": new_module.id, + } + ) replaced_by_module = self.module_branch._replaced_by_module_in_target_version( - self.module_branch.next_odoo_version_id + next_branch ) self.assertEqual(replaced_by_module, new_module) # We target 17.0 to check if intermediate data in 16.0 is found diff --git a/odoo_repository_migration/views/odoo_module_branch.xml b/odoo_repository_migration/views/odoo_module_branch.xml index 914d7dc9..302deea1 100644 --- a/odoo_repository_migration/views/odoo_module_branch.xml +++ b/odoo_repository_migration/views/odoo_module_branch.xml @@ -9,44 +9,19 @@
+
-
- Starting from version , - this module - - - - -
diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index 868b338d..15b53d0a 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -145,8 +145,9 @@ diff --git a/odoo_repository_migration/views/odoo_module_branch_timeline.xml b/odoo_repository_migration/views/odoo_module_branch_timeline.xml new file mode 100644 index 00000000..60ec528a --- /dev/null +++ b/odoo_repository_migration/views/odoo_module_branch_timeline.xml @@ -0,0 +1,102 @@ + + + + + + odoo.module.branch.timeline.form + odoo.module.branch.timeline + + + + + + + + + + + + + + + + + + + + odoo.module.branch.timeline.tree + odoo.module.branch.timeline + + + + + + + + + + + + odoo.module.branch.timeline.search + odoo.module.branch.timeline + search + + + + + + + + + + + + + + + + + + + + + + Timelines + ir.actions.act_window + odoo.module.branch.timeline + + {'search_default_group_by_org_id': 1} + + + + + From 0eb152bf4432f3db9ce8f1ecb7c61d2a61a4c0db Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 3 Dec 2025 03:33:36 +0000 Subject: [PATCH 34/65] [UPD] README.rst --- odoo_repository_migration/README.rst | 42 +++++++++++----- .../static/description/index.html | 49 ++++++++++++------- 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index cafdf9b9..3f2df64a 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ============================== Odoo Repository Migration Data ============================== @@ -7,20 +11,26 @@ Odoo Repository Migration Data !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:4705947f581f1953f9c146b61253c9ebd83975867442c894ece00ac3c5f142e0 + !! source digest: sha256:ca1a8de995fd9b4e82e491087c7f380a6a17ae31d175e8709f2ddb43fd9f95cb !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 -.. |badge3| image:: https://img.shields.io/badge/github-camptocamp%2Fodoo--repository-lightgray.png?logo=github - :target: https://github.com/camptocamp/odoo-repository/tree/16.0/odoo_repository_migration - :alt: camptocamp/odoo-repository - -|badge1| |badge2| |badge3| +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fodoo--repository-lightgray.png?logo=github + :target: https://github.com/OCA/odoo-repository/tree/16.0/odoo_repository_migration + :alt: OCA/odoo-repository +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/odoo-repository-16-0/odoo-repository-16-0-odoo_repository_migration + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/odoo-repository&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| This module collects modules migration data from Odoo repositories. @@ -32,10 +42,10 @@ This module collects modules migration data from Odoo repositories. Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. +Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -56,6 +66,16 @@ Contributors Maintainers ~~~~~~~~~~~ -This module is part of the `camptocamp/odoo-repository `_ project on GitHub. +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/odoo-repository `_ project on GitHub. -You are welcome to contribute. +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index cc60c0b7..6ba7c13c 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -1,18 +1,18 @@ - -Odoo Repository Migration Data +README.rst -
-

Odoo Repository Migration Data

+
+ + +Odoo Community Association + +
+

Odoo Repository Migration Data

-

Beta License: AGPL-3 camptocamp/odoo-repository

+

Beta License: AGPL-3 OCA/odoo-repository Translate me on Weblate Try me on Runboat

This module collects modules migration data from Odoo repositories.

Table of contents

@@ -384,32 +389,40 @@

Odoo Repository Migration Data

-

Bug Tracker

-

Bugs are tracked on GitHub Issues. +

Bug Tracker

+

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Camptocamp
-

Contributors

+

Contributors

-

Maintainers

-

This module is part of the camptocamp/odoo-repository project on GitHub.

-

You are welcome to contribute.

+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/odoo-repository project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
From bd3e3dd4475e234a77c18357ec4cf2150d20018c Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 3 Dec 2025 03:33:36 +0000 Subject: [PATCH 35/65] [ADD] icon.png --- .../static/description/icon.png | Bin 0 -> 10254 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 odoo_repository_migration/static/description/icon.png diff --git a/odoo_repository_migration/static/description/icon.png b/odoo_repository_migration/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1dcc49c24f364e9adf0afbc6fc0bac6dbecdeb11 GIT binary patch literal 10254 zcmbt)WmufcvhH9Zc!C8B?l8#UE&&o;gF7=g3=D(IAOS+K1lK^25Zv7%L4sRw_uvvF z*qyAk?>c**=lnR&y+1yw{;I3Hy6Ua2{<d0kcR+VvBo; zA_X`>;1;xAPL9rQqFxd#f5{a^zW*uaW+r3+U{|fRunu`GZhy$X z8_|Zi{zd#vIokczl8Xh*4Wi@i0+C?Rg1AB5VOEg8B>buLFCi~r5DPd2ED7QP2>^LO zKpr7+?*I1bPaFSLLEa0l2$tj*;u8Qtc=&(RUc*VK@ zjIN{I--GfO@vl+&r^eqy_BZ3dndN_PDzMc*W^!?dIsWAWU@LBjBg6^f4F6*!-hUYh zY$Xb}gF8b0%S1Ac@c%Rs()UCiEu3v6SiFE>h_!{gBb-H2{e=wB5o!YkT0>#LKZFw$ z?CuD0Gvfsb(|XbVxx0AL0%`gG2X+6|f;jiTHU9shtjoW-{2!| zMN*WuOj6elhD4zqgjNpX>F#JP{)hAbenX<+FPr>7jXM&q{|x+pbj8cU<=>Ej zWE1_%qoFVzDAZB%g@v<+1ud%<#2E~ML11jOV5pUZoXktGmzB38%te^i-3o9i$lge>z>tBcK|P2K0H9w{l#|i%$~egM)Ys{q>p<9yaE*%v2cy1wXE{AXqG1_b znfyg@Fq*e@yC)^(@$R*j^E;skyEM6pmL$1ctg*mWiWM&q1{nj>E^)Odw$RPr zhjesSk}k}@-e_%uZTy0t_*TJD&6%*HV0KH>xE@oBex6CL@`Ty3nH_2OF#M?6j(j|9 znRKGSfp3Q2i+|>}w?>8g$>r`|OcvG5r;p)z8DO8+O>EvYQ=_~`p}9!ReUEjUnNL@6 z+C*aoo67(sd|7QgW54@V9Y8PnBW$Q+7ZsRFA}Vj*viA!yWUfb!s*yJi6JKsXZCH4j z*B%nJpad-DDvJ8d>xrxkkh6A}i7V3nULqHCiG~|)YY6{NE3M}c^s#PQhzhsJUf^QW zR+F;up-dN*!)M1ZYl@d0HoqfVD2PNiQcPdzq4NDKO!8mUl{!t*ntBg_+-+lRlI0~Lr>5v!PiQj|hD7B-YFIs~6hIY*R6USZA zlb}=UxqxpSzIsL3pPmiuixCN|3LFBd?0Ih8Y6GWQ;U>dkdXtQaQ&8H|TGAQbuHY=F z_R83&B{1_hP7L#$^eAe?GPB_83y#HZKTwD>e-@E2P>Gk$BBb9|Ivfmdp za~s>3=aj(;xmz8n)sI}uFO$|C>0CZbcTY$Bq6~L-Bc9=vl@X#0S~Q@j8iKzuPeQE_ zQSI)wNz~CvJ>!%QszoCfUm9}h^DL!WYAN|FtMO#kpDXq74sYC87(uvv*jiCjV?Ta& zgO1D0OP3TEN3YnBpD6GnmsEolzEbGM{&VlTz_)J(o{nl0+TmNt{xL%L6G&UR$^aYC zQOA#W7R%9JsC5oTZJE>_?!Ci}mNH{0ObyUd%Q!k%5J8Z`8sR!m`~|Taje`(bLD7=a z-{-=d7w;k@DIrgU{I@K}eN`>S**Lg<@ChAf$M(&kV9TLUixqFQ>YoYHrI!K#R6`S> z%?d5hQ@&;Gje<|uRQZb%Hhibocl9(buI?=0aZW{JYXx?ZS@Lr%G8L<d+riEi2~+{HfHK{K^VrGYNi{2-WJOiC>Pz?f*)cxKCl>1H1=$jb!^ zpmYw>eoiM0Hy7$xbbX_e5o*+{7T2&-t%-h4i7MMo;k|tSqQAeNkwHS9hWY#EV7r3| zTmOmN{;b9OUZpp`LP(I9Wo%R#$b6YdH7GD4*p6>a2N2A04pQ*n;INQMh%+mj;x7>S z_(H?uJ^n!r1)kJH1*s+%$al#?C^Cw{H@RA^QGB=Dubyc)XUaY>f`(VKTlIO-YNCp{1n zOl*>jT?Dtf5fD$DY-j&B*Xmn|2-u2OB zBL@-lFs5lhcQKXBR*cIXmi%~EJcc^5#Xpg!E^A6sXf1#$qJGRpmU~A zcdj-cvBfx(fIRAMU(1obztJR%I7v3R-%$#~r!0sS^I(iC*5i6296*88A7I=_JhU3p zya!aCti0R5*RFT%LW0R|;u&oJ6=P-c$le4J0bi}u!!@;xzao|l6fJ{;Mld9hGhrJg zr_B)=4yktp)yPB@tCC_L9h1>GzXD6DA!W7xt{1)8!07~gONkEWC8@y%lciB{9ojy) zWm$drJ_9uVJ>Q$-`@q%OM7_S>(K=__CGYB~@@mE^Z=eT|x0Rv?Z-N)LLWR zod*Zy3v)iMX@usPX-OKBDgC8yq?fMhqf8H)A&C)Hi29YFn!NVf5!J0-F{wC&L5-3`#id=4?=2>Zp6Pdu4N6#bG&atu7 z8IET&ciXy_Tp4YjMx3yIAbw#_e2#jgGJ~ogkv-|M7|%Gio%2@mnS89NKUOM#Bzg4_ z9e9oN;^m>G*#?)AawODi6YckRPmkSKD_4b4WFpj|@|eS!B0WN@?QscYzTH`~6e%iz z!z1>ps)CG37%(E=kZ_>re)@ODv^0^=rWU^*m;6M&gD10EYImO98JVabRe5{#wrogYUKPB@_(#e7Ej9_x;n1oHDj5GawU)A&1hWj|HzJB(q{vMTX>jOW;Jz zBsW&SqTaR7!NXXg_A}$XnFpg_n)Zi;{e9eb*k|b(y$a}12boJ7rqQXQpVhU8HxHTl zt8Ln!KLFyfq!%}hdMXle^qajw2g6S{z&7tQ6J(w9 z3+!HTO{_TqM{9o$RR~lKFf4b4(xLUP?QG;McNFQc_Yd_mig9Ejy9%q~Ye>rIn3};U z)w&1@QCK;cC(;x0G&YuSad+>{c@ZsFJcUdcs@PP-x{mrO)|6_#CjMlXsMJx;Cr?FF zVFrlt@$Z-Ll^*7d0#`5Uez@bb{Xn(BQLhScBhF!6+aIso0=l{PP7P(6-ru>nVy%AP z+|eZpY(ooMU7rtG$l#14v=Z?@ebOjm(A2)5k_${|wAA$oq+;42wiS78ezjgWWnTrF z`1!i2h{fM91aD8uxz?tZpE(PsL37e3$*I6%un5Bzzpn10p`j72R;3=Oaug_|Z(y)@ z9$SJN@-5d1tNIy0=7|d&_HAnDx!yDd-u#qmfuDh)0a_CVje{hvQz9rDFHJTpQ0Dg@ zGQ3t*gZlcFSXfx%OG@Cds&NDROxd^osY_)abmo^dKMUY!R~kGH%*;rutPF@Mx$zrv z6Q1soKnYYRW#;Bi-!H)>Br0<`y+Wy~p7_<>{ljuG`Dpje=v1x}-ND<)bWBr|<}v6B zkDTUZ^@VsH>CyR}ml4j2rB{}0q8eGwX>ExkI9yZN0)(P}$N(yi$AxmBY#Xj`(7zs{ zJbn2&jE`-*0lww_r;|fNaWm_xp;c9JHIv|RExZGKP%18qjgYa);`N-^VqXNVz{~)~ z?^&D;ouy!pKPy?%@xH`A zSR z7x%N3@o&{YEjfa|1;*eW_4TU{ zt;qCcY3Hj(<0DJuny*QL!y!StcG{>bhpUP%eVMq=1xcR>yZT8X9)1;rXOmQjPcANs zr>&Qb{rr66;s|4v3iGmQlMjr9j;G6pqNs%;TsyVNd3{i~hpDX8ugdcnd&UQJzj)rH zh>S6#n`cCJ9CwHv<2Ht$o`R5(h#r||VB?%J?s5W48;^o)b`Pi1^~}5{Y19lg{&W@LfHt*gc1`w$RfLrK{~H?A1$5 z;5v?AIhpN%gQsR6+Act9-3y z8>jCTMnWQq-^s3#Lb|WalgB$k3F>}lyCxs<2&A;LS0}s#<|hPx9kM#B+Lu2DiD_3P zelg;N!80(j@HNc2pXs}re%sHi+{aqBt~qUOy86?zN>7)yiCEJqy@2Gh#gzJE6j6Rx zBQK{77zW?gLWtQ20Dzntu16k9^N>DQ@Nmbx*mOg=F=k)8VJfM%y(Xu41;8YCz+@K| z9u7vhlT`BOnk_oMTeC;u@OhhoTeA`^34^iMihCLM_uVD>rI-9@4l7ocZl@DJ8FWZU zB0lRBIqkHj4#pE&mD(X!e!~;G$`7f47k* zOznM2@`&KM(|f5}sz)z%2}yJ5YmMj5Zwzr-W?v3R&@KuJ+l0zo==N@)nsbMHqHV}w z7#_ntMGCNM21RuH^SYG+RH0sHUsF2z7ams57@2xbPj0y5)8h+caqv@P^q!do+}>+X zzUBx|mikTawzXWYzJ4(AqAJpBF4ObmD_@gyg->oFGB6`k(8+?rFRV5P1yDkFM=8(c z%RI)iG(rKtq-^V%B_(R9;tk6WIzA?x@cESTXg zWYDBxkoNB5v6J8BP&n@HVtBNb@r+XYpjgub zR4oE*$ffXJuh2g8TCaLnpNoSxJ~Jx@ayx9z5Osa)=AI#bg^5eQb<6gpR%c+Qs#N*e z@XE4pAmjdI#0%pV7sIN>mNa^jTkd=<==2_#t-}9Ju&Z^|Lp$%B92@eN%=MRc)LK$% z@!XAg;dQ8bt=@ZNey7+a(dy^o;QKGP@Rb5NJYQRrGEC{J=FB(Irw-MAfoP(9RK;)&jlxSCT=W;ODCf($WqRFhqN#LR^qVhK zWhEp4`{Nnk;n0FHj}eNCZpRM`Y-@MIM&pvr7zQOZ3Ik5;CmZbR99b&22(!-07YNF) z$o0MKej-jnvQV39{TH4r2R5univa1{ASc|VOTi4c@`t2FId|xkh5typ-rdU;1j){adk@*+( zkHj{5B~eSy&HrPOOvl_FJ98)0V;^d`0-u0FTslgiLBQVGSTiSyu zgMGAu&R}SbNa-DgKJb?;fe3Qys$?=;5?V`eRiq*Kj$I`}Z*x4rC~eNM=DsOq(=nUW>(+7o@O8K-_U(X? zTyg032nXKax5W~SF5|eBj%r8Fa>i!ejC72*sd}zJ)t7Xy!gFvM`c4@*Iw>z$u)j_l zR-Uqxymg}>Ti>i%9j*4kwfC33i~kyIQ``n)r(L z!|H2*)Mwj4dk%e*L0tgFdW185>j4<7YwLXwcOsed`%6mS{+=&d@d!B}GkbDV*0 zNIWzW^|trz!&;qeI&mPiVDOUL70xpqVv0fpN9tjpu)@1LD9D<9}9{57j9!W$`zC6&i zl9lKkmPh`x)5+h>>JtiRNNBW5$_)%-)#+SVSGsjX2T=+SRX05>yJZd`1hyk<@{%1+ zDu^k>J$d*Qz6BZMwHx!@O**^Tx&fsHDw%$@J0nfj^je^Ihy*aIx{B(hkBvSvh46Z9 zRO)BjjXL_IHXKo~$4es=8Wxk;Y+&nVBCXA;=MVuLgVn8Mk(*y^+kP3f?Pr~4^A}hXj9UHS}qeI%XKD3KhHnkrNH0(Y20BWl&!Kfm`EVh2;i5C zpirU^K0nc2-I{cqvjZKVx z=&hH#-d=gDWjVE}cMNAPJf;#NYdQ=h`twjX6yquXuCNgGx1~uk{YHAmFpQF`ZLGC=~ukEyj?cFDI zH=@XvV#AY1EY4qb`y*;Ki>KuFB|2|toL7__Cr0S1Dl{s#y0=~7HSq~&7lpBc*VLua zvv3r&-LM*{hq%IYP7<@)dG-G$kMrZaqs(MYoZ zugEeJ@u(ip9rMoVtoFe;dF`^Br5x7v!rr5`hb5mJ#ocGqXHnm9m`yILjd0>UQSMv) z^v}l5^bM6RZ6M%{mkI) zHOoSp&dX)*xUt+kXscna#a`XxI;Ul2Sxa^i5sZc=(Q)oA^2-_;!pfYHAul+oA@Ilelm;rw@FYR+SIaWS?;_ zUdw<|qqaYq(nqu>rG48E9dYAoT6GH;QRuBYK1}W#C_Z_?7~k*pJ3?MzVt&rhZTsBy zw?nN$_Z>kimtwWcy`0?G#!)&7GjOcxCQps@p&ml8>~z(t=sjhR$6aFh!Vw5GA(lTh z5GM)jCwloa6a}7mdfqNYE7oi`Jv$m5>5qR%9eZ=)=a z+K4j5NpcDHHdepCS+P*{@o=yNp&TE(Sd4b0Notqso-Kt_mhDk1<-fa>T4KdY2N`U) zxu41vD%T&k$Gl?CW81%7r#-o1TZ0&PCcy}L4TPiV;sz`|S!&w8-s$rLdM zF&)>@`7=)65PWn#oi|8tXNb|((2ojf9d0fNZ^l7xY~dX~%*Xf-v2W-2n$i~s!4?H; z2qbQscFN21tqB{|x1+(^G~xQSrvX&Y;V-%?b1}zjBQX{GOFcVYTcwm>>}>6^HA=$x zn+z^Biv_5}0!#@7z1~YXJFCT2?D^jm+kH7jAqBo?M@ZdMl|2|66oLnSJXUOJtVLxe z0vH)N^t*qrjq=eFRMV>BFEfS)-2RzKlt973;d3D}4edwIE>kGc5-o=JV56ird)RlS z{Jg@0t-b#Ife80%!E~(7`qkZ8O~Q-8_{j7G&tqwX&&>^tm-#*{v7j-f1n0}mCR#7P z-4FkajD2$9?4Fc7-C_|0Z_G^bxIs%tWk|aFgSQ(qkM+5PRh=g&ZeAZg35$-kn~}_;~&fP-dCNCzg>{gyW!~LZpn?aZ~Va3~H0Ta)z z<4XPVk@;#%1S@fq<(2#8T04#8$mz>vM;(jek0>Qh!K%t5*4tU(fVYwD3Ri~=D!AmI zV$Dt#TEDX7{lpW%tF&DOlTO)vZodn_%wYu~)ZQ}Qo^cBbDHd{YajkzNxttQW>ST<^ z2~^xhB_y1sjIF5;xchvCn{QVugIE2eYZDZ!-Y-4lJdb34*k({@M zJ5!9Di^||~(IZ4iOoAbtggao+CaYvJynmB^;4r-tY2gS_*P!?U?hlEX;l+^*{%B2n z)|1j9wOHQQ^5Xha>{Cu8_w^8=#6;Dz7kU~RgTqn;ynDm6{xdlkf2vk0UK^oS3yVy4 zE+v&qnlYtPHBk#X&2}r7`@K`J@^e~Qm?iRJ*tbAaZDZTmB&mWMkZp7Kj7^kth#_uX z5z>gC(8Xz|Ie(+#&wiF3;Aey|Db(R*-U)!6;l_5@u?-$>j0SgEl5+c}Lfe-$p-dFH zB_$bC<)x6#A_2Uuo8=^l1@}vK!gvbF#b&MoH8ac3xMxUz$LFb8KU(x$YhtHanM_sw zYOFMBX2iNNSe&a}!;G9nv(tsW4@%3iQcqczOCF*JOBQ@4Orw=o?_vc(9$hfO`>U6& zyY_CUa9pASiJpmv`@oR!k;&$`h8!)$uS=}d-fPddfIdMDUW@%3y1LI(1Q=e$)sz(QC*E;Nfl99YTgk+|@jl`+iF?<_D?4YqV0Zl)lO8YWC@1ZWW^mi{5ePQN<~FQ2NMG$|K{py5akJa zkezmqhN)>MGMp$7=sOo2(7ppv``dCIwf&MaQQis7S596kkiw8Do(jO?EY4iJ4Hec6 z4Hymzu`w)cI9Pbq6GPtTP)x&Lmk;FT=ZCB4>(5}c0?;2l`p&?>&<;2(P8a3lOTNP# zdEzF5qDpkRR&PZC&cS{7xD@qV;(g5X%xI?m$9Q Date: Wed, 3 Dec 2025 14:07:36 +0100 Subject: [PATCH 36/65] [IMP] odoo_repository_migration: update README --- odoo_repository_migration/README.rst | 88 +++++++++-- .../readme/CONTRIBUTORS.md | 2 + .../readme/CONTRIBUTORS.rst | 2 - .../readme/DESCRIPTION.md | 27 ++++ .../readme/DESCRIPTION.rst | 1 - odoo_repository_migration/readme/USAGE.md | 10 ++ .../static/description/index.html | 137 ++++++++++++++---- 7 files changed, 221 insertions(+), 46 deletions(-) create mode 100644 odoo_repository_migration/readme/CONTRIBUTORS.md delete mode 100644 odoo_repository_migration/readme/CONTRIBUTORS.rst create mode 100644 odoo_repository_migration/readme/DESCRIPTION.md delete mode 100644 odoo_repository_migration/readme/DESCRIPTION.rst create mode 100644 odoo_repository_migration/readme/USAGE.md diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index 3f2df64a..146f7698 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ============================== Odoo Repository Migration Data ============================== @@ -17,7 +13,7 @@ Odoo Repository Migration Data .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fodoo--repository-lightgray.png?logo=github @@ -32,13 +28,82 @@ Odoo Repository Migration Data |badge1| |badge2| |badge3| |badge4| |badge5| -This module collects modules migration data from Odoo repositories. +This module collects and records modules migration data from Odoo +repositories. + +Thanks to `oca-port `__ and data +collected by ``odoo_repository``, this module will generate migration +data on each module for declared migration paths (e.g. 16.0 -> 18.0). + +Also, for specific cases you can declare what a module became in a given +Odoo version, like OCA ``web_domain_field`` replaced by standard Odoo +module ``web`` starting from 17.0 (with an optional explanation that +could help users, like how to use this new module compared to the +previous one). + +Given a migration path, a module can get one of this migration status: + ++-----------------------------+---------------------------------------+ +| Status | Description | ++=============================+=======================================+ +| *Fully Ported* | All commits from source version are | +| | present in target version | ++-----------------------------+---------------------------------------+ +| *To migrate* | The module doesn't exist on target | +| | version | ++-----------------------------+---------------------------------------+ +| *Ported (missing commits?)* | Some commits from source version are | +| | not ported in target version (could | +| | be false-positive) | ++-----------------------------+---------------------------------------+ +| *To review* | A migration PR has been detected | ++-----------------------------+---------------------------------------+ +| *Replaced* | The module has been replaced by | +| | another one (not sharing the same git | +| | history) | ++-----------------------------+---------------------------------------+ +| *Moved to standard?* | The module name has been detected in | +| | Odoo standard repositories for target | +| | version. High chance this module is | +| | or should be replaced by another one | +| | instead (by creating a timeline), so | +| | it mainly helps to detect such cases. | ++-----------------------------+---------------------------------------+ +| *Moved to OCA* | the module name is available in an | +| | OCA repository (could be a | +| | false-positive because sharing the | +| | same name, in such case a timeline | +| | has to be created) | ++-----------------------------+---------------------------------------+ +| *Moved to generic repo* | a specific module (only available in | +| | a project) in source version now | +| | exists in a generic repository (could | +| | be a false-positive if both modules | +| | have only their name in common) | ++-----------------------------+---------------------------------------+ + +It helps to build a consolidated knowledge database accross different +Odoo versions for everyone: functionals and developers. **Table of contents** .. contents:: :local: +Usage +===== + +To enable this feature, the option *Collect migration data* should be +enabled on repositories (opt-in). + +To record what a module became starting from a given Odoo version, you +should do it through the *Odoo Repositories / Data / Modules / +Timelines* menu. + +Once the scheduled action ran, the migration data are available on each +module in *Migration* tab, or through the *Odoo Repositories / Data / +Modules / Migrations* menu. + Bug Tracker =========== @@ -53,18 +118,19 @@ Credits ======= Authors -~~~~~~~ +------- * Camptocamp Contributors -~~~~~~~~~~~~ +------------ -* Camptocamp - * Sébastien Alix +- Camptocamp + + - Sébastien Alix Maintainers -~~~~~~~~~~~ +----------- This module is maintained by the OCA. diff --git a/odoo_repository_migration/readme/CONTRIBUTORS.md b/odoo_repository_migration/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..17752927 --- /dev/null +++ b/odoo_repository_migration/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Camptocamp + - Sébastien Alix \ diff --git a/odoo_repository_migration/readme/CONTRIBUTORS.rst b/odoo_repository_migration/readme/CONTRIBUTORS.rst deleted file mode 100644 index a0c91e35..00000000 --- a/odoo_repository_migration/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1,2 +0,0 @@ -* Camptocamp - * Sébastien Alix diff --git a/odoo_repository_migration/readme/DESCRIPTION.md b/odoo_repository_migration/readme/DESCRIPTION.md new file mode 100644 index 00000000..6bd474cf --- /dev/null +++ b/odoo_repository_migration/readme/DESCRIPTION.md @@ -0,0 +1,27 @@ +This module collects and records modules migration data from Odoo +repositories. + +Thanks to [oca-port](https://github.com/OCA/oca-port/) and data +collected by `odoo_repository`, this module will generate migration data +on each module for declared migration paths (e.g. 16.0 -> 18.0). + +Also, for specific cases you can declare what a module became in a given +Odoo version, like OCA `web_domain_field` replaced by standard Odoo module +`web` starting from 17.0 (with an optional explanation that could help +users, like how to use this new module compared to the previous one). + +Given a migration path, a module can get one of this migration status: + +| Status | Description | +| ------ | ----------- | +| *Fully Ported* | All commits from source version are present in target version | +| *To migrate* | The module doesn't exist on target version | +| *Ported (missing commits?)* | Some commits from source version are not ported in target version (could be false-positive) | +| *To review* | A migration PR has been detected | +| *Replaced* | The module has been replaced by another one (not sharing the same git history) | +| *Moved to standard?* | The module name has been detected in Odoo standard repositories for target version. High chance this module is or should be replaced by another one instead (by creating a timeline), so it mainly helps to detect such cases. | +| *Moved to OCA* | the module name is available in an OCA repository (could be a false-positive because sharing the same name, in such case a timeline has to be created) | +| *Moved to generic repo* | a specific module (only available in a project) in source version now exists in a generic repository (could be a false-positive if both modules have only their name in common) | + +It helps to build a consolidated knowledge database accross different +Odoo versions for everyone: functionals and developers. diff --git a/odoo_repository_migration/readme/DESCRIPTION.rst b/odoo_repository_migration/readme/DESCRIPTION.rst deleted file mode 100644 index fa0b3fab..00000000 --- a/odoo_repository_migration/readme/DESCRIPTION.rst +++ /dev/null @@ -1 +0,0 @@ -This module collects modules migration data from Odoo repositories. diff --git a/odoo_repository_migration/readme/USAGE.md b/odoo_repository_migration/readme/USAGE.md new file mode 100644 index 00000000..daae5c2e --- /dev/null +++ b/odoo_repository_migration/readme/USAGE.md @@ -0,0 +1,10 @@ +To enable this feature, the option *Collect migration data* should be +enabled on repositories (opt-in). + +To record what a module became starting from a given Odoo version, you +should do it through the *Odoo Repositories / Data / Modules / +Timelines* menu. + +Once the scheduled action ran, the migration data are available on each +module in *Migration* tab, or through the *Odoo Repositories / Data / +Modules / Migrations* menu. diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index 6ba7c13c..ed2c3dfb 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -2,19 +2,18 @@ - -README.rst + +Odoo Repository Migration Data -
+
+

Odoo Repository Migration Data

- - -Odoo Community Association - -
-

Odoo Repository Migration Data

-

Beta License: AGPL-3 OCA/odoo-repository Translate me on Weblate Try me on Runboat

-

This module collects modules migration data from Odoo repositories.

+

Beta License: AGPL-3 OCA/odoo-repository Translate me on Weblate Try me on Runboat

+

This module collects and records modules migration data from Odoo +repositories.

+

Thanks to oca-port and data +collected by odoo_repository, this module will generate migration +data on each module for declared migration paths (e.g. 16.0 -> 18.0).

+

Also, for specific cases you can declare what a module became in a given +Odoo version, like OCA web_domain_field replaced by standard Odoo +module web starting from 17.0 (with an optional explanation that +could help users, like how to use this new module compared to the +previous one).

+

Given a migration path, a module can get one of this migration status:

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
StatusDescription
Fully PortedAll commits from source version are +present in target version
To migrateThe module doesn’t exist on target +version
Ported (missing commits?)Some commits from source version are +not ported in target version (could +be false-positive)
To reviewA migration PR has been detected
ReplacedThe module has been replaced by +another one (not sharing the same git +history)
Moved to standard?The module name has been detected in +Odoo standard repositories for target +version. High chance this module is +or should be replaced by another one +instead (by creating a timeline), so +it mainly helps to detect such cases.
Moved to OCAthe module name is available in an +OCA repository (could be a +false-positive because sharing the +same name, in such case a timeline +has to be created)
Moved to generic repoa specific module (only available in +a project) in source version now +exists in a generic repository (could +be a false-positive if both modules +have only their name in common)
+

It helps to build a consolidated knowledge database accross different +Odoo versions for everyone: functionals and developers.

Table of contents

+
+

Usage

+

To enable this feature, the option Collect migration data should be +enabled on repositories (opt-in).

+

To record what a module became starting from a given Odoo version, you +should do it through the Odoo Repositories / Data / Modules / +Timelines menu.

+

Once the scheduled action ran, the migration data are available on each +module in Migration tab, or through the Odoo Repositories / Data / +Modules / Migrations menu.

+
-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -397,26 +471,26 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Camptocamp
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

- -Odoo Community Association - +Odoo Community Association

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

@@ -425,6 +499,5 @@

Maintainers

-
From a98b16e30550d83967fe4b6290f1527ef7cc3f56 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 8 Dec 2025 11:19:19 +0000 Subject: [PATCH 37/65] [UPD] README.rst --- odoo_repository_migration/README.rst | 83 +++++++++--------- .../static/description/index.html | 86 ++++++++++--------- 2 files changed, 90 insertions(+), 79 deletions(-) diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index 146f7698..4adf3cfb 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ============================== Odoo Repository Migration Data ============================== @@ -7,13 +11,13 @@ Odoo Repository Migration Data !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:ca1a8de995fd9b4e82e491087c7f380a6a17ae31d175e8709f2ddb43fd9f95cb + !! source digest: sha256:0fbac9f1a41dc3b98caa116fb5f5b775aeedd201e7a99c8f8aebae5c9bdaef84 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fodoo--repository-lightgray.png?logo=github @@ -43,44 +47,43 @@ previous one). Given a migration path, a module can get one of this migration status: -+-----------------------------+---------------------------------------+ -| Status | Description | -+=============================+=======================================+ -| *Fully Ported* | All commits from source version are | -| | present in target version | -+-----------------------------+---------------------------------------+ -| *To migrate* | The module doesn't exist on target | -| | version | -+-----------------------------+---------------------------------------+ -| *Ported (missing commits?)* | Some commits from source version are | -| | not ported in target version (could | -| | be false-positive) | -+-----------------------------+---------------------------------------+ -| *To review* | A migration PR has been detected | -+-----------------------------+---------------------------------------+ -| *Replaced* | The module has been replaced by | -| | another one (not sharing the same git | -| | history) | -+-----------------------------+---------------------------------------+ -| *Moved to standard?* | The module name has been detected in | -| | Odoo standard repositories for target | -| | version. High chance this module is | -| | or should be replaced by another one | -| | instead (by creating a timeline), so | -| | it mainly helps to detect such cases. | -+-----------------------------+---------------------------------------+ -| *Moved to OCA* | the module name is available in an | -| | OCA repository (could be a | -| | false-positive because sharing the | -| | same name, in such case a timeline | -| | has to be created) | -+-----------------------------+---------------------------------------+ -| *Moved to generic repo* | a specific module (only available in | -| | a project) in source version now | -| | exists in a generic repository (could | -| | be a false-positive if both modules | -| | have only their name in common) | -+-----------------------------+---------------------------------------+ ++-----------------------------+----------------------------------------+ +| Status | Description | ++=============================+========================================+ +| *Fully Ported* | All commits from source version are | +| | present in target version | ++-----------------------------+----------------------------------------+ +| *To migrate* | The module doesn't exist on target | +| | version | ++-----------------------------+----------------------------------------+ +| *Ported (missing commits?)* | Some commits from source version are | +| | not ported in target version (could be | +| | false-positive) | ++-----------------------------+----------------------------------------+ +| *To review* | A migration PR has been detected | ++-----------------------------+----------------------------------------+ +| *Replaced* | The module has been replaced by | +| | another one (not sharing the same git | +| | history) | ++-----------------------------+----------------------------------------+ +| *Moved to standard?* | The module name has been detected in | +| | Odoo standard repositories for target | +| | version. High chance this module is or | +| | should be replaced by another one | +| | instead (by creating a timeline), so | +| | it mainly helps to detect such cases. | ++-----------------------------+----------------------------------------+ +| *Moved to OCA* | the module name is available in an OCA | +| | repository (could be a false-positive | +| | because sharing the same name, in such | +| | case a timeline has to be created) | ++-----------------------------+----------------------------------------+ +| *Moved to generic repo* | a specific module (only available in a | +| | project) in source version now exists | +| | in a generic repository (could be a | +| | false-positive if both modules have | +| | only their name in common) | ++-----------------------------+----------------------------------------+ It helps to build a consolidated knowledge database accross different Odoo versions for everyone: functionals and developers. diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index ed2c3dfb..4bd319e5 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -2,18 +2,19 @@ - -Odoo Repository Migration Data + +README.rst -
-

Odoo Repository Migration Data

+
+ + +Odoo Community Association + +
+

Odoo Repository Migration Data

-

Beta License: AGPL-3 OCA/odoo-repository Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/odoo-repository Translate me on Weblate Try me on Runboat

This module collects and records modules migration data from Odoo repositories.

Thanks to oca-port and data @@ -382,8 +388,8 @@

Odoo Repository Migration Data

Given a migration path, a module can get one of this migration status:

--++ @@ -401,8 +407,8 @@

Odoo Repository Migration Data

+not ported in target version (could be +false-positive) @@ -415,24 +421,23 @@

Odoo Repository Migration Data

- + - +
Status
Ported (missing commits?) Some commits from source version are -not ported in target version (could -be false-positive)
To review A migration PR has been detected
Moved to standard? The module name has been detected in Odoo standard repositories for target -version. High chance this module is -or should be replaced by another one +version. High chance this module is or +should be replaced by another one instead (by creating a timeline), so it mainly helps to detect such cases.
Moved to OCAthe module name is available in an -OCA repository (could be a -false-positive because sharing the -same name, in such case a timeline -has to be created)the module name is available in an OCA +repository (could be a false-positive +because sharing the same name, in such +case a timeline has to be created)
Moved to generic repoa specific module (only available in -a project) in source version now -exists in a generic repository (could -be a false-positive if both modules -have only their name in common)a specific module (only available in a +project) in source version now exists +in a generic repository (could be a +false-positive if both modules have +only their name in common)
@@ -441,18 +446,18 @@

Odoo Repository Migration Data

Table of contents

-

Usage

+

Usage

To enable this feature, the option Collect migration data should be enabled on repositories (opt-in).

To record what a module became starting from a given Odoo version, you @@ -463,7 +468,7 @@

Usage

Modules / Migrations menu.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -471,15 +476,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Camptocamp
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

@@ -499,5 +506,6 @@

Maintainers

+
From d2032a9b08efc4fceb001deb5f35703facea6abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 18 Dec 2025 10:01:24 +0100 Subject: [PATCH 38/65] [FIX] odoo_repository_migration: fix module scanning when it is replaced Bugfix following-up commit 39e6bd21026d28aacdb8bd3d11f3c217b73271cd. --- odoo_repository_migration/models/odoo_repository.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index b3a23203..dcf6f907 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -186,12 +186,13 @@ def _scan_migration_module( self.env["odoo.migration.path"].browse(migration_path_id).exists() ) # Skip migration scan if module is replaced in next versions - if module._replaced_by_module_in_target_version( + replaced_by_module = module._replaced_by_module_in_target_version( migration_path.target_branch_id - ): + ) + if replaced_by_module: return ( f"{module.name} is now replaced by " - f"{module.next_odoo_version_module_id.name}, no need to collect " + f"{replaced_by_module.name}, no need to collect " "migration data." ) # Check if module has already been migrated on target version but in a From 88f5e781ea5526a223956c71f30c66e9469eac75 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 18 Dec 2025 09:19:36 +0000 Subject: [PATCH 39/65] [BOT] post-merge updates --- odoo_repository_migration/README.rst | 18 +++++++++--------- odoo_repository_migration/__manifest__.py | 2 +- .../static/description/index.html | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index 4adf3cfb..312b59aa 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -11,7 +11,7 @@ Odoo Repository Migration Data !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:0fbac9f1a41dc3b98caa116fb5f5b775aeedd201e7a99c8f8aebae5c9bdaef84 + !! source digest: sha256:ae395cc60d699192b6cf738dc35db88f7eff53c3647a78e2351c179128320aa5 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -20,14 +20,14 @@ Odoo Repository Migration Data .. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 -.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fodoo--repository-lightgray.png?logo=github - :target: https://github.com/OCA/odoo-repository/tree/16.0/odoo_repository_migration - :alt: OCA/odoo-repository +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmodule--composition--analysis-lightgray.png?logo=github + :target: https://github.com/OCA/module-composition-analysis/tree/16.0/odoo_repository_migration + :alt: OCA/module-composition-analysis .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/odoo-repository-16-0/odoo-repository-16-0-odoo_repository_migration + :target: https://translation.odoo-community.org/projects/module-composition-analysis-16-0/module-composition-analysis-16-0-odoo_repository_migration :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/odoo-repository&target_branch=16.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/module-composition-analysis&target_branch=16.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -110,10 +110,10 @@ Modules / Migrations* menu. Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. +Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -145,6 +145,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/odoo-repository `_ project on GitHub. +This module is part of the `OCA/module-composition-analysis `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index 26385d82..3963cc85 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "16.0.1.3.0", + "version": "16.0.1.3.1", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/camptocamp/odoo-repository", diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index 4bd319e5..451a3745 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -372,9 +372,9 @@

Odoo Repository Migration Data

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:0fbac9f1a41dc3b98caa116fb5f5b775aeedd201e7a99c8f8aebae5c9bdaef84 +!! source digest: sha256:ae395cc60d699192b6cf738dc35db88f7eff53c3647a78e2351c179128320aa5 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/odoo-repository Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

This module collects and records modules migration data from Odoo repositories.

Thanks to oca-port and data @@ -469,10 +469,10 @@

Usage

Bug Tracker

-

Bugs are tracked on GitHub Issues. +

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -501,7 +501,7 @@

Maintainers

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/odoo-repository project on GitHub.

+

This module is part of the OCA/module-composition-analysis project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

From 61b98767293d39130f4c05fd1f75a2de135e62db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Sat, 17 Jan 2026 13:16:12 +0100 Subject: [PATCH 40/65] Update all modules manifest and README with new repo info --- odoo_repository_migration/README.rst | 2 +- odoo_repository_migration/__manifest__.py | 2 +- odoo_repository_migration/static/description/index.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index 312b59aa..dc92ea43 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -11,7 +11,7 @@ Odoo Repository Migration Data !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:ae395cc60d699192b6cf738dc35db88f7eff53c3647a78e2351c179128320aa5 + !! source digest: sha256:99462624428f4cf892ad96b5d989adf5014ec700b8668f653e14869d7f18dd66 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index 3963cc85..61b1b28c 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -6,7 +6,7 @@ "version": "16.0.1.3.1", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", - "website": "https://github.com/camptocamp/odoo-repository", + "website": "https://github.com/OCA/module-composition-analysis", "data": [ "security/ir.model.access.csv", "data/queue_job.xml", diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index 451a3745..a23e78c6 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -372,7 +372,7 @@

Odoo Repository Migration Data

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:ae395cc60d699192b6cf738dc35db88f7eff53c3647a78e2351c179128320aa5 +!! source digest: sha256:99462624428f4cf892ad96b5d989adf5014ec700b8668f653e14869d7f18dd66 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

This module collects and records modules migration data from Odoo From ab1af194c0fedf5e1745f6a25d2e7e3f9fa62faf Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Sat, 17 Jan 2026 12:45:07 +0000 Subject: [PATCH 41/65] [BOT] post-merge updates --- odoo_repository_migration/README.rst | 2 +- odoo_repository_migration/__manifest__.py | 2 +- odoo_repository_migration/static/description/index.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index dc92ea43..d643be3a 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -11,7 +11,7 @@ Odoo Repository Migration Data !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:99462624428f4cf892ad96b5d989adf5014ec700b8668f653e14869d7f18dd66 + !! source digest: sha256:897a7456693242289caa6a0ae4c8e32ea2c2d336832aa6ef5a4e50262e2865e6 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index 61b1b28c..1fbde63e 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "16.0.1.3.1", + "version": "16.0.1.3.2", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/OCA/module-composition-analysis", diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index a23e78c6..b7f783c6 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -372,7 +372,7 @@

Odoo Repository Migration Data

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:99462624428f4cf892ad96b5d989adf5014ec700b8668f653e14869d7f18dd66 +!! source digest: sha256:897a7456693242289caa6a0ae4c8e32ea2c2d336832aa6ef5a4e50262e2865e6 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

This module collects and records modules migration data from Odoo From 53989faa83f4e4fa67b5ddf8096c2f81d5403e9e Mon Sep 17 00:00:00 2001 From: oca-ci Date: Sun, 1 Feb 2026 10:53:27 +0000 Subject: [PATCH 42/65] [UPD] Update odoo_repository_migration.pot --- .../i18n/odoo_repository_migration.pot | 483 ++++++++++++++++++ 1 file changed, 483 insertions(+) create mode 100644 odoo_repository_migration/i18n/odoo_repository_migration.pot diff --git a/odoo_repository_migration/i18n/odoo_repository_migration.pot b/odoo_repository_migration/i18n/odoo_repository_migration.pot new file mode 100644 index 00000000..4a8547ad --- /dev/null +++ b/odoo_repository_migration/i18n/odoo_repository_migration.pot @@ -0,0 +1,483 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * odoo_repository_migration +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_form +msgid "Next Versions" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__active +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__active +msgid "Active" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__author_ids +msgid "Authors" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_repository__collect_migration_data +msgid "Collect migration data" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_repository__collect_migration_data +msgid "Collect migration data based on the configured migration paths." +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_search +msgid "Commits to Port" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__create_uid +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__create_uid +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__create_uid +msgid "Created by" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__create_date +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__create_date +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__create_date +msgid "Created on" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_form +msgid "Data" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model,name:odoo_repository_migration.model_odoo_migration_path +msgid "Define a migration path (from one branch to another)" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__display_name +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__display_name +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__display_name +msgid "Display Name" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__fully_ported +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Fully Ported" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Group By" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__id +msgid "ID" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_form +msgid "Info" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path____last_update +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration____last_update +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline____last_update +msgid "Last Modified on" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__last_source_scanned_commit +msgid "Last Source Scanned Commit" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__last_target_scanned_commit +msgid "Last Target Scanned Commit" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__write_uid +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__write_uid +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__write_date +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__write_date +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__write_date +msgid "Last Updated on" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__maintainer_ids +msgid "Maintainers" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__migration_path_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Migration Path" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.actions.act_window,name:odoo_repository_migration.odoo_migration_path_action +#: model:ir.ui.menu,name:odoo_repository_migration.odoo_migration_path_menu +msgid "Migration Paths" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_scan +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__migration_scan +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_scan +msgid "Migration Scan" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__state +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Migration Status" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model,name:odoo_repository_migration.model_odoo_module_branch_migration +msgid "Migration data for a module of a given branch." +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Migration to review" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.actions.act_window,name:odoo_repository_migration.odoo_module_branch_migration_action +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_ids +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_ids +#: model:ir.ui.menu,name:odoo_repository_migration.odoo_module_branch_migration_menu +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_form +msgid "Migrations" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__module_branch_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Module" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_oca +msgid "" +"Module now available in OCA. This module is maybe not exactly the same, and " +"doesn't have the same scope so it deserves a check during a migration." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_standard +msgid "" +"Module now available in Odoo standard code. This module is maybe not exactly" +" the same, and doesn't have the same scope so it deserves a check during a " +"migration." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_oca +msgid "Moved To Oca" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_standard +msgid "Moved To Standard" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__moved_to_oca +msgid "Moved to OCA" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__moved_to_generic +msgid "Moved to generic repo" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__moved_to_standard +msgid "Moved to standard?" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__name +msgid "Name" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__next_module_branch_id +msgid "New module" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__next_module_id +msgid "New module name" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__next_odoo_version_module_branch_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__next_odoo_version_module_branch_id +msgid "Next Odoo Version Module Branch" +msgstr "" + +#. module: odoo_repository_migration +#. odoo-python +#: code:addons/odoo_repository_migration/models/odoo_module_branch.py:0 +#, python-format +msgid "Next versions" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__note +msgid "Note" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form +msgid "Notes" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_generic +msgid "Now generic" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model,name:odoo_repository_migration.model_odoo_module_branch +msgid "Odoo Module Branch" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model,name:odoo_repository_migration.model_odoo_module_branch_timeline +msgid "Odoo Module Timeline (renaming/replacement)" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model,name:odoo_repository_migration.model_odoo_repository +msgid "Odoo Modules Repository" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Odoo Version" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__org_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__org_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Organization" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__pr_url +msgid "PR URL" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__port_commits +msgid "Ported (missing commits?)" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__process +msgid "Process" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Renamed" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__renamed_to_module_id +msgid "Renamed to" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_timeline__state +msgid "" +"Renamed: modules still share the same Git history (it allows to check commits that could be ported)\n" +"Replaced: module has been replaced (or merged) by another one that fulfill the same feature" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__replaced +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Replaced" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__replaced_by_module_id +msgid "Replaced by" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__repository_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__repository_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Repository" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__results +msgid "Results" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__results_text +msgid "Results Text" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_migration_path_view_form +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_form +msgid "Scan" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__odoo_version_sequence +msgid "Sequence" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__module_branch_id +msgid "Source" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__source_branch_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__source_branch_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Source Branch" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_generic +msgid "" +"Specific module now available in a generic repository. This module is maybe " +"not exactly the same, and doesn't have the same scope so it deserves a check" +" during a migration." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__odoo_version_id +msgid "Starting from version" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__state +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "State" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__target_module_branch_id +msgid "Target" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__target_branch_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__target_branch_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Target Branch" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_migration__migration_scan +msgid "Technical field telling if this migration path needs a migration scan." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch__migration_scan +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_project_module__migration_scan +msgid "" +"Technical field telling if this module is elligible for a migration scan." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__module_id +msgid "Technical name" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.constraint,message:odoo_repository_migration.constraint_odoo_migration_path_migration_path_uniq +msgid "This migration path already exists." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.constraint,message:odoo_repository_migration.constraint_odoo_module_branch_migration_module_migration_path_uniq +msgid "This module migration path already exists." +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_migration_path_view_form +msgid "This operation will scan all relevant repositories. Are you sure?" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__timeline_ids +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__timeline_ids +msgid "Timeline" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.actions.act_window,name:odoo_repository_migration.odoo_module_branch_timeline_action +#: model:ir.ui.menu,name:odoo_repository_migration.odoo_module_branch_timeline_menu +msgid "Timelines" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_search +msgid "To Migrate" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__migrate +msgid "To migrate" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__review_migration +msgid "To review" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_timeline__state__renamed +msgid "has been renamed to" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_timeline__state__replaced +msgid "has been replaced by" +msgstr "" From f344d60b36d7457b4a1c14ed0dfb31e94452014d Mon Sep 17 00:00:00 2001 From: mymage Date: Mon, 2 Feb 2026 07:58:12 +0000 Subject: [PATCH 43/65] Added translation using Weblate (Italian) --- odoo_repository_migration/i18n/it.po | 484 +++++++++++++++++++++++++++ 1 file changed, 484 insertions(+) create mode 100644 odoo_repository_migration/i18n/it.po diff --git a/odoo_repository_migration/i18n/it.po b/odoo_repository_migration/i18n/it.po new file mode 100644 index 00000000..51d8784d --- /dev/null +++ b/odoo_repository_migration/i18n/it.po @@ -0,0 +1,484 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * odoo_repository_migration +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_form +msgid "Next Versions" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__active +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__active +msgid "Active" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__author_ids +msgid "Authors" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_repository__collect_migration_data +msgid "Collect migration data" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_repository__collect_migration_data +msgid "Collect migration data based on the configured migration paths." +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_search +msgid "Commits to Port" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__create_uid +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__create_uid +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__create_uid +msgid "Created by" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__create_date +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__create_date +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__create_date +msgid "Created on" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_form +msgid "Data" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model,name:odoo_repository_migration.model_odoo_migration_path +msgid "Define a migration path (from one branch to another)" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__display_name +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__display_name +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__display_name +msgid "Display Name" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__fully_ported +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Fully Ported" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Group By" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__id +msgid "ID" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_form +msgid "Info" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path____last_update +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration____last_update +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline____last_update +msgid "Last Modified on" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__last_source_scanned_commit +msgid "Last Source Scanned Commit" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__last_target_scanned_commit +msgid "Last Target Scanned Commit" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__write_uid +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__write_uid +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__write_date +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__write_date +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__write_date +msgid "Last Updated on" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__maintainer_ids +msgid "Maintainers" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__migration_path_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Migration Path" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.actions.act_window,name:odoo_repository_migration.odoo_migration_path_action +#: model:ir.ui.menu,name:odoo_repository_migration.odoo_migration_path_menu +msgid "Migration Paths" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_scan +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__migration_scan +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_scan +msgid "Migration Scan" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__state +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Migration Status" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model,name:odoo_repository_migration.model_odoo_module_branch_migration +msgid "Migration data for a module of a given branch." +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Migration to review" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.actions.act_window,name:odoo_repository_migration.odoo_module_branch_migration_action +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_ids +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_ids +#: model:ir.ui.menu,name:odoo_repository_migration.odoo_module_branch_migration_menu +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_form +msgid "Migrations" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__module_branch_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Module" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_oca +msgid "" +"Module now available in OCA. This module is maybe not exactly the same, and " +"doesn't have the same scope so it deserves a check during a migration." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_standard +msgid "" +"Module now available in Odoo standard code. This module is maybe not exactly" +" the same, and doesn't have the same scope so it deserves a check during a " +"migration." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_oca +msgid "Moved To Oca" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_standard +msgid "Moved To Standard" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__moved_to_oca +msgid "Moved to OCA" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__moved_to_generic +msgid "Moved to generic repo" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__moved_to_standard +msgid "Moved to standard?" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__name +msgid "Name" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__next_module_branch_id +msgid "New module" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__next_module_id +msgid "New module name" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__next_odoo_version_module_branch_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__next_odoo_version_module_branch_id +msgid "Next Odoo Version Module Branch" +msgstr "" + +#. module: odoo_repository_migration +#. odoo-python +#: code:addons/odoo_repository_migration/models/odoo_module_branch.py:0 +#, python-format +msgid "Next versions" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__note +msgid "Note" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form +msgid "Notes" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_generic +msgid "Now generic" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model,name:odoo_repository_migration.model_odoo_module_branch +msgid "Odoo Module Branch" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model,name:odoo_repository_migration.model_odoo_module_branch_timeline +msgid "Odoo Module Timeline (renaming/replacement)" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model,name:odoo_repository_migration.model_odoo_repository +msgid "Odoo Modules Repository" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Odoo Version" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__org_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__org_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Organization" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__pr_url +msgid "PR URL" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__port_commits +msgid "Ported (missing commits?)" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__process +msgid "Process" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Renamed" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__renamed_to_module_id +msgid "Renamed to" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_timeline__state +msgid "" +"Renamed: modules still share the same Git history (it allows to check commits that could be ported)\n" +"Replaced: module has been replaced (or merged) by another one that fulfill the same feature" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__replaced +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Replaced" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__replaced_by_module_id +msgid "Replaced by" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__repository_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__repository_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Repository" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__results +msgid "Results" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__results_text +msgid "Results Text" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_migration_path_view_form +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_form +msgid "Scan" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__odoo_version_sequence +msgid "Sequence" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__module_branch_id +msgid "Source" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__source_branch_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__source_branch_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Source Branch" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_generic +msgid "" +"Specific module now available in a generic repository. This module is maybe " +"not exactly the same, and doesn't have the same scope so it deserves a check" +" during a migration." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__odoo_version_id +msgid "Starting from version" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__state +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "State" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__target_module_branch_id +msgid "Target" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__target_branch_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__target_branch_id +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +msgid "Target Branch" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_migration__migration_scan +msgid "Technical field telling if this migration path needs a migration scan." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch__migration_scan +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_project_module__migration_scan +msgid "" +"Technical field telling if this module is elligible for a migration scan." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__module_id +msgid "Technical name" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.constraint,message:odoo_repository_migration.constraint_odoo_migration_path_migration_path_uniq +msgid "This migration path already exists." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.constraint,message:odoo_repository_migration.constraint_odoo_module_branch_migration_module_migration_path_uniq +msgid "This module migration path already exists." +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_migration_path_view_form +msgid "This operation will scan all relevant repositories. Are you sure?" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__timeline_ids +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__timeline_ids +msgid "Timeline" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.actions.act_window,name:odoo_repository_migration.odoo_module_branch_timeline_action +#: model:ir.ui.menu,name:odoo_repository_migration.odoo_module_branch_timeline_menu +msgid "Timelines" +msgstr "" + +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_search +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_search +msgid "To Migrate" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__migrate +msgid "To migrate" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_migration__state__review_migration +msgid "To review" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_timeline__state__renamed +msgid "has been renamed to" +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields.selection,name:odoo_repository_migration.selection__odoo_module_branch_timeline__state__replaced +msgid "has been replaced by" +msgstr "" From 23bff5afaf42ca736620a7749d5ea7acbc8a1750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 16 Feb 2026 08:54:28 +0100 Subject: [PATCH 44/65] [FIX] odoo_repository_migration: fix tests OCA repositories do not have external IDs anymore. --- .../tests/test_odoo_module_branch.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index b5b38356..8da7cc45 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -1,4 +1,5 @@ # Copyright 2024 Camptocamp SA +# Copyright 2026 Sébastien Alix # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo.addons.odoo_repository.tests import common @@ -22,7 +23,14 @@ def setUp(self): last_scanned_commit="sha", ) self.std_repository = self.env.ref("odoo_repository.odoo_repository_odoo_odoo") - self.oca_repository = self.env.ref("odoo_repository.repo_oca_server_tools") + oca_org = self.env.ref("odoo_repository.odoo_repository_org_oca") + self.oca_repository = self.env["odoo.repository"].create( + { + "org_id": oca_org.id, + "name": "test-repo", + "repo_url": "https://github.com/OCA/test-repo", + } + ) self.gen_repository = self.env["odoo.repository"].create( { "name": "new_repo", From 4e37605a5c6019fbaff3360a98924a80d9410bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Fri, 27 Mar 2026 09:36:21 +0100 Subject: [PATCH 45/65] [FIX] odoo_repository_migration: explicit commits are not allowed in queue jobs --- odoo_repository_migration/utils/scanner.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/odoo_repository_migration/utils/scanner.py b/odoo_repository_migration/utils/scanner.py index a5734fcc..18f01796 100644 --- a/odoo_repository_migration/utils/scanner.py +++ b/odoo_repository_migration/utils/scanner.py @@ -95,6 +95,4 @@ def _push_scanned_data(self, module_branch_id: int, data: dict): res = self.env["odoo.module.branch.migration"].push_scanned_data( module_branch_id, data ) - # Commit after each scan - self.env.cr.commit() # pylint: disable=invalid-commit return res From 66308ef5daa64f9432288902eaccbd19e2dc18d0 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 27 Mar 2026 08:43:17 +0000 Subject: [PATCH 46/65] [BOT] post-merge updates --- odoo_repository_migration/README.rst | 2 +- odoo_repository_migration/__manifest__.py | 2 +- odoo_repository_migration/static/description/index.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index d643be3a..ec3192f0 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -11,7 +11,7 @@ Odoo Repository Migration Data !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:897a7456693242289caa6a0ae4c8e32ea2c2d336832aa6ef5a4e50262e2865e6 + !! source digest: sha256:568479ec26f358ee26894cc3b2409f3337827426661abba55f0e3f3b8db19cc3 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index 1fbde63e..de0bc143 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "16.0.1.3.2", + "version": "16.0.1.3.3", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/OCA/module-composition-analysis", diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index b7f783c6..a326915b 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -372,7 +372,7 @@

Odoo Repository Migration Data

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:897a7456693242289caa6a0ae4c8e32ea2c2d336832aa6ef5a4e50262e2865e6 +!! source digest: sha256:568479ec26f358ee26894cc3b2409f3337827426661abba55f0e3f3b8db19cc3 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

This module collects and records modules migration data from Odoo From 9ae5820b572ef6798dfaa014c6e5ec0bf250e95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 30 Mar 2026 12:43:55 +0200 Subject: [PATCH 47/65] [IMP] odoo_repository_migration: pre-commit auto fixes --- odoo_repository_migration/data/queue_job.xml | 34 ++-- odoo_repository_migration/pyproject.toml | 3 + .../views/odoo_migration_path.xml | 70 ++++--- .../views/odoo_module_branch.xml | 74 ++++---- .../views/odoo_module_branch_migration.xml | 174 +++++++++--------- .../views/odoo_module_branch_timeline.xml | 132 ++++++------- .../views/odoo_repository.xml | 22 +-- 7 files changed, 253 insertions(+), 256 deletions(-) create mode 100644 odoo_repository_migration/pyproject.toml diff --git a/odoo_repository_migration/data/queue_job.xml b/odoo_repository_migration/data/queue_job.xml index 448affa1..37a5d3ee 100644 --- a/odoo_repository_migration/data/queue_job.xml +++ b/odoo_repository_migration/data/queue_job.xml @@ -2,30 +2,28 @@ + + odoo_repository_scan_migration + + - - odoo_repository_scan_migration - - - - - - _scan_migration_paths - - - + + _scan_migration_paths + + + - - - _scan_migration_module - - - - + + _scan_migration_module + + + diff --git a/odoo_repository_migration/pyproject.toml b/odoo_repository_migration/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/odoo_repository_migration/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/odoo_repository_migration/views/odoo_migration_path.xml b/odoo_repository_migration/views/odoo_migration_path.xml index a2c5c716..d7766d81 100644 --- a/odoo_repository_migration/views/odoo_migration_path.xml +++ b/odoo_repository_migration/views/odoo_migration_path.xml @@ -2,14 +2,13 @@ - - - odoo.migration.path.form - odoo.migration.path - -

-
-
- - - - - - -
- - + + + + + + + + + + - - odoo.migration.path.tree - odoo.migration.path - - - - - - - + + odoo.migration.path.tree + odoo.migration.path + + + + + + + - - Migration Paths - ir.actions.act_window - odoo.migration.path - - + + Migration Paths + ir.actions.act_window + odoo.migration.path + + - - diff --git a/odoo_repository_migration/views/odoo_module_branch.xml b/odoo_repository_migration/views/odoo_module_branch.xml index 302deea1..abe6aaff 100644 --- a/odoo_repository_migration/views/odoo_module_branch.xml +++ b/odoo_repository_migration/views/odoo_module_branch.xml @@ -2,57 +2,55 @@ - - - odoo.module.branch.form.inherit - odoo.module.branch - - -
- - -
- - - - - - - - - - - - -
-
+ Next Versions + +
+ + + + + + + + + + + + +
+ - - odoo.module.branch.search.inherit - odoo.module.branch - - - - + odoo.module.branch.search.inherit + odoo.module.branch + + + + - - - - - +
+
+
diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index 15b53d0a..5d2aecb3 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -2,152 +2,152 @@ - - - odoo.module.branch.migration.form - odoo.module.branch.migration - -
- -
-

-
- - - - + odoo.module.branch.migration.form + odoo.module.branch.migration + + + +
+

+ +

+
+ + + + - - - - - - - - - - - - - - -
- -
-
+ + + + + + + + + + + + + + + + + - - odoo.module.branch.migration.tree - odoo.module.branch.migration - - - - - - - - - - - - + + odoo.module.branch.migration.tree + odoo.module.branch.migration + + + + + + + + + + + + - - odoo.module.branch.migration.search - odoo.module.branch.migration - search - - - - - - - - - - - - + odoo.module.branch.migration.search + odoo.module.branch.migration + search + + + + + + + + + + + + - - - - - + - - - - - - - - - - + + + + - - Migrations - ir.actions.act_window - odoo.module.branch.migration - - + Migrations + ir.actions.act_window + odoo.module.branch.migration + + {'search_default_group_by_migration_path_id': 1, 'search_default_group_by_state': 2} - + - -
diff --git a/odoo_repository_migration/views/odoo_module_branch_timeline.xml b/odoo_repository_migration/views/odoo_module_branch_timeline.xml index 60ec528a..e5ca6c9f 100644 --- a/odoo_repository_migration/views/odoo_module_branch_timeline.xml +++ b/odoo_repository_migration/views/odoo_module_branch_timeline.xml @@ -2,101 +2,103 @@ - - - odoo.module.branch.timeline.form - odoo.module.branch.timeline - -
- - - - - - - + odoo.module.branch.timeline.form + odoo.module.branch.timeline + + + + + + + + + - - - - - - - -
+ + + + + + + + - - odoo.module.branch.timeline.tree - odoo.module.branch.timeline - - - - - - - - - + + odoo.module.branch.timeline.tree + odoo.module.branch.timeline + + + + + + + + + - - odoo.module.branch.timeline.search - odoo.module.branch.timeline - search - - - - - - - - - - + odoo.module.branch.timeline.search + odoo.module.branch.timeline + search + + + + + + + + + + - - + - - - - - - - + + + + - - Timelines - ir.actions.act_window - odoo.module.branch.timeline - - {'search_default_group_by_org_id': 1} - + + Timelines + ir.actions.act_window + odoo.module.branch.timeline + + {'search_default_group_by_org_id': 1} + - -
diff --git a/odoo_repository_migration/views/odoo_repository.xml b/odoo_repository_migration/views/odoo_repository.xml index a8961598..67418efa 100644 --- a/odoo_repository_migration/views/odoo_repository.xml +++ b/odoo_repository_migration/views/odoo_repository.xml @@ -2,16 +2,14 @@ - - - odoo.repository.form.inherit - odoo.repository - - - - - - - - + + odoo.repository.form.inherit + odoo.repository + + + + + + + From 435ecd0f81334d935dc349fba97c2130d981c36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 30 Mar 2026 12:48:23 +0200 Subject: [PATCH 48/65] [IMP] odoo_repository_migration: pre-commit fixes --- odoo_repository_migration/models/odoo_module_branch.py | 5 ++++- odoo_repository_migration/models/odoo_repository.py | 4 ++-- odoo_repository_migration/tests/test_odoo_module_branch.py | 3 ++- odoo_repository_migration/utils/scanner.py | 4 +++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index 62e40d81..683572af 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -25,7 +25,10 @@ class OdooModuleBranch(models.Model): migration_scan = fields.Boolean( compute="_compute_migration_scan", store=True, - help="Technical field telling if this module is elligible for a migration scan.", + help=( + "Technical field telling if this module is elligible " + "for a migration scan." + ), ) @api.depends("branch_id.next_id", "timeline_ids") diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index dcf6f907..ef01706d 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -82,10 +82,10 @@ def _create_subsequent_jobs( migration_paths_param = {} for migration_path in migration_paths: source_rb = self.branch_ids.filtered( - lambda rb: rb.branch_id == migration_path.source_branch_id + lambda rb, mp=migration_path: rb.branch_id == mp.source_branch_id ) target_rb = self.branch_ids.filtered( - lambda rb: rb.branch_id == migration_path.target_branch_id + lambda rb, mp=migration_path: rb.branch_id == mp.target_branch_id ) # Need the two Odoo versions of the migration path available # in the scanned repository diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index 8da7cc45..b62ca072 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -183,7 +183,8 @@ def test_migration_scan_target_module_in_review_then_merged(self): self.assertTrue(self.module_branch.migration_ids.migration_scan) self.assertTrue(self.module_branch.migration_scan) # Simulate the migration scan. - # The source module is fully ported and doesn't need a migration scan afterwards. + # The source module is fully ported and doesn't need a migration + # scan afterwards. self._simulate_migration_scan("target_commit2", report={"results": {}}) self.assertEqual(self.module_branch.migration_ids.state, "fully_ported") self.assertFalse(self.module_branch.migration_ids.migration_scan) diff --git a/odoo_repository_migration/utils/scanner.py b/odoo_repository_migration/utils/scanner.py index 18f01796..f377e181 100644 --- a/odoo_repository_migration/utils/scanner.py +++ b/odoo_repository_migration/utils/scanner.py @@ -81,7 +81,9 @@ def _get_odoo_module_branch_migration_data( migration = self.env["odoo.module.branch.migration"].search(args) if migration: data = { - "last_source_scanned_commit": migration.module_branch_id.last_scanned_commit, + "last_source_scanned_commit": ( + migration.module_branch_id.last_scanned_commit + ), "last_target_scanned_commit": ( migration.target_module_branch_id.last_scanned_commit ), From e33f327b982511260fdd98fbb180c76fe727d271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 30 Mar 2026 12:49:20 +0200 Subject: [PATCH 49/65] [MIG] odoo_repository_migration: Migration to 18.0 --- odoo_repository_migration/__manifest__.py | 2 +- odoo_repository_migration/hooks.py | 5 +- .../migrations/16.0.1.0.1/post-migration.py | 22 -------- .../migrations/16.0.1.1.0/post-migration.py | 26 --------- .../migrations/16.0.1.2.0/post-migration.py | 56 ------------------- .../migrations/16.0.1.3.0/post-migration.py | 35 ------------ .../views/odoo_migration_path.xml | 6 +- .../views/odoo_module_branch.xml | 6 +- .../views/odoo_module_branch_migration.xml | 10 ++-- .../views/odoo_module_branch_timeline.xml | 8 +-- 10 files changed, 17 insertions(+), 159 deletions(-) delete mode 100644 odoo_repository_migration/migrations/16.0.1.0.1/post-migration.py delete mode 100644 odoo_repository_migration/migrations/16.0.1.1.0/post-migration.py delete mode 100644 odoo_repository_migration/migrations/16.0.1.2.0/post-migration.py delete mode 100644 odoo_repository_migration/migrations/16.0.1.3.0/post-migration.py diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index de0bc143..f8dd27e1 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "16.0.1.3.3", + "version": "18.0.1.0.0", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/OCA/module-composition-analysis", diff --git a/odoo_repository_migration/hooks.py b/odoo_repository_migration/hooks.py index ee035c26..203aa658 100644 --- a/odoo_repository_migration/hooks.py +++ b/odoo_repository_migration/hooks.py @@ -1,12 +1,9 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import SUPERUSER_ID, api - -def update_oca_repositories(cr, registry): +def update_oca_repositories(env): """Configure OCA repositories to collect migration data.""" - env = api.Environment(cr, SUPERUSER_ID, {}) org = env["odoo.repository.org"].search([("name", "=", "OCA")]) if org: repositories = ( diff --git a/odoo_repository_migration/migrations/16.0.1.0.1/post-migration.py b/odoo_repository_migration/migrations/16.0.1.0.1/post-migration.py deleted file mode 100644 index 8262205f..00000000 --- a/odoo_repository_migration/migrations/16.0.1.0.1/post-migration.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2024 Camptocamp SA -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) - -import logging - -from odoo import SUPERUSER_ID, api - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - if not version: - return - env = api.Environment(cr, SUPERUSER_ID, {}) - recompute_migration_scan(env) - - -def recompute_migration_scan(env): - _logger.info("Recompute '.migration_scan' field...") - mods = env["odoo.module.branch"].search([]) - mods.modified(["last_scanned_commit"]) - mods.flush_recordset(["migration_scan"]) diff --git a/odoo_repository_migration/migrations/16.0.1.1.0/post-migration.py b/odoo_repository_migration/migrations/16.0.1.1.0/post-migration.py deleted file mode 100644 index 7de23bfe..00000000 --- a/odoo_repository_migration/migrations/16.0.1.1.0/post-migration.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2024 Camptocamp SA -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) - -import logging - -from odoo import SUPERUSER_ID, api - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - if not version: - return - env = api.Environment(cr, SUPERUSER_ID, {}) - recompute_migration_scan(env) - - -def recompute_migration_scan(env): - _logger.info("Recompute '.migration_scan' field...") - mb_model = env["odoo.module.branch"] - mods = mb_model.search([]) - mods.modified(["last_scanned_commit"]) - mods.flush_recordset(["migration_scan"]) - # Reset last target scanned commit on those that need a migration scan - mods_to_scan = mb_model.search([("migration_scan", "=", True)]) - mods_to_scan.migration_ids.write({"last_target_scanned_commit": False}) diff --git a/odoo_repository_migration/migrations/16.0.1.2.0/post-migration.py b/odoo_repository_migration/migrations/16.0.1.2.0/post-migration.py deleted file mode 100644 index 471bdb3e..00000000 --- a/odoo_repository_migration/migrations/16.0.1.2.0/post-migration.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2024 Camptocamp SA -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) - -import logging - -from odoo import SUPERUSER_ID, api - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - if not version: - return - env = api.Environment(cr, SUPERUSER_ID, {}) - fix_migration_states(env) - - -def fix_migration_states(env): - _logger.info("Plan a scan to fix '.state' field...") - query = """ - SELECT mig.id - FROM odoo_module_branch_migration mig - JOIN odoo_module_branch AS source - ON mig.module_branch_id=source.id - JOIN odoo_module_branch AS target - ON mig.target_module_branch_id=target.id - WHERE source.repository_branch_id IS NOT NULL - AND target.repository_branch_id IS NOT NULL - AND source.repository_id != target.repository_id; - """ - env.cr.execute(query) - mig_ids = [row[0] for row in env.cr.fetchall()] - # Reset 'last_target_scanned_commit' to recompute 'migration_scan' - if mig_ids: - query = """ - UPDATE odoo_module_branch_migration - SET last_target_scanned_commit=NULL - WHERE id IN %s; - """ - args = (tuple(mig_ids),) - env.cr.execute(query, args) - # Reset collected migration data on modules that shouldn't have any - mbm_model = env["odoo.module.branch.migration"] - migs = mbm_model.search( - [ - ("results", "!=", False), - ("repository_id.collect_migration_data", "=", False), - ] - ) - migs.results = False - # Recompute migration state/flag, especially for modules moved to another repo - # that won't trigger a migration scan. - migs = mbm_model.search([("id", "in", mig_ids)]) - env.add_to_compute(migs._fields["state"], migs) - env.add_to_compute(migs._fields["migration_scan"], migs) - migs.modified(["state", "migration_scan"]) diff --git a/odoo_repository_migration/migrations/16.0.1.3.0/post-migration.py b/odoo_repository_migration/migrations/16.0.1.3.0/post-migration.py deleted file mode 100644 index af988af2..00000000 --- a/odoo_repository_migration/migrations/16.0.1.3.0/post-migration.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2025 Camptocamp SA -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) - -import logging - -from odoo import SUPERUSER_ID, api - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - if not version: - return - env = api.Environment(cr, SUPERUSER_ID, {}) - migrate_odoo_module_branch_timelines(env) - - -def migrate_odoo_module_branch_timelines(env): - _logger.info("Migrate Odoo module timelines to 'odoo.module.branch.timeline'...") - query = """ - SELECT id, next_odoo_version_state, next_odoo_version_module_id - FROM odoo_module_branch - WHERE next_odoo_version_state != 'same'; - """ - env.cr.execute(query) - rows = env.cr.dictfetchall() - vals_list = [] - for row in rows: - vals = { - "module_branch_id": row["id"], - "state": row["next_odoo_version_state"], - "next_module_id": row["next_odoo_version_module_id"], - } - vals_list.append(vals) - env["odoo.module.branch.timeline"].create(vals_list) diff --git a/odoo_repository_migration/views/odoo_migration_path.xml b/odoo_repository_migration/views/odoo_migration_path.xml index d7766d81..ad15565b 100644 --- a/odoo_repository_migration/views/odoo_migration_path.xml +++ b/odoo_repository_migration/views/odoo_migration_path.xml @@ -28,13 +28,13 @@ - odoo.migration.path.tree + odoo.migration.path.list odoo.migration.path - + - + diff --git a/odoo_repository_migration/views/odoo_module_branch.xml b/odoo_repository_migration/views/odoo_module_branch.xml index abe6aaff..71b1df46 100644 --- a/odoo_repository_migration/views/odoo_module_branch.xml +++ b/odoo_repository_migration/views/odoo_module_branch.xml @@ -14,7 +14,7 @@ type="object" class="oe_stat_button" icon="fa-arrow-up" - attrs="{'invisible': [('next_odoo_version_module_branch_id', '=', False)]}" + invisible="not next_odoo_version_module_branch_id" > Next Versions @@ -22,12 +22,12 @@ - + - + diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index 5d2aecb3..8a5b311d 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -18,11 +18,11 @@ @@ -43,10 +43,10 @@ - odoo.module.branch.migration.tree + odoo.module.branch.migration.list odoo.module.branch.migration - + @@ -54,7 +54,7 @@ - + diff --git a/odoo_repository_migration/views/odoo_module_branch_timeline.xml b/odoo_repository_migration/views/odoo_module_branch_timeline.xml index e5ca6c9f..eb20d374 100644 --- a/odoo_repository_migration/views/odoo_module_branch_timeline.xml +++ b/odoo_repository_migration/views/odoo_module_branch_timeline.xml @@ -15,7 +15,7 @@ @@ -27,15 +27,15 @@ - odoo.module.branch.timeline.tree + odoo.module.branch.timeline.list odoo.module.branch.timeline - + - + From 565ed021f013c3f4111ebc3e812ecd2303e05410 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Fri, 3 Apr 2026 08:26:07 +0000 Subject: [PATCH 50/65] [UPD] Update odoo_repository_migration.pot --- .../i18n/odoo_repository_migration.pot | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/odoo_repository_migration/i18n/odoo_repository_migration.pot b/odoo_repository_migration/i18n/odoo_repository_migration.pot index 4a8547ad..05462be7 100644 --- a/odoo_repository_migration/i18n/odoo_repository_migration.pot +++ b/odoo_repository_migration/i18n/odoo_repository_migration.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" +"Project-Id-Version: Odoo Server 18.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -100,13 +100,6 @@ msgstr "" msgid "Info" msgstr "" -#. module: odoo_repository_migration -#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path____last_update -#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration____last_update -#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline____last_update -msgid "Last Modified on" -msgstr "" - #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__last_source_scanned_commit msgid "Last Source Scanned Commit" @@ -151,7 +144,6 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_scan #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__migration_scan -#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_scan msgid "Migration Scan" msgstr "" @@ -174,7 +166,6 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.actions.act_window,name:odoo_repository_migration.odoo_module_branch_migration_action #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_ids -#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_ids #: model:ir.ui.menu,name:odoo_repository_migration.odoo_module_branch_migration_menu #: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_form msgid "Migrations" @@ -243,14 +234,12 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__next_odoo_version_module_branch_id -#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__next_odoo_version_module_branch_id msgid "Next Odoo Version Module Branch" msgstr "" #. module: odoo_repository_migration #. odoo-python #: code:addons/odoo_repository_migration/models/odoo_module_branch.py:0 -#, python-format msgid "Next versions" msgstr "" @@ -419,7 +408,6 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch__migration_scan -#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_project_module__migration_scan msgid "" "Technical field telling if this module is elligible for a migration scan." msgstr "" @@ -446,7 +434,6 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__timeline_ids -#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__timeline_ids msgid "Timeline" msgstr "" From c5213d4e2a6e77f75594671acab6f6346e2056a1 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 3 Apr 2026 08:28:16 +0000 Subject: [PATCH 51/65] [BOT] post-merge updates --- odoo_repository_migration/README.rst | 12 ++++++------ .../static/description/index.html | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index ec3192f0..b99a01f5 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -11,7 +11,7 @@ Odoo Repository Migration Data !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:568479ec26f358ee26894cc3b2409f3337827426661abba55f0e3f3b8db19cc3 + !! source digest: sha256:033689f8087e17ae816e845ce900836925c632a12e933fbcc7521f5f1e10dc9e !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -21,13 +21,13 @@ Odoo Repository Migration Data :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmodule--composition--analysis-lightgray.png?logo=github - :target: https://github.com/OCA/module-composition-analysis/tree/16.0/odoo_repository_migration + :target: https://github.com/OCA/module-composition-analysis/tree/18.0/odoo_repository_migration :alt: OCA/module-composition-analysis .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/module-composition-analysis-16-0/module-composition-analysis-16-0-odoo_repository_migration + :target: https://translation.odoo-community.org/projects/module-composition-analysis-18-0/module-composition-analysis-18-0-odoo_repository_migration :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/module-composition-analysis&target_branch=16.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/module-composition-analysis&target_branch=18.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -113,7 +113,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -145,6 +145,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/module-composition-analysis `_ project on GitHub. +This module is part of the `OCA/module-composition-analysis `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index a326915b..264d0eb7 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -372,9 +372,9 @@

Odoo Repository Migration Data

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:568479ec26f358ee26894cc3b2409f3337827426661abba55f0e3f3b8db19cc3 +!! source digest: sha256:033689f8087e17ae816e845ce900836925c632a12e933fbcc7521f5f1e10dc9e !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

This module collects and records modules migration data from Odoo repositories.

Thanks to oca-port and data @@ -472,7 +472,7 @@

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -501,7 +501,7 @@

Maintainers

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/module-composition-analysis project on GitHub.

+

This module is part of the OCA/module-composition-analysis project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

From 92c0f50100db7835ea56de35d8f98de3409dfaa8 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Fri, 3 Apr 2026 12:40:40 +0000 Subject: [PATCH 52/65] [UPD] Update odoo_repository_migration.pot --- odoo_repository_migration/i18n/odoo_repository_migration.pot | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/odoo_repository_migration/i18n/odoo_repository_migration.pot b/odoo_repository_migration/i18n/odoo_repository_migration.pot index 05462be7..a3bbf821 100644 --- a/odoo_repository_migration/i18n/odoo_repository_migration.pot +++ b/odoo_repository_migration/i18n/odoo_repository_migration.pot @@ -144,6 +144,7 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_scan #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__migration_scan +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_scan msgid "Migration Scan" msgstr "" @@ -166,6 +167,7 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.actions.act_window,name:odoo_repository_migration.odoo_module_branch_migration_action #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_ids +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_ids #: model:ir.ui.menu,name:odoo_repository_migration.odoo_module_branch_migration_menu #: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_form msgid "Migrations" @@ -234,6 +236,7 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__next_odoo_version_module_branch_id +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__next_odoo_version_module_branch_id msgid "Next Odoo Version Module Branch" msgstr "" @@ -408,6 +411,7 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch__migration_scan +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_project_module__migration_scan msgid "" "Technical field telling if this module is elligible for a migration scan." msgstr "" @@ -434,6 +438,7 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__timeline_ids +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__timeline_ids msgid "Timeline" msgstr "" From 7ad2daff75544073aee0e04c1b8852c7f29dd5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Fri, 10 Apr 2026 09:51:40 +0200 Subject: [PATCH 53/65] [IMP] odoo_repository_migration: tests, add a MigrationCommon class --- odoo_repository_migration/tests/common.py | 62 +++++++++++++++++++ .../tests/test_odoo_module_branch.py | 58 +---------------- 2 files changed, 64 insertions(+), 56 deletions(-) create mode 100644 odoo_repository_migration/tests/common.py diff --git a/odoo_repository_migration/tests/common.py b/odoo_repository_migration/tests/common.py new file mode 100644 index 00000000..493145d2 --- /dev/null +++ b/odoo_repository_migration/tests/common.py @@ -0,0 +1,62 @@ +# Copyright 2024 Camptocamp SA +# Copyright 2026 Sébastien Alix +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.addons.odoo_repository.tests import common + + +class MigrationCommon(common.Common): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.module = cls._create_odoo_module("my_module") + cls.repo_branch = cls._create_odoo_repository_branch( + cls.odoo_repository, cls.branch + ) + cls.repo_branch2 = cls._create_odoo_repository_branch( + cls.odoo_repository, cls.branch2 + ) + cls.module_branch = cls._create_odoo_module_branch( + cls.module, + cls.branch, + specific=False, + repository_branch_id=cls.repo_branch.id, + last_scanned_commit="sha", + ) + cls.std_repository = cls.env.ref("odoo_repository.odoo_repository_odoo_odoo") + oca_org = cls.env.ref("odoo_repository.odoo_repository_org_oca") + cls.oca_repository = cls.env["odoo.repository"].create( + { + "org_id": oca_org.id, + "name": "test-repo", + "repo_url": "https://github.com/OCA/test-repo", + } + ) + cls.gen_repository = cls.env["odoo.repository"].create( + { + "name": "new_repo", + "org_id": cls.odoo_repository.org_id.id, + "repo_url": "http://example.net/new_repo", + "specific": False, + "to_scan": False, + } + ) + cls.gen_repository.addons_path_ids = cls.odoo_repository.addons_path_ids + + def _simulate_migration_scan(cls, target_commit, report=None): + """Helper method that pushes scanned migration data.""" + data = { + "module": cls.module_branch.module_name, + "source_version": cls.branch.name, + "source_branch": cls.branch.name, + "target_version": cls.branch2.name, + "target_branch": cls.branch2.name, + "source_commit": cls.module_branch.last_scanned_commit, + "target_commit": target_commit, + } + if report is not None: + data["report"] = report + return cls.env["odoo.module.branch.migration"].push_scanned_data( + cls.module_branch.id, + data, + ) diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index b62ca072..20db9569 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -2,64 +2,10 @@ # Copyright 2026 Sébastien Alix # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo.addons.odoo_repository.tests import common +from .common import MigrationCommon -class TestOdooModuleBranch(common.Common): - def setUp(self): - super().setUp() - self.module = self._create_odoo_module("my_module") - self.repo_branch = self._create_odoo_repository_branch( - self.odoo_repository, self.branch - ) - self.repo_branch2 = self._create_odoo_repository_branch( - self.odoo_repository, self.branch2 - ) - self.module_branch = self._create_odoo_module_branch( - self.module, - self.branch, - specific=False, - repository_branch_id=self.repo_branch.id, - last_scanned_commit="sha", - ) - self.std_repository = self.env.ref("odoo_repository.odoo_repository_odoo_odoo") - oca_org = self.env.ref("odoo_repository.odoo_repository_org_oca") - self.oca_repository = self.env["odoo.repository"].create( - { - "org_id": oca_org.id, - "name": "test-repo", - "repo_url": "https://github.com/OCA/test-repo", - } - ) - self.gen_repository = self.env["odoo.repository"].create( - { - "name": "new_repo", - "org_id": self.odoo_repository.org_id.id, - "repo_url": "http://example.net/new_repo", - "specific": False, - "to_scan": False, - } - ) - self.gen_repository.addons_path_ids = self.odoo_repository.addons_path_ids - - def _simulate_migration_scan(self, target_commit, report=None): - """Helper method that pushes scanned migration data.""" - data = { - "module": self.module_branch.module_name, - "source_version": self.branch.name, - "source_branch": self.branch.name, - "target_version": self.branch2.name, - "target_branch": self.branch2.name, - "source_commit": self.module_branch.last_scanned_commit, - "target_commit": target_commit, - } - if report is not None: - data["report"] = report - return self.env["odoo.module.branch.migration"].push_scanned_data( - self.module_branch.id, - data, - ) - +class TestOdooModuleBranch(MigrationCommon): def test_migration_scan_removed(self): self.module_branch.removed = True self.assertFalse(self.module_branch.migration_scan) From 8ab3619ba802dc0e8ada0ec6795c3fbec12e3086 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 10 Apr 2026 08:17:22 +0000 Subject: [PATCH 54/65] [BOT] post-merge updates --- odoo_repository_migration/README.rst | 2 +- odoo_repository_migration/__manifest__.py | 2 +- odoo_repository_migration/static/description/index.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index b99a01f5..ef343be3 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -11,7 +11,7 @@ Odoo Repository Migration Data !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:033689f8087e17ae816e845ce900836925c632a12e933fbcc7521f5f1e10dc9e + !! source digest: sha256:420dde7e62d8c5f7421d2e9190cff25d51896a02cfe1b4fea54a6981ac4f45b3 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index f8dd27e1..f9083731 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "18.0.1.0.0", + "version": "18.0.1.0.1", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/OCA/module-composition-analysis", diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index 264d0eb7..c172a5cd 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -372,7 +372,7 @@

Odoo Repository Migration Data

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:033689f8087e17ae816e845ce900836925c632a12e933fbcc7521f5f1e10dc9e +!! source digest: sha256:420dde7e62d8c5f7421d2e9190cff25d51896a02cfe1b4fea54a6981ac4f45b3 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

This module collects and records modules migration data from Odoo From 1f496cfc79d42596de22f66682b9243b9e75f582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Fri, 10 Apr 2026 10:35:10 +0200 Subject: [PATCH 55/65] [FIX] odoo_repository_migration: tests, fix classmethod --- odoo_repository_migration/tests/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/odoo_repository_migration/tests/common.py b/odoo_repository_migration/tests/common.py index 493145d2..70824309 100644 --- a/odoo_repository_migration/tests/common.py +++ b/odoo_repository_migration/tests/common.py @@ -43,6 +43,7 @@ def setUpClass(cls): ) cls.gen_repository.addons_path_ids = cls.odoo_repository.addons_path_ids + @classmethod def _simulate_migration_scan(cls, target_commit, report=None): """Helper method that pushes scanned migration data.""" data = { From 3c05891d5a5391e58da0deb28f1452e064db96f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Wed, 27 May 2026 14:31:08 +0200 Subject: [PATCH 56/65] [IMP] odoo_repository_migration: test 'next_odoo_version_module_branch_id' --- .../tests/test_odoo_module_branch.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index 20db9569..cca1a9fa 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -6,6 +6,46 @@ class TestOdooModuleBranch(MigrationCommon): + def test_next_odoo_version_module_branch_id(self): + """Test next_odoo_version_module_branch_id computed field.""" + # Create next branch module + next_module_branch = self._create_odoo_module_branch( + self.module, + self.branch2, + specific=False, + repository_branch_id=self.repo_branch2.id, + last_scanned_commit="sha", + ) + # Test next_odoo_version_module_branch_id + self.assertEqual( + self.module_branch.next_odoo_version_module_branch_id, next_module_branch + ) + + def test_next_odoo_version_module_branch_id_with_renamed_module(self): + """Test next_odoo_version_module_branch_id with renamed module.""" + # Create a new module name for the renamed module + next_module = self.module.copy({"name": "next_module"}) + # Create next branch module with new name + next_module_branch = self._create_odoo_module_branch( + next_module, + self.branch2, + specific=False, + repository_branch_id=self.repo_branch2.id, + last_scanned_commit="sha", + ) + # Add timeline entry for renaming + self.module_branch.timeline_ids.create( + { + "module_branch_id": self.module_branch.id, + "state": "renamed", + "next_module_id": next_module.id, + } + ) + # Test next_odoo_version_module_branch_id follows renaming + self.assertEqual( + self.module_branch.next_odoo_version_module_branch_id, next_module_branch + ) + def test_migration_scan_removed(self): self.module_branch.removed = True self.assertFalse(self.module_branch.migration_scan) From 423b8315472e33fc436ab11f544d550b6621bc73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Wed, 27 May 2026 11:27:02 +0200 Subject: [PATCH 57/65] [IMP] odoo_repository_migration: tests, improve '_simulate_migration_scan()' Set explicit source and target branches. --- odoo_repository_migration/tests/common.py | 23 ++++++---- .../tests/test_odoo_module_branch.py | 44 +++++++++++++++---- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/odoo_repository_migration/tests/common.py b/odoo_repository_migration/tests/common.py index 70824309..f17b19c9 100644 --- a/odoo_repository_migration/tests/common.py +++ b/odoo_repository_migration/tests/common.py @@ -44,20 +44,27 @@ def setUpClass(cls): cls.gen_repository.addons_path_ids = cls.odoo_repository.addons_path_ids @classmethod - def _simulate_migration_scan(cls, target_commit, report=None): + def _simulate_migration_scan(cls, target_commit, source, target, report=None): """Helper method that pushes scanned migration data.""" + module_branch = cls.env["odoo.module.branch"].search( + [ + ("module_id", "=", cls.module.id), + ("branch_id", "=", source.id), + ] + ) + assert module_branch data = { - "module": cls.module_branch.module_name, - "source_version": cls.branch.name, - "source_branch": cls.branch.name, - "target_version": cls.branch2.name, - "target_branch": cls.branch2.name, - "source_commit": cls.module_branch.last_scanned_commit, + "module": module_branch.module_name, + "source_version": source.name, + "source_branch": source.name, + "target_version": target.name, + "target_branch": target.name, + "source_commit": module_branch.last_scanned_commit, "target_commit": target_commit, } if report is not None: data["report"] = report return cls.env["odoo.module.branch.migration"].push_scanned_data( - cls.module_branch.id, + module_branch.id, data, ) diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index cca1a9fa..a96d9f14 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -84,7 +84,10 @@ def test_migration_scan_missing_migration_path(self): # Once we collected migration data for the expected branch+commit # the module doesn't require a migration scan anymore self._simulate_migration_scan( - "target_commit1", report={"process": "migrate", "results": {}} + "target_commit1", + source=self.branch, + target=self.branch2, + report={"process": "migrate", "results": {}}, ) self.assertTrue(self.module_branch.migration_ids) self.assertFalse(self.module_branch.migration_scan) @@ -113,7 +116,10 @@ def test_migration_scan_target_module_in_review_then_merged(self): self.assertFalse(self.module_branch.migration_ids) self.assertTrue(self.module_branch.migration_scan) self._simulate_migration_scan( - "target_commit1", report={"process": "migrate", "results": {}} + "target_commit1", + source=self.branch, + target=self.branch2, + report={"process": "migrate", "results": {}}, ) self.assertTrue(self.module_branch.migration_ids) self.assertFalse(self.module_branch.migration_ids.migration_scan) @@ -141,6 +147,8 @@ def test_migration_scan_target_module_in_review_then_merged(self): # The source module doesn't need a migration scan anymore. self._simulate_migration_scan( "target_commit1", + source=self.branch, + target=self.branch2, report={ "process": "migrate", "results": {"existing_pr": {"url": target_module_branch.pr_url}}, @@ -171,7 +179,12 @@ def test_migration_scan_target_module_in_review_then_merged(self): # Simulate the migration scan. # The source module is fully ported and doesn't need a migration # scan afterwards. - self._simulate_migration_scan("target_commit2", report={"results": {}}) + self._simulate_migration_scan( + "target_commit2", + source=self.branch, + target=self.branch2, + report={"results": {}}, + ) self.assertEqual(self.module_branch.migration_ids.state, "fully_ported") self.assertFalse(self.module_branch.migration_ids.migration_scan) self.assertFalse(self.module_branch.migration_scan) @@ -187,7 +200,10 @@ def test_migration_scan_target_module_moved_to_standard(self): } ) self._simulate_migration_scan( - "target_commit1", report={"process": "migrate", "results": {}} + "target_commit1", + source=self.branch, + target=self.branch2, + report={"process": "migrate", "results": {}}, ) self.assertTrue(self.module_branch.migration_ids) mig = self.module_branch.migration_ids @@ -224,7 +240,10 @@ def test_migration_scan_target_module_moved_to_oca(self): } ) self._simulate_migration_scan( - "target_commit1", report={"process": "migrate", "results": {}} + "target_commit1", + source=self.branch, + target=self.branch2, + report={"process": "migrate", "results": {}}, ) self.assertTrue(self.module_branch.migration_ids) mig = self.module_branch.migration_ids @@ -261,7 +280,10 @@ def test_migration_scan_target_module_moved_to_generic(self): } ) self._simulate_migration_scan( - "target_commit1", report={"process": "migrate", "results": {}} + "target_commit1", + source=self.branch, + target=self.branch2, + report={"process": "migrate", "results": {}}, ) self.assertTrue(self.module_branch.migration_ids) mig = self.module_branch.migration_ids @@ -310,7 +332,10 @@ def test_renamed_to_module_in_target_version(self): } ) self._simulate_migration_scan( - "target_commit1", report={"process": "migrate", "results": {}} + "target_commit1", + source=self.branch, + target=next_branch, + report={"process": "migrate", "results": {}}, ) # Module has been renamed starting from 16.0 self.module_branch.timeline_ids.create( @@ -365,7 +390,10 @@ def test_replaced_by_module_in_target_version(self): } ) self._simulate_migration_scan( - "target_commit1", report={"process": "migrate", "results": {}} + "target_commit1", + source=self.branch, + target=next_branch, + report={"process": "migrate", "results": {}}, ) # New module is replacing current one starting from 16.0 self.module_branch.timeline_ids.create( From 6a66e21eaf716b4e9086877f228ea8957c58e330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Sun, 31 May 2026 18:49:19 +0200 Subject: [PATCH 58/65] [FIX] odoo_repository_migration: ensure a migration record exists In `_scan_migration_module`, ensure a migration record exists for replaced modules. --- odoo_repository_migration/models/odoo_repository.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index ef01706d..17f299e8 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -190,6 +190,15 @@ def _scan_migration_module( migration_path.target_branch_id ) if replaced_by_module: + # Ensure a migration record exists before returning + values = { + "module_branch_id": module_branch_id, + "migration_path_id": migration_path_id, + "last_source_scanned_commit": module.last_scanned_commit, + } + self.env["odoo.module.branch.migration"]._create_or_update( + module_branch_id, migration_path, values + ) return ( f"{module.name} is now replaced by " f"{replaced_by_module.name}, no need to collect " From 6b3201abba1542dba20808e1422098ed3719dd73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Tue, 9 Jun 2026 18:57:59 +0200 Subject: [PATCH 59/65] [IMP] odoo_repository_migration: add helper to get all related timelines recursively --- .../models/odoo_module_branch.py | 17 ++++ .../models/odoo_module_branch_timeline.py | 45 +++++++++ odoo_repository_migration/tests/common.py | 10 ++ .../tests/test_odoo_module_branch_timeline.py | 99 +++++++++++++++++++ 4 files changed, 171 insertions(+) create mode 100644 odoo_repository_migration/tests/test_odoo_module_branch_timeline.py diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index 683572af..4b932425 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -208,3 +208,20 @@ def open_next_module_branches(self): action["name"] = _("Next versions") action["domain"] = [("id", "in", self._get_next_module_branches().ids)] return action + + def _get_related_timelines(self): + """Return timelines related to current module. + + A module A could be renamed to a module B in version X, then renamed + again to C in version X+1, etc. + When calling this method on any module, all related timelines are returned. + """ + self.ensure_one() + timeline = self.env["odoo.module.branch.timeline"].search( + [ + "|", + ("module_branch_id.module_id", "=", self.module_id.id), + ("next_module_id", "=", self.module_id.id), + ] + ) + return timeline._get_related_timelines() if timeline else timeline diff --git a/odoo_repository_migration/models/odoo_module_branch_timeline.py b/odoo_repository_migration/models/odoo_module_branch_timeline.py index b924a991..2258df0d 100644 --- a/odoo_repository_migration/models/odoo_module_branch_timeline.py +++ b/odoo_repository_migration/models/odoo_module_branch_timeline.py @@ -100,3 +100,48 @@ def _inverse_next_fields(self): migrations._compute_renamed_to_module_id() migrations._compute_replaced_by_module_id() migrations._compute_state() + + def _get_related_timelines(self): + """Return timelines related to current ones regarding impacted modules. + + A module A could be renamed to a module B in version X, then renamed + again to C in version X+1, etc. + When calling this method on any timeline, the others are returned. + """ + impacted_modules = self._get_all_impacted_modules() + return self.search( + [ + "|", + ("module_branch_id.module_id", "in", impacted_modules.ids), + ("next_module_id", "in", impacted_modules.ids), + ] + ) + + # TODO: add ormcache? + def _get_all_impacted_modules(self, visited=None): + """Recursively collect all modules involved in the chain of timelines.""" + if visited is None: + visited = self.env["odoo.module.branch.timeline"].browse() + impacted_modules = self.env["odoo.module"].browse() + for rec in self: + if rec in visited: + continue + visited |= rec + impacted_modules |= rec.module_branch_id.module_id + rec.next_module_id + # Find previous timelines (current module is next_module_id) + previous_timelines = self.search( + [ + ("next_module_id", "=", rec.module_branch_id.module_id.id), + ] + ) + for timeline in previous_timelines: + impacted_modules |= timeline._get_all_impacted_modules(visited) + # Find next timelines (next_module_id is the module_branch_id) + next_timelines = self.search( + [ + ("module_branch_id.module_id", "=", rec.next_module_id.id), + ] + ) + for timeline in next_timelines: + impacted_modules |= timeline._get_all_impacted_modules(visited) + return impacted_modules diff --git a/odoo_repository_migration/tests/common.py b/odoo_repository_migration/tests/common.py index f17b19c9..7819a6c7 100644 --- a/odoo_repository_migration/tests/common.py +++ b/odoo_repository_migration/tests/common.py @@ -68,3 +68,13 @@ def _simulate_migration_scan(cls, target_commit, source, target, report=None): module_branch.id, data, ) + + @classmethod + def _create_timeline(cls, module_branch, next_module, state): + return cls.env["odoo.module.branch.timeline"].create( + { + "module_branch_id": module_branch.id, + "state": state, + "next_module_id": next_module.id, + } + ) diff --git a/odoo_repository_migration/tests/test_odoo_module_branch_timeline.py b/odoo_repository_migration/tests/test_odoo_module_branch_timeline.py new file mode 100644 index 00000000..002e61aa --- /dev/null +++ b/odoo_repository_migration/tests/test_odoo_module_branch_timeline.py @@ -0,0 +1,99 @@ +# Copyright 2026 Akretion (https://www.akretion.com). +# @author Sébastien Alix +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from .common import MigrationCommon + + +class TestOdooModuleBranchTimeline(MigrationCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.branch4_name = str(float(cls.branch3.name) + 1) + cls.branch4 = ( + cls.env["odoo.branch"] + .with_context(active_test=False) + .search([("name", "=", cls.branch4_name)]) + ) + if not cls.branch4: + cls.branch4 = cls.env["odoo.branch"].create( + { + "name": cls.branch4_name, + } + ) + cls.repo_branch4 = cls._create_odoo_repository_branch( + cls.odoo_repository, cls.branch4 + ) + # Create modules A, B, C and D + cls.module_a = cls.module.copy({"name": "module_a"}) + cls.module_branch_a = cls._create_odoo_module_branch( + cls.module_a, + cls.branch, + specific=False, + repository_branch_id=cls.repo_branch.id, + last_scanned_commit="sha_a", + ) + cls.module_b = cls.module.copy({"name": "module_b"}) + cls.module_branch_b = cls._create_odoo_module_branch( + cls.module_b, + cls.branch2, + specific=False, + repository_branch_id=cls.repo_branch2.id, + last_scanned_commit="sha_b", + ) + cls.module_c = cls.module.copy({"name": "module_c"}) + cls.module_branch_c = cls._create_odoo_module_branch( + cls.module_c, + cls.branch3, + specific=False, + repository_branch_id=cls.repo_branch3.id, + last_scanned_commit="sha_c", + ) + cls.module_d = cls.module.copy({"name": "module_d"}) + cls.module_branch_d = cls._create_odoo_module_branch( + cls.module_d, + cls.branch4, + specific=False, + repository_branch_id=cls.repo_branch4.id, + last_scanned_commit="sha_d", + ) + + def test_get_related_timelines(self): + """Test _get_related_timelines returns all timelines in a chain. + + Data input: + - module A renamed/replaced to B in X+1 + - module B renamed/replaced to C in X+2 + - module C renamed/replaced to D in X+3 + + Expected output: + - Calling _get_related_timelines on any timeline should return all timelines + """ + # Create timelines + timeline_b = self._create_timeline( + self.module_branch_a, self.module_b, "renamed" + ) + timeline_c = self._create_timeline( + self.module_branch_b, self.module_c, "replaced" + ) + timeline_d = self._create_timeline( + self.module_branch_c, self.module_d, "renamed" + ) + # Test from first timeline (A -> B) + related_timelines = timeline_b._get_related_timelines() + self.assertIn(timeline_b, related_timelines) + self.assertIn(timeline_c, related_timelines) + self.assertIn(timeline_d, related_timelines) + self.assertEqual(len(related_timelines), 3) + # Test from second timeline (B -> C) + related_timelines = timeline_c._get_related_timelines() + self.assertIn(timeline_b, related_timelines) + self.assertIn(timeline_c, related_timelines) + self.assertIn(timeline_d, related_timelines) + self.assertEqual(len(related_timelines), 3) + # Test from third (and last) timeline (C -> D) + related_timelines = timeline_d._get_related_timelines() + self.assertIn(timeline_b, related_timelines) + self.assertIn(timeline_c, related_timelines) + self.assertIn(timeline_d, related_timelines) + self.assertEqual(len(related_timelines), 3) From 9424899dbfc93b2a7bdc09c0629f07d81ffa0224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Wed, 27 May 2026 14:36:18 +0200 Subject: [PATCH 60/65] [FIX] odoo_repository_migration: handle modules renamed/replaced multiple times A module can be renamed/replaced multiple time between two Odoo versions, and the migration data should show the last relevant module. Also, the computation of relevant migration data is automatically triggered when timelines are created/updated. A convenient "Scan" button has been added on timeline form to trigger a scan on relevant modules if needed, as well as a "Migrations" smart button to access related migration data records. --- odoo_repository_migration/README.rst | 6 +- odoo_repository_migration/__manifest__.py | 4 +- .../migrations/18.0.1.1.0/post-migrate.py | 23 ++ .../models/odoo_module_branch.py | 83 ++-- .../models/odoo_module_branch_migration.py | 38 +- .../models/odoo_module_branch_timeline.py | 107 ++++- .../static/description/index.html | 26 +- odoo_repository_migration/tests/common.py | 3 + .../tests/test_odoo_module_branch.py | 375 ++++++++++++++++-- .../views/odoo_module_branch_migration.xml | 1 + .../views/odoo_module_branch_timeline.xml | 57 ++- 11 files changed, 610 insertions(+), 113 deletions(-) create mode 100644 odoo_repository_migration/migrations/18.0.1.1.0/post-migrate.py diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index ef343be3..fe1f7235 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ============================== Odoo Repository Migration Data ============================== @@ -17,7 +13,7 @@ Odoo Repository Migration Data .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmodule--composition--analysis-lightgray.png?logo=github diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index f9083731..8b6b5e61 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -1,9 +1,11 @@ # Copyright 2023 Camptocamp SA +# Copyright 2026 Akretion (https://www.akretion.com). +# @author Sébastien Alix # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "18.0.1.0.1", + "version": "18.0.1.1.0", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/OCA/module-composition-analysis", diff --git a/odoo_repository_migration/migrations/18.0.1.1.0/post-migrate.py b/odoo_repository_migration/migrations/18.0.1.1.0/post-migrate.py new file mode 100644 index 00000000..44fd4109 --- /dev/null +++ b/odoo_repository_migration/migrations/18.0.1.1.0/post-migrate.py @@ -0,0 +1,23 @@ +# Copyright 2026 Akretion (https://www.akretion.com). +# @author Sébastien Alix +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import logging + +from odoo import SUPERUSER_ID, api + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + if not version: + return + env = api.Environment(cr, SUPERUSER_ID, {}) + recompute_timeline_migration_data(env) + + +def recompute_timeline_migration_data(env): + """Recompute migration data related to timelines.""" + timelines = env["odoo.module.branch.timeline"].search([]) + _logger.info("Recompute migration data related to %s timelines...", len(timelines)) + timelines.migration_ids.force_update() diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index 4b932425..96ff6ced 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -1,4 +1,6 @@ # Copyright 2023 Camptocamp SA +# Copyright 2026 Akretion (https://www.akretion.com). +# @author Sébastien Alix # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo import _, api, fields, models @@ -69,13 +71,18 @@ def _replaced_by_module_in_target_version(self, target_branch): We give the priority to last modules while checking them. """ self.ensure_one() - modules = self._get_next_versions(target_branch) - for module in modules.sorted( - key=lambda mod: mod.branch_id.sequence, reverse=True - ): - if module.timeline_ids.state == "replaced": - return module.timeline_ids.next_module_id - return self.env["odoo.module"] + replaced_by = self.env["odoo.module"] + next_ = self + while next_ and next_.branch_sequence < target_branch.sequence: + timeline = next_.timeline_ids + if timeline.state == "replaced": + replaced_by = timeline.next_module_id + next_ = next_.next_odoo_version_module_branch_id + # If a module has been renamed after been replaced, the module to + # return has to be the renamed one + if next_ and replaced_by: + replaced_by = next_.module_id + return replaced_by def _renamed_to_module_in_target_version(self, target_branch): """Return the new module technical name in last module versions. @@ -87,22 +94,42 @@ def _renamed_to_module_in_target_version(self, target_branch): We give the priority to last modules while checking them. """ self.ensure_one() - modules = self._get_next_versions(target_branch) - for module in modules.sorted( - key=lambda mod: mod.branch_id.sequence, reverse=True - ): - if module.timeline_ids.state == "renamed": - return module.timeline_ids.next_module_id - return self.env["odoo.module"] - - def _get_next_versions(self, target_branch): + renamed_to = self.env["odoo.module"] + next_ = self + while next_ and next_.branch_sequence < target_branch.sequence: + timeline = next_.timeline_ids + # As soon as the module has been replaced in the chain of versions + # it cannot be considered as renamed anymore + if timeline.state == "replaced": + renamed_to = self.env["odoo.module"] + next_ = False + continue + if timeline.state == "renamed": + renamed_to = timeline.next_module_id + next_ = next_.next_odoo_version_module_branch_id + return renamed_to + + def _get_next_versions( + self, target_branch=None, order="branch_sequence", limit=None + ): + """Return all available upcoming versions. + + It doesn't take into account module renaming or replacement. Only versions + for the current module name are returned. + """ self.ensure_one() + if not target_branch: + target_branch = self.env["odoo.branch"]._get_last_version() return self.env["odoo.module.branch"].search( [ ("module_id", "=", self.module_id.id), + ("repository_id", "!=", False), + ("installable", "=", True), ("branch_id.sequence", ">=", self.branch_id.sequence), ("branch_id.sequence", "<", target_branch.sequence), - ] + ], + order=order, + limit=limit, ) def _get_next_module_branches(self, target_branch=None): @@ -177,29 +204,31 @@ def _to_dict(self): @api.model_create_multi def create(self, vals_list): recs = super().create(vals_list) - recs._update_migration_target_module_id() + recs._update_migration_data() return recs def write(self, vals): res = super().write(vals) # When 'pr_url' is set or unset, this means the module has been found - # in a PR or has been merged upstream. We want to recompute the target - # module in migration data in such case. + # in a PR or has been merged upstream. We want to refresh relevant + # migration data in such case. if "pr_url" in vals: - self._update_migration_target_module_id() + self._update_migration_data() return res - def _update_migration_target_module_id(self): - """Update `target_module_id` field on relevant module migration records.""" + def _update_migration_data(self): + """Update relevant module migration records.""" for rec in self: + timelines = rec._get_related_timelines() + impacted_modules = rec.module_id | timelines._get_all_impacted_modules() migrations = self.env["odoo.module.branch.migration"].search( [ - ("module_id", "=", rec.module_id.id), - ("target_branch_id", "=", rec.branch_id.id), + ("module_id", "in", impacted_modules.ids), + ("source_branch_id.sequence", "<=", rec.branch_id.sequence), + ("target_branch_id.sequence", ">=", rec.branch_id.sequence), ] ) - # Recompute 'target_module_id' field - migrations._compute_target_module_branch_id() + migrations.force_update() def open_next_module_branches(self): self.ensure_one() diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index 24980f0e..084f6109 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -1,4 +1,6 @@ # Copyright 2023 Camptocamp SA +# Copyright 2026 Akretion (https://www.akretion.com). +# @author Sébastien Alix # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) import pprint @@ -205,11 +207,9 @@ def _compute_moved_to_generic(self): and not rec.target_module_branch_id.repository_id.specific ) - @api.depends( - "module_branch_id.timeline_ids.state", - "module_branch_id.timeline_ids.next_module_id", - "target_branch_id", - ) + # NOTE: _renamed_to_module_in_target_version relies also on timelines, and + # this computed field will be recomputed automatically on timeline updates. + @api.depends("target_branch_id") def _compute_renamed_to_module_id(self): for rec in self: rec.renamed_to_module_id = ( @@ -218,11 +218,9 @@ def _compute_renamed_to_module_id(self): ) ) - @api.depends( - "module_branch_id.timeline_ids.state", - "module_branch_id.timeline_ids.next_module_id", - "target_branch_id", - ) + # NOTE: _replaced_by_module_in_target_version relies also on timelines, and + # this computed field will be recomputed automatically on timeline updates. + @api.depends("target_branch_id") def _compute_replaced_by_module_id(self): for rec in self: rec.replaced_by_module_id = ( @@ -260,7 +258,14 @@ def _compute_state(self): # Specific module moved to a generic repository (public or private) rec.state = "moved_to_generic" continue - rec.state = rec.process or "fully_ported" + rec.state = rec.process or ( + "fully_ported" + if ( + rec.target_module_branch_id + and not rec.target_module_branch_id.pr_url + ) + else "migrate" + ) if rec.process == "migrate" and rec.pr_url: rec.state = "review_migration" @@ -313,6 +318,17 @@ def _compute_migration_scan(self): elif rec.target_module_branch_id.pr_url != rec.pr_url: rec.migration_scan = True + def force_update(self): + """Ensure the migration data is updated or will be updated on next scan.""" + self_sudo = self.sudo() + self_sudo.last_target_scanned_commit = False + self_sudo.results = self_sudo.process = False + self_sudo._compute_replaced_by_module_id() + self_sudo._compute_renamed_to_module_id() + self_sudo._compute_target_module_branch_id() + self_sudo._compute_state() + self_sudo._compute_migration_scan() + @api.model @api.returns("odoo.module.branch.migration") def push_scanned_data(self, module_branch_id, data): diff --git a/odoo_repository_migration/models/odoo_module_branch_timeline.py b/odoo_repository_migration/models/odoo_module_branch_timeline.py index 2258df0d..fae36ce6 100644 --- a/odoo_repository_migration/models/odoo_module_branch_timeline.py +++ b/odoo_repository_migration/models/odoo_module_branch_timeline.py @@ -1,4 +1,6 @@ # Copyright 2025 Camptocamp SA +# Copyright 2026 Akretion (https://www.akretion.com). +# @author Sébastien Alix # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo import api, fields, models @@ -15,6 +17,12 @@ class OdooModuleBranchTimeline(models.Model): required=True, index=True, ) + last_known_module_branch_id = fields.Many2one( + string="Last Known Module", + comodel_name="odoo.module.branch", + compute="_compute_last_known_module_branch_id", + help="Technical field to check if current selected module is the last one.", + ) org_id = fields.Many2one( string="Organization", related="module_branch_id.org_id", @@ -42,7 +50,6 @@ class OdooModuleBranchTimeline(models.Model): ("renamed", "has been renamed to"), ("replaced", "has been replaced by"), ], - inverse="_inverse_next_fields", default="renamed", required=True, index=True, @@ -56,7 +63,6 @@ class OdooModuleBranchTimeline(models.Model): next_module_id = fields.Many2one( string="New module name", comodel_name="odoo.module", - inverse="_inverse_next_fields", ondelete="restrict", index=True, ) @@ -65,6 +71,16 @@ class OdooModuleBranchTimeline(models.Model): related="module_branch_id.next_odoo_version_module_branch_id", ) note = fields.Html() + migration_ids = fields.Many2many( + comodel_name="odoo.module.branch.migration", + string="Migrations", + compute="_compute_migration_ids", + ) + migration_scan = fields.Boolean( + compute="_compute_migration_scan", + help="Technical field telling if this timeline needs a migration scan.", + ) + active = fields.Boolean(default=True) @api.depends("odoo_version_id", "module_branch_id", "next_module_id") def _compute_display_name(self): @@ -74,32 +90,93 @@ def _compute_display_name(self): f"{rec.module_branch_id.module_id.name} > {rec.next_module_id.name}" ) - def _inverse_next_fields(self): - # When a module is renamed or replaced, we reset the - # last target scan commits on all impacted migration paths. - # E.g. - # if a module on 17.0 is set as renamed starting from 18.0, - # all migration paths of this module targetting versions >= 18.0 - # should re-trigger a migration scan. + @api.depends("module_branch_id") + def _compute_last_known_module_branch_id(self): + for rec in self: + rec.last_known_module_branch_id = self.env["odoo.module.branch"].search( + [ + ("module_id", "=", rec.module_branch_id.module_id.id), + ("repository_id", "!=", False), + ("installable", "=", True), + ( + "branch_id.sequence", + ">=", + rec.module_branch_id.branch_id.sequence, + ), + ], + order="branch_sequence DESC", + limit=1, + ) + + @api.depends("module_branch_id", "next_module_id", "odoo_version_id") + def _compute_migration_ids(self): for rec in self: - migrations = ( + current_module = rec.module_branch_id.module_id + next_module = rec.next_module_id + impacted_modules = current_module + next_module + rec.migration_ids = ( self.env["odoo.module.branch.migration"] .search( [ - ("module_id", "=", rec.module_branch_id.module_id.id), ( "target_branch_id.sequence", ">=", rec.odoo_version_id.sequence, ), + "|", + "|", + ("module_id", "=", current_module.id), + ("renamed_to_module_id", "in", impacted_modules.ids), + ("replaced_by_module_id", "in", impacted_modules.ids), ] ) .sudo() ) - migrations.last_target_scanned_commit = False - migrations._compute_renamed_to_module_id() - migrations._compute_replaced_by_module_id() - migrations._compute_state() + + def _compute_migration_scan(self): + for rec in self: + rec.migration_scan = any(rec.migration_ids.mapped("migration_scan")) + + # Update relevant migration records on timeline update. + # + # When a module is renamed or replaced, we refresh all impacted + # migration data records and reset their last scan commits to trigger + # a new migration scan. + # + # E.g. + # if a module on 17.0 is set as renamed starting from 18.0, existing + # migration data (17.0 -> 18.0, or 17.0 -> 19.0) will be aware of such + # change and all related modules will have to be scanned again. + + @api.model_create_multi + def create(self, vals_list): + records = super().create(vals_list) + records.migration_ids.force_update() + return records + + @api.model + def write(self, vals): + migrations = self.migration_ids + res = super().write(vals) + (self.migration_ids | migrations).force_update() + return res + + def unlink(self): + migrations = self.migration_ids + res = super().unlink() + migrations.force_update() + return res + + def open_migrations(self): + self.ensure_one() + xml_id = "odoo_repository_migration.odoo_module_branch_migration_action" + action = self.env["ir.actions.actions"]._for_xml_id(xml_id) + action["domain"] = [("id", "in", self.migration_ids.ids)] + return action + + def action_scan(self): + self.migration_ids.module_branch_id.repository_branch_id.action_scan() + return True def _get_related_timelines(self): """Return timelines related to current ones regarding impacted modules. diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index c172a5cd..cd5a519b 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Odoo Repository Migration Data -

+
+

Odoo Repository Migration Data

- - -Odoo Community Association - -
-

Odoo Repository Migration Data

-

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

This module collects and records modules migration data from Odoo repositories.

Thanks to oca-port and data @@ -457,7 +452,7 @@

Odoo Repository Migration Data

-

Usage

+

Usage

To enable this feature, the option Collect migration data should be enabled on repositories (opt-in).

To record what a module became starting from a given Odoo version, you @@ -468,7 +463,7 @@

Usage

Modules / Migrations menu.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -476,15 +471,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Camptocamp
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -506,6 +501,5 @@

Maintainers

-
diff --git a/odoo_repository_migration/tests/common.py b/odoo_repository_migration/tests/common.py index 7819a6c7..ce18f620 100644 --- a/odoo_repository_migration/tests/common.py +++ b/odoo_repository_migration/tests/common.py @@ -16,6 +16,9 @@ def setUpClass(cls): cls.repo_branch2 = cls._create_odoo_repository_branch( cls.odoo_repository, cls.branch2 ) + cls.repo_branch3 = cls._create_odoo_repository_branch( + cls.odoo_repository, cls.branch3 + ) cls.module_branch = cls._create_odoo_module_branch( cls.module, cls.branch, diff --git a/odoo_repository_migration/tests/test_odoo_module_branch.py b/odoo_repository_migration/tests/test_odoo_module_branch.py index a96d9f14..322105f4 100644 --- a/odoo_repository_migration/tests/test_odoo_module_branch.py +++ b/odoo_repository_migration/tests/test_odoo_module_branch.py @@ -34,13 +34,7 @@ def test_next_odoo_version_module_branch_id_with_renamed_module(self): last_scanned_commit="sha", ) # Add timeline entry for renaming - self.module_branch.timeline_ids.create( - { - "module_branch_id": self.module_branch.id, - "state": "renamed", - "next_module_id": next_module.id, - } - ) + self._create_timeline(self.module_branch, next_module, "renamed") # Test next_odoo_version_module_branch_id follows renaming self.assertEqual( self.module_branch.next_odoo_version_module_branch_id, next_module_branch @@ -132,7 +126,7 @@ def test_migration_scan_target_module_in_review_then_merged(self): self.module, self.branch2, specific=False, - repository_branch_id=self.repo_branch.id, + repository_branch_id=self.repo_branch2.id, # Module available in a PR pr_url="https://my/pr", ) @@ -167,9 +161,6 @@ def test_migration_scan_target_module_in_review_then_merged(self): "pr_url": False, } ) - self.module_branch.migration_ids.last_target_scanned_commit = ( - target_module_branch.last_scanned_commit - ) self.assertEqual( self.module_branch.migration_ids.target_module_branch_id, target_module_branch, @@ -308,9 +299,19 @@ def test_migration_scan_target_module_moved_to_generic(self): self.assertEqual(mig.state, "moved_to_generic") self.assertFalse(mig.migration_scan) - def test_renamed_to_module_in_target_version(self): + def test_renamed_module(self): + """Test migration data of a module renamed. + + Data input: + - source version = X + - target version = X+1 + - module renamed in X+1 + + Expected ouput: + - 'renamed_to_module_id' should target module renamed in X+1 + """ self.odoo_repository.collect_migration_data = True - # Next version is 16.0 + # Next version is X+1 next_branch = self.env["odoo.branch"].search( [("sequence", "=", self.branch.sequence + 1)] ) @@ -321,10 +322,10 @@ def test_renamed_to_module_in_target_version(self): new_module, next_branch, specific=False, - repository_branch_id=self.repo_branch.id, + repository_branch_id=self.repo_branch2.id, last_scanned_commit="sha", ) - # Generate migration data records + # Generate migration data records from X to X+1 self.env["odoo.migration.path"].create( { "source_branch_id": self.branch.id, @@ -337,19 +338,13 @@ def test_renamed_to_module_in_target_version(self): target=next_branch, report={"process": "migrate", "results": {}}, ) - # Module has been renamed starting from 16.0 - self.module_branch.timeline_ids.create( - { - "module_branch_id": self.module_branch.id, - "state": "renamed", - "next_module_id": new_module.id, - } - ) + # Module has been renamed starting from X+1 + self._create_timeline(self.module_branch, new_module, "renamed") renamed_to_module = self.module_branch._renamed_to_module_in_target_version( next_branch ) self.assertEqual(renamed_to_module, new_module) - # We target 17.0 to check if intermediate data in 16.0 is found + # We target X+2 to check if intermediate data in X+1 is found target_branch = self.env["odoo.branch"].search( [("sequence", "=", self.branch.sequence + 2)] ) @@ -363,12 +358,97 @@ def test_renamed_to_module_in_target_version(self): self.assertFalse(mig.replaced_by_module_id) self.assertEqual(mig.target_module_branch_id, target_module_branch) self.assertFalse(mig.last_target_scanned_commit) - self.assertEqual(mig.state, "migrate") + self.assertEqual(mig.state, "fully_ported") self.assertTrue(mig.migration_scan) - def test_replaced_by_module_in_target_version(self): + def test_renamed_module_twice(self): + """Test migration data of a module renamed twice. + + Data input: + - source version = X + - target version = X+2 + - module renamed in X+1 + - module renamed again in X+2 + + Expected ouput: + - 'renamed_to_module_id' should target module renamed in X+2 + """ self.odoo_repository.collect_migration_data = True - # Next version is 16.0 + # Next version is X+1 + next_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 1)] + ) + self.assertEqual(self.branch.next_id, next_branch) + # Target version is X+2 + target_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 2)] + ) + self.assertEqual(self.branch.next_id.next_id, target_branch) + # Create the next module + next_module = self.module.copy({"name": "next_module"}) + next_module_branch = self._create_odoo_module_branch( + next_module, + next_branch, + specific=False, + repository_branch_id=self.repo_branch2.id, + last_scanned_commit="sha_next", + ) + # Create the target module + target_module = self.module.copy({"name": "target_module"}) + target_module_branch = self._create_odoo_module_branch( + target_module, + target_branch, + specific=False, + repository_branch_id=self.repo_branch3.id, + last_scanned_commit="sha_target", + ) + # Generate migration data records from X to X+2 (with a gap) + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": target_branch.id, + } + ) + self._simulate_migration_scan( + "target_commit1", + source=self.branch, + target=target_branch, + report={"process": "migrate", "results": {}}, + ) + # Module has been renamed in X+1 + self._create_timeline(self.module_branch, next_module, "renamed") + renamed_to_module = self.module_branch._renamed_to_module_in_target_version( + next_branch + ) + self.assertEqual(renamed_to_module, next_module) + # Module has been renamed again in X+2 + self._create_timeline(next_module_branch, target_module, "renamed") + renamed_to_module = next_module_branch._renamed_to_module_in_target_version( + target_branch + ) + self.assertEqual(renamed_to_module, target_module) + # Check migration data + mig = self.module_branch.migration_ids + self.assertEqual(mig.renamed_to_module_id, target_module) + self.assertFalse(mig.replaced_by_module_id) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertFalse(mig.last_target_scanned_commit) + self.assertEqual(mig.state, "fully_ported") + self.assertTrue(mig.migration_scan) + + def test_replaced_module(self): + """Test migration data of a module replaced. + + Data input: + - source version = X + - target version = X+1 + - module replaced in X+1 + + Expected ouput: + - 'replaced_by_module_id' should target module replaced in X+1 + """ + self.odoo_repository.collect_migration_data = True + # Next version is X+1 next_branch = self.env["odoo.branch"].search( [("sequence", "=", self.branch.sequence + 1)] ) @@ -395,19 +475,13 @@ def test_replaced_by_module_in_target_version(self): target=next_branch, report={"process": "migrate", "results": {}}, ) - # New module is replacing current one starting from 16.0 - self.module_branch.timeline_ids.create( - { - "module_branch_id": self.module_branch.id, - "state": "replaced", - "next_module_id": new_module.id, - } - ) + # New module is replacing current one starting from X+1 + self._create_timeline(self.module_branch, new_module, "replaced") replaced_by_module = self.module_branch._replaced_by_module_in_target_version( next_branch ) self.assertEqual(replaced_by_module, new_module) - # We target 17.0 to check if intermediate data in 16.0 is found + # We target X+2 to check if intermediate data in X+1 is found target_branch = self.env["odoo.branch"].search( [("sequence", "=", self.branch.sequence + 2)] ) @@ -422,4 +496,233 @@ def test_replaced_by_module_in_target_version(self): self.assertEqual(mig.target_module_branch_id, target_module_branch) self.assertFalse(mig.last_target_scanned_commit) self.assertEqual(mig.state, "replaced") + # No migration scan needed for replaced modules + self.assertFalse(mig.migration_scan) + + def test_replaced_module_twice(self): + """Test migration data of a module replaced twice. + + Data input: + - source version = X + - target version = X+2 + - module replaced in X+1 + - module replaced again in X+2 + + Expected ouput: + - 'replaced_by_module_id' should target module replaced in X+2 + """ + self.odoo_repository.collect_migration_data = True + # Next version is X+1 + next_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 1)] + ) + self.assertEqual(self.branch.next_id, next_branch) + # Target version is X+2 + target_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 2)] + ) + self.assertEqual(self.branch.next_id.next_id, target_branch) + # Create the next module + next_module = self.module.copy({"name": "next_module"}) + next_module_branch = self._create_odoo_module_branch( + next_module, + next_branch, + specific=False, + repository_branch_id=self.repo_branch2.id, + last_scanned_commit="sha_next", + ) + # Create the target module + target_module = self.module.copy({"name": "target_module"}) + target_module_branch = self._create_odoo_module_branch( + target_module, + target_branch, + specific=False, + repository_branch_id=self.repo_branch3.id, + last_scanned_commit="sha_target", + ) + # Generate migration data records from X to X+2 (with a gap) + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": target_branch.id, + } + ) + self._simulate_migration_scan( + "target_commit1", + source=self.branch, + target=target_branch, + report={"process": "migrate", "results": {}}, + ) + # Module has been replaced in X+1 + self._create_timeline(self.module_branch, next_module, "replaced") + replaced_by_module = self.module_branch._replaced_by_module_in_target_version( + next_branch + ) + self.assertEqual(replaced_by_module, next_module) + # Module has been replaced again in X+2 + self._create_timeline(next_module_branch, target_module, "replaced") + replaced_by_module = next_module_branch._replaced_by_module_in_target_version( + target_branch + ) + self.assertEqual(replaced_by_module, target_module) + # Check migration data + mig = self.module_branch.migration_ids + self.assertEqual(mig.replaced_by_module_id, target_module) + self.assertFalse(mig.renamed_to_module_id) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertFalse(mig.last_target_scanned_commit) + self.assertEqual(mig.state, "replaced") + # No migration scan needed for replaced modules + self.assertFalse(mig.migration_scan) + + def test_renamed_then_replaced_module(self): + """Test migration data of a module renamed then replaced. + + Data input: + - source version = X + - target version = X+2 + - module renamed in X+1 + - module replaced in X+2 + + Expected ouput: + - 'renamed_to_module_id' should be empty + - 'replaced_by_module_id' should target module replaced in X+2 + """ + self.odoo_repository.collect_migration_data = True + # Next version is X+1 + next_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 1)] + ) + self.assertEqual(self.branch.next_id, next_branch) + # Target version is X+2 + target_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 2)] + ) + self.assertEqual(self.branch.next_id.next_id, target_branch) + # Create the next module + next_module = self.module.copy({"name": "next_module"}) + next_module_branch = self._create_odoo_module_branch( + next_module, + next_branch, + specific=False, + repository_branch_id=self.repo_branch2.id, + last_scanned_commit="sha_next", + ) + # Create the target module + target_module = self.module.copy({"name": "target_module"}) + target_module_branch = self._create_odoo_module_branch( + target_module, + target_branch, + specific=False, + repository_branch_id=self.repo_branch3.id, + last_scanned_commit="sha_target", + ) + # Generate migration data records from X to X+2 (with a gap) + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": target_branch.id, + } + ) + self._simulate_migration_scan( + "target_commit1", + source=self.branch, + target=target_branch, + report={"process": "migrate", "results": {}}, + ) + # Module has been renamed in X+1 + self._create_timeline(self.module_branch, next_module, "renamed") + renamed_to_module = self.module_branch._renamed_to_module_in_target_version( + next_branch + ) + self.assertEqual(renamed_to_module, next_module) + # Module has been replaced in X+2 + self._create_timeline(next_module_branch, target_module, "replaced") + replaced_by_module = next_module_branch._replaced_by_module_in_target_version( + target_branch + ) + self.assertEqual(replaced_by_module, target_module) + # Check migration data + mig = self.module_branch.migration_ids + self.assertFalse(mig.renamed_to_module_id) + self.assertEqual(mig.replaced_by_module_id, target_module) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertFalse(mig.last_target_scanned_commit) + self.assertEqual(mig.state, "replaced") + self.assertFalse(mig.migration_scan) + + def test_replaced_then_renamed_module(self): + """Test migration data of a module replaced then renamed. + + Data input: + - source version = X + - target version = X+2 + - module replaced in X+1 + - module renamed in X+2 + + Expected ouput: + - 'renamed_to_module_id' should be empty + - 'replaced_by_module_id' should target module renamed in X+2 + """ + self.odoo_repository.collect_migration_data = True + # Next version is X+1 + next_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 1)] + ) + self.assertEqual(self.branch.next_id, next_branch) + # Target version is X+2 + target_branch = self.env["odoo.branch"].search( + [("sequence", "=", self.branch.sequence + 2)] + ) + self.assertEqual(self.branch.next_id.next_id, target_branch) + # Create the next module + next_module = self.module.copy({"name": "next_module"}) + next_module_branch = self._create_odoo_module_branch( + next_module, + next_branch, + specific=False, + repository_branch_id=self.repo_branch2.id, + last_scanned_commit="sha_next", + ) + # Create the target module + target_module = self.module.copy({"name": "target_module"}) + target_module_branch = self._create_odoo_module_branch( + target_module, + target_branch, + specific=False, + repository_branch_id=self.repo_branch3.id, + last_scanned_commit="sha_target", + ) + # Generate migration data records from X to X+2 (with a gap) + self.env["odoo.migration.path"].create( + { + "source_branch_id": self.branch.id, + "target_branch_id": target_branch.id, + } + ) + self._simulate_migration_scan( + "target_commit1", + source=self.branch, + target=target_branch, + report={"process": "migrate", "results": {}}, + ) + # Module has been replaced in X+1 + self._create_timeline(self.module_branch, next_module, "replaced") + replaced_by_module = self.module_branch._replaced_by_module_in_target_version( + next_branch + ) + self.assertEqual(replaced_by_module, next_module) + # Module has been renamed in X+2 + self._create_timeline(next_module_branch, target_module, "renamed") + renamed_to_module = next_module_branch._renamed_to_module_in_target_version( + target_branch + ) + self.assertEqual(renamed_to_module, target_module) + # Check migration data + mig = self.module_branch.migration_ids + self.assertFalse(mig.renamed_to_module_id) + self.assertEqual(mig.replaced_by_module_id, target_module) + self.assertEqual(mig.target_module_branch_id, target_module_branch) + self.assertFalse(mig.last_target_scanned_commit) + self.assertEqual(mig.state, "replaced") self.assertFalse(mig.migration_scan) diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index 8a5b311d..ff94afa1 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -8,6 +8,7 @@
+

diff --git a/odoo_repository_migration/views/odoo_module_branch_timeline.xml b/odoo_repository_migration/views/odoo_module_branch_timeline.xml index eb20d374..f361aa80 100644 --- a/odoo_repository_migration/views/odoo_module_branch_timeline.xml +++ b/odoo_repository_migration/views/odoo_module_branch_timeline.xml @@ -1,5 +1,7 @@ @@ -7,10 +9,55 @@ odoo.module.branch.timeline +
+
- - +
+
+ + + + + + + Date: Sat, 13 Jun 2026 13:02:45 +0000 Subject: [PATCH 61/65] [UPD] Update odoo_repository_migration.pot --- .../i18n/odoo_repository_migration.pot | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/odoo_repository_migration/i18n/odoo_repository_migration.pot b/odoo_repository_migration/i18n/odoo_repository_migration.pot index a3bbf821..ce1e38c1 100644 --- a/odoo_repository_migration/i18n/odoo_repository_migration.pot +++ b/odoo_repository_migration/i18n/odoo_repository_migration.pot @@ -18,12 +18,25 @@ msgstr "" msgid "Next Versions" msgstr "" +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form +msgid "" +"A scan of related modules is required to update relevant migration data. " +"Wait for the next scheduled scan, or trigger one manually." +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__active #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__active +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__active msgid "Active" msgstr "" +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Archived" +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__author_ids msgid "Authors" @@ -97,9 +110,15 @@ msgstr "" #. module: odoo_repository_migration #: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_form +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form msgid "Info" msgstr "" +#. module: odoo_repository_migration +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__last_known_module_branch_id +msgid "Last Known Module" +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__last_source_scanned_commit msgid "Last Source Scanned Commit" @@ -144,6 +163,7 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_scan #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__migration_scan +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__migration_scan #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_scan msgid "Migration Scan" msgstr "" @@ -167,8 +187,10 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.actions.act_window,name:odoo_repository_migration.odoo_module_branch_migration_action #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_ids +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__migration_ids #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_ids #: model:ir.ui.menu,name:odoo_repository_migration.odoo_module_branch_migration_menu +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form #: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_form msgid "Migrations" msgstr "" @@ -340,6 +362,11 @@ msgstr "" msgid "Repository" msgstr "" +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form +msgid "Restore" +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__results msgid "Results" @@ -353,6 +380,7 @@ msgstr "" #. module: odoo_repository_migration #: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_migration_path_view_form #: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_form +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form msgid "Scan" msgstr "" @@ -416,11 +444,28 @@ msgid "" "Technical field telling if this module is elligible for a migration scan." msgstr "" +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_timeline__migration_scan +msgid "Technical field telling if this timeline needs a migration scan." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_timeline__last_known_module_branch_id +msgid "Technical field to check if current selected module is the last one." +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__module_id msgid "Technical name" msgstr "" +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form +msgid "" +"The selected module version is not the last one available.\n" +" Recording such timeline will ignore all newest versions when computing related migration data." +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.constraint,message:odoo_repository_migration.constraint_odoo_migration_path_migration_path_uniq msgid "This migration path already exists." From f388ba61ff951f0a0c220fc5a4334fdeb37dbb60 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Sat, 13 Jun 2026 13:04:57 +0000 Subject: [PATCH 62/65] [BOT] post-merge updates --- odoo_repository_migration/README.rst | 8 ++++-- .../static/description/index.html | 28 +++++++++++-------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index fe1f7235..d0569e21 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ============================== Odoo Repository Migration Data ============================== @@ -7,13 +11,13 @@ Odoo Repository Migration Data !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:420dde7e62d8c5f7421d2e9190cff25d51896a02cfe1b4fea54a6981ac4f45b3 + !! source digest: sha256:30958839b2e43d4e211d93f56d5ac4fa79f4bf2ff38f62c0c2c9b01dba613b60 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmodule--composition--analysis-lightgray.png?logo=github diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index cd5a519b..3a0c4b51 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -3,7 +3,7 @@ -Odoo Repository Migration Data +README.rst -
-

Odoo Repository Migration Data

+
+ + +Odoo Community Association + +
+

Odoo Repository Migration Data

-

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

This module collects and records modules migration data from Odoo repositories.

Thanks to oca-port and data @@ -452,7 +457,7 @@

Odoo Repository Migration Data

-

Usage

+

Usage

To enable this feature, the option Collect migration data should be enabled on repositories (opt-in).

To record what a module became starting from a given Odoo version, you @@ -463,7 +468,7 @@

Usage

Modules / Migrations menu.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -471,15 +476,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Camptocamp
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -501,5 +506,6 @@

Maintainers

+
From 55655855b0b38d0118cf60e05dd7d413315758aa Mon Sep 17 00:00:00 2001 From: Weblate Date: Sat, 13 Jun 2026 13:05:11 +0000 Subject: [PATCH 63/65] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: module-composition-analysis-18.0/module-composition-analysis-18.0-odoo_repository_migration Translate-URL: https://translation.odoo-community.org/projects/module-composition-analysis-18-0/module-composition-analysis-18-0-odoo_repository_migration/ --- odoo_repository_migration/i18n/it.po | 62 +++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/odoo_repository_migration/i18n/it.po b/odoo_repository_migration/i18n/it.po index 51d8784d..f56eb37c 100644 --- a/odoo_repository_migration/i18n/it.po +++ b/odoo_repository_migration/i18n/it.po @@ -19,12 +19,25 @@ msgstr "" msgid "Next Versions" msgstr "" +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form +msgid "" +"A scan of related modules is required to update relevant migration data. " +"Wait for the next scheduled scan, or trigger one manually." +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path__active #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__active +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__active msgid "Active" msgstr "" +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_search +msgid "Archived" +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__author_ids msgid "Authors" @@ -98,14 +111,13 @@ msgstr "" #. module: odoo_repository_migration #: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_form +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form msgid "Info" msgstr "" #. module: odoo_repository_migration -#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_migration_path____last_update -#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration____last_update -#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline____last_update -msgid "Last Modified on" +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__last_known_module_branch_id +msgid "Last Known Module" msgstr "" #. module: odoo_repository_migration @@ -152,6 +164,7 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_scan #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__migration_scan +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__migration_scan #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_scan msgid "Migration Scan" msgstr "" @@ -175,8 +188,10 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.actions.act_window,name:odoo_repository_migration.odoo_module_branch_migration_action #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch__migration_ids +#: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_timeline__migration_ids #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_project_module__migration_ids #: model:ir.ui.menu,name:odoo_repository_migration.odoo_module_branch_migration_menu +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form #: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_view_form msgid "Migrations" msgstr "" @@ -197,8 +212,8 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_standard msgid "" -"Module now available in Odoo standard code. This module is maybe not exactly" -" the same, and doesn't have the same scope so it deserves a check during a " +"Module now available in Odoo standard code. This module is maybe not exactly " +"the same, and doesn't have the same scope so it deserves a check during a " "migration." msgstr "" @@ -251,7 +266,6 @@ msgstr "" #. module: odoo_repository_migration #. odoo-python #: code:addons/odoo_repository_migration/models/odoo_module_branch.py:0 -#, python-format msgid "Next versions" msgstr "" @@ -326,8 +340,10 @@ msgstr "" #. module: odoo_repository_migration #: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_timeline__state msgid "" -"Renamed: modules still share the same Git history (it allows to check commits that could be ported)\n" -"Replaced: module has been replaced (or merged) by another one that fulfill the same feature" +"Renamed: modules still share the same Git history (it allows to check " +"commits that could be ported)\n" +"Replaced: module has been replaced (or merged) by another one that fulfill " +"the same feature" msgstr "" #. module: odoo_repository_migration @@ -349,6 +365,11 @@ msgstr "" msgid "Repository" msgstr "" +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form +msgid "Restore" +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__results msgid "Results" @@ -362,6 +383,7 @@ msgstr "" #. module: odoo_repository_migration #: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_migration_path_view_form #: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_migration_view_form +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form msgid "Scan" msgstr "" @@ -386,8 +408,8 @@ msgstr "" #: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_migration__moved_to_generic msgid "" "Specific module now available in a generic repository. This module is maybe " -"not exactly the same, and doesn't have the same scope so it deserves a check" -" during a migration." +"not exactly the same, and doesn't have the same scope so it deserves a check " +"during a migration." msgstr "" #. module: odoo_repository_migration @@ -425,11 +447,29 @@ msgid "" "Technical field telling if this module is elligible for a migration scan." msgstr "" +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_timeline__migration_scan +msgid "Technical field telling if this timeline needs a migration scan." +msgstr "" + +#. module: odoo_repository_migration +#: model:ir.model.fields,help:odoo_repository_migration.field_odoo_module_branch_timeline__last_known_module_branch_id +msgid "Technical field to check if current selected module is the last one." +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.fields,field_description:odoo_repository_migration.field_odoo_module_branch_migration__module_id msgid "Technical name" msgstr "" +#. module: odoo_repository_migration +#: model_terms:ir.ui.view,arch_db:odoo_repository_migration.odoo_module_branch_timeline_view_form +msgid "" +"The selected module version is not the last one available.\n" +" Recording such timeline will ignore all newest " +"versions when computing related migration data." +msgstr "" + #. module: odoo_repository_migration #: model:ir.model.constraint,message:odoo_repository_migration.constraint_odoo_migration_path_migration_path_uniq msgid "This migration path already exists." From 6435021d20d32b4272b2c383acea45796e5bcade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Alan=20Ramos=20Rodr=C3=ADguez?= Date: Wed, 29 Jul 2026 06:42:27 -0600 Subject: [PATCH 64/65] [MIG] odoo_repository_migration: Migration to 19.0 --- odoo_repository_migration/README.rst | 10 +-- odoo_repository_migration/__manifest__.py | 4 +- .../migrations/18.0.1.1.0/post-migrate.py | 23 ------ .../models/odoo_migration_path.py | 11 ++- .../models/odoo_module_branch.py | 7 +- .../models/odoo_module_branch_migration.py | 12 ++-- .../models/odoo_repository.py | 7 +- .../static/description/index.html | 6 +- .../views/odoo_module_branch_migration.xml | 72 +++++++++---------- .../views/odoo_module_branch_timeline.xml | 42 ++++++----- requirements.txt | 2 + 11 files changed, 83 insertions(+), 113 deletions(-) delete mode 100644 odoo_repository_migration/migrations/18.0.1.1.0/post-migrate.py create mode 100644 requirements.txt diff --git a/odoo_repository_migration/README.rst b/odoo_repository_migration/README.rst index d0569e21..6948075e 100644 --- a/odoo_repository_migration/README.rst +++ b/odoo_repository_migration/README.rst @@ -21,13 +21,13 @@ Odoo Repository Migration Data :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmodule--composition--analysis-lightgray.png?logo=github - :target: https://github.com/OCA/module-composition-analysis/tree/18.0/odoo_repository_migration + :target: https://github.com/OCA/module-composition-analysis/tree/19.0/odoo_repository_migration :alt: OCA/module-composition-analysis .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/module-composition-analysis-18-0/module-composition-analysis-18-0-odoo_repository_migration + :target: https://translation.odoo-community.org/projects/module-composition-analysis-19-0/module-composition-analysis-19-0-odoo_repository_migration :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/module-composition-analysis&target_branch=18.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/module-composition-analysis&target_branch=19.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -113,7 +113,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -145,6 +145,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/module-composition-analysis `_ project on GitHub. +This module is part of the `OCA/module-composition-analysis `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/odoo_repository_migration/__manifest__.py b/odoo_repository_migration/__manifest__.py index 8b6b5e61..5519b656 100644 --- a/odoo_repository_migration/__manifest__.py +++ b/odoo_repository_migration/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Odoo Repository Migration Data", "summary": "Collect modules migration data for Odoo Repositories.", - "version": "18.0.1.1.0", + "version": "19.0.1.0.0", "category": "Tools", "author": "Camptocamp, Odoo Community Association (OCA)", "website": "https://github.com/OCA/module-composition-analysis", @@ -24,7 +24,7 @@ ], "external_dependencies": { "python": [ - "oca-port", + "oca_port", ], }, "license": "AGPL-3", diff --git a/odoo_repository_migration/migrations/18.0.1.1.0/post-migrate.py b/odoo_repository_migration/migrations/18.0.1.1.0/post-migrate.py deleted file mode 100644 index 44fd4109..00000000 --- a/odoo_repository_migration/migrations/18.0.1.1.0/post-migrate.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2026 Akretion (https://www.akretion.com). -# @author Sébastien Alix -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) - -import logging - -from odoo import SUPERUSER_ID, api - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - if not version: - return - env = api.Environment(cr, SUPERUSER_ID, {}) - recompute_timeline_migration_data(env) - - -def recompute_timeline_migration_data(env): - """Recompute migration data related to timelines.""" - timelines = env["odoo.module.branch.timeline"].search([]) - _logger.info("Recompute migration data related to %s timelines...", len(timelines)) - timelines.migration_ids.force_update() diff --git a/odoo_repository_migration/models/odoo_migration_path.py b/odoo_repository_migration/models/odoo_migration_path.py index b3db3de6..3468256a 100644 --- a/odoo_repository_migration/models/odoo_migration_path.py +++ b/odoo_repository_migration/models/odoo_migration_path.py @@ -22,13 +22,10 @@ class OdooMigrationPath(models.Model): required=True, ) - _sql_constraints = [ - ( - "migration_path_uniq", - "UNIQUE (source_branch_id, target_branch_id)", - "This migration path already exists.", - ), - ] + _migration_path_uniq = models.Constraint( + "UNIQUE (source_branch_id, target_branch_id)", + "This migration path already exists.", + ) @api.depends("source_branch_id.name", "target_branch_id.name") def _compute_name(self): diff --git a/odoo_repository_migration/models/odoo_module_branch.py b/odoo_repository_migration/models/odoo_module_branch.py index 96ff6ced..b77b7b4b 100644 --- a/odoo_repository_migration/models/odoo_module_branch.py +++ b/odoo_repository_migration/models/odoo_module_branch.py @@ -3,7 +3,7 @@ # @author Sébastien Alix # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import _, api, fields, models +from odoo import api, fields, models class OdooModuleBranch(models.Model): @@ -28,8 +28,7 @@ class OdooModuleBranch(models.Model): compute="_compute_migration_scan", store=True, help=( - "Technical field telling if this module is elligible " - "for a migration scan." + "Technical field telling if this module is elligible for a migration scan." ), ) @@ -234,7 +233,7 @@ def open_next_module_branches(self): self.ensure_one() xml_id = "odoo_repository.odoo_module_branch_action" action = self.env["ir.actions.actions"]._for_xml_id(xml_id) - action["name"] = _("Next versions") + action["name"] = self.env._("Next versions") action["domain"] = [("id", "in", self._get_next_module_branches().ids)] return action diff --git a/odoo_repository_migration/models/odoo_module_branch_migration.py b/odoo_repository_migration/models/odoo_module_branch_migration.py index 084f6109..a90278c4 100644 --- a/odoo_repository_migration/models/odoo_module_branch_migration.py +++ b/odoo_repository_migration/models/odoo_module_branch_migration.py @@ -133,13 +133,10 @@ class OdooModuleBranchMigration(models.Model): help="Technical field telling if this migration path needs a migration scan.", ) - _sql_constraints = [ - ( - "module_migration_path_uniq", - "UNIQUE (module_branch_id, migration_path_id)", - "This module migration path already exists.", - ), - ] + _module_migration_path_uniq = models.Constraint( + "UNIQUE (module_branch_id, migration_path_id)", + "This module migration path already exists.", + ) @api.depends( "module_branch_id.module_id", @@ -330,7 +327,6 @@ def force_update(self): self_sudo._compute_migration_scan() @api.model - @api.returns("odoo.module.branch.migration") def push_scanned_data(self, module_branch_id, data): migration_path = self.env["odoo.migration.path"].search( [ diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index 17f299e8..e667fb84 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -73,7 +73,10 @@ def _create_subsequent_jobs( ("source_branch_id", "in", all_versions), ("target_branch_id", "in", all_versions), ] - migration_paths = self.env["odoo.migration.path"].search(args) + # Without version filter all migration paths are wanted (small table) + migration_paths = self.env["odoo.migration.path"].search( # pylint: disable=no-search-all + args + ) # Launch one job for all migration_paths if migration_paths: # Migration paths parameter containing the migration path ID + @@ -154,7 +157,7 @@ def _migration_create_jobs_scan_module( for module in modules_to_scan: delayable = self.delayable( description=( - f"Collect {module.name} migration data " f"({' > '.join(mig_path)})" + f"Collect {module.name} migration data ({' > '.join(mig_path)})" ), identity_key=identity_exact, ) diff --git a/odoo_repository_migration/static/description/index.html b/odoo_repository_migration/static/description/index.html index 3a0c4b51..8cc02864 100644 --- a/odoo_repository_migration/static/description/index.html +++ b/odoo_repository_migration/static/description/index.html @@ -374,7 +374,7 @@

Odoo Repository Migration Data

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:30958839b2e43d4e211d93f56d5ac4fa79f4bf2ff38f62c0c2c9b01dba613b60 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/module-composition-analysis Translate me on Weblate Try me on Runboat

This module collects and records modules migration data from Odoo repositories.

Thanks to oca-port and data @@ -472,7 +472,7 @@

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -501,7 +501,7 @@

Maintainers

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/module-composition-analysis project on GitHub.

+

This module is part of the OCA/module-composition-analysis project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

diff --git a/odoo_repository_migration/views/odoo_module_branch_migration.xml b/odoo_repository_migration/views/odoo_module_branch_migration.xml index ff94afa1..124551c1 100644 --- a/odoo_repository_migration/views/odoo_module_branch_migration.xml +++ b/odoo_repository_migration/views/odoo_module_branch_migration.xml @@ -94,43 +94,41 @@ string="Commits to Port" domain="[('state', '=', 'port_commits')]" /> - - - - - - - - - + + + + + + + diff --git a/odoo_repository_migration/views/odoo_module_branch_timeline.xml b/odoo_repository_migration/views/odoo_module_branch_timeline.xml index f361aa80..fa8ef8e7 100644 --- a/odoo_repository_migration/views/odoo_module_branch_timeline.xml +++ b/odoo_repository_migration/views/odoo_module_branch_timeline.xml @@ -114,28 +114,26 @@ name="archived" domain="[('active', '=', False)]" /> - - - - - - + + + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..18a2e995 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +# generated from manifests external_dependencies +oca_port From 08f52f72249e98f89696c80b63aea56e66ba62a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Alan=20Ramos=20Rodr=C3=ADguez?= Date: Wed, 5 Aug 2026 10:25:28 -0600 Subject: [PATCH 65/65] [FIX] odoo_repository_migration: wait for GitHub API rate limit reset When oca-port reaches the GitHub API rate limit, migration scan jobs were retried until reaching the max. retries, ending up as failed jobs and spamming administrators with emails while still hammering the API for each repository. Postpone the job until the limit is reset without increasing its retry counter. Co-Authored-By: Claude Fable 5 --- odoo_repository_migration/models/odoo_repository.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/odoo_repository_migration/models/odoo_repository.py b/odoo_repository_migration/models/odoo_repository.py index e667fb84..71800f2e 100644 --- a/odoo_repository_migration/models/odoo_repository.py +++ b/odoo_repository_migration/models/odoo_repository.py @@ -238,6 +238,16 @@ def _scan_migration_module( module_names=module_names, ) except Exception as exc: + # oca-port requests the GitHub API on its own: when the rate + # limit is reached, postpone the job until the limit is reset + # (at worst 1h for an authenticated user) without increasing + # its retry counter. + if "rate limit" in str(exc).lower(): + raise RetryableJobError( + "GitHub API rate limit reached, waiting for reset", + seconds=3600, + ignore_retry=True, + ) from exc raise RetryableJobError("Scanner error") from exc def _migration_get_modules_to_scan(self, migration_path):