from odoo import models, fields, api, _ from odoo.exceptions import UserError import os, zipfile, base64, io, subprocess class SyncraDevOps(models.Model): _inherit = 'syncra.welcome' module_zip = fields.Binary(string="Upload Módulo (.zip)") module_filename = fields.Char(string="Nome do Ficheiro") def action_deploy_module(self): """Descompacta o ZIP na custom_addons2 e sincroniza com o Gitea do gelson.souto""" if not self.module_zip: raise UserError(_("Por favor, carregue um ficheiro .zip primeiro.")) addons_path = '/root/odoo-18.0+e.20251216/custom_addons2/' # URL exato validado no terminal da HilariBD git_url = "http://gelson.souto:Luanda244@173.208.243.178:3000/gelson.souto/syncra_addons.git" try: # 1. Descompactar os ficheiros zip_data = base64.b64decode(self.module_zip) with zipfile.ZipFile(io.BytesIO(zip_data)) as z: z.extractall(addons_path) # 2. Operações Git os.chdir(addons_path) # Configurações de identidade local subprocess.run(['git', 'config', 'user.email', 'gelson.souto@syncra.com'], check=True) subprocess.run(['git', 'config', 'user.name', 'Gelson do Souto'], check=True) # Adicionar e Commit (com verificação para não falhar se não houver mudanças) subprocess.run(['git', 'add', '.'], check=True) # O status verifica se há algo novo antes de tentar o commit status = subprocess.run(['git', 'status', '--porcelain'], capture_output=True, text=True) if status.stdout: subprocess.run(['git', 'commit', '-m', f"Auto-deploy SYNCRA: {self.module_filename}"], check=True) # 3. Push Direto e Seguro # Usamos -c credential.helper= para garantir que ele ignore senhas antigas e use o git_url subprocess.run(['git', '-c', 'credential.helper=', 'push', git_url, 'HEAD:main'], check=True) # 4. Atualizar lista de módulos no Odoo self.env['ir.module.module'].update_list() self.module_zip = False return { 'effect': { 'fadeout': 'slow', 'message': 'Sucesso! Módulo em custom_addons2 e Gitea atualizado.', 'type': 'rainbow_man' } } except subprocess.CalledProcessError as e: raise UserError(_("Erro no comando Git (Verifique o terminal): %s") % (e.stderr or str(e))) except Exception as e: raise UserError(_("Erro inesperado no Deploy: %s") % str(e)) def action_restart_odoo(self): """Reinicia o serviço sem causar erro de SIGTERM no ecrã""" try: # O comando 'sleep 1' dá tempo ao Odoo para enviar o Rainbow Man antes de cair restart_command = "sleep 1 && sudo systemctl restart odoo" # Executa de forma desvinculada (nohup ou fork) subprocess.Popen(['/bin/bash', '-c', restart_command]) return { 'effect': { 'fadeout': 'slow', 'message': 'Sinal de reinício enviado! O sistema voltará em instantes.', 'type': 'rainbow_man', } } except Exception as e: raise UserError(_("Falha ao agendar reinício: %s") % str(e))