# Copyright (C) 2024 Cetmix OÜ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import _, api, fields, models class CxTowerGitSource(models.Model): """ Git Source. Implements single git source. Each source can include multiple remotes which can be branches or pull requests of different repositories. """ _name = "cx.tower.git.source" _description = "Cetmix Tower Git Source" _inherit = [ "cx.tower.reference.mixin", "cx.tower.yaml.mixin", ] _order = "sequence, name" active = fields.Boolean(related="git_project_id.active", store=True, readonly=True) enabled = fields.Boolean( default=True, help="Enable in configuration and exported to files" ) name = fields.Char(required=False) sequence = fields.Integer(default=10) git_project_id = fields.Many2one( comodel_name="cx.tower.git.project", string="Git Configuration", required=True, ondelete="cascade", auto_join=True, ) remote_ids = fields.One2many( comodel_name="cx.tower.git.remote", inverse_name="source_id", auto_join=True, copy=True, ) remote_count = fields.Integer( compute="_compute_remote_count", string="Remotes", ) remote_count_private = fields.Integer( compute="_compute_remote_count", string="Private Remotes", ) @api.depends("remote_ids", "remote_ids.enabled", "remote_ids.is_private") def _compute_remote_count(self): for record in self: remote_count = private_remote_count = 0 for remote in record.remote_ids: if not remote.enabled: continue if remote.is_private: private_remote_count += 1 remote_count += 1 record.update( { "remote_count": remote_count, "remote_count_private": private_remote_count, } ) @api.model_create_multi def create(self, vals_list): res = super().create(vals_list) # Update name res._compose_name() # Update related files and templates on create res._update_related_files_and_templates() return res def write(self, vals): res = super().write(vals) # Compose name if "name" in vals and not vals.get("name"): self._compose_name() # Update related files and templates on update self._update_related_files_and_templates() return res def unlink(self): """ Override to update related files and templates on unlink """ related_files = self.mapped("git_project_id").mapped("git_project_rel_ids") related_templates = self.mapped("git_project_id").mapped( "git_project_file_template_rel_ids" ) res = super().unlink() # Update related files and templates on unlink if related_files: related_files._save_to_file() if related_templates: related_templates._save_to_file_template() return res def _compose_name(self): """Compose name if not provided explicitly""" for source in self: if source.name: continue remote = fields.first(source.remote_ids) if not remote: source.name = _("Empty Source") continue remote_repo = remote.repo_id source.name = f"{remote_repo.owner_id.name}/{remote_repo.repo}" def _update_related_files_and_templates(self): # Update related files and templates on update related_files = self.mapped("git_project_id").mapped("git_project_rel_ids") if related_files: related_files._save_to_file() related_templates = self.mapped("git_project_id").mapped( "git_project_file_template_rel_ids" ) if related_templates: related_templates._save_to_file_template() # ------------------------------ # Reference mixin methods # ------------------------------ def _get_pre_populated_model_data(self): res = super()._get_pre_populated_model_data() res.update({"cx.tower.git.source": ["cx.tower.git.project", "git_project_id"]}) return res # ------------------------------ # YAML mixin methods # ------------------------------ def _get_fields_for_yaml(self): res = super()._get_fields_for_yaml() res += [ "name", "enabled", "sequence", "remote_ids", ] return res # ------------------------------ # Git Aggregator related methods # ------------------------------ def _git_aggregator_prepare_record(self): """Prepare json structure for git aggregator. Returns: Dict: Json structure for git aggregator """ self.ensure_one() # Prepare remotes, merges and target remotes = {} merges = [] target = None for remote in self.remote_ids: if remote.enabled: remotes.update({remote.name: remote._git_aggregator_prepare_url()}) merges.append( { "remote": remote.name, "ref": remote._git_aggregator_prepare_head(), } ) # Set target to first remote name if not target: target = remote.name # If no remotes, return empty dict if not remotes: return {} vals = { "remotes": remotes, "merges": merges, "target": target, } # Fetch only first commit if there is only one remote if len(remotes) == 1: vals.update({"defaults": {"depth": 1}}) return vals