diff --git a/docker/scripts/run_odoo_data_workflows.py b/docker/scripts/run_odoo_data_workflows.py index e911327..3f31f02 100644 --- a/docker/scripts/run_odoo_data_workflows.py +++ b/docker/scripts/run_odoo_data_workflows.py @@ -3098,9 +3098,10 @@ def _openupgrade_target_version(self) -> str: def _is_native_20_upgrade(self) -> bool: return self._openupgrade_target_version().split(".", 1)[0] == "20" - def _uses_openupgrade_framework(self) -> bool: + def _uses_openupgrade_framework(self, *, crossing: bool = False) -> bool: requested = self.local.openupgrade_load_framework - if requested and self._is_native_20_upgrade(): + enabled = requested if requested is not None else crossing or not self._is_native_20_upgrade() + if enabled and self._is_native_20_upgrade(): _, framework = self._resolve_openupgrade_assets(require_framework=True) try: manifest = ast.literal_eval((framework / "__manifest__.py").read_text()) if framework is not None else {} @@ -3108,10 +3109,8 @@ def _uses_openupgrade_framework(self) -> bool: if not isinstance(version, str) or version.split(".", 1)[0] != self._openupgrade_target_version().split(".", 1)[0]: raise ValueError("framework target mismatch") except (OSError, SyntaxError, ValueError, AttributeError) as error: - raise OdooRestorerError( - "Explicit framework loading requires a matching target-series framework manifest." - ) from error - return requested if requested is not None else not self._is_native_20_upgrade() + raise OdooRestorerError("Framework loading requires a matching target-series framework manifest.") from error + return enabled def reset_openupgrade_versions(self) -> list[str]: if self._is_native_20_upgrade(): @@ -3148,7 +3147,7 @@ def run_openupgrade(self) -> None: target_version = self._openupgrade_target_version() native_20 = self._is_native_20_upgrade() - framework = self._uses_openupgrade_framework() + framework = self._uses_openupgrade_framework(crossing=True) scripts_paths, _ = self._resolve_openupgrade_assets(require_framework=framework) if target_version: self.os_env["OPENUPGRADE_TARGET_VERSION"] = target_version diff --git a/docs/tooling/workspace-cli.md b/docs/tooling/workspace-cli.md index 093c043..c673705 100644 --- a/docs/tooling/workspace-cli.md +++ b/docs/tooling/workspace-cli.md @@ -637,7 +637,7 @@ Notes authenticates nothing, and Odoo's own neutralize keeps it too. - For an OpenUpgrade target in major 20, selected from the explicit target or `ODOO_VERSION`, the existing packaged runner - uses native `base,web` loading and selects its SQL-only + loads `base,web,openupgrade_framework` and selects its SQL-only `prepare_core_references_20.py` with native `--pre-upgrade-scripts`. Explicit `ODOO_INSTALL_MODULES` (comma- or colon-separated) selects initial installs with `--init` in the same invocation as `--update all`, so their @@ -646,8 +646,9 @@ Notes selection during OpenUpgrade. The later restore install phase cannot substitute for an initial migration ownership handoff. It retains the discovered upgrade roots; it does not load the Odoo 19 - OpenUpgrade framework or reset module states or versions. Subsequent addon - maintenance and release-plan shells also skip that framework. Other targets + OpenUpgrade framework or reset module states or versions. Supply the matching + upstream20 framework in the artifact inputs. Subsequent addon + maintenance and release-plan shells skip it unless explicitly requested. Other targets retain their existing OpenUpgrade selection. Native 20 rejects `--reset-versions` before asset discovery or database access; run it without that flag to preserve the installed versions. @@ -655,7 +656,8 @@ Notes for major20 too, including subsequent addon commands. Supply the matching upstream20 source first in `ODOO_ADDONS_PATH`; its manifest must match the selected target series. This does not convert a19 - framework into a20 framework. Unset keeps the existing selection above. + framework into a20 framework. `OPENUPGRADE_LOAD_FRAMEWORK=false` selects an + explicit framework-free pass; unset loads the framework during the crossing. For a disposable saved19-to20 diagnostic, set `ODOO_MIGRATION_DIAGNOSTIC_REPORT` to a private JSONL report path, with `ODOO_COPY_PURPOSE=migration`, `PLATFORM_INSTANCE=rehearsal` and target20.0. diff --git a/tests/test_odoo_data_workflows.py b/tests/test_odoo_data_workflows.py index 874aefd..e876456 100644 --- a/tests/test_odoo_data_workflows.py +++ b/tests/test_odoo_data_workflows.py @@ -797,6 +797,7 @@ def test_isolated_upgrade_and_module_commands_prepare_before_starting_odoo(self) settings = self._local_settings() settings.openupgrade_enabled = True settings.openupgrade_target_version = "20.0" + settings.openupgrade_load_framework = False settings.install_modules = "replacement" settings.copy_purpose = "migration" runner = odoo_data_workflows.OdooDataWorkflowRunner(settings, upstream=None, env_file=None) @@ -829,10 +830,11 @@ def test_isolated_upgrade_and_module_commands_prepare_before_starting_odoo(self) operation() execute.assert_not_called() - def test_native_20_uses_migration_roots_without_requiring_the_legacy_framework(self) -> None: + def test_explicit_framework_opt_out_keeps_native_20_migration_roots(self) -> None: settings = self._local_settings() settings.openupgrade_enabled = True settings.openupgrade_target_version = "20.0" + settings.openupgrade_load_framework = False runner = odoo_data_workflows.OdooDataWorkflowRunner(settings, upstream=None, env_file=None) with TemporaryDirectory() as directory: root = Path(directory) @@ -852,6 +854,7 @@ def test_native_20_uses_migration_roots_without_requiring_the_legacy_framework(s self.assertEqual(Path(selected_roots[0]), (checkout / "src").resolve()) selected_modules = {module for path in selected_roots for module in runner._collect_openupgrade_modules(Path(path))} self.assertIn("mail", selected_modules) + settings.openupgrade_load_framework = True settings.openupgrade_target_version = "19.0" with self.assertRaises(odoo_data_workflows.OdooRestorerError): runner.run_openupgrade() @@ -875,6 +878,7 @@ def test_native_20_selects_only_declared_initial_installs_alongside_update_all(s settings.openupgrade_target_version = target settings.odoo_version = runtime settings.install_modules = declaration + settings.openupgrade_load_framework = False runner = odoo_data_workflows.OdooDataWorkflowRunner(settings, upstream=None, env_file=None) with ( patch.object(runner, "_resolve_openupgrade_assets", return_value=([Path("/scripts")], None)), @@ -889,11 +893,11 @@ def test_native_20_selects_only_declared_initial_installs_alongside_update_all(s self.assertNotIn("--init", command) self.assertEqual(command[command.index("--update") + 1], "all") - def test_explicit_framework_load_applies_to_native_20_and_requires_its_source(self) -> None: + def test_default_framework_load_applies_to_native_20_and_rejects_a_19_framework(self) -> None: settings = self._local_settings() settings.openupgrade_enabled = True settings.openupgrade_target_version = "20.0" - settings.openupgrade_load_framework = True + settings.openupgrade_load_framework = None runner = odoo_data_workflows.OdooDataWorkflowRunner(settings, upstream=None, env_file=None) with TemporaryDirectory() as directory: framework = Path(directory)