[IMP] runbot: match prs with build errors automatically

Automatically link the pr with the build error if it contains anything
along the lines of `runbot.*error.*{id}`.
Among the changes:
 - Add a path for the build error act_window so that it include
   runbot-error
 - Fix the widget for `pr_body` in branch form view to use `text`
   instead of char as they are most likely multi line string.
This commit is contained in:
William Braeckman 2025-02-26 13:47:08 +01:00
parent 31b101233d
commit fb9861ce26
4 changed files with 63 additions and 1 deletions

View File

@ -87,6 +87,28 @@ class Branch(models.Model):
reference_name = f'{forced_version.name}---{reference_name}' reference_name = f'{forced_version.name}---{reference_name}'
branch.reference_name = reference_name branch.reference_name = reference_name
def _match_errors_from_body(self):
"""
Checks the pr_body for any reference to runbot errors and links the branch
to those errors if they exist.
"""
self.ensure_one()
if not self.pr_body:
return
pr_body_regex = r'^.*runbot.*error[^\d]*(\d+)[ \S]*\r?$'
error_ids = re.findall(
pr_body_regex,
self.pr_body,
re.IGNORECASE | re.MULTILINE
)
# We need to check if they exist and search does nothing if error_ids is empty
errors = self.env['runbot.build.error'].sudo().search([
('id', 'in', error_ids), ('fixing_pr_id', '=', False),
])
errors.fixing_pr_id = self
for error in errors:
error.message_post(body='Fixing pr automatically set through PR message')
def _update_branch_infos(self, pull_info=None): def _update_branch_infos(self, pull_info=None):
"""compute branch_url, pull_head_name and target_branch_name based on name""" """compute branch_url, pull_head_name and target_branch_name based on name"""
name_to_remote = {} name_to_remote = {}
@ -131,6 +153,7 @@ class Branch(models.Model):
owner, repo_name = pull_head_repo_name.split('/') owner, repo_name = pull_head_repo_name.split('/')
name_to_remote[pull_head_repo_name] = self.env['runbot.remote'].search([('owner', '=', owner), ('repo_name', '=', repo_name)], limit=1) name_to_remote[pull_head_repo_name] = self.env['runbot.remote'].search([('owner', '=', owner), ('repo_name', '=', repo_name)], limit=1)
branch.pull_head_remote_id = name_to_remote[pull_head_repo_name] branch.pull_head_remote_id = name_to_remote[pull_head_repo_name]
branch._match_errors_from_body()
except (TypeError, AttributeError): except (TypeError, AttributeError):
_logger.exception('Error for pr %s using pull_info %s', branch.name, pi) _logger.exception('Error for pr %s using pull_info %s', branch.name, pi)
raise raise

View File

@ -65,6 +65,44 @@ class TestBranch(RunbotCase):
self.Branch.search([('dname', '=', branch.dname)]), self.Branch.search([('dname', '=', branch.dname)]),
) )
def test_automatic_match_with_error(self):
self.env['runbot.build.error'].search([]).active = False
errors = self.env['runbot.build.error'].create([{} for _ in range(5)])
branch = self.Branch.create({
'name': '18.0-test',
'remote_id': self.remote_server.id,
'is_pr': True,
})
# Does not crash without pr_body or nothing on pr_body
branch._match_errors_from_body()
branch.pr_body = ''
branch._match_errors_from_body()
# Does not link unknown errors
branch.pr_body = f'This is an error {errors[0].id}'
branch._match_errors_from_body()
self.assertFalse(errors.fixing_pr_id)
# Test multiple formats
branch.pr_body = f"""
Runbot error {errors[0].id}
runbot error {errors[1].id}
runbot-error-{errors[2].id}
https://domain.com/odoo/runbot.build.error/{errors[3].id}
https://domain.com/odoo/runbot-error/{errors[4].id}/
"""
branch._match_errors_from_body()
self.assertTrue(all(e.fixing_pr_id == branch for e in errors))
# Test that it does not reassign
errors.fixing_pr_id = False
branch.pr_body = f'Runbot ERror {errors[0].id}'
other_branch = self.Branch.create({
'name': 'other-branch',
'remote_id': self.remote_server.id,
'is_pr': True,
})
errors[0].fixing_pr_id = other_branch
branch._match_errors_from_body()
self.assertEqual(errors[0].fixing_pr_id, other_branch)
class TestBranchRelations(RunbotCase): class TestBranchRelations(RunbotCase):
def setUp(self): def setUp(self):

View File

@ -21,7 +21,7 @@
<field name="head"/> <field name="head"/>
<field name="alive"/> <field name="alive"/>
<field name="pr_title"/> <field name="pr_title"/>
<field name="pr_body"/> <field name="pr_body" widget="text"/>
</group> </group>
</sheet> </sheet>
</form> </form>

View File

@ -468,6 +468,7 @@
<field name="res_model">runbot.build.error</field> <field name="res_model">runbot.build.error</field>
<field name="view_mode">list,form</field> <field name="view_mode">list,form</field>
<field name="context">{'search_default_not_fixed_errors': True, 'active_test': False}</field> <field name="context">{'search_default_not_fixed_errors': True, 'active_test': False}</field>
<field name="path">runbot-error</field>
</record> </record>
<record id="open_view_build_error_content_tree" model="ir.actions.act_window"> <record id="open_view_build_error_content_tree" model="ir.actions.act_window">