diff --git a/docker/scripts/run_odoo_data_workflows.py b/docker/scripts/run_odoo_data_workflows.py index 109c641..f270a88 100644 --- a/docker/scripts/run_odoo_data_workflows.py +++ b/docker/scripts/run_odoo_data_workflows.py @@ -90,6 +90,11 @@ def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: action="store_true", help="Run addon update only (no restore/bootstrap)", ) + parser.add_argument( + "--post-deploy-maintenance", + action="store_true", + help="Run addon update plus non-destructive post-deploy overrides and service-user provisioning", + ) return parser.parse_args(argv) @@ -113,6 +118,10 @@ def main(argv: Sequence[str] | None = None) -> int: bootstrap = args.bootstrap or local_settings.bootstrap no_sanitize = args.no_sanitize or local_settings.no_sanitize update_only = bool(args.update_only) + post_deploy_maintenance = bool(args.post_deploy_maintenance) + if update_only and post_deploy_maintenance: + _logger.error("--update-only cannot be combined with --post-deploy-maintenance") + return ExitCode.INVALID_ARGS upstream_settings: UpstreamServerSettings | None = None upstream_settings_error: ValidationError | None = None @@ -133,6 +142,10 @@ def main(argv: Sequence[str] | None = None) -> int: _logger.info("Addon update completed successfully.") return ExitCode.SUCCESS + if post_deploy_maintenance: + workflow_runner.run_post_deploy_maintenance() + return ExitCode.SUCCESS + if bootstrap: workflow_runner.run_bootstrap(do_sanitize=not no_sanitize) return ExitCode.SUCCESS @@ -1494,6 +1507,19 @@ def run_bootstrap(self, *, do_sanitize: bool) -> None: self.ensure_gpt_users() _logger.info("Bootstrap completed successfully.") + def run_post_deploy_maintenance(self) -> None: + _logger.info("Starting post-deploy maintenance for database '%s'", self.local.db_name) + self.update_addons(reason="post-deploy upgrade") + self.connect_to_db() + self.reconcile_missing_manifest_install_queue() + self.assert_install_queue_is_resolvable() + self.apply_environment_overrides() + self.ensure_admin_user() + self.connect_to_db() + self.assert_core_schema_healthy() + self.ensure_gpt_users() + _logger.info("Post-deploy maintenance completed successfully.") + def compute_update_module_list(self) -> list[str]: """Return sorted addon names discovered from local addon directories.""" return sorted(self._resolve_local_module_paths().keys()) diff --git a/tests/test_odoo_data_workflows.py b/tests/test_odoo_data_workflows.py index 55d9033..41c0fdf 100644 --- a/tests/test_odoo_data_workflows.py +++ b/tests/test_odoo_data_workflows.py @@ -60,6 +60,61 @@ def test_data_workflow_shell_prepends_runtime_scripts_to_pythonpath(self) -> Non self.assertEqual(runner.os_env["PYTHONPATH"], "/volumes/scripts:/opt/custom") + def test_post_deploy_maintenance_runs_overrides_and_service_user_provisioning(self) -> None: + calls: list[str] = [] + runner = odoo_data_workflows.OdooDataWorkflowRunner(self._local_settings(), upstream=None, env_file=None) + + with ( + patch.object(runner, "update_addons", side_effect=lambda **_kwargs: calls.append("update_addons")), + patch.object(runner, "connect_to_db", side_effect=lambda: calls.append("connect_to_db")), + patch.object( + runner, + "reconcile_missing_manifest_install_queue", + side_effect=lambda: calls.append("reconcile_missing_manifest_install_queue"), + ), + patch.object( + runner, + "assert_install_queue_is_resolvable", + side_effect=lambda: calls.append("assert_install_queue_is_resolvable"), + ), + patch.object( + runner, + "apply_environment_overrides", + side_effect=lambda: calls.append("apply_environment_overrides"), + ), + patch.object(runner, "ensure_admin_user", side_effect=lambda: calls.append("ensure_admin_user")), + patch.object( + runner, + "assert_core_schema_healthy", + side_effect=lambda: calls.append("assert_core_schema_healthy"), + ), + patch.object(runner, "ensure_gpt_users", side_effect=lambda: calls.append("ensure_gpt_users")), + patch.object(runner, "sanitize_database", side_effect=lambda: calls.append("sanitize_database")), + ): + runner.run_post_deploy_maintenance() + + self.assertEqual( + calls, + [ + "update_addons", + "connect_to_db", + "reconcile_missing_manifest_install_queue", + "assert_install_queue_is_resolvable", + "apply_environment_overrides", + "ensure_admin_user", + "connect_to_db", + "assert_core_schema_healthy", + "ensure_gpt_users", + ], + ) + self.assertNotIn("sanitize_database", calls) + + def test_update_only_and_post_deploy_maintenance_are_mutually_exclusive(self) -> None: + with patch.dict(os.environ, {}, clear=True): + result = odoo_data_workflows.main(["--update-only", "--post-deploy-maintenance"]) + + self.assertEqual(result, odoo_data_workflows.ExitCode.INVALID_ARGS) + if __name__ == "__main__": unittest.main()