Skip to content

Commit

Permalink
[IMP] runbot: introduce matrix for upgrade
Browse files Browse the repository at this point in the history
The initial way to configure upgrades was working fine and automatically
but does not alway match the reality of what should be supported.

Saying, upgrade from the last main version and last intermediate
(for 18, from 17.0 and 17.4) we may also want to test from 17.2 because
for some reason there are many people in 17.2 taht will need to upgrade
in 18.0. But the transition 17.2->17.3 doesn't make sense anymore
after a while since people will go in 18 imediatly at some point.

This matrix can be generated automatically from the parameters and new
versions should appear automatically, but it is possible to tweak the
values on demand.
  • Loading branch information
Xavier-Do committed Feb 21, 2025
1 parent 48cd3a7 commit 53cbb5c
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 5 deletions.
1 change: 1 addition & 0 deletions runbot/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
'views/res_config_settings_views.xml',
'views/stat_views.xml',
'views/upgrade.xml',
'views/upgrade_matrix_views.xml',
'views/warning_views.xml',
'views/custom_trigger_wizard_views.xml',
'wizards/stat_regex_wizard_views.xml',
Expand Down
3 changes: 3 additions & 0 deletions runbot/models/bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ def create(self, values_list):
if records.is_base:
model = self.browse()
model.env.registry.clear_cache()
matrix = self.env['runbot.upgrade.matrix'].search([('project_id', '=', record.project_id.id)], limit=1)
if matrix:
matrix._update_matrix_entries()
elif record.project_id.tmp_prefix and record.name.startswith(record.project_id.tmp_prefix):
record['no_build'] = True
elif record.project_id.staging_prefix and record.name.startswith(record.project_id.staging_prefix):
Expand Down
118 changes: 117 additions & 1 deletion runbot/models/upgrade.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import re
from odoo import models, fields
from odoo import models, fields, api
from odoo.exceptions import UserError


Expand Down Expand Up @@ -69,3 +69,119 @@ def _parse_upgrade_errors(self):
return res
else:
raise UserError('Nothing found here')


class UpgradeMatrix(models.Model):
_name = 'runbot.upgrade.matrix'
_description = 'Upgrade matrix'
_inherit = ['mail.thread', 'mail.activity.mixin']

name = fields.Char('Name', required=True)
project_id = fields.Many2one('runbot.project', 'Project', required=True)
entry_ids = fields.One2many('runbot.upgrade.matrix.entry', 'matrix_id', 'Entries')
auto_update = fields.Boolean('Auto update', default=True, help="Automatically update the matrix entries enabled state when new versions are created")

# fields defining default behaviour to generate the matix,
upgrade_to_major_versions = fields.Boolean()
upgrade_to_all_versions = fields.Boolean()
upgrade_from_previous_major_version = fields.Boolean()
upgrade_from_last_intermediate_version = fields.Boolean()
upgrade_from_all_intermediate_version = fields.Boolean()


def update_matrix_entries(self):
for metric in self:
metric._update_matrix_entries()

def _update_matrix_entries(self):
self.ensure_one()
existing_entries = self.with_context(active_test=False).entry_ids
entries_per_versions = {(e.from_version_id.id, e.to_version_id.id): e for e in existing_entries}

# get all versions
versions = self.env['runbot.bundle'].search([('project_id', '=', self.project_id.id), ('is_base', '=', True)]).mapped('version_id')
for target_version in versions:
compatible_versions = target_version.intermediate_version_ids | target_version.previous_major_version_id
for source_version in versions:
if (source_version.id, target_version.id) not in entries_per_versions:
if target_version == source_version:
continue
if source_version not in compatible_versions:
continue
self.env['runbot.upgrade.matrix.entry'].create({
'matrix_id': self.id,
'from_version_id': source_version.id,
'to_version_id': target_version.id
})
if self.auto_update:
existing_entries._update_enabled()

def reset_matrix_enabled(self):
for matrix in self:
matrix.entry_ids._update_enabled(force=True)


class UpgradeMatrixEntry(models.Model):
_name = 'runbot.upgrade.matrix.entry'
_description = 'Upgrade matrix entry'
_order = 'to_version_number desc, from_version_number desc, id desc'

