[IMP] runbot: create build when old HEAD is force-pushed

_find_new_commits will check if a build exists with current branch HEAD
before creating build. This is crutial to avoid to create a new build
at each loop turn. The problem is that in some rare cases, when
force-pushing an old head on a branch, the build won't appear and the only
way to update the branch is to find the corresponding build that may be
hidden in hitory. This may be confusing for the user that will rebuild the
created build with a commit that doesn't represent the head of the branch.

This commit only search for the last build of each branch, in order to
only skip build creation if the last build as the same hash. The new
created build should be marked as the duplicate of the first one.
This commit is contained in:
Xavier-Do 2019-11-12 15:25:23 +01:00 committed by XavierDo
parent 064546441f
commit 54ecee8c4e
2 changed files with 31 additions and 6 deletions

View File

@ -270,10 +270,9 @@ class runbot_repo(models.Model):
max_age = int(icp.get_param('runbot.runbot_max_age', default=30)) max_age = int(icp.get_param('runbot.runbot_max_age', default=30))
self.env.cr.execute(""" self.env.cr.execute("""
WITH t (build, branch_id) AS (SELECT unnest(%s), unnest(%s)) SELECT DISTINCT ON (branch_id) name, branch_id
SELECT b.name, b.branch_id FROM runbot_build WHERE branch_id in %s ORDER BY branch_id,id DESC;
FROM t LEFT JOIN runbot_build b ON (b.name = t.build) AND (b.branch_id = t.branch_id) """, (tuple([ref_branches[r[0]] for r in refs]),))
""", ([r[1] for r in refs], [ref_branches[r[0]] for r in refs]))
# generate a set of tuples (branch_id, sha) # generate a set of tuples (branch_id, sha)
builds_candidates = {(r[1], r[0]) for r in self.env.cr.fetchall()} builds_candidates = {(r[1], r[0]) for r in self.env.cr.fetchall()}

View File

@ -57,7 +57,7 @@ class Test_Repo(common.TransactionCase):
'name': 'refs/heads/bidon' 'name': 'refs/heads/bidon'
}) })
self.commit_list = [('refs/heads/bidon', first_commit = [('refs/heads/bidon',
'd0d0caca', 'd0d0caca',
datetime.datetime.now().strftime("%Y-%m-%d, %H:%M:%S"), datetime.datetime.now().strftime("%Y-%m-%d, %H:%M:%S"),
'Marc Bidule', 'Marc Bidule',
@ -65,7 +65,15 @@ class Test_Repo(common.TransactionCase):
'A nice subject', 'A nice subject',
'Marc Bidule', 'Marc Bidule',
'<marc.bidule@somewhere.com>')] '<marc.bidule@somewhere.com>')]
mock_fetch_head_time.side_effect = [100000.0, 100001.0, 100002.0] self.commit_list = first_commit
def counter():
i = 100000
while True:
i += 1
yield i
mock_fetch_head_time.side_effect = counter()
with patch('odoo.addons.runbot.models.repo.runbot_repo._git', new=self.mock_git_helper()): with patch('odoo.addons.runbot.models.repo.runbot_repo._git', new=self.mock_git_helper()):
repo._create_pending_builds() repo._create_pending_builds()
@ -117,6 +125,7 @@ class Test_Repo(common.TransactionCase):
self.assertEqual(branch_count, 1, 'No new branch should have been created') self.assertEqual(branch_count, 1, 'No new branch should have been created')
build = self.env['runbot.build'].search([('repo_id', '=', repo.id), ('branch_id', '=', branch.id), ('name', '=', 'b00b')]) build = self.env['runbot.build'].search([('repo_id', '=', repo.id), ('branch_id', '=', branch.id), ('name', '=', 'b00b')])
self.assertEqual(len(build), 1)
self.assertEqual(build.subject, 'Another subject') self.assertEqual(build.subject, 'Another subject')
self.assertEqual(build.local_state, 'pending') self.assertEqual(build.local_state, 'pending')
self.assertFalse(build.local_result) self.assertFalse(build.local_result)
@ -125,6 +134,23 @@ class Test_Repo(common.TransactionCase):
self.assertEqual(previous_build.local_state, 'done', 'Previous pending build should be done') self.assertEqual(previous_build.local_state, 'done', 'Previous pending build should be done')
self.assertEqual(previous_build.local_result, 'skipped', 'Previous pending build result should be skipped') self.assertEqual(previous_build.local_result, 'skipped', 'Previous pending build result should be skipped')
self.commit_list = first_commit # branch reseted hard to an old commit
builds = self.env['runbot.build'].search([('repo_id', '=', repo.id), ('branch_id', '=', branch.id), ('name', '=', 'd0d0caca')])
self.assertEqual(len(builds), 1)
with patch('odoo.addons.runbot.models.repo.runbot_repo._git', new=self.mock_git_helper()):
repo._create_pending_builds()
last_build = self.env['runbot.build'].search([], limit=1)
self.assertEqual(last_build.name, 'd0d0caca')
builds = self.env['runbot.build'].search([('repo_id', '=', repo.id), ('branch_id', '=', branch.id), ('name', '=', 'd0d0caca')])
self.assertEqual(len(builds), 2)
# self.assertEqual(last_build.duplicate_id, previous_build) False because previous_build is skipped
with patch('odoo.addons.runbot.models.repo.runbot_repo._git', new=self.mock_git_helper()):
other_repo._create_pending_builds()
builds = self.env['runbot.build'].search([('repo_id', '=', repo.id), ('branch_id', '=', branch.id), ('name', '=', 'd0d0caca')])
self.assertEqual(len(builds), 2)
@skip('This test is for performances. It needs a lot of real branches in DB to mean something') @skip('This test is for performances. It needs a lot of real branches in DB to mean something')
@patch('odoo.addons.runbot.models.repo.runbot_repo._root') @patch('odoo.addons.runbot.models.repo.runbot_repo._root')
def test_repo_perf_find_new_commits(self, mock_root): def test_repo_perf_find_new_commits(self, mock_root):