This commit is contained in:
Xavier-Do 2025-03-14 17:22:09 +01:00
parent 9097aa4545
commit d0684c130a
10 changed files with 178 additions and 1 deletions

View File

@ -32,6 +32,7 @@
'templates/bundle.xml', 'templates/bundle.xml',
'templates/commit.xml', 'templates/commit.xml',
'templates/dashboard.xml', 'templates/dashboard.xml',
'templates/error_merge.xml',
'templates/frontend.xml', 'templates/frontend.xml',
'templates/git.xml', 'templates/git.xml',
'templates/nginx.xml', 'templates/nginx.xml',
@ -40,6 +41,7 @@
'views/branch_views.xml', 'views/branch_views.xml',
'views/build_error_link_views.xml', 'views/build_error_link_views.xml',
'views/build_error_views.xml', 'views/build_error_views.xml',
'views/build_error_merge_views.xml',
'views/build_views.xml', 'views/build_views.xml',
'views/bundle_views.xml', 'views/bundle_views.xml',
'views/codeowner_views.xml', 'views/codeowner_views.xml',

View File

@ -3,3 +3,4 @@
from . import frontend from . import frontend
from . import hook from . import hook
from . import badge from . import badge
from . import errors

View File

@ -0,0 +1,11 @@
from odoo.http import Controller, Response, request, route
class ErrorControlle(Controller):
@route('/runbot/error/merge/result/<filter_id>', type='http', auth='public', website=True)
def error_filter_result(self, filter_id=None, **kwargs):
merger = request.env['runbot.build.error.merge'].browse(int(filter_id))
if not merger:
return Response('Error merge not found', status=404)
return request.render('runbot.error_merge_result', {'merger': merger, 'results': merger._get_matching_groups()})

View File

@ -6,6 +6,7 @@ from . import build
from . import build_config from . import build_config
from . import build_config_codeowner from . import build_config_codeowner
from . import build_error from . import build_error
from . import build_error_merge
from . import bundle from . import bundle
from . import codeowner from . import codeowner
from . import commit from . import commit

View File

@ -595,6 +595,7 @@ class BuildErrorContent(models.Model):
_inherit = ('mail.thread', 'mail.activity.mixin', 'runbot.build.error.seen.mixin') _inherit = ('mail.thread', 'mail.activity.mixin', 'runbot.build.error.seen.mixin')
_rec_name = "id" _rec_name = "id"
active = fields.Boolean('Active', related='error_id.active')
error_id = fields.Many2one('runbot.build.error', 'Linked to', index=True, required=True) error_id = fields.Many2one('runbot.build.error', 'Linked to', index=True, required=True)
error_display_id = fields.Integer(compute='_compute_error_display_id', string="Error id") error_display_id = fields.Integer(compute='_compute_error_display_id', string="Error id")
content = fields.Text('Error message', required=True) content = fields.Text('Error message', required=True)

View File

@ -0,0 +1,73 @@
from odoo import models, fields, api
from odoo.osv import expression
class BuildErrorMerge(models.Model):
_name = 'runbot.build.error.merge'
_description = 'Error Merge patterns'
_inherit = ['mail.thread']
name = fields.Char('Name', required=True)
merge_filter_ids = fields.One2many('runbot.build.error.merge.filters', 'error_merge_id', 'Merge Lines')
description = fields.Char('Description', compute='_compute_description', store=True, tracking=True)
oneline_description = fields.Char('One Line Description', compute='_compute_description_online')
auto_merge = fields.Boolean('Auto Merge', default=False)
def _get_read_group_params(self, domain=None):
domain = domain if domain is not None else [('active', '=', True)]
filters_without_value = self.merge_filter_ids.filtered(lambda f: not f.value)
for filter in self.merge_filter_ids:
if filter.value:
domain = expression.AND([domain, [(filter.field_name, '=', filter.value)]])
else:
domain = expression.AND([domain, [(filter.field_name, '!=', False)]])
groups = filters_without_value.mapped('field_name')
assert groups
return (
domain,
groups,
)
def _get_matching_groups(self):
domain, groups = self._get_read_group_params()
result = self.env['runbot.build.error.content']._read_group(
domain,
groups,
['id:array_agg'],
[('error_id:count_distinct', '>', 1)]
)
return result
def _get_similar_domain(self, error_content):
if any(f.value and error_content[f.field_name] != f.value for f in self.merge_filter_ids):
return
domain = [(f.field_name, '==', f.value | error_content[f.field_name]) for f in self.merge_filter_ids if error_content[f.field_name]]
return domain
def action_open_candidates(self):
self.ensure_one()
return {
'name': 'Error Candidates',
'type': 'ir.actions.act_url',
'url': f"/runbot/error/merge/result/{self.id}",
}
@api.depends('merge_filter_ids.field_name', 'merge_filter_ids.value')
def _compute_description(self):
for record in self:
record.description = '\n'.join(f'{f.field_name}: {f.value or "Any"}' for f in record.merge_filter_ids)
@api.depends('description')
def _compute_description_online(self):
for record in self:
record.oneline_description = record.description.replace('\n', ', ')
class BuildErrorMergeFilter(models.Model):
_name = 'runbot.build.error.merge.filters'
_description = 'Error Merge patterns filters'
field = fields.Many2one('ir.model.fields', 'Field', domain=[('model_id.model', '=', 'runbot.build.error.content')], required=True, ondelete='cascade')
field_name = fields.Char('Field Name', related='field.name', store=True, readonly=True)
value = fields.Char('Value')
error_merge_id = fields.Many2one('runbot.build.error.merge', 'Error Merge', required=True)