matrix_id = fields.Many2one('runbot.upgrade.matrix', 'Matrix', required=True, ondelete='cascade')
from_version_id = fields.Many2one('runbot.version', 'From version', required=True, ondelete='cascade')
to_version_id = fields.Many2one('runbot.version', 'To version', required=True, ondelete='cascade')
from_version_number = fields.Char(related='from_version_id.number', store=True)
to_version_number = fields.Char(related='to_version_id.number', store=True)
target_bundle_id = fields.Many2one('runbot.bundle', compute='_compute_target_bundle_id', store=True)
enabled = fields.Boolean('Enabled', default=True)
active = fields.Boolean('Active', compute='_compute_active', store=True)
manually_edited = fields.Boolean('Manually edited', default=False)

_sql_constraints = [
('unique_matrix_entry', 'unique(matrix_id, from_version_id, to_version_id)', 'Matrix entry already exists')
]

@api.onchange('enabled')
def _onchange_enabled(self):
self.manually_edited = True

def create(self, vals):
entry = super().create(vals)
entry._update_enabled()

@api.depends('to_version_id', 'matrix_id.project_id')
def _compute_target_bundle_id(self):
for entry in self:
entry.target_bundle_id = entry.to_version_id.with_context(project_id=entry.matrix_id.project_id.id).base_bundle_id

@api.depends('target_bundle_id.sticky')
def _compute_active(self):
for entry in self:
entry.active = entry.target_bundle_id.sticky

def _update_enabled(self, force=False):
for entry in self:
if entry.manually_edited and not force:
continue
entry.manually_edited = False

matrix = entry.matrix_id
to_enabled = False
from_enabled = False

if matrix.upgrade_to_all_versions:
to_enabled = True

elif matrix.upgrade_to_major_versions and entry.to_version_id.is_major:
to_enabled = True

if not to_enabled:
entry.enabled = False
continue

if matrix.upgrade_from_all_intermediate_version:
from_enabled = True
elif matrix.upgrade_from_last_intermediate_version and entry.to_version_id.intermediate_version_ids and entry.from_version_id == entry.to_version_id.intermediate_version_ids[-1]:
from_enabled = True
elif matrix.upgrade_from_previous_major_version and entry.from_version_id == entry.to_version_id.previous_major_version_id:
from_enabled = True
entry.enabled = to_enabled and from_enabled
10 changes: 10 additions & 0 deletions runbot/security/ir.model.access.csv
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,18 @@ access_runbot_upgrade_regex_user,access_runbot_upgrade_regex_user,runbot.model_r
access_runbot_upgrade_regex_admin,access_runbot_upgrade_regex_admin,runbot.model_runbot_upgrade_regex,runbot.group_runbot_admin,1,1,1,1

access_runbot_upgrade_exception_user,access_runbot_upgrade_exception_user,runbot.model_runbot_upgrade_exception,runbot.group_user,1,0,0,0
access_runbot_upgrade_exception_manager,access_runbot_upgrade_exception_manager,runbot.model_runbot_upgrade_exception,runbot.group_runbot_upgrade_manager,1,1,1,1
access_runbot_upgrade_exception_admin,access_runbot_upgrade_exception_admin,runbot.model_runbot_upgrade_exception,runbot.group_runbot_admin,1,1,1,1

access_runbot_upgrade_matrix_user,access_runbot_upgrade_matrix_user,runbot.model_runbot_upgrade_matrix,runbot.group_user,1,0,0,0
access_runbot_upgrade_matrix_manager,access_runbot_upgrade_matrix_manager,runbot.model_runbot_upgrade_matrix,runbot.group_runbot_upgrade_manager,1,1,1,1
access_runbot_upgrade_matrix_admin,access_runbot_upgrade_matrix_admin,runbot.model_runbot_upgrade_matrix,runbot.group_runbot_admin,1,1,1,1

access_runbot_upgrade_matrix_entry_user,access_runbot_upgrade_matrix_entry_user,runbot.model_runbot_upgrade_matrix_entry,runbot.group_user,1,0,0,0
access_runbot_upgrade_matrix_entry_manager,access_runbot_upgrade_matrix_entry_manager,runbot.model_runbot_upgrade_matrix_entry,runbot.group_runbot_upgrade_manager,1,1,1,1
access_runbot_upgrade_matrix_entry_admin,access_runbot_upgrade_matrix_entry_admin,runbot.model_runbot_upgrade_matrix_entry,runbot.group_runbot_admin,1,1,1,1


