mirror of
https://github.com/odoo/runbot.git
synced 2025-03-15 23:45:44 +07:00
[IMP] runbot: introduce matrix for upgrade
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.
This commit is contained in:
parent
1ed9278d6e
commit
fb7b8480b8
@ -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',
|
||||
|
@ -189,6 +189,9 @@ class ConfigStep(models.Model):
|
||||
upgrade_from_all_intermediate_version = fields.Boolean() # 13.2 # 13.1
|
||||
upgrade_from_version_ids = fields.Many2many('runbot.version', relation='runbot_upgrade_from_version_ids', string='Forced version to use as source (cartesian with target)')
|
||||
|
||||
# wip replace previous field by matrix
|
||||
upgrade_matrix_id = fields.Many2one('runbot.upgrade.matrix', 'Upgrade matrix', tracking=True)
|
||||
|
||||
upgrade_flat = fields.Boolean("Flat", help="Take all decisions in on build")
|
||||
|
||||
upgrade_config_id = fields.Many2one('runbot.build.config',string='Upgrade Config', tracking=True, index=True)
|
||||
|
@ -204,6 +204,9 @@ class Bundle(models.Model):
|
||||
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):
|
||||
|
@ -1,5 +1,5 @@
|
||||
import re
|
||||
from odoo import models, fields
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
@ -69,3 +69,138 @@ class BuildResult(models.Model):
|
||||
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()
|
||||
|
||||
matrix_summary = fields.Text('Matrix summary', compute='_compute_matrix_summary', store=True, tracking=True)
|
||||
|
||||
@api.depends('entry_ids.from_version_id', 'entry_ids.to_version_id', 'entry_ids.enabled')
|
||||
def _compute_matrix_summary(self):
|
||||
for matrix in self:
|
||||
matrix.matrix_summary = ''
|
||||
versions = {}
|
||||
lines = []
|
||||
for entry in self.entry_ids.sorted(lambda e: (e.to_version_id.number, e.from_version_id.number)):
|
||||
if entry.enabled:
|
||||
versions.setdefault(entry.to_version_id, []).append(entry.from_version_id.number)
|
||||
else:
|
||||
versions.setdefault(entry.to_version_id, []).append('-')
|
||||
for to_version, from_versions in versions.items():
|
||||
from_versions_string = ', '.join(sorted(from_versions))
|
||||
lines.append(f'{to_version.number} - ({from_versions_string})')
|
||||
matrix.matrix_summary = '\n'.join(lines)
|
||||
print('recompute')
|
||||
print(matrix.matrix_summary)
|
||||
|
||||
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', string="To version number", store=True)
|
||||
to_version_number = fields.Char(related='to_version_id.number', string="From version 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
|
||||
|
@ -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
|
||||
|
||||
|
|
@ -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>
|
||||
|
||||
|
||||
|
@ -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')
|
||||
}
|
||||
@ -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
runbot/static/src/js/fields/fields.xml
Normal file
28
runbot/static/src/js/fields/fields.xml
Normal file
@ -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>
|
@ -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"/>
|
||||
@ -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"/>
|
||||
|
73
runbot/views/upgrade_matrix_views.xml
Normal file
73
runbot/views/upgrade_matrix_views.xml
Normal file
@ -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>
|
Loading…
Reference in New Issue
Block a user