View File

@ -151,4 +151,9 @@ access_runbot_build_stat_regex_wizard,access_runbot_build_stat_regex_wizard,mode
access_runbot_host_message,access_runbot_host_message,runbot.model_runbot_host_message,runbot.group_runbot_admin,1,0,0,0 access_runbot_host_message,access_runbot_host_message,runbot.model_runbot_host_message,runbot.group_runbot_admin,1,0,0,0
access_runbot_build_error_merge,access_runbot_build_error_merge,runbot.model_runbot_build_error_merge,base.group_user,1,0,0,0
access_runbot_build_error_merge_filters,access_runbot_build_error_merge_filters,runbot.model_runbot_build_error_merge_filters,base.group_user,1,0,0,0
access_runbot_build_error_merge,access_runbot_build_error_merge,runbot.model_runbot_build_error_merge,runbot.group_runbot_admin,1,1,1,1
access_runbot_build_error_merge_filters,access_runbot_build_error_merge_filters,runbot.model_runbot_build_error_merge_filters,runbot.group_runbot_admin,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
151
152
153
154
155
156
157
158
159

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="runbot.error_merge_result">
<t t-call='runbot.layout'>
<t t-set="title">Error Merge</t>
<h3>Error merge rule: <t t-esc="merger.name"/> (<t t-esc="len(results)"/>)</h3>
<div class="row">
<t t-foreach="results" t-as="result">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<span><t t-esc="'|'.join(result[:-1])"/></span><button class="btn btn-primary float-end" onclick="merge()">Merge (TODO)</button>
</div>
<div class="card-body">
<table class="table">
<t t-foreach="env['runbot.build.error.content'].browse(result[-1])" t-as="error_content">
<tr>
<td><t t-esc="error_content.error_id.active"/></td>
<td><a t-attf-href="/odoo/error_content/{{error_content.id}}"><t t-esc="error_content.id"/></a></td>
<td><a t-attf-href="/odoo/error/{{error_content.error_id.id}}"><t t-esc="error_content.error_id.id"/></a></td>
<td>
<span data-bs-toggle="collapse" t-attf-data-bs-target="#collapse{{error_content.id}}"><t t-esc="error_content.content.split('\n')[0]"/></span>
<pre t-attf-id="collapse{{error_content.id}}" class="collapse"><t t-esc="error_content.content"/></pre>
</td>
</tr>
</t>
</table>
</div>
</div>
</div>
</t>
</div>
</t>
</template>
</data>
</odoo>

View File

@ -0,0 +1,44 @@
<odoo>
<data>
<record id="build_error_merge_view_form" model="ir.ui.view">
<field name="name">runbot.build.error.merge.form</field>
<field name="model">runbot.build.error.merge</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_open_candidates" string="Open candidates" type="object" class="oe_highlight"/>
</header>
<sheet>
<group>
<field name="name"/>
<field name="auto_merge"/>
<field name="merge_filter_ids">
<list editable="bottom">
<field name="field"/>
<field name="value"/>
<field name="field_name"/>
</list>
</field>
</group>
</sheet>
</form>
</field>
</record>
<record id="build_error_merge_view_list" model="ir.ui.view">
<field name="name">runbot.build.error.merge.list</field>
<field name="model">runbot.build.error.merge</field>
<field name="arch" type="xml">
<list string="Error merge">
<field name="name"/>
<field name="auto_merge"/>
<field name="oneline_description"/>
</list>
</field>
</record>
<record id="open_build_error_merge_views" model="ir.actions.act_window">
<field name="name">Error merge</field>
<field name="res_model">runbot.build.error.merge</field>
<field name="view_mode">list,form</field>
</record>
</data>
</odoo>

View File

@ -38,6 +38,8 @@
<menuitem name="Errors" id="runbot_menu_build_error_tree" parent="runbot_menu_manage_errors" sequence="5" action="open_view_build_error_tree"/> <menuitem name="Errors" id="runbot_menu_build_error_tree" parent="runbot_menu_manage_errors" sequence="5" action="open_view_build_error_tree"/>
<menuitem name="Errors contents" id="runbot_menu_build_error_content_tree" parent="runbot_menu_manage_errors" sequence="10" action="open_view_build_error_content_tree"/> <menuitem name="Errors contents" id="runbot_menu_build_error_content_tree" parent="runbot_menu_manage_errors" sequence="10" action="open_view_build_error_content_tree"/>
<menuitem name="Errors Qualifying" id="runbot_menu_build_error_qualify_regex_tree" parent="runbot_menu_manage_errors" sequence="10" action="open_view_build_error_qualify_regex_tree"/> <menuitem name="Errors Qualifying" id="runbot_menu_build_error_qualify_regex_tree" parent="runbot_menu_manage_errors" sequence="10" action="open_view_build_error_qualify_regex_tree"/>
<menuitem name="Error regex" id="runbot_menu_error_regex_tree" parent="runbot_menu_manage_errors" sequence="20" action="open_view_error_regex"/>
<menuitem name="Error auto merge" id="runbot_menu_error_auto_merge" parent="runbot_menu_manage_errors" sequence="25" action="open_build_error_merge_views"/>
<menuitem name="Teams" id="runbot_menu_teams" parent="runbot_menu_root" sequence="1000"/> <menuitem name="Teams" id="runbot_menu_teams" parent="runbot_menu_root" sequence="1000"/>
<menuitem name="Teams" id="runbot_menu_team_tree" parent="runbot_menu_teams" sequence="30" action="open_view_runbot_team"/> <menuitem name="Teams" id="runbot_menu_team_tree" parent="runbot_menu_teams" sequence="30" action="open_view_runbot_team"/>
@ -59,7 +61,7 @@
<menuitem id="runbot_menu_upgrade_regex_tree" parent="menu_runbot_settings" sequence="60" action="open_view_upgrade_regex_tree"/> <menuitem id="runbot_menu_upgrade_regex_tree" parent="menu_runbot_settings" sequence="60" action="open_view_upgrade_regex_tree"/>
<menuitem name="Stats Regexes" id="runbot_menu_stat" parent="menu_runbot_settings" sequence="70" action="open_view_stat_regex_tree"/> <menuitem name="Stats Regexes" id="runbot_menu_stat" parent="menu_runbot_settings" sequence="70" action="open_view_stat_regex_tree"/>
<menuitem name="Stat Regex Wizard" id="runbot_menu_stat_wizard" parent="menu_runbot_settings" sequence="80" action="runbot_stat_regex_wizard_action"/> <menuitem name="Stat Regex Wizard" id="runbot_menu_stat_wizard" parent="menu_runbot_settings" sequence="80" action="runbot_stat_regex_wizard_action"/>
<menuitem name="Error regex" id="runbot_menu_error_regex_tree" parent="menu_runbot_settings" sequence="20" action="open_view_error_regex"/>
<menuitem name="Technical" id="runbot_menu_technical" parent="menu_runbot_settings" sequence="10000" groups="base.group_system"/> <menuitem name="Technical" id="runbot_menu_technical" parent="menu_runbot_settings" sequence="10000" groups="base.group_system"/>
<menuitem id="runbot_menu_ir_cron_act" action="base.ir_cron_act" parent="runbot_menu_technical"/> <menuitem id="runbot_menu_ir_cron_act" action="base.ir_cron_act" parent="runbot_menu_technical"/>