access_runbot_dockerfile_user,access_runbot_dockerfile_user,runbot.model_runbot_dockerfile,runbot.group_user,1,0,0,0
access_runbot_dockerfile_admin,access_runbot_dockerfile_admin,runbot.model_runbot_dockerfile,runbot.group_runbot_admin,1,1,1,1

Expand Down
7 changes: 6 additions & 1 deletion runbot/security/runbot_security.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,16 @@
<field name="category_id" ref="module_project"/>
</record>

<record id="group_runbot_upgrade_manager" model="res.groups">
<field name="name">Upgrade manager</field>
<field name="category_id" ref="module_project"/>
</record>

<record id="group_runbot_admin" model="res.groups">
<field name="name">Runbot administrator</field>
<field name="category_id" ref="module_project"/>
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
<field name="implied_ids" eval="[(4, ref('runbot.group_runbot_advanced_user')), (4, ref('runbot.group_user')), (4, ref('runbot.group_build_config_administrator'))]"/>
<field name="implied_ids" eval="[(4, ref('runbot.group_runbot_advanced_user')), (4, ref('runbot.group_user')), (4, ref('runbot.group_build_config_administrator')), (4, ref('runbot.group_runbot_upgrade_manager'))]"/>
</record>


Expand Down
59 changes: 58 additions & 1 deletion runbot/static/src/js/fields/fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ import { useDynamicPlaceholder } from "@web/views/fields/dynamic_placeholder_hoo
import { standardFieldProps } from "@web/views/fields/standard_field_props";
import { useInputField } from "@web/views/fields/input_field_hook";

import { useRef, xml, Component } from "@odoo/owl";
import { useRef, xml, Component, onWillRender } from "@odoo/owl";
import { useAutoresize } from "@web/core/utils/autoresize";
import { getFormattedValue } from "@web/views/utils";

import { UrlField } from "@web/views/fields/url/url_field";

import { X2ManyField } from "@web/views/fields/x2many/x2many_field";
import { formatX2many } from "@web/views/fields/formatters";

import { BooleanToggleField } from "@web/views/fields/boolean_toggle/boolean_toggle_field";


function stringify(obj) {
return JSON.stringify(obj, null, '\t')
}
Expand Down Expand Up @@ -170,3 +176,54 @@ registry.category("fields").add("pull_request_url", {
//}
//
//registry.category("fields").add("github_team", GithubTeamWidget);




export class Matrixx2ManyField extends X2ManyField {
static template = 'runbot.Matrixx2ManyField';
static props = { ...standardFieldProps };

static components = { BooleanToggleField };

setup() {

onWillRender(() => {
this.initValues();
})
}

getEntry(from, to) {
const entry = this.entry_per_version[[from, to]];
return entry
}

initValues() {
this.data = this.props.record.data[this.props.name]
this.to_versions = [];
this.from_versions = [];
this.entry_per_version = {};
this.data.records.forEach((record) => {
if (!this.to_versions.includes(record.data.to_version_number)) {
this.to_versions.push(record.data.to_version_number)
}
if (!this.from_versions.includes(record.data.from_version_number)) {
this.from_versions.push(record.data.from_version_number)
}
this.entry_per_version[[record.data.from_version_number, record.data.to_version_number]] = record;
});
console.log(this.to_versions)
this.to_versions.sort()
this.to_versions.reverse()
this.from_versions.sort()
this.from_versions.reverse()
}
}

export const matrixx2ManyField = {
component: Matrixx2ManyField,
useSubView: false,
};


registry.category("fields").add("version_matrix", matrixx2ManyField);
28 changes: 28 additions & 0 deletions runbot/static/src/js/fields/fields.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">

<t t-name="runbot.Matrixx2ManyField">
<table class="table table-sm">
<thead>
<tr>
<th></th>
<th class="thead-dark" t-foreach="to_versions" t-as="to_version" t-esc="to_version" t-key="to_version"/>
</tr>
</thead>
<tbody>
<tr t-foreach="from_versions" t-as="from_version" t-key="from_version">
<th class="thead-dark" t-esc="from_version"/>
<t t-foreach="to_versions" t-as="to_version" t-key="to_version">
<t t-set="entry" t-value="getEntry(from_version, to_version)"/>
<td t-att-style="entry and entry.data.manually_edited ? 'background-color: #CCC' : ''">
<span t-if="entry" >
<BooleanToggleField name="'enabled'" record="entry"/>
</span>
</td>
</t>
</tr>
</tbody>
</table>
</t>

</templates>
6 changes: 4 additions & 2 deletions runbot/views/menus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
<menuitem id="runbot_menu_job_config_tree" parent="runbot_menu_configs" sequence="10" action="open_view_job_config_tree"/>
<menuitem id="runbot_menu_job_tree" parent="runbot_menu_configs" sequence="20" action="open_view_job_tree"/>

<menuitem id="runbot_menu_upgrade_exceptions_tree" parent="runbot_menu_root" sequence="700" action="open_view_upgrade_exception_tree"/>
<menuitem name="Upgrades" id="runbot_menu_upgrade" parent="runbot_menu_root" sequence="700"/>
<menuitem id="runbot_menu_upgrade_exceptions_tree" parent="runbot_menu_upgrade" sequence="10" action="open_view_upgrade_exception_tree"/>
<menuitem id="runbot_menu_upgrade_regex_tree" parent="runbot_menu_upgrade" sequence="20" action="open_view_upgrade_regex_tree"/>
<menuitem name="Upgrade Matrix" id="runbot_menu_upgrade_matrix_tree" parent="runbot_menu_upgrade" sequence="30" action="open_view_runbot_upgrade_matrix_tree"/>

<menuitem name="Docker" id="menu_dockerfile" parent="runbot_menu_root" sequence="800"/>
<menuitem name="Docker files" id="menu_dockerfiles" parent="menu_dockerfile" action="open_view_dockerfile_tree" sequence="801"/>
Expand Down Expand Up @@ -56,7 +59,6 @@
<menuitem id="runbot_menu_repos" parent="menu_runbot_settings" sequence="30" action="runbot_repos_action"/>
<menuitem id="runbot_menu_remotes" parent="menu_runbot_settings" sequence="40" action="runbot_remotes_action"/>
<menuitem id="runbot_menu_trigger_category" parent="menu_runbot_settings" sequence="50" action="runbot_triggers_category_action"/>
<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="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"/>
Expand Down
73 changes: 73 additions & 0 deletions runbot/views/upgrade_matrix_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<odoo>
<data>
<record id="runbot_upgrade_matrix_form" model="ir.ui.view">
<field name="name">runbot.upgrade.matrix.form</field>
<field name="model">runbot.upgrade.matrix</field>
<field name="arch" type="xml">
<form>
<sheet>
<header>
<button name="update_matrix_entries" string="Create missing entries" type="object" class="oe_highlight"/>
<button name="reset_matrix_enabled" string="Reset matrix entries default enabled state" type="object" class="oe_highlight"/>
</header>
<group >
<group >
<field name="name"/>
<field name="project_id"/>
<field name="auto_update"/>
</group >
<group >
<field name="upgrade_to_major_versions"/>
<field name="upgrade_to_all_versions"/>
<field name="upgrade_from_previous_major_version"/>
<field name="upgrade_from_last_intermediate_version"/>
<field name="upgrade_from_all_intermediate_version"/>

</group >
</group >
<group >

<field name="entry_ids" widget="version_matrix">
<list>
<field name="from_version_id"/>
<field name="to_version_id"/>
<field name="to_version_number"/>
<field name="from_version_number"/>
<field name="enabled" widget="boolean_toggle"/>
<field name="manually_edited"/>
</list>
</field>

<field name="entry_ids">
<list>
<field name="from_version_id"/>
<field name="to_version_id"/>
<field name="enabled" widget="boolean_toggle"/>
</list>
</field>

</group>
</sheet>
<chatter/>
</form>
</field>
</record>

<record id="runbot_upgrade_matrix_tree" model="ir.ui.view">
<field name="name">runbot.upgrade.matrix.tree</field>
<field name="model">runbot.upgrade.matrix</field>
<field name="arch" type="xml">
<list string="Upgrade matrix">
<field name="name" optional="show" readonly="1"/>
<field name="project_id" optional="hide" readonly="1"/>
</list>
</field>
</record>

<record id="open_view_runbot_upgrade_matrix_tree" model="ir.actions.act_window">
<field name="name">Upgrade matrix</field>
<field name="res_model">runbot.upgrade.matrix</field>
<field name="view_mode">list,form</field>
</record>
</data>
</odoo>

0 comments on commit 53cbb5c

Please sign in to comment.