From 45104b635f99cf4c61a6f080eae9772ef4d167e0 Mon Sep 17 00:00:00 2001 From: Xavier-Do Date: Tue, 20 Jun 2023 09:47:20 +0200 Subject: [PATCH] [REL] adapt for 16.0 --- runbot/models/build.py | 12 +++---- runbot/models/build_config.py | 60 +++++++++++++++++--------------- runbot/models/build_error.py | 4 +-- runbot/models/bundle.py | 2 ++ runbot/models/database.py | 15 ++++---- runbot/models/dockerfile.py | 4 +-- runbot/models/host.py | 11 +++--- runbot/models/project.py | 4 +-- runbot/models/repo.py | 4 +-- runbot/models/runbot.py | 4 ++- runbot/models/team.py | 17 ++++----- runbot/tests/common.py | 7 ++-- runbot/tests/test_branch.py | 4 +-- runbot/tests/test_build.py | 10 +++--- runbot/tests/test_build_error.py | 3 +- runbot/tests/test_build_stat.py | 6 ++-- runbot/tests/test_commit.py | 11 +++--- runbot/tests/test_repo.py | 8 ++--- runbot/tests/test_upgrade.py | 6 ++-- runbot/views/config_views.xml | 4 +-- runbot/views/error_log_views.xml | 2 +- runbot_populate/models/runbot.py | 4 +-- 22 files changed, 108 insertions(+), 94 deletions(-) diff --git a/runbot/models/build.py b/runbot/models/build.py index 468ea9e1..336741ea 100644 --- a/runbot/models/build.py +++ b/runbot/models/build.py @@ -108,16 +108,15 @@ class BuildParameters(models.Model): params.commit_ids = params.commit_link_ids.commit_id @api.model_create_multi - def create(self, values_list): + def create(self, vals_list): records = self.browse() - for values in values_list: - params = self.new(values) + for vals in vals_list: + params = self.new(vals) record = self._find_existing(params.fingerprint) if record: records |= record else: - values = self._convert_to_write(params._cache) - records |= super().create(values) + records |= super().create(self._convert_to_write(params._cache)) return records def _find_existing(self, fingerprint): @@ -214,7 +213,7 @@ class BuildResult(models.Model): # -> build_link ? parent_id = fields.Many2one('runbot.build', 'Parent Build', index=True) - parent_path = fields.Char('Parent path', index=True) + parent_path = fields.Char('Parent path', index=True, unaccent=False) top_parent = fields.Many2one('runbot.build', compute='_compute_top_parent') ancestors = fields.Many2many('runbot.build', compute='_compute_ancestors') # should we add a has children stored boolean? @@ -815,6 +814,7 @@ class BuildResult(models.Model): ro_volumes[f'/home/{user}/.odoorc'] = self._path('.odoorc') kwargs.pop('build_dir', False) # todo check python steps build_dir = self._path() + self.env.flush_all() def start_docker(): docker_run(cmd=cmd, build_dir=build_dir, ro_volumes=ro_volumes, **kwargs) return start_docker diff --git a/runbot/models/build_config.py b/runbot/models/build_config.py index 83ffa90c..5f70e5f5 100644 --- a/runbot/models/build_config.py +++ b/runbot/models/build_config.py @@ -59,9 +59,9 @@ class Config(models.Model): group = fields.Many2one('runbot.build.config', 'Configuration group', help="Group of config's and config steps") group_name = fields.Char('Group name', related='group.name') - @api.model_create_single - def create(self, values): - res = super(Config, self).create(values) + @api.model_create_multi + def create(self, vals_list): + res = super(Config, self).create(vals_list) res._check_step_ids_order() return res @@ -85,19 +85,21 @@ class Config(models.Model): return [ordered_step.step_id for ordered_step in self.step_order_ids.sorted('sequence')] def _check_step_ids_order(self): - install_job = False - step_ids = self.step_ids() - for step in step_ids: - if step.job_type == 'install_odoo': - install_job = True - if step.job_type == 'run_odoo': - if step != step_ids[-1]: - raise UserError('Jobs of type run_odoo should be the last one') - if not install_job: - raise UserError('Jobs of type run_odoo should be preceded by a job of type install_odoo') - self._check_recustion() + for record in self: + install_job = False + step_ids = record.step_ids() + for step in step_ids: + if step.job_type == 'install_odoo': + install_job = True + if step.job_type == 'run_odoo': + if step != step_ids[-1]: + raise UserError('Jobs of type run_odoo should be the last one') + if not install_job: + raise UserError('Jobs of type run_odoo should be preceded by a job of type install_odoo') + record._check_recustion() def _check_recustion(self, visited=None): + self.ensure_one() visited = visited or [] recursion = False if self in visited: @@ -236,10 +238,11 @@ class ConfigStep(models.Model): copy._write({'protected': False}) return copy - @api.model_create_single - def create(self, values): - self._check(values) - return super(ConfigStep, self).create(values) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + self._check(vals) + return super().create(vals_list) def write(self, values): self._check(values) @@ -398,8 +401,6 @@ class ConfigStep(models.Model): docker_name = build._get_docker_name() build_port = build.port - self.env.cr.commit() # commit before docker run to be 100% sure that db state is consistent with dockers - self.invalidate_cache() self.env['runbot.runbot']._reload_nginx() return dict(cmd=cmd, log_path=log_path, container_name=docker_name, exposed_ports=[build_port, build_port + 1], ro_volumes=exports, env_variables=env_variables) @@ -1146,12 +1147,13 @@ class ConfigStepOrder(models.Model): def _onchange_step_id(self): self.sequence = self.step_id.default_sequence - @api.model_create_single - def create(self, values): - if 'sequence' not in values and values.get('step_id'): - values['sequence'] = self.env['runbot.build.config.step'].browse(values.get('step_id')).default_sequence - if self.pool._init: # do not duplicate entry on install - existing = self.search([('sequence', '=', values.get('sequence')), ('config_id', '=', values.get('config_id')), ('step_id', '=', values.get('step_id'))]) - if existing: - return - return super(ConfigStepOrder, self).create(values) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if 'sequence' not in vals and vals.get('step_id'): + vals['sequence'] = self.env['runbot.build.config.step'].browse(vals.get('step_id')).default_sequence + if self.pool._init: # do not duplicate entry on install + existing = self.search([('sequence', '=', vals.get('sequence')), ('config_id', '=', vals.get('config_id')), ('step_id', '=', vals.get('step_id'))]) + if existing: + return + return super().create(vals_list) diff --git a/runbot/models/build_error.py b/runbot/models/build_error.py index dcd4ec94..063b0061 100644 --- a/runbot/models/build_error.py +++ b/runbot/models/build_error.py @@ -47,9 +47,9 @@ class BuildError(models.Model): children_build_ids = fields.Many2many('runbot.build', compute='_compute_children_build_ids', string='Children builds') error_history_ids = fields.Many2many('runbot.build.error', compute='_compute_error_history_ids', string='Old errors', context={'active_test': False}) first_seen_build_id = fields.Many2one('runbot.build', compute='_compute_first_seen_build_id', string='First Seen build') - first_seen_date = fields.Datetime(string='First Seen Date', related='first_seen_build_id.create_date') + first_seen_date = fields.Datetime(string='First Seen Date', related='first_seen_build_id.create_date', depends=['first_seen_build_id']) last_seen_build_id = fields.Many2one('runbot.build', compute='_compute_last_seen_build_id', string='Last Seen build', store=True) - last_seen_date = fields.Datetime(string='Last Seen Date', related='last_seen_build_id.create_date', store=True) + last_seen_date = fields.Datetime(string='Last Seen Date', related='last_seen_build_id.create_date', store=True, depends=['last_seen_build_id']) test_tags = fields.Char(string='Test tags', help="Comma separated list of test_tags to use to reproduce/remove this error", tracking=True) @api.constrains('test_tags') diff --git a/runbot/models/bundle.py b/runbot/models/bundle.py index 4c86d3cf..3355e5b8 100644 --- a/runbot/models/bundle.py +++ b/runbot/models/bundle.py @@ -163,6 +163,8 @@ class Bundle(models.Model): @api.depends_context('category_id') def _compute_last_done_batch(self): if self: + self.env['runbot.batch'].flush_model() + self.env['runbot.bundle'].flush_model() # self.env['runbot.batch'].flush() for bundle in self: bundle.last_done_batch = False diff --git a/runbot/models/database.py b/runbot/models/database.py index 98948f1a..4d7f1016 100644 --- a/runbot/models/database.py +++ b/runbot/models/database.py @@ -15,9 +15,12 @@ class Database(models.Model): for record in self: record.db_suffix = record.name.replace('%s-' % record.build_id.dest, '') - @api.model_create_single - def create(self, values): - res = self.search([('name', '=', values['name']), ('build_id', '=', values['build_id'])]) - if res: - return res - return super().create(values) + @api.model_create_multi + def create(self, vals_list): + records = self.browse() + for vals in vals_list: + res = self.search([('name', '=', vals['name']), ('build_id', '=', vals['build_id'])]) + if res: + records |= res + else: + records |= super().create(vals) diff --git a/runbot/models/dockerfile.py b/runbot/models/dockerfile.py index 003c0b10..c02ad3e0 100644 --- a/runbot/models/dockerfile.py +++ b/runbot/models/dockerfile.py @@ -1,7 +1,7 @@ import logging import re from odoo import models, fields, api -from odoo.addons.base.models.qweb import QWebException +from odoo.addons.base.models.ir_qweb import QWebException _logger = logging.getLogger(__name__) @@ -37,7 +37,7 @@ class Dockerfile(models.Model): def _compute_dockerfile(self): for rec in self: try: - res = rec.template_id.sudo()._render() if rec.template_id else '' + res = rec.template_id._render_template(rec.template_id.id) if rec.template_id else '' rec.dockerfile = re.sub(r'^\s*$', '', res, flags=re.M).strip() except QWebException: rec.dockerfile = '' diff --git a/runbot/models/host.py b/runbot/models/host.py index f1f8de9f..947928e4 100644 --- a/runbot/models/host.py +++ b/runbot/models/host.py @@ -56,11 +56,12 @@ class Host(models.Model): for host in self: host.build_ids = self.env['runbot.build'].search([('host', '=', host.name), ('local_state', '!=', 'done')]) - @api.model_create_single - def create(self, values): - if 'disp_name' not in values: - values['disp_name'] = values['name'] - return super().create(values) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if 'disp_name' not in vals: + vals['disp_name'] = vals['name'] + return super().create(vals_list) def _bootstrap_local_logs_db(self): """ bootstrap a local database that will collect logs from builds """ diff --git a/runbot/models/project.py b/runbot/models/project.py index b4836ca2..6e203648 100644 --- a/runbot/models/project.py +++ b/runbot/models/project.py @@ -19,8 +19,8 @@ class Project(models.Model): dummy_bundle_id = fields.Many2one('runbot.bundle', string='Dummy bundle') @api.model_create_multi - def create(self, create_values): - projects = super().create(create_values) + def create(self, vals_list): + projects = super().create(vals_list) base_bundle_values = [] dummy_bundle_values = [] for project in projects: diff --git a/runbot/models/repo.py b/runbot/models/repo.py index 12c1228b..f55ead02 100644 --- a/runbot/models/repo.py +++ b/runbot/models/repo.py @@ -276,12 +276,12 @@ class Repo(models.Model): def set_hook_time(self, value): for repo in self: self.env['runbot.repo.hooktime'].create({'time': value, 'repo_id': repo.id}) - self.invalidate_cache() + self.invalidate_recordset(['hook_time']) def set_ref_time(self, value): for repo in self: self.env['runbot.repo.reftime'].create({'time': value, 'repo_id': repo.id}) - self.invalidate_cache() + self.invalidate_recordset(['get_ref_time']) def _gc_times(self): self.env.cr.execute(""" diff --git a/runbot/models/runbot.py b/runbot/models/runbot.py index 8f978031..696609a0 100644 --- a/runbot/models/runbot.py +++ b/runbot/models/runbot.py @@ -21,7 +21,7 @@ from odoo.modules.module import get_module_resource _logger = logging.getLogger(__name__) -# after this point, not realy a repo buisness + class Runbot(models.AbstractModel): _name = 'runbot.runbot' _description = 'Base runbot model' @@ -41,6 +41,7 @@ class Runbot(models.AbstractModel): self._commit() processed = 0 for build in host.get_builds([('requested_action', 'in', ['wake_up', 'deathrow'])]): + build = build.browse(build.id) processed += 1 build._process_requested_actions() self._commit() @@ -56,6 +57,7 @@ class Runbot(models.AbstractModel): self._commit() if callable(result): result() # start docker + self._commit() processed += self._assign_pending_builds(host, host.nb_worker, [('build_type', '!=', 'scheduled')]) self._commit() processed += self._assign_pending_builds(host, host.nb_worker - 1 or host.nb_worker) diff --git a/runbot/models/team.py b/runbot/models/team.py index e7db3f54..63635402 100644 --- a/runbot/models/team.py +++ b/runbot/models/team.py @@ -43,14 +43,15 @@ class RunbotTeam(models.Model): skip_team_pr = fields.Boolean('Skip team pr', help="Don't add codeowner if pr was created by a member of the team", tracking=True) skip_fw_pr = fields.Boolean('Skip forward-port pr', help="Don't add codeowner if pr is a forwardport, even when forced pushed", tracking=True) - @api.model_create_single - def create(self, values): - if 'dashboard_id' not in values or values['dashboard_id'] == False: - dashboard = self.env['runbot.dashboard'].search([('name', '=', values['name'])]) - if not dashboard: - dashboard = dashboard.create({'name': values['name']}) - values['dashboard_id'] = dashboard.id - return super().create(values) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if 'dashboard_id' not in vals or vals['dashboard_id'] == False: + dashboard = self.env['runbot.dashboard'].search([('name', '=', vals['name'])]) + if not dashboard: + dashboard = dashboard.create({'name': vals['name']}) + vals['dashboard_id'] = dashboard.id + return super().create(vals_list) @api.model def _get_team(self, file_path, repos=None): diff --git a/runbot/tests/common.py b/runbot/tests/common.py index ff8a80a6..fd89543d 100644 --- a/runbot/tests/common.py +++ b/runbot/tests/common.py @@ -201,13 +201,14 @@ class RunbotCase(TransactionCase): def start_patcher(self, patcher_name, patcher_path, return_value=DEFAULT, side_effect=DEFAULT, new=DEFAULT): - def stop_patcher_wrapper(): - self.stop_patcher(patcher_name) + if patcher_name in self.patcher_objects: + raise Exception(f'Patcher {patcher_name} already started') patcher = patch(patcher_path, new=new) + self.patcher_objects[patcher_name] = patcher if not hasattr(patcher, 'is_local'): res = patcher.start() - self.addCleanup(stop_patcher_wrapper) + self.addCleanup(patcher.stop) self.patchers[patcher_name] = res self.patcher_objects[patcher_name] = patcher if side_effect != DEFAULT: diff --git a/runbot/tests/test_branch.py b/runbot/tests/test_branch.py index 994e48f0..9d5f32e9 100644 --- a/runbot/tests/test_branch.py +++ b/runbot/tests/test_branch.py @@ -50,8 +50,8 @@ class TestBranchRelations(RunbotCase): create_base('13.0') create_base('saas-13.1') self.last = create_base('saas-13.2') - self.env['runbot.bundle'].flush() - self.env['runbot.version'].flush() + self.env['runbot.bundle'].flush_model() + self.env['runbot.version'].flush_model() def test_relations_master_dev(self): b = self.Branch.create({ diff --git a/runbot/tests/test_build.py b/runbot/tests/test_build.py index 2e514106..3a86211d 100644 --- a/runbot/tests/test_build.py +++ b/runbot/tests/test_build.py @@ -300,7 +300,7 @@ class TestBuildResult(RunbotCase): self.assertEqual(child_delta.days, 24) # test the real _local_cleanup method - self.stop_patcher('_local_cleanup_patcher') + self.patcher_objects['_local_cleanup_patcher'].stop() self.start_patcher('build_local_pgadmin_cursor_patcher', 'odoo.addons.runbot.models.build.local_pgadmin_cursor') self.start_patcher('build_path_patcher', 'odoo.addons.runbot.models.build.Path') dbname = '%s-foobar' % build.dest @@ -374,10 +374,10 @@ class TestBuildResult(RunbotCase): self.assertEqual('pending', build1_1_1.global_state) self.assertEqual('pending', build1_1_2.global_state) - build1_2.flush() + build1_2.flush_recordset() with self.assertQueries(['''UPDATE "runbot_build" SET "global_state"=%s,"local_state"=%s,"write_date"=%s,"write_uid"=%s WHERE id IN %s''']): build1_2.local_state = "testing" - build1_2.flush() + build1_2.flush_recordset() self.assertEqual('testing', build1.global_state) self.assertEqual('testing', build1_2.global_state) @@ -387,14 +387,14 @@ class TestBuildResult(RunbotCase): # with self.assertQueries(['''UPDATE "runbot_build" SET "global_state"=%s,"local_state"=%s,"write_date"=%s,"write_uid"=%s WHERE id IN %s''']): build1.local_state = 'done' - build1.flush() + build1.flush_recordset() self.assertEqual('waiting', build1.global_state) self.assertEqual('testing', build1_1.global_state) # with self.assertQueries([]): # write the same value, no update should be triggered build1.local_state = 'done' - build1.flush() + build1.flush_recordset() build1_1.local_state = 'done' diff --git a/runbot/tests/test_build_error.py b/runbot/tests/test_build_error.py index 0d616ab6..7662a2f3 100644 --- a/runbot/tests/test_build_error.py +++ b/runbot/tests/test_build_error.py @@ -214,13 +214,12 @@ class TestBuildError(RunbotCase): self.additionnal_setup() bundle = self.env['runbot.bundle'].search([('project_id', '=', self.project.id)]) bundle.last_batch.state = 'done' - bundle.flush() bundle._compute_last_done_batch() # force the recompute self.assertTrue(bool(bundle.last_done_batch.exists())) # simulate a failed build that we want to monitor failed_build = bundle.last_done_batch.slot_ids[0].build_id failed_build.global_result = 'ko' - failed_build.flush() + failed_build.flush_recordset() team = self.env['runbot.team'].create({'name': 'Test team'}) dashboard = self.env['runbot.dashboard.tile'].create({ diff --git a/runbot/tests/test_build_stat.py b/runbot/tests/test_build_stat.py index 8e2872c3..bab2311b 100644 --- a/runbot/tests/test_build_stat.py +++ b/runbot/tests/test_build_stat.py @@ -55,7 +55,7 @@ some garbage nothing to see here """ self.start_patcher( - "isdir", "odoo.addons.runbot.models.build_stat_regex.os.path.exists", True + "stats_file_exists", "odoo.addons.runbot.models.build_stat_regex.os.path.exists", True ) with patch("builtins.open", mock_open(read_data=file_content)): self.config_step._make_stats(self.build) @@ -98,7 +98,7 @@ chocolate 15 self.StatRegex.create({"name": "chocolate_count", "regex": r"(?Pchocolate) (?P\d+)"}) self.start_patcher( - "isdir", "odoo.addons.runbot.models.build_stat_regex.os.path.exists", True + "stats_file_exists", "odoo.addons.runbot.models.build_stat_regex.os.path.exists", True ) with patch("builtins.open", mock_open(read_data=file_content)): self.config_step._make_stats(self.build) @@ -127,7 +127,7 @@ chocolate 15 log_data += noise_lines * 10000 self.start_patcher( - "isdir", "odoo.addons.runbot.models.build_stat_regex.os.path.exists", True + "stats_file_exists", "odoo.addons.runbot.models.build_stat_regex.os.path.exists", True ) with patch("builtins.open", mock_open(read_data=log_data)): self.config_step._make_stats(self.build) diff --git a/runbot/tests/test_commit.py b/runbot/tests/test_commit.py index c29d684c..96b3f505 100644 --- a/runbot/tests/test_commit.py +++ b/runbot/tests/test_commit.py @@ -54,12 +54,15 @@ class TestCommitStatus(HttpCase): # 3. test that a non-existsing commit_status returns a 404 # 3.1 find a non existing commit status id - non_existing_id = self.env['runbot.commit.status'].browse(50000).exists() or 50000 - while self.env['runbot.commit.status'].browse(non_existing_id).exists(): - non_existing_id += 1 + non_existing_commit_status = self.env['runbot.commit.status'].create({ + 'commit_id': self.server_commit.id, + 'context': 'ci/test', + 'state': 'failure', + }) + non_existing_commit_status.unlink() self.authenticate('runbot_admin', 'admin') - response = self.url_open('/runbot/commit/resend/%s' % non_existing_id) + response = self.url_open('/runbot/commit/resend/%s' % non_existing_commit_status.id) self.assertEqual(response.status_code, 404) #4.1 Test that a status not sent (with not sent_date) can be manually resend diff --git a/runbot/tests/test_repo.py b/runbot/tests/test_repo.py index 93b945d8..1699878c 100644 --- a/runbot/tests/test_repo.py +++ b/runbot/tests/test_repo.py @@ -297,7 +297,7 @@ class TestRepo(RunbotCaseMinimalSetup): self.assertEqual(repo1[field_name], 1.3) self.assertEqual(repo2[field_name], 1.4) - self.Repo.invalidate_cache() + self.Repo.invalidate_model() self.assertEqual(repo1[field_name], 1.3) self.assertEqual(repo2[field_name], 1.4) @@ -414,7 +414,7 @@ class TestIdentityFile(RunbotCase): def test_identity_file(self): """test that the identity file is used in git command""" - self.stop_patcher('git_patcher') + self.patcher_objects['git_patcher'].stop() self.start_patcher('check_output_patcher', 'odoo.addons.runbot.models.repo.subprocess.check_output', new=self.check_output_helper()) self.repo_server.identity_file = 'fake_identity' @@ -466,8 +466,8 @@ class TestRepoScheduler(RunbotCase): host = self.env['runbot.host']._get_current() self.Runbot._scheduler(host) - build.invalidate_cache() - scheduled_build.invalidate_cache() + build.invalidate_recordset() + scheduled_build.invalidate_recordset() self.assertFalse(build.host) self.assertFalse(scheduled_build.host) diff --git a/runbot/tests/test_upgrade.py b/runbot/tests/test_upgrade.py index ac290b0a..7294200e 100644 --- a/runbot/tests/test_upgrade.py +++ b/runbot/tests/test_upgrade.py @@ -265,7 +265,7 @@ class TestUpgradeFlow(RunbotCase): with self.assertRaises(UserError): self.step_upgrade_server.job_type = 'install_odoo' - self.trigger_upgrade_server.flush(['upgrade_step_id']) + self.trigger_upgrade_server.flush_recordset(['upgrade_step_id']) batch = self.master_bundle._force() batch._prepare() @@ -414,9 +414,9 @@ class TestUpgradeFlow(RunbotCase): ['account', 'l10n_be', 'l10n_ch', 'mail', 'stock'] # is this order ok? ) current_build = db_builds[0] - for current_build in db_builds: - self.start_patcher('docker_state', 'odoo.addons.runbot.models.build.docker_state', 'END') + self.start_patcher('docker_state', 'odoo.addons.runbot.models.build.docker_state', 'END') + for current_build in db_builds: suffix = current_build.params_id.dump_db.db_suffix source_dest = current_build.params_id.dump_db.build_id.dest diff --git a/runbot/views/config_views.xml b/runbot/views/config_views.xml index 84bc04d7..fceada0b 100644 --- a/runbot/views/config_views.xml +++ b/runbot/views/config_views.xml @@ -18,7 +18,7 @@ - + @@ -44,7 +44,7 @@ - + diff --git a/runbot/views/error_log_views.xml b/runbot/views/error_log_views.xml index b2e63c7e..bc5c7304 100644 --- a/runbot/views/error_log_views.xml +++ b/runbot/views/error_log_views.xml @@ -43,7 +43,7 @@ runbot.error.log -