diff --git a/CHANGELOG b/CHANGELOG index 91b3d0a2..214b13c1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,12 +1,17 @@ # Change Log All notable changes to this project will be documented in this file. -## [4.0.0] - 2024-03-06 +## [4.0.0] - 2024-08-09 # Removed - Support of tasks as it unused to due its incompatibility with GitHub. # Added - Bert-E's status notifications through a build status check. +- Support of development branches with only major version: `development/x`. + +# Changed +- Integration queue branches pattern is now `q/w/{pr.id}/{integration.branch}` + instead of `q/{pr.id}/{integration.branch}`. ## [3.12.0] - 2024-02-26 # Added diff --git a/bert_e/jobs/delete_queues.py b/bert_e/jobs/delete_queues.py index 26bcc375..ef2c1a8d 100644 --- a/bert_e/jobs/delete_queues.py +++ b/bert_e/jobs/delete_queues.py @@ -49,11 +49,11 @@ def delete_queues(job: DeleteQueuesJob): if not queue_branches: raise exceptions.JobSuccess() - branch_factory( - repo, - 'development/{}.{}'.format(queue_branches[0].major, - queue_branches[0].minor) - ).checkout() + queue_branch = queue_branches[0] + if queue_branch.minor is None: + repo.checkout(f"development/{queue_branch.major}") + else: + repo.checkout(f"development/{queue_branch.major}.{queue_branch.minor}") for branch in queue_branches: branch.remove(do_push=False) diff --git a/bert_e/lib/versions.py b/bert_e/lib/versions.py new file mode 100644 index 00000000..572cd8dc --- /dev/null +++ b/bert_e/lib/versions.py @@ -0,0 +1,6 @@ +def version_key(version): + """Key function to sort versions in descending order.""" + parts = version.split('.') + parts = tuple(int(part) for part in parts) + # Convert parts to integers and fill missing parts with float('inf') + return parts + (float('inf'),) * (4 - len(parts)) diff --git a/bert_e/server/status.py b/bert_e/server/status.py index d4d11a72..907b2599 100644 --- a/bert_e/server/status.py +++ b/bert_e/server/status.py @@ -15,6 +15,7 @@ """This module defines the server status page.""" import logging from flask import Blueprint, current_app, render_template, request +from bert_e.lib.versions import version_key from ..git_host.cache import BUILD_STATUS_CACHE @@ -44,8 +45,7 @@ def display(): for version, _ in queued_commits: versions.add(version) - versions = sorted(versions, reverse=True) - + versions = sorted(versions, key=version_key, reverse=True) for pr_id, queued_commits in queue_data.items(): if int(pr_id) in [i['id'] for i in merged_prs]: continue diff --git a/bert_e/tests/test_bert_e.py b/bert_e/tests/test_bert_e.py index 26721d4a..44810f11 100644 --- a/bert_e/tests/test_bert_e.py +++ b/bert_e/tests/test_bert_e.py @@ -26,6 +26,7 @@ from unittest.mock import Mock from unittest.mock import patch from urllib.parse import quote_plus +from os import getenv import requests import requests_mock @@ -90,6 +91,9 @@ """ # noqa +log = logging.getLogger(__name__) + + def initialize_git_repo(repo, username, usermail): """resets the git repo""" assert '/ring/' not in repo._url # This is a security, do not remove @@ -121,6 +125,9 @@ def initialize_git_repo(repo, username, usermail): create_branch(repo, 'development/' + major_minor, 'stabilization/' + full_version, file_=True, do_push=False) + create_branch(repo, f'development/{major}', + f'development/{major_minor}', + file_=True, do_push=False) if major != 6 and major != 10: repo.cmd('git tag %s.%s.%s', major, minor, micro - 1) @@ -339,7 +346,9 @@ def finalize_cascade(self, branches, tags, destination, c.add_branch(branch, my_dst) for tag in tags: - c.update_micro(tag) + c.update_versions(tag) + + c._update_major_versions() # check merge_paths now (finalize not required) if merge_paths: @@ -687,6 +696,65 @@ def test_with_v_prefix(self): # expect the same result self.finalize_cascade(branches, v_tags, destination, fixver) + def test_major_development_branch(self): + destination = 'development/4.3' + branches = OrderedDict({ + 1: {'name': 'stabilization/4.3.18', 'ignore': True}, + 2: {'name': 'development/4.3', 'ignore': False}, + 3: {'name': 'development/4', 'ignore': False}, + 4: {'name': 'stabilization/5.1.4', 'ignore': True}, + 5: {'name': 'development/5.1', 'ignore': False}, + 6: {'name': 'development/10.0', 'ignore': False}, + 7: {'name': 'development/10', 'ignore': False} + }) + tags = ['4.3.16', '4.3.17', '4.3.18_rc1', + 'v5.1.3', 'v5.1.4_rc1', 'v10.0.1'] + fixver = [] + with self.assertRaises(AssertionError): + self.finalize_cascade(branches, tags, destination, fixver) + fixver = ['4.3.19', '4.4.0', '5.1.5', '10.0.2', '10.1.0'] + self.finalize_cascade(branches, tags, destination, fixver) + + destination = 'development/4' + branches = OrderedDict({ + 1: {'name': 'development/4.3', 'ignore': True}, + 2: {'name': 'development/4', 'ignore': False} + }) + fixver = ['4.4.0'] + self.finalize_cascade(branches, tags, destination, fixver) + + branches = OrderedDict({ + 1: {'name': 'development/4', 'ignore': False} + }) + self.finalize_cascade(branches, tags, destination, fixver) + + def test_major_development_branch_no_tag_bump(self): + destination = 'development/4.3' + branches = OrderedDict({ + 1: {'name': 'stabilization/4.3.18', 'ignore': True}, + 2: {'name': 'development/4.3', 'ignore': False}, + 3: {'name': 'development/4', 'ignore': False}, + 4: {'name': 'stabilization/5.1.4', 'ignore': True}, + 5: {'name': 'development/5.1', 'ignore': False}, + 6: {'name': 'development/10.0', 'ignore': False}, + 7: {'name': 'development/10', 'ignore': False} + }) + tags = ['4.3.16', '4.3.17', '4.3.18_rc1', + 'v5.1.3', 'v5.1.4_rc1'] + fixver = ['4.3.19', '4.4.0', '5.1.5', '10.0.0', '10.1.0'] + self.finalize_cascade(branches, tags, destination, fixver) + + def test_major_dev_branch_lonely_stab(self): + destination = 'stabilization/6.1.5' + branches = OrderedDict({ + 1: {'name': 'stabilization/6.1.5', 'ignore': False}, + 2: {'name': 'development/6', 'ignore': False} + }) + tags = [] + fixver = ['6.1.5', '6.2.0'] + with self.assertRaises(exns.DevBranchDoesNotExist): + self.finalize_cascade(branches, tags, destination, fixver) + def test_retry_handler(self): class DummyError(Exception): pass @@ -790,6 +858,10 @@ def bypass_all_but(self, exceptions): def setUp(self): warnings.resetwarnings() warnings.simplefilter('ignore') + self.ci: bool = bool(getenv('CI', False)) + if self.ci: + # print a group with the test name that is about to run + log.info(f"\n::group::{self.__class__}.{self._testMethodName}") self.admin_id = None self.contributor_id = None # repo creator and reviewer @@ -854,6 +926,9 @@ def tearDown(self): self.admin_bb.delete() self.gitrepo.delete() + if self.ci: + # end the group with the test name that just ran + log.info("\n::endgroup::") def create_pr( self, @@ -925,7 +1000,7 @@ def handle_legacy(self, token, backtrace): except ValueError: # token is a sha1, use it to filter on content sha1 = token - command = 'git branch -r --contains %s --list origin/q/[0-9]*/*' + command = 'git branch -r --contains %s --list origin/q/w/[0-9]*/*' for qint in self.gitrepo.cmd(command, sha1) \ .replace(" ", "") \ .replace("origin/", "") \ @@ -1127,7 +1202,7 @@ def test_create_integration_pr_manually(self): admins: - {admin} """ # noqa - pr = self.create_pr('feature/TEST-0002', 'development/4.3') + pr = self.create_pr('feature/TEST-0002', 'development/5') options = ['create_pull_requests', 'bypass_jira_check'] try: self.handle(pr.id) @@ -1170,7 +1245,7 @@ def test_comments_without_integration_pull_requests(self): - {admin} """ # noqa options = self.bypass_all_but(['bypass_build_status']) - pr = self.create_pr('feature/TEST-0042', 'development/10.0') + pr = self.create_pr('feature/TEST-0042', 'development/10') self.handle(pr.id, settings=settings, options=options) self.assertIs(len(list(pr.get_comments())), 1) self.assertIn('Hello %s' % self.args.contributor_username, @@ -1367,7 +1442,7 @@ def test_integration_branch_creation_latest_branch(self): - {admin} """ # noqa options = self.bypass_all_but(['bypass_build_status']) - pr = self.create_pr('feature/TEST-0069', 'development/10.0') + pr = self.create_pr('feature/TEST-0069', 'development/10') self.handle(pr.id, settings=settings, options=options) self.assertEqual(len(list(pr.get_comments())), 1) @@ -1472,16 +1547,21 @@ def test_merge_without_integration_prs(self): with self.assertRaises(Exception): self.handle(pr.id + 1, settings=settings, options=options) self.gitrepo.cmd('git fetch --all') - sha1_w_5_1 = self.gitrepo \ - .cmd('git rev-parse origin/w/5.1/%s' % src_branch) \ - .rstrip() - sha1_w_10_0 = self.gitrepo \ - .cmd('git rev-parse origin/w/10.0/%s' % src_branch) \ - .rstrip() - self.set_build_status_on_pr_id(pr.id, 'SUCCESSFUL') - self.set_build_status(sha1=sha1_w_5_1, state='SUCCESSFUL') - self.set_build_status(sha1=sha1_w_10_0, state='INPROGRESS') + integration_branches = [ + f'w/4/{src_branch}', + f'w/5.1/{src_branch}', + f'w/5/{src_branch}', + f'w/10.0/{src_branch}', + f'w/10/{src_branch}', + ] + # loop through all integration_branches but the latest + for branch in integration_branches[:-1]: + sha = self.gitrepo.cmd(f'git rev-parse origin/{branch}').rstrip() + self.set_build_status(sha, 'SUCCESSFUL') + sha1_w_10 = self.gitrepo.cmd( + f'git rev-parse origin/{integration_branches[-1]}').rstrip() + self.set_build_status(sha1=sha1_w_10, state='INPROGRESS') if self.args.git_host == 'github': pr.add_comment('@%s approve' % (self.args.robot_username)) else: @@ -1489,12 +1569,19 @@ def test_merge_without_integration_prs(self): with self.assertRaises(exns.BuildInProgress): self.handle(pr.id, settings=settings, options=options, backtrace=True) - self.set_build_status(sha1=sha1_w_10_0, state='SUCCESSFUL') + self.set_build_status(sha1=sha1_w_10, state='SUCCESSFUL') with self.assertRaises(exns.SuccessMessage): - self.handle(sha1_w_10_0, settings=settings, + self.handle(sha1_w_10, settings=settings, options=options, backtrace=True) - - for dev in ['development/4.3', 'development/5.1', 'development/10.0']: + dev_branches = [ + 'development/4.3', + 'development/4', + 'development/5.1', + 'development/5', + 'development/10.0', + 'development/10', + ] + for dev in dev_branches: branch = gwfb.branch_factory(self.gitrepo, dev) branch.checkout() self.gitrepo.cmd('git pull origin %s', dev) @@ -1544,7 +1631,7 @@ def test_conflict(self): file_='toto.txt') pr3 = self.create_pr('improvement/TEST-0006', 'development/10.0', file_='toto.txt') - pr4 = self.create_pr('improvement/TEST-0006-other', 'development/5.1', + pr4 = self.create_pr('improvement/TEST-0006-other', 'development/5', file_='toto.txt') pr5 = self.create_pr('improvement/TEST-0006-last', 'development/4.3', file_='toto.txt') @@ -1611,14 +1698,14 @@ def test_conflict(self): except exns.Conflict as e: self.assertIn( '`w/10.0/improvement/TEST-0006-last` with contents from ' - '`w/5.1/improvement/TEST-0006-last`\nand `development/10.0`', + '`w/5/improvement/TEST-0006-last`\nand `development/10.0`', e.msg) # Bert-E MUST instruct the user to modify the integration # branch with the same target as the original PR self.assertIn( "git checkout -B w/10.0/improvement/TEST-0006", e.msg) self.assertIn( - "git merge origin/w/5.1/improvement/TEST-0006", e.msg) + "git merge origin/w/5/improvement/TEST-0006", e.msg) self.assertIn("git push -u origin w/10.0/improvement/TEST-0006", e.msg) else: @@ -2895,7 +2982,7 @@ def test_bypass_author_options_build_status(self): - bypass_peer_approval """ # noqa # test bypass branch prefix through comment - pr = self.create_pr('bugfix/TEST-00081', 'development/4.3') + pr = self.create_pr('bugfix/TEST-00081', 'development/5') # test build not started with self.assertRaises(exns.BuildNotStarted): @@ -2913,7 +3000,7 @@ def test_bypass_author_options_build_status(self): backtrace=True) except exns.BuildFailed as excp: self.assertIn( - "did not succeed in branch w/10.0/bugfix/TEST-00081", + "did not succeed in branch w/10/bugfix/TEST-00081", excp.msg, ) else: @@ -2994,7 +3081,7 @@ def test_bypass_author_comment_check(self): {contributor}: - bypass_jira_check """ # noqa - pr = self.create_pr('feature/TEST-0042', 'development/10.0') + pr = self.create_pr('feature/TEST-0042', 'development/10') self.handle(pr.id, settings=settings) self.assertIs(len(list(pr.get_comments())), 2) self.assertIn('bypass_jira_check', self.get_last_pr_comment(pr)) @@ -3016,7 +3103,7 @@ def test_bypass_author_comment_check(self): {contributor}: - bypass_author_approval """ # noqa - pr = self.create_pr('feature/TEST-0043', 'development/10.0') + pr = self.create_pr('feature/TEST-0043', 'development/10') self.handle(pr.id, settings=settings) self.assertIs(len(list(pr.get_comments())), 2) self.assertIn('bypass_author_approval', self.get_last_pr_comment(pr)) @@ -3039,7 +3126,7 @@ def test_bypass_author_comment_check(self): {contributor}: - bypass_peer_approval """ # noqa - pr = self.create_pr('feature/TEST-0044', 'development/10.0') + pr = self.create_pr('feature/TEST-0044', 'development/10') self.handle(pr.id, settings=settings) self.assertIs(len(list(pr.get_comments())), 2) self.assertIn('bypass_peer_approval', self.get_last_pr_comment(pr)) @@ -3062,7 +3149,7 @@ def test_bypass_author_comment_check(self): {contributor}: - bypass_build_status """ # noqa - pr = self.create_pr('feature/TEST-0045', 'development/10.0') + pr = self.create_pr('feature/TEST-0045', 'development/10') self.handle(pr.id, settings=settings) self.assertIs(len(list(pr.get_comments())), 2) self.assertIn('bypass_build_status', self.get_last_pr_comment(pr)) @@ -3303,7 +3390,7 @@ def _create_pull_requests(*args, **kwargs): gwf.create_integration_pull_requests = real def test_build_status(self): - pr = self.create_pr('bugfix/TEST-00081', 'development/4.3') + pr = self.create_pr('bugfix/TEST-00081', 'development/5') # test build not started with self.assertRaises(exns.BuildNotStarted): @@ -3333,7 +3420,7 @@ def test_build_status(self): backtrace=True) except exns.BuildFailed as excp: self.assertIn( - "did not succeed in branch w/10.0/bugfix/TEST-00081", + "did not succeed in branch w/10/bugfix/TEST-00081", excp.msg, ) else: @@ -3373,7 +3460,8 @@ def test_build_status_triggered_by_build_result(self): # github enforces valid build urls url='https://builds.test.com/DEADBEEF' ) - self.set_build_status_on_pr_id(pr.id + 1, 'SUCCESSFUL') + for pr_id in range(pr.id + 1, pr.id + 4): + self.set_build_status_on_pr_id(pr_id, 'SUCCESSFUL') with self.assertRaises(exns.BuildFailed) as err: childpr = self.robot_bb.get_pull_request( @@ -3626,15 +3714,21 @@ def test_successful_merge_into_stabilization_branch(self): dest = 'stabilization/4.3.18' res = ["origin/bugfix/TEST-00001", "origin/development/4.3", + "origin/development/4", + "origin/development/5", "origin/development/5.1", + "origin/development/10", "origin/development/10.0", "origin/stabilization/4.3.18"] if not self.args.disable_queues: res.extend([ "origin/q/4.3.18", "origin/q/4.3", + "origin/q/4", "origin/q/5.1", + "origin/q/5", "origin/q/10.0", + "origin/q/10", ]) self.successful_merge_into_stabilization_branch(dest, res) @@ -3642,13 +3736,17 @@ def test_successful_merge_into_stabilization_branch_middle_cascade(self): dest = 'stabilization/5.1.4' res = ["origin/bugfix/TEST-00001", "origin/development/5.1", + "origin/development/5", "origin/development/10.0", + "origin/development/10", "origin/stabilization/5.1.4"] if not self.args.disable_queues: res.extend([ "origin/q/5.1.4", "origin/q/5.1", + "origin/q/5", "origin/q/10.0", + "origin/q/10", ]) self.successful_merge_into_stabilization_branch(dest, res) @@ -3659,15 +3757,19 @@ def test_success_message_content(self): 'bypass_build_status', 'bypass_leader_approval', 'bypass_peer_approval', - 'bypass_author_approval'], + 'bypass_author_approval', + 'bypass_jira_check'], backtrace=True) except exns.SuccessMessage as e: self.assertIn('* :heavy_check_mark: `stabilization/5.1.4`', e.msg) self.assertIn('* :heavy_check_mark: `development/5.1`', e.msg) + self.assertIn('* :heavy_check_mark: `development/5`', e.msg) self.assertIn('* :heavy_check_mark: `development/10.0`', e.msg) + self.assertIn('* :heavy_check_mark: `development/10`', e.msg) self.assertIn('* `stabilization/4.3.18`', e.msg) self.assertIn('* `stabilization/10.0.0`', e.msg) self.assertIn('* `development/4.3`', e.msg) + self.assertIn('* `development/4`', e.msg) def test_unanimity_option(self): """Test unanimity by passing option to bert-e""" @@ -3975,11 +4077,15 @@ def test_integration_pr_declined(self): self.handle( pr.id, options=self.bypass_all_but(['bypass_build_status'])) + exp_int_branches = [ + 'w/4/bugfix/TEST-0001', + 'w/5.1/bugfix/TEST-0001', + 'w/5/bugfix/TEST-0001', + 'w/10.0/bugfix/TEST-0001', + 'w/10/bugfix/TEST-0001' + ] int_prs = list(self.contributor_bb.get_pull_requests( - src_branch=[ - 'w/5.1/bugfix/TEST-0001', - 'w/10.0/bugfix/TEST-0001' - ]) + src_branch=exp_int_branches) ) self.gitrepo.cmd('git checkout bugfix/TEST-0001') @@ -3990,13 +4096,13 @@ def test_integration_pr_declined(self): self.handle(pr.id, options=self.bypass_all, backtrace=True) # Decline integration pull requests - self.assertEqual(len(int_prs), 2) + self.assertEqual(len(int_prs), 5) for ipr in int_prs: ipr.decline() # Delete integration branches - self.gitrepo.push(':w/5.1/bugfix/TEST-0001 ' - ':w/10.0/bugfix/TEST-0001') + for branch in exp_int_branches: + self.gitrepo.push(f':{branch}') with self.assertRaises(exns.SuccessMessage): self.handle(pr.id, options=self.bypass_all, backtrace=True) @@ -4117,7 +4223,7 @@ def test_settings(self): ) # test with different build key - pr = self.create_pr('bugfix/TEST-00003', 'development/10.0') + pr = self.create_pr('bugfix/TEST-00003', 'development/10') settings = """ repository_owner: {owner} repository_slug: {slug} @@ -4338,14 +4444,14 @@ def test_stabilization_and_dev_branch_addition(self): self.gitrepo.cmd('git fetch --prune') self.gitrepo.cmd('git checkout -B development/5.1' - ' origin/development/4.3') + ' origin/development/4') self.gitrepo.cmd('git checkout -B stabilization/5.1.4' ' development/5.1') self.gitrepo.cmd('git push -u origin ' 'development/5.1 stabilization/5.1.4') if not self.args.disable_queues: - self.gitrepo.cmd('git push origin :q/4.3 :q/10.0') + self.gitrepo.cmd('git push origin :q/4.3 :q/4 :q/5 :q/10.0 :q/10') with self.assertRaises(exns.BranchHistoryMismatch): self.handle(pr.id, options=self.bypass_all, backtrace=True) @@ -4800,6 +4906,64 @@ def test_set_bot_status(self): pr.src_commit, key="bert-e") == "queued" self.handle(pr.id, settings=settings, options=self.bypass_all) + def test_dev_major_only(self): + """Test Bert-E's capability to handle a development/x branch.""" + # Can we merge a dev/4.3 with a dev/4 + pr1 = self.create_pr('bugfix/TEST-01', 'development/4.3') + with self.assertRaises(exns.SuccessMessage): + self.handle(pr1.id, options=self.bypass_all, backtrace=True) + # Can we merge a stab/4.3.18 with a dev/4 + pr2 = self.create_pr('bugfix/TEST-02', 'stabilization/4.3.18') + with self.assertRaises(exns.SuccessMessage): + self.handle(pr2.id, options=self.bypass_all, backtrace=True) + # Can we merge directly on a dev/4 + pr3 = self.create_pr('bugfix/TEST-03', 'development/4') + with self.assertRaises(exns.SuccessMessage): + self.handle(pr3.id, options=self.bypass_all, backtrace=True) + # Ensure the gitwaterflow is set correctly between all the branches + self.gitrepo.cmd('git fetch') + self.gitrepo.cmd( + 'git merge-base --is-ancestor ' + 'origin/stabilization/4.3.18 ' + 'origin/development/4.3' + ) + self.gitrepo.cmd( + 'git merge-base --is-ancestor ' + 'origin/development/4.3 ' + 'origin/development/4' + ) + with self.assertRaises(CommandError): + self.gitrepo.cmd( + 'git merge-base --is-ancestor ' + 'origin/development/4 ' + 'origin/development/4.3' + ) + with self.assertRaises(CommandError): + self.gitrepo.cmd( + 'git merge-base --is-ancestor ' + 'origin/development/4 ' + 'origin/stabilization/4.3.18' + ) + with self.assertRaises(CommandError): + self.gitrepo.cmd( + 'git merge-base --is-ancestor ' + 'origin/development/4.3 ' + 'origin/stabilization/4.3.18' + ) + + def test_dev_major_lonely_stab(self): + """Test Bert-E's handling of a lonely stabilization/x.y.z branch.""" + # create a stabilization 4.5.2 branch from development/4 + self.gitrepo.cmd('git fetch') + self.gitrepo.cmd( + 'git checkout -b stabilization/4.5.2 origin/development/4') + self.gitrepo.cmd('git push -u origin stabilization/4.5.2') + # create a PR from the stabilization branch + pr = self.create_pr('bugfix/TEST-01', 'stabilization/4.5.2') + # expect a DevBranchDoesNotExist exception + with self.assertRaises(exns.DevBranchDoesNotExist): + self.handle(pr.id, options=self.bypass_all, backtrace=True) + class TestQueueing(RepositoryTests): """Tests which validate all things related to the merge queue. @@ -4834,28 +4998,28 @@ def submit_problem(self, problem, build_key='pipeline'): # set build status on q branches if problem[pr]['dst'] == 'development/4.3': branches = [ - 'q/{pr}/4.3/{name}', - 'q/{pr}/5.1/{name}', - 'q/{pr}/10.0/{name}' + 'q/w/{pr}/4.3/{name}', + 'q/w/{pr}/5.1/{name}', + 'q/w/{pr}/10.0/{name}' ] elif problem[pr]['dst'] == 'stabilization/5.1.4': branches = [ - 'q/{pr}/5.1.4/{name}', - 'q/{pr}/5.1/{name}', - 'q/{pr}/10.0/{name}' + 'q/w/{pr}/5.1.4/{name}', + 'q/w/{pr}/5.1/{name}', + 'q/w/{pr}/10.0/{name}' ] elif problem[pr]['dst'] == 'development/5.1': branches = [ - 'q/{pr}/5.1/{name}', - 'q/{pr}/10.0/{name}' + 'q/w/{pr}/5.1/{name}', + 'q/w/{pr}/10.0/{name}' ] elif problem[pr]['dst'] == 'development/10.0': branches = [ - 'q/{pr}/10.0/{name}' + 'q/w/{pr}/10.0/{name}' ] elif problem[pr]['dst'] == 'hotfix/4.2.17': branches = [ - 'q/{pr}/4.2.17.1/{name}' + 'q/w/{pr}/4.2.17.1/{name}' ] else: raise Exception('invalid dst branch name') @@ -4886,7 +5050,7 @@ def get_qbranches(self): def get_qint_branches(self): return (self.gitrepo - .cmd('git branch -r --list "origin/q/[0-9]*/*"') + .cmd('git branch -r --list "origin/q/w/[0-9]*/*"') .replace(" ", "") .replace("origin/", "") .split('\n')[:-1]) @@ -4936,7 +5100,7 @@ def test_qint_branch(self): self.qint_branch("q/6.2/feature/RELENG-001-plop") qint_branch = gwfb.branch_factory(FakeGitRepo(), - "q/10/6.2/feature/RELENG-001-plop") + "q/w/10/6.2/feature/RELENG-001-plop") self.assertEqual(type(qint_branch), gwfb.QueueIntegrationBranch) self.assertEqual(qint_branch.version_t, (6, 2)) self.assertEqual(qint_branch.version, "6.2") @@ -4979,14 +5143,26 @@ def empty_solution(self): gwfb.QueueBranch: self.queue_branch('q/4.3'), gwfb.QueueIntegrationBranch: [] }), + ((4, None), { + gwfb.QueueBranch: self.queue_branch('q/4'), + gwfb.QueueIntegrationBranch: [] + }), ((5, 1), { gwfb.QueueBranch: self.queue_branch('q/5.1'), gwfb.QueueIntegrationBranch: [] }), + ((5, None), { + gwfb.QueueBranch: self.queue_branch('q/5'), + gwfb.QueueIntegrationBranch: [] + }), ((10, 0), { gwfb.QueueBranch: self.queue_branch('q/10.0'), gwfb.QueueIntegrationBranch: [] }), + ((10, None), { + gwfb.QueueBranch: self.queue_branch('q/10'), + gwfb.QueueIntegrationBranch: [] + }), ]) @property @@ -4996,25 +5172,49 @@ def standard_solution(self): ((4, 3), { gwfb.QueueBranch: self.queue_branch('q/4.3'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/7/4.3/improvement/bar2'), - self.qint_branch('q/1/4.3/improvement/bar') + self.qint_branch('q/w/13/4.3/improvement/bar2'), + self.qint_branch('q/w/1/4.3/improvement/bar') + ] + }), + ((4, None), { + gwfb.QueueBranch: self.queue_branch('q/4'), + gwfb.QueueIntegrationBranch: [ + self.qint_branch('q/w/13/4/improvement/bar2'), + self.qint_branch('q/w/1/4/improvement/bar') ] }), ((5, 1), { gwfb.QueueBranch: self.queue_branch('q/5.1'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/7/5.1/improvement/bar2'), - self.qint_branch('q/5/5.1/bugfix/bar'), - self.qint_branch('q/1/5.1/improvement/bar') + self.qint_branch('q/w/13/5.1/improvement/bar2'), + self.qint_branch('q/w/9/5.1/bugfix/bar'), + self.qint_branch('q/w/1/5.1/improvement/bar') + ] + }), + ((5, None), { + gwfb.QueueBranch: self.queue_branch('q/5'), + gwfb.QueueIntegrationBranch: [ + self.qint_branch('q/w/13/5/improvement/bar2'), + self.qint_branch('q/w/9/5/bugfix/bar'), + self.qint_branch('q/w/1/5/improvement/bar') ] }), ((10, 0), { gwfb.QueueBranch: self.queue_branch('q/10.0'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/7/10.0/improvement/bar2'), - self.qint_branch('q/5/10.0/bugfix/bar'), - self.qint_branch('q/4/10.0/feature/foo'), - self.qint_branch('q/1/10.0/improvement/bar') + self.qint_branch('q/w/13/10.0/improvement/bar2'), + self.qint_branch('q/w/9/10.0/bugfix/bar'), + self.qint_branch('q/w/7/10.0/feature/foo'), + self.qint_branch('q/w/1/10.0/improvement/bar') + ] + }), + ((10, None), { + gwfb.QueueBranch: self.queue_branch('q/10'), + gwfb.QueueIntegrationBranch: [ + self.qint_branch('q/w/13/10/improvement/bar2'), + self.qint_branch('q/w/9/10/bugfix/bar'), + self.qint_branch('q/w/7/10/feature/foo'), + self.qint_branch('q/w/1/10/improvement/bar') ] }), ]) @@ -5025,8 +5225,8 @@ def test_queueing_standard_problem(self): qc.finalize() qc.validate() self.assertEqual(qc._queues, self.standard_solution) - self.assertEqual(qc.queued_prs, [1, 4, 5, 7]) - self.assertEqual(qc.mergeable_prs, [1, 4, 5, 7]) + self.assertEqual(qc.queued_prs, [1, 7, 9, 13]) + self.assertEqual(qc.mergeable_prs, [1, 7, 9, 13]) self.assertEqual(qc.mergeable_queues, self.standard_solution) def test_queueing_standard_problem_reverse(self): @@ -5035,8 +5235,8 @@ def test_queueing_standard_problem_reverse(self): qc.finalize() qc.validate() self.assertEqual(qc._queues, self.standard_solution) - self.assertEqual(qc.queued_prs, [1, 4, 5, 7]) - self.assertEqual(qc.mergeable_prs, [1, 4, 5, 7]) + self.assertEqual(qc.queued_prs, [1, 7, 9, 13]) + self.assertEqual(qc.mergeable_prs, [1, 7, 9, 13]) self.assertEqual(qc.mergeable_queues, self.standard_solution) def test_queueing_standard_problem_without_octopus(self): @@ -5050,8 +5250,8 @@ def test_queueing_standard_problem_without_octopus(self): qc.finalize() qc.validate() self.assertEqual(qc._queues, self.standard_solution) - self.assertEqual(qc.queued_prs, [1, 4, 5, 7]) - self.assertEqual(qc.mergeable_prs, [1, 4, 5, 7]) + self.assertEqual(qc.queued_prs, [1, 7, 9, 13]) + self.assertEqual(qc.mergeable_prs, [1, 7, 9, 13]) self.assertEqual(qc.mergeable_queues, self.standard_solution) finally: gwfi.octopus_merge = git_utils.octopus_merge @@ -5062,15 +5262,18 @@ def test_queueing_last_pr_build_not_started(self): problem[4]['status'][2] = {} solution = deepcopy(self.standard_solution) solution[(4, 3)][gwfb.QueueIntegrationBranch].pop(0) + solution[(4, None)][gwfb.QueueIntegrationBranch].pop(0) solution[(5, 1)][gwfb.QueueIntegrationBranch].pop(0) + solution[(5, None)][gwfb.QueueIntegrationBranch].pop(0) solution[(10, 0)][gwfb.QueueIntegrationBranch].pop(0) + solution[(10, None)][gwfb.QueueIntegrationBranch].pop(0) qbranches = self.submit_problem(problem) qc = self.feed_queue_collection(qbranches) qc.finalize() qc.validate() self.assertEqual(qc._queues, self.standard_solution) - self.assertEqual(qc.queued_prs, [1, 4, 5, 7]) - self.assertEqual(qc.mergeable_prs, [1, 4, 5]) + self.assertEqual(qc.queued_prs, [1, 7, 9, 13]) + self.assertEqual(qc.mergeable_prs, [1, 7, 9]) self.assertEqual(qc.mergeable_queues, solution) def test_queueing_last_pr_build_failed(self): @@ -5078,15 +5281,18 @@ def test_queueing_last_pr_build_failed(self): problem[4]['status'][2] = {'pipeline': 'FAILED'} solution = deepcopy(self.standard_solution) solution[(4, 3)][gwfb.QueueIntegrationBranch].pop(0) + solution[(4, None)][gwfb.QueueIntegrationBranch].pop(0) solution[(5, 1)][gwfb.QueueIntegrationBranch].pop(0) + solution[(5, None)][gwfb.QueueIntegrationBranch].pop(0) solution[(10, 0)][gwfb.QueueIntegrationBranch].pop(0) + solution[(10, None)][gwfb.QueueIntegrationBranch].pop(0) qbranches = self.submit_problem(problem) qc = self.feed_queue_collection(qbranches) qc.finalize() qc.validate() self.assertEqual(qc._queues, self.standard_solution) - self.assertEqual(qc.queued_prs, [1, 4, 5, 7]) - self.assertEqual(qc.mergeable_prs, [1, 4, 5]) + self.assertEqual(qc.queued_prs, [1, 7, 9, 13]) + self.assertEqual(qc.mergeable_prs, [1, 7, 9]) self.assertEqual(qc.mergeable_queues, solution) def test_queueing_last_pr_other_key(self): @@ -5094,15 +5300,18 @@ def test_queueing_last_pr_other_key(self): problem[4]['status'][2] = {'other': 'SUCCESSFUL'} solution = deepcopy(self.standard_solution) solution[(4, 3)][gwfb.QueueIntegrationBranch].pop(0) + solution[(4, None)][gwfb.QueueIntegrationBranch].pop(0) solution[(5, 1)][gwfb.QueueIntegrationBranch].pop(0) + solution[(5, None)][gwfb.QueueIntegrationBranch].pop(0) solution[(10, 0)][gwfb.QueueIntegrationBranch].pop(0) + solution[(10, None)][gwfb.QueueIntegrationBranch].pop(0) qbranches = self.submit_problem(problem) qc = self.feed_queue_collection(qbranches) qc.finalize() qc.validate() self.assertEqual(qc._queues, self.standard_solution) - self.assertEqual(qc.queued_prs, [1, 4, 5, 7]) - self.assertEqual(qc.mergeable_prs, [1, 4, 5]) + self.assertEqual(qc.queued_prs, [1, 7, 9, 13]) + self.assertEqual(qc.mergeable_prs, [1, 7, 9]) self.assertEqual(qc.mergeable_queues, solution) def test_queueing_fail_masked_by_success(self): @@ -5115,8 +5324,8 @@ def test_queueing_fail_masked_by_success(self): qc.finalize() qc.validate() self.assertEqual(qc._queues, self.standard_solution) - self.assertEqual(qc.queued_prs, [1, 4, 5, 7]) - self.assertEqual(qc.mergeable_prs, [1, 4, 5, 7]) + self.assertEqual(qc.queued_prs, [1, 7, 9, 13]) + self.assertEqual(qc.mergeable_prs, [1, 7, 9, 13]) self.assertEqual(qc.mergeable_queues, self.standard_solution) def test_queueing_all_failed(self): @@ -5129,7 +5338,7 @@ def test_queueing_all_failed(self): qc.finalize() qc.validate() self.assertEqual(qc._queues, self.standard_solution) - self.assertEqual(qc.queued_prs, [1, 4, 5, 7]) + self.assertEqual(qc.queued_prs, [1, 7, 9, 13]) self.assertEqual(qc.mergeable_prs, []) self.assertEqual(qc.mergeable_queues, self.empty_solution) @@ -5143,7 +5352,7 @@ def test_queueing_all_inprogress(self): qc.finalize() qc.validate() self.assertEqual(qc._queues, self.standard_solution) - self.assertEqual(qc.queued_prs, [1, 4, 5, 7]) + self.assertEqual(qc.queued_prs, [1, 7, 9, 13]) self.assertEqual(qc.mergeable_prs, []) self.assertEqual(qc.mergeable_queues, self.empty_solution) @@ -5157,7 +5366,7 @@ def test_queueing_mixed_fails(self): qc.finalize() qc.validate() self.assertEqual(qc._queues, self.standard_solution) - self.assertEqual(qc.queued_prs, [1, 4, 5, 7]) + self.assertEqual(qc.queued_prs, [1, 7, 9, 13]) self.assertEqual(qc.mergeable_prs, []) self.assertEqual(qc.mergeable_queues, self.empty_solution) @@ -5186,7 +5395,7 @@ def test_queues_not_validated(self): qc = self.feed_queue_collection(qbranches) qc.finalize() with self.assertRaises(exns.QueuesNotValidated): - qc.mergeable_prs == [1, 4, 5, 7] + qc.mergeable_prs == [1, 7, 9, 13] def assert_error_codes(self, excp, errors): msg = excp.exception.args[0] @@ -5270,7 +5479,7 @@ def test_validation_masterq_diverged(self): def test_validation_vertical_inclusion(self): qbranches = self.submit_problem(self.standard_problem) - add_file_to_branch(self.gitrepo, 'q/7/5.1/improvement/bar2', + add_file_to_branch(self.gitrepo, 'q/w/13/5.1/improvement/bar2', 'file_pushed_without_bert-e.txt', do_push=True) qc = self.feed_queue_collection(qbranches) qc.finalize() @@ -5282,7 +5491,7 @@ def test_validation_vertical_inclusion(self): def test_validation_with_missing_first_intq(self): self.skipTest("skipping until completeness check is implemented") qbranches = self.submit_problem(self.standard_problem) - qbranches.remove('q/1/4.3/improvement/bar') + qbranches.remove('q/w/1/4.3/improvement/bar') qc = self.feed_queue_collection(qbranches) qc.finalize() with self.assertRaises(exns.IncoherentQueues) as excp: @@ -5291,7 +5500,7 @@ def test_validation_with_missing_first_intq(self): def test_validation_with_missing_middle_intq(self): qbranches = self.submit_problem(self.standard_problem) - qbranches.remove('q/1/5.1/improvement/bar') + qbranches.remove('q/w/1/5.1/improvement/bar') qc = self.feed_queue_collection(qbranches) qc.finalize() with self.assertRaises(exns.IncoherentQueues) as excp: @@ -5315,30 +5524,53 @@ def test_validation_with_stabilization_branch(self): ((4, 3), { gwfb.QueueBranch: self.queue_branch('q/4.3'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/7/4.3/bugfix/last') + self.qint_branch('q/w/12/4.3/bugfix/last') + ] + }), + ((4, None), { + gwfb.QueueBranch: self.queue_branch('q/4'), + gwfb.QueueIntegrationBranch: [ + self.qint_branch('q/w/12/4/bugfix/last') ] }), ((5, 1, 4), { gwfb.QueueBranch: self.queue_branch('q/5.1.4'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/4/5.1.4/bugfix/foo') + self.qint_branch('q/w/7/5.1.4/bugfix/foo') ] }), ((5, 1), { gwfb.QueueBranch: self.queue_branch('q/5.1'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/7/5.1/bugfix/last'), - self.qint_branch('q/4/5.1/bugfix/foo'), - self.qint_branch('q/1/5.1/bugfix/bar') + self.qint_branch('q/w/12/5.1/bugfix/last'), + self.qint_branch('q/w/7/5.1/bugfix/foo'), + self.qint_branch('q/w/1/5.1/bugfix/bar') + ] + }), + ((5, None), { + gwfb.QueueBranch: self.queue_branch('q/5'), + gwfb.QueueIntegrationBranch: [ + self.qint_branch('q/w/12/5/bugfix/last'), + self.qint_branch('q/w/7/5/bugfix/foo'), + self.qint_branch('q/w/1/5/bugfix/bar') ] }), ((10, 0), { gwfb.QueueBranch: self.queue_branch('q/10.0'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/7/10.0/bugfix/last'), - self.qint_branch('q/4/10.0/bugfix/foo'), - self.qint_branch('q/3/10.0/feature/foo'), - self.qint_branch('q/1/10.0/bugfix/bar') + self.qint_branch('q/w/12/10.0/bugfix/last'), + self.qint_branch('q/w/7/10.0/bugfix/foo'), + self.qint_branch('q/w/5/10.0/feature/foo'), + self.qint_branch('q/w/1/10.0/bugfix/bar') + ] + }), + ((10, None), { + gwfb.QueueBranch: self.queue_branch('q/10'), + gwfb.QueueIntegrationBranch: [ + self.qint_branch('q/w/12/10/bugfix/last'), + self.qint_branch('q/w/7/10/bugfix/foo'), + self.qint_branch('q/w/5/10/feature/foo'), + self.qint_branch('q/w/1/10/bugfix/bar') ] }), ]) @@ -5347,7 +5579,7 @@ def test_validation_with_stabilization_branch(self): qc.finalize() qc.validate() self.assertEqual(qc._queues, solution) - self.assertEqual(qc.mergeable_prs, [1, 3, 4, 7]) + self.assertEqual(qc.mergeable_prs, [1, 5, 7, 12]) self.assertEqual(qc.mergeable_queues, solution) def test_validation_with_failed_stabilization_branch(self): @@ -5369,27 +5601,47 @@ def test_validation_with_failed_stabilization_branch(self): ((4, 3), { gwfb.QueueBranch: self.queue_branch('q/4.3'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/4/4.3/bugfix/targeting_old'), + self.qint_branch('q/w/6/4.3/bugfix/targeting_old'), + ] + }), + ((4, None), { + gwfb.QueueBranch: self.queue_branch('q/4'), + gwfb.QueueIntegrationBranch: [ + self.qint_branch('q/w/6/4/bugfix/targeting_old'), ] }), ((5, 1, 4), { gwfb.QueueBranch: self.queue_branch('q/5.1.4'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/1/5.1.4/bugfix/targeting_stab'), + self.qint_branch('q/w/1/5.1.4/bugfix/targeting_stab'), ] }), ((5, 1), { gwfb.QueueBranch: self.queue_branch('q/5.1'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/4/5.1/bugfix/targeting_old'), - self.qint_branch('q/1/5.1/bugfix/targeting_stab'), + self.qint_branch('q/w/6/5.1/bugfix/targeting_old'), + self.qint_branch('q/w/1/5.1/bugfix/targeting_stab'), + ] + }), + ((5, None), { + gwfb.QueueBranch: self.queue_branch('q/5'), + gwfb.QueueIntegrationBranch: [ + self.qint_branch('q/w/6/5/bugfix/targeting_old'), + self.qint_branch('q/w/1/5/bugfix/targeting_stab'), ] }), ((10, 0), { gwfb.QueueBranch: self.queue_branch('q/10.0'), gwfb.QueueIntegrationBranch: [ - self.qint_branch('q/4/10.0/bugfix/targeting_old'), - self.qint_branch('q/1/10.0/bugfix/targeting_stab'), + self.qint_branch('q/w/6/10.0/bugfix/targeting_old'), + self.qint_branch('q/w/1/10.0/bugfix/targeting_stab'), + ] + }), + ((10, None), { + gwfb.QueueBranch: self.queue_branch('q/10'), + gwfb.QueueIntegrationBranch: [ + self.qint_branch('q/w/6/10/bugfix/targeting_old'), + self.qint_branch('q/w/1/10/bugfix/targeting_stab'), ] }), ]) @@ -5400,6 +5652,10 @@ def test_validation_with_failed_stabilization_branch(self): gwfb.QueueBranch: self.queue_branch('q/4.3'), gwfb.QueueIntegrationBranch: [] }), + ((4, None), { + gwfb.QueueBranch: self.queue_branch('q/4'), + gwfb.QueueIntegrationBranch: [] + }), ((5, 1, 4), { gwfb.QueueBranch: self.queue_branch('q/5.1.4'), gwfb.QueueIntegrationBranch: [] @@ -5408,10 +5664,18 @@ def test_validation_with_failed_stabilization_branch(self): gwfb.QueueBranch: self.queue_branch('q/5.1'), gwfb.QueueIntegrationBranch: [] }), + ((5, None), { + gwfb.QueueBranch: self.queue_branch('q/5'), + gwfb.QueueIntegrationBranch: [] + }), ((10, 0), { gwfb.QueueBranch: self.queue_branch('q/10.0'), gwfb.QueueIntegrationBranch: [] }), + ((10, None), { + gwfb.QueueBranch: self.queue_branch('q/10'), + gwfb.QueueIntegrationBranch: [] + }), ]) self.assertEqual(qc.mergeable_queues, solution) @@ -5450,7 +5714,7 @@ def test_notify_pr_on_queue_fail(self): pr = self.create_pr('bugfix/TEST-01', 'development/4.3') with self.assertRaises(exns.Queued): self.handle(pr.id, options=self.bypass_all, backtrace=True) - branch = f"q/{pr.id}/4.3/{pr.src_branch}" + branch = f"q/w/{pr.id}/4.3/{pr.src_branch}" self.set_build_status_on_branch_tip(branch, 'INPROGRESS') with self.assertRaises(exns.NothingToDo): self.handle(pr.id, options=self.bypass_all, backtrace=True) @@ -5462,20 +5726,19 @@ def test_notify_pr_on_queue_fail(self): assert "Queue build failed" in comment def test_system_nominal_case(self): - pr = self.create_pr('bugfix/TEST-00001', 'development/4.3') + pr = self.create_pr('bugfix/TEST-00001', 'development/5') self.handle(pr.id, options=self.bypass_all_but(['bypass_build_status'])) # add a commit to w/5.1 branch self.gitrepo.cmd('git fetch') - self.gitrepo.cmd('git checkout w/5.1/bugfix/TEST-00001') + self.gitrepo.cmd('git checkout w/10.0/bugfix/TEST-00001') self.gitrepo.cmd('touch abc') self.gitrepo.cmd('git add abc') self.gitrepo.cmd('git commit -m "add new file"') self.gitrepo.cmd('git push origin') - sha1_w_5_1 = self.gitrepo \ - .cmd('git rev-parse w/5.1/bugfix/TEST-00001') \ - .rstrip() + sha1_w_10_0 = self.gitrepo.cmd( + 'git rev-parse w/10.0/bugfix/TEST-00001').rstrip() with self.assertRaises(exns.Queued): self.handle(pr.id, options=self.bypass_all, backtrace=True) @@ -5483,56 +5746,55 @@ def test_system_nominal_case(self): # get the new sha1 on w/10.0 (set_build_status_on_pr_id won't # detect the new commit in mocked mode) self.gitrepo.cmd('git fetch') - self.gitrepo.cmd('git checkout w/10.0/bugfix/TEST-00001') + self.gitrepo.cmd('git checkout w/10/bugfix/TEST-00001') self.gitrepo.cmd('git pull') - sha1_w_10_0 = self.gitrepo \ - .cmd('git rev-parse w/10.0/bugfix/TEST-00001') \ - .rstrip() + sha1_w_10 = self.gitrepo.cmd( + 'git rev-parse w/10/bugfix/TEST-00001').rstrip() # check expected branches exist self.gitrepo.cmd('git fetch --prune') expected_branches = [ - 'q/1/4.3/bugfix/TEST-00001', - 'q/1/5.1/bugfix/TEST-00001', - 'q/1/10.0/bugfix/TEST-00001', - 'w/5.1/bugfix/TEST-00001', - 'w/10.0/bugfix/TEST-00001' + 'q/w/1/5/bugfix/TEST-00001', + 'q/w/1/10.0/bugfix/TEST-00001', + 'q/w/1/10/bugfix/TEST-00001', + 'w/10.0/bugfix/TEST-00001', + 'w/10/bugfix/TEST-00001' ] for branch in expected_branches: self.assertTrue(self.gitrepo.remote_branch_exists(branch)) # set build status self.set_build_status_on_pr_id(pr.id, 'SUCCESSFUL') - self.set_build_status(sha1=sha1_w_5_1, state='SUCCESSFUL') - self.set_build_status(sha1=sha1_w_10_0, state='FAILED') + self.set_build_status(sha1=sha1_w_10_0, state='SUCCESSFUL') + self.set_build_status(sha1=sha1_w_10, state='FAILED') with self.assertRaises(exns.QueueBuildFailed): self.handle(pr.id, options=self.bypass_all, backtrace=True) with self.assertRaises(exns.QueueBuildFailed): self.handle(pr.src_commit, options=self.bypass_all, backtrace=True) - self.set_build_status(sha1=sha1_w_10_0, state='INPROGRESS') + self.set_build_status(sha1=sha1_w_10, state='INPROGRESS') with self.assertRaises(exns.NothingToDo): self.handle(pr.src_commit, options=self.bypass_all, backtrace=True) - self.set_build_status(sha1=sha1_w_10_0, state='SUCCESSFUL') + self.set_build_status(sha1=sha1_w_10, state='SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(pr.src_commit, options=self.bypass_all, backtrace=True) # check validity of repo and branches - for branch in ['q/4.3', 'q/5.1', 'q/10.0']: + for branch in ['q/5', 'q/10.0', 'q/10']: self.assertTrue(self.gitrepo.remote_branch_exists(branch)) for branch in expected_branches: self.assertFalse(self.gitrepo.remote_branch_exists(branch, True)) - for dev in ['development/4.3', 'development/5.1', 'development/10.0']: + for dev in ['development/5', 'development/10.0', 'development/10']: branch = gwfb.branch_factory(self.gitrepo, dev) branch.checkout() self.gitrepo.cmd('git pull origin %s', dev) self.assertTrue(branch.includes_commit(pr.src_commit)) - if dev == 'development/4.3': - self.assertFalse(branch.includes_commit(sha1_w_5_1)) + if dev == 'development/5': + self.assertFalse(branch.includes_commit(sha1_w_10_0)) else: - self.assertTrue(branch.includes_commit(sha1_w_5_1)) + self.assertTrue(branch.includes_commit(sha1_w_10_0)) self.gitrepo.cmd('cat abc') last_comment = pr.comments[-1].text @@ -5545,7 +5807,7 @@ def test_system_missing_integration_queue_before_in_queue(self): pr2 = self.create_pr('bugfix/TEST-00002', 'development/4.3') - self.gitrepo.cmd('git push origin :q/1/5.1/bugfix/TEST-00001') + self.gitrepo.cmd('git push origin :q/w/1/5.1/bugfix/TEST-00001') with self.assertRaises(exns.QueueOutOfOrder): self.handle(pr2.id, options=self.bypass_all, backtrace=True) @@ -5591,7 +5853,7 @@ def test_reconstruction(self): self.handle(pr2.id, options=self.bypass_all, backtrace=True) def test_decline_queued_pull_request(self): - pr = self.create_pr('bugfix/TEST-00001', 'development/5.1') + pr = self.create_pr('bugfix/TEST-00001', 'development/10.0') with self.assertRaises(exns.Queued): self.handle(pr.id, options=self.bypass_all, backtrace=True) @@ -5607,7 +5869,7 @@ def test_decline_queued_pull_request(self): self.handle(pr.src_commit, options=self.bypass_all, backtrace=True) def test_lose_integration_branches_after_queued(self): - pr = self.create_pr('bugfix/TEST-00001', 'development/5.1') + pr = self.create_pr('bugfix/TEST-00001', 'development/10.0') with self.assertRaises(exns.Queued): self.handle(pr.id, options=self.bypass_all, backtrace=True) @@ -5616,8 +5878,8 @@ def test_lose_integration_branches_after_queued(self): # delete integration branch self.gitrepo.cmd('git fetch') - dev = gwfb.branch_factory(self.gitrepo, 'development/10.0') - intb = gwfb.branch_factory(self.gitrepo, 'w/10.0/bugfix/TEST-00001') + dev = gwfb.branch_factory(self.gitrepo, 'development/10') + intb = gwfb.branch_factory(self.gitrepo, 'w/10/bugfix/TEST-00001') intb.dst_branch = dev intb.checkout() intb.remove(do_push=True) @@ -5636,14 +5898,16 @@ def test_last_stab_branch(self): self.handle(pr.id, options=self.bypass_all, backtrace=True) self.set_build_status_on_pr_id(pr.id, 'SUCCESSFUL') self.set_build_status_on_pr_id(pr.id + 1, 'SUCCESSFUL') + self.set_build_status_on_pr_id(pr.id + 2, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(pr.id, options=self.bypass_all, backtrace=True) - # Now let's try to merge a PR targetting development/5.1 - pr_dev = self.create_pr('bugfix/TEST-002', 'development/5.1') + # Now let's try to merge a PR targetting development/5 + pr_dev = self.create_pr('bugfix/TEST-002', 'development/5') with self.assertRaises(exns.Queued): self.handle(pr_dev.id, options=self.bypass_all, backtrace=True) self.set_build_status_on_pr_id(pr_dev.id, 'SUCCESSFUL') self.set_build_status_on_pr_id(pr_dev.id + 1, 'SUCCESSFUL') + self.set_build_status_on_pr_id(pr_dev.id + 2, 'SUCCESSFUL') # When merging the PR it will fail with KeyError with self.assertRaises(exns.Merged): self.handle(pr_dev.id, options=self.bypass_all, backtrace=True) @@ -5670,13 +5934,13 @@ def test_delete_all_integration_queues_of_one_pull_request(self): self.gitrepo.cmd('git fetch') dev = gwfb.branch_factory(self.gitrepo, 'development/10.0') intq1 = gwfb.branch_factory( - self.gitrepo, 'q/1/10.0/bugfix/TEST-00001') + self.gitrepo, 'q/w/1/10.0/bugfix/TEST-00001') intq1.checkout() dev.checkout() intq1.remove(do_push=True) sha1 = self.set_build_status_on_branch_tip( - 'q/3/10.0/bugfix/TEST-00002', 'SUCCESSFUL') + 'q/w/3/10.0/bugfix/TEST-00002', 'SUCCESSFUL') with self.assertRaises(exns.IncoherentQueues): self.handle(sha1, options=self.bypass_all, backtrace=True) @@ -5703,7 +5967,7 @@ def test_delete_main_queues(self): self.handle(pr.src_commit, options=self.bypass_all, backtrace=True) def test_feature_branch_augmented_after_queued(self): - pr = self.create_pr('bugfix/TEST-00001', 'development/10.0') + pr = self.create_pr('bugfix/TEST-00001', 'development/10') with self.assertRaises(exns.Queued): self.handle(pr.id, options=self.bypass_all, backtrace=True) @@ -5738,12 +6002,12 @@ def test_feature_branch_augmented_after_queued(self): self.gitrepo.cmd('git checkout bugfix/TEST-00001') self.gitrepo.cmd('git pull') self.gitrepo.cmd('cat abc') - self.gitrepo.cmd('git checkout q/10.0') + self.gitrepo.cmd('git checkout q/10') self.gitrepo.cmd('git pull') self.gitrepo.cmd('cat abc') def test_feature_branch_rewritten_after_queued(self): - pr = self.create_pr('bugfix/TEST-00001', 'development/10.0') + pr = self.create_pr('bugfix/TEST-00001', 'development/10') with self.assertRaises(exns.Queued): self.handle(pr.id, options=self.bypass_all, backtrace=True) @@ -5770,7 +6034,7 @@ def test_feature_branch_rewritten_after_queued(self): self.handle(pr.id, options=self.bypass_all, backtrace=True) def test_integration_branch_augmented_after_queued(self): - pr = self.create_pr('bugfix/TEST-00001', 'development/5.1') + pr = self.create_pr('bugfix/TEST-00001', 'development/10.0') with self.assertRaises(exns.Queued): self.handle(pr.id, options=self.bypass_all, backtrace=True) @@ -5779,12 +6043,12 @@ def test_integration_branch_augmented_after_queued(self): # Add a new commit self.gitrepo.cmd('git fetch') - self.gitrepo.cmd('git checkout w/10.0/bugfix/TEST-00001') + self.gitrepo.cmd('git checkout w/10/bugfix/TEST-00001') self.gitrepo.cmd('touch abc') self.gitrepo.cmd('git add abc') self.gitrepo.cmd('git commit -m "add new file"') sha1 = Branch(self.gitrepo, - 'w/10.0/bugfix/TEST-00001').get_latest_commit() + 'w/10/bugfix/TEST-00001').get_latest_commit() self.gitrepo.cmd('git push origin') with self.assertRaises(exns.Merged): @@ -5796,10 +6060,10 @@ def test_integration_branch_augmented_after_queued(self): self.gitrepo.cmd('git fetch') # Check the additional commit was not merged self.assertFalse( - Branch(self.gitrepo, 'development/10.0').includes_commit(sha1)) + Branch(self.gitrepo, 'development/10').includes_commit(sha1)) def test_integration_branches_dont_follow_dev(self): - pr1 = self.create_pr('bugfix/TEST-00001', 'development/4.3') + pr1 = self.create_pr('bugfix/TEST-00001', 'development/5') # create integration branches but don't queue yet self.handle(pr1.id, options=self.bypass_all_but(['bypass_build_status'])) @@ -5807,7 +6071,7 @@ def test_integration_branches_dont_follow_dev(self): # get the sha1's of integration branches self.gitrepo.cmd('git fetch') sha1s = dict() - for version in ['5.1', '10.0']: + for version in ['10.0', '10']: self.gitrepo.cmd('git checkout w/%s/bugfix/TEST-00001', version) self.gitrepo.cmd('git pull') sha1s[version] = self.gitrepo \ @@ -5815,7 +6079,7 @@ def test_integration_branches_dont_follow_dev(self): .rstrip() # merge some other work - pr2 = self.create_pr('bugfix/TEST-00002', 'development/5.1') + pr2 = self.create_pr('bugfix/TEST-00002', 'development/10.0') with self.assertRaises(exns.Queued): self.handle(pr2.id, options=self.bypass_all, backtrace=True) self.set_build_status_on_pr_id(pr2.id, 'SUCCESSFUL') @@ -5830,7 +6094,7 @@ def test_integration_branches_dont_follow_dev(self): # verify self.gitrepo.cmd('git fetch') - for version in ['5.1', '10.0']: + for version in ['10.0', '10']: self.gitrepo.cmd('git checkout w/%s/bugfix/TEST-00001', version) self.gitrepo.cmd('git pull') self.assertEqual( @@ -5882,7 +6146,7 @@ def prs_in_queue(self): def test_new_stab_branch_appears(self): # introduce a new version self.gitrepo.cmd('git fetch') - self.gitrepo.cmd('git checkout development/10.0') + self.gitrepo.cmd('git checkout development/5') self.gitrepo.cmd('git checkout -b development/5.2') self.gitrepo.cmd('git push -u origin development/5.2') @@ -5892,10 +6156,12 @@ def test_new_stab_branch_appears(self): self.set_build_status_on_pr_id(pr1.id, 'SUCCESSFUL') self.set_build_status_on_pr_id(pr1.id + 1, 'SUCCESSFUL') + self.set_build_status_on_pr_id(pr1.id + 2, 'SUCCESSFUL') + self.set_build_status_on_pr_id(pr1.id + 3, 'SUCCESSFUL') # introduce a new stab, but not its queue branches self.gitrepo.cmd('git fetch') - self.gitrepo.cmd('git checkout development/10.0') + self.gitrepo.cmd('git checkout development/5') self.gitrepo.cmd('git checkout -b stabilization/5.2.0') self.gitrepo.cmd('git push -u origin stabilization/5.2.0') @@ -5911,12 +6177,9 @@ def test_new_stab_branch_appears(self): self.assertEqual(self.prs_in_queue(), {pr2.id}) - self.set_build_status_on_branch_tip( - 'q/%d/5.2.0/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') - self.set_build_status_on_branch_tip( - 'q/%d/5.2/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') - self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') + for queue in ['5.2.0', '5.2', '5', '10.0', '10']: + self.set_build_status_on_branch_tip( + f'q/w/{pr2.id}/{queue}/bugfix/TEST-00002', 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(pr2.src_commit, options=self.bypass_all, @@ -5929,10 +6192,10 @@ def test_pr_dev_and_hotfix_with_hotfix_merged_first(self): self.gitrepo.cmd('git push --tags') self.gitrepo.cmd('git push origin :stabilization/10.0.0') - pr0 = self.create_pr('bugfix/TEST-00000', 'development/5.1') + pr0 = self.create_pr('bugfix/TEST-00000', 'development/10.0') with self.assertRaises(exns.Queued): self.handle(pr0.id, options=self.bypass_all, backtrace=True) - pr1 = self.create_pr('bugfix/TEST-00001', 'development/10.0') + pr1 = self.create_pr('bugfix/TEST-00001', 'development/10') with self.assertRaises(exns.Queued): self.handle(pr1.id, options=self.bypass_all, backtrace=True) pr2 = self.create_pr('bugfix/TEST-00002', 'hotfix/10.0.0') @@ -5942,29 +6205,30 @@ def test_pr_dev_and_hotfix_with_hotfix_merged_first(self): self.assertEqual(self.prs_in_queue(), {pr0.id, pr1.id, pr2.id}) self.set_build_status_on_branch_tip( - 'q/%d/5.1/bugfix/TEST-00000' % pr0.id, 'FAILED') + 'q/w/%d/10.0/bugfix/TEST-00000' % pr0.id, 'FAILED') self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00000' % pr0.id, 'FAILED') + 'q/w/%d/10/bugfix/TEST-00000' % pr0.id, 'FAILED') self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00001' % pr1.id, 'FAILED') + 'q/w/%d/10/bugfix/TEST-00001' % pr1.id, 'FAILED') sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0.0.1/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') + 'q/w/%d/10.0.0.1/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr0.id, pr1.id}) sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') with self.assertRaises(exns.QueueBuildFailed): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr0.id, pr1.id}) self.set_build_status_on_branch_tip( - 'q/%d/5.1/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') + 'q/w/%d/10.0/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') + self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) @@ -5975,10 +6239,10 @@ def test_pr_dev_and_hotfix_with_dev_merged_first(self): self.gitrepo.cmd('git push --tags') self.gitrepo.cmd('git push origin :stabilization/10.0.0') - pr0 = self.create_pr('bugfix/TEST-00000', 'development/5.1') + pr0 = self.create_pr('bugfix/TEST-00000', 'development/10.0') with self.assertRaises(exns.Queued): self.handle(pr0.id, options=self.bypass_all, backtrace=True) - pr1 = self.create_pr('bugfix/TEST-00001', 'development/10.0') + pr1 = self.create_pr('bugfix/TEST-00001', 'development/10') with self.assertRaises(exns.Queued): self.handle(pr1.id, options=self.bypass_all, backtrace=True) pr2 = self.create_pr('bugfix/TEST-00002', 'hotfix/10.0.0') @@ -5988,20 +6252,20 @@ def test_pr_dev_and_hotfix_with_dev_merged_first(self): self.assertEqual(self.prs_in_queue(), {pr0.id, pr1.id, pr2.id}) self.set_build_status_on_branch_tip( - 'q/%d/5.1/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') + 'q/w/%d/10.0/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0.0.1/bugfix/TEST-00002' % pr2.id, 'FAILED') + 'q/w/%d/10.0.0.1/bugfix/TEST-00002' % pr2.id, 'FAILED') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr2.id}) sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0.0.1/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') + 'q/w/%d/10.0.0.1/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) @@ -6022,16 +6286,12 @@ def test_pr_stab_and_hotfix_merged_in_the_same_time(self): self.assertEqual(self.prs_in_queue(), {pr0.id, pr1.id}) - self.set_build_status_on_branch_tip( - 'q/%d/4.3.18/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') - self.set_build_status_on_branch_tip( - 'q/%d/4.3/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') - self.set_build_status_on_branch_tip( - 'q/%d/5.1/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') - self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') + for queue in ['4.3.18', '4.3', '4', '5.1', '5', '10.0', '10']: + self.set_build_status_on_branch_tip( + f'q/w/{pr0.id}/{queue}/bugfix/TEST-00000', 'SUCCESSFUL') + sha1 = self.set_build_status_on_branch_tip( - 'q/%d/4.3.17.3/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') + 'q/w/%d/4.3.17.3/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) @@ -6042,10 +6302,10 @@ def test_pr_dev_and_hotfix_merged_in_the_same_time(self): self.gitrepo.cmd('git push --tags') self.gitrepo.cmd('git push origin :stabilization/10.0.0') - pr0 = self.create_pr('bugfix/TEST-00000', 'development/5.1') + pr0 = self.create_pr('bugfix/TEST-00000', 'development/10.0') with self.assertRaises(exns.Queued): self.handle(pr0.id, options=self.bypass_all, backtrace=True) - pr1 = self.create_pr('bugfix/TEST-00001', 'development/10.0') + pr1 = self.create_pr('bugfix/TEST-00001', 'development/10') with self.assertRaises(exns.Queued): self.handle(pr1.id, options=self.bypass_all, backtrace=True) pr2 = self.create_pr('bugfix/TEST-00002', 'hotfix/10.0.0') @@ -6055,13 +6315,13 @@ def test_pr_dev_and_hotfix_merged_in_the_same_time(self): self.assertEqual(self.prs_in_queue(), {pr0.id, pr1.id, pr2.id}) self.set_build_status_on_branch_tip( - 'q/%d/5.1/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') + 'q/w/%d/10.0/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0.0.1/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') + 'q/w/%d/10.0.0.1/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) @@ -6078,33 +6338,33 @@ def test_pr_hotfix_alone(self): self.assertEqual(self.prs_in_queue(), {pr0.id}) sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0.0.1/bugfix/TEST-00000' % pr0.id, 'FAILED') + 'q/w/%d/10.0.0.1/bugfix/TEST-00000' % pr0.id, 'FAILED') with self.assertRaises(exns.QueueBuildFailed): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr0.id}) sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0.0.1/bugfix/TEST-00000' % pr0.id, 'INPROGRESS') + 'q/w/%d/10.0.0.1/bugfix/TEST-00000' % pr0.id, 'INPROGRESS') with self.assertRaises(exns.NothingToDo): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr0.id}) sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0.0.1/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') + 'q/w/%d/10.0.0.1/bugfix/TEST-00000' % pr0.id, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), set()) def test_multi_branch_queues(self): - pr1 = self.create_pr('bugfix/TEST-00001', 'development/4.3') + pr1 = self.create_pr('bugfix/TEST-00001', 'development/5') with self.assertRaises(exns.Queued): self.handle(pr1.id, options=self.bypass_all, backtrace=True) - pr2 = self.create_pr('bugfix/TEST-00002', 'stabilization/5.1.4') + pr2 = self.create_pr('bugfix/TEST-00002', 'stabilization/10.0.0') with self.assertRaises(exns.Queued): self.handle(pr2.id, options=self.bypass_all, backtrace=True) - pr3 = self.create_pr('bugfix/TEST-00003', 'development/4.3') + pr3 = self.create_pr('bugfix/TEST-00003', 'development/5') with self.assertRaises(exns.Queued): self.handle(pr3.id, options=self.bypass_all, backtrace=True) @@ -6116,25 +6376,25 @@ def test_multi_branch_queues(self): {pr1.id, pr2.id, pr3.id, pr4217.id}) self.set_build_status_on_branch_tip( - 'q/%d/4.3/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') + 'q/w/%d/5/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/5.1/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') + 'q/w/%d/10.0/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00001' % pr1.id, 'FAILED') + 'q/w/%d/10/bugfix/TEST-00001' % pr1.id, 'FAILED') self.set_build_status_on_branch_tip( - 'q/%d/5.1.4/bugfix/TEST-00002' % pr2.id, 'FAILED') + 'q/w/%d/10.0.0/bugfix/TEST-00002' % pr2.id, 'FAILED') self.set_build_status_on_branch_tip( - 'q/%d/5.1/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') + 'q/w/%d/10.0/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00002' % pr2.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/4.3/bugfix/TEST-00003' % pr3.id, 'SUCCESSFUL') + 'q/w/%d/5/bugfix/TEST-00003' % pr3.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/5.1/bugfix/TEST-00003' % pr3.id, 'SUCCESSFUL') + 'q/w/%d/10.0/bugfix/TEST-00003' % pr3.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/4.2.17.1/bugfix/TEST-00004217' % pr4217.id, 'FAILED') + 'q/w/%d/4.2.17.1/bugfix/TEST-00004217' % pr4217.id, 'FAILED') sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00003' % pr3.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00003' % pr3.id, 'SUCCESSFUL') with self.assertRaises(exns.QueueBuildFailed): self.handle(sha1, options=self.bypass_all, backtrace=True) @@ -6142,18 +6402,18 @@ def test_multi_branch_queues(self): pr4217.id}) self.set_build_status_on_branch_tip( - 'q/%d/4.2.17.1/bugfix/TEST-00004217' % pr4217.id, 'SUCCESSFUL') + 'q/w/%d/4.2.17.1/bugfix/TEST-00004217' % pr4217.id, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr1.id, pr2.id, pr3.id}) self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr2.id, pr3.id}) - pr4 = self.create_pr('bugfix/TEST-00004', 'stabilization/5.1.4') + pr4 = self.create_pr('bugfix/TEST-00004', 'stabilization/10.0.0') with self.assertRaises(exns.Queued): self.handle(pr4.id, options=self.bypass_all, backtrace=True) with self.assertRaises(exns.NothingToDo): @@ -6161,22 +6421,22 @@ def test_multi_branch_queues(self): self.assertEqual(self.prs_in_queue(), {pr2.id, pr3.id, pr4.id}) self.set_build_status_on_branch_tip( - 'q/%d/5.1.4/bugfix/TEST-00004' % pr4.id, 'SUCCESSFUL') + 'q/w/%d/10.0.0/bugfix/TEST-00004' % pr4.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/5.1/bugfix/TEST-00004' % pr4.id, 'SUCCESSFUL') + 'q/w/%d/10.0/bugfix/TEST-00004' % pr4.id, 'SUCCESSFUL') sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00004' % pr4.id, 'FAILED') + 'q/w/%d/10/bugfix/TEST-00004' % pr4.id, 'FAILED') with self.assertRaises(exns.QueueBuildFailed): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr2.id, pr3.id, pr4.id}) - pr5 = self.create_pr('bugfix/TEST-00005', 'development/10.0') + pr5 = self.create_pr('bugfix/TEST-00005', 'development/10') with self.assertRaises(exns.Queued): self.handle(pr5.id, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr2.id, pr3.id, pr4.id, pr5.id}) sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00005' % pr5.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00005' % pr5.id, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) @@ -6188,36 +6448,36 @@ def test_multi_branch_queues(self): with self.assertRaises(exns.DeprecatedStabilizationBranch): self.handle(pr1000.id, options=self.bypass_all, backtrace=True) - self.gitrepo.cmd('git push origin :stabilization/10.0.0') + self.gitrepo.cmd('git push origin :stabilization/10.0.0 :q/10.0.0') with self.assertRaises(exns.Queued): self.handle(pr1000.id, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr1000.id}) sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0.0.1/bugfix/TEST-00001000' % pr1000.id, 'SUCCESSFUL') + 'q/w/%d/10.0.0.1/bugfix/TEST-00001000' % pr1000.id, 'SUCCESSFUL') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), set()) def test_multi_branch_queues_2(self): - pr1 = self.create_pr('bugfix/TEST-00001', 'development/4.3') + pr1 = self.create_pr('bugfix/TEST-00001', 'development/5') with self.assertRaises(exns.Queued): self.handle(pr1.id, options=self.bypass_all, backtrace=True) - pr2 = self.create_pr('bugfix/TEST-00002', 'development/10.0') + pr2 = self.create_pr('bugfix/TEST-00002', 'development/10') with self.assertRaises(exns.Queued): self.handle(pr2.id, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr1.id, pr2.id}) self.set_build_status_on_branch_tip( - 'q/%d/4.3/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') + 'q/w/%d/5/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/5.1/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') + 'q/w/%d/10.0/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') + 'q/w/%d/10/bugfix/TEST-00001' % pr1.id, 'SUCCESSFUL') sha1 = self.set_build_status_on_branch_tip( - 'q/%d/10.0/bugfix/TEST-00002' % pr2.id, 'FAILED') + 'q/w/%d/10/bugfix/TEST-00002' % pr2.id, 'FAILED') with self.assertRaises(exns.Merged): self.handle(sha1, options=self.bypass_all, backtrace=True) self.assertEqual(self.prs_in_queue(), {pr2.id}) @@ -6357,11 +6617,17 @@ def test_status_with_queue(self): # check expected branches exist self.gitrepo.cmd('git fetch --prune') expected_branches = [ - 'q/1/4.3/bugfix/TEST-00001', - 'q/1/5.1/bugfix/TEST-00001', - 'q/1/10.0/bugfix/TEST-00001', + 'q/w/1/4.3/bugfix/TEST-00001', + 'q/w/1/4/bugfix/TEST-00001', + 'q/w/1/5.1/bugfix/TEST-00001', + 'q/w/1/5/bugfix/TEST-00001', + 'q/w/1/10.0/bugfix/TEST-00001', + 'q/w/1/10/bugfix/TEST-00001', + 'w/4/bugfix/TEST-00001', 'w/5.1/bugfix/TEST-00001', - 'w/10.0/bugfix/TEST-00001' + 'w/5/bugfix/TEST-00001', + 'w/10.0/bugfix/TEST-00001', + 'w/10/bugfix/TEST-00001' ] for branch in expected_branches: self.assertTrue(self.gitrepo.remote_branch_exists(branch), @@ -6373,9 +6639,9 @@ def test_status_with_queue(self): status = self.berte.status.get('merge queue', OrderedDict()) self.assertIn(1, status) - self.assertEqual(len(status[1]), 3) + self.assertEqual(len(status[1]), 6) versions = tuple(version for version, _ in status[1]) - self.assertEqual(versions, ('10.0', '5.1', '4.3')) + self.assertEqual(versions, ('10', '10.0', '5', '5.1', '4', '4.3')) for _, sha1 in status[1]: self.set_build_status(sha1=sha1, state='SUCCESSFUL') self.process_sha1_job(sha1_q_10_0, 'Merged') @@ -6397,11 +6663,17 @@ def test_status_with_queue_without_octopus(self): # check expected branches exist self.gitrepo.cmd('git fetch --prune') expected_branches = [ - 'q/1/4.3/bugfix/TEST-00001', - 'q/1/5.1/bugfix/TEST-00001', - 'q/1/10.0/bugfix/TEST-00001', + 'q/w/1/4.3/bugfix/TEST-00001', + 'q/w/1/4/bugfix/TEST-00001', + 'q/w/1/5.1/bugfix/TEST-00001', + 'q/w/1/5/bugfix/TEST-00001', + 'q/w/1/10.0/bugfix/TEST-00001', + 'q/w/1/10/bugfix/TEST-00001', + 'w/4/bugfix/TEST-00001', 'w/5.1/bugfix/TEST-00001', - 'w/10.0/bugfix/TEST-00001' + 'w/5/bugfix/TEST-00001', + 'w/10.0/bugfix/TEST-00001', + 'w/10/bugfix/TEST-00001' ] for branch in expected_branches: self.assertTrue(self.gitrepo.remote_branch_exists(branch), @@ -6415,7 +6687,7 @@ def test_status_with_queue_without_octopus(self): versions = tuple(version for version, _ in status[1]) for _, sha1 in status[1]: self.set_build_status(sha1=sha1, state='SUCCESSFUL') - self.assertEqual(versions, ('10.0', '5.1', '4.3')) + self.assertEqual(versions, ('10', '10.0', '5', '5.1', '4', '4.3')) self.process_sha1_job(sha1_q_10_0, 'Merged') merged_pr = self.berte.status.get('merged PRs', []) @@ -6742,10 +7014,10 @@ def test_job_create_branch_dev_start(self): 'q/4.3', 'q/5.1', 'q/10.0', - 'q/1/2.9/feature/TEST-9999', - 'q/1/4.3/feature/TEST-9999', - 'q/1/5.1/feature/TEST-9999', - 'q/1/10.0/feature/TEST-9999', + 'q/w/1/2.9/feature/TEST-9999', + 'q/w/1/4.3/feature/TEST-9999', + 'q/w/1/5.1/feature/TEST-9999', + 'q/w/1/10.0/feature/TEST-9999', ] self.gitrepo._get_remote_branches(force=True) for branch in expected_branches: @@ -6783,9 +7055,9 @@ def test_job_create_branch_dev_middle(self): 'q/5.1', 'q/5.2', 'q/10.0', - 'q/1/5.1/feature/TEST-9999', - 'q/1/5.2/feature/TEST-9999', - 'q/1/10.0/feature/TEST-9999', + 'q/w/1/5.1/feature/TEST-9999', + 'q/w/1/5.2/feature/TEST-9999', + 'q/w/1/10.0/feature/TEST-9999', ] self.gitrepo._get_remote_branches(force=True) for branch in expected_branches: @@ -6819,7 +7091,7 @@ def test_job_create_branch_dev_end(self): self.gitrepo._get_remote_branches(force=True) self.assertEqual( self.gitrepo._remote_branches['development/11.3'], - self.gitrepo._remote_branches['development/10.0'] + self.gitrepo._remote_branches['development/10'] ) # consume the 3 expected pr jobs sitting in the job queue @@ -6844,24 +7116,36 @@ def test_job_create_branch_dev_end(self): 'q/4.3.18', 'q/10.0', 'q/11.3', - 'q/1/4.3/feature/TEST-01', - 'q/1/5.1/feature/TEST-01', - 'q/1/10.0/feature/TEST-01', - 'q/1/11.3/feature/TEST-01', - 'q/2/4.3/feature/TEST-02', - 'q/2/5.1/feature/TEST-02', - 'q/2/10.0/feature/TEST-02', - 'q/2/11.3/feature/TEST-02', - 'q/3/4.3/feature/TEST-03', - 'q/3/5.1/feature/TEST-03', - 'q/3/10.0/feature/TEST-03', - 'q/3/11.3/feature/TEST-03', - 'q/13/11.3/feature/TEST-9997', - 'q/14/4.3.18/feature/TEST-9998', - 'q/14/4.3/feature/TEST-9998', - 'q/14/5.1/feature/TEST-9998', - 'q/14/10.0/feature/TEST-9998', - 'q/14/11.3/feature/TEST-9998', + 'q/w/1/4.3/feature/TEST-01', + 'q/w/1/4/feature/TEST-01', + 'q/w/1/5.1/feature/TEST-01', + 'q/w/1/5/feature/TEST-01', + 'q/w/1/10.0/feature/TEST-01', + 'q/w/1/10/feature/TEST-01', + 'q/w/1/11.3/feature/TEST-01', + 'q/w/2/4.3/feature/TEST-02', + 'q/w/2/4/feature/TEST-02', + 'q/w/2/5.1/feature/TEST-02', + 'q/w/2/5/feature/TEST-02', + 'q/w/2/10.0/feature/TEST-02', + 'q/w/2/10/feature/TEST-02', + 'q/w/2/11.3/feature/TEST-02', + 'q/w/3/4.3/feature/TEST-03', + 'q/w/3/4/feature/TEST-03', + 'q/w/3/5.1/feature/TEST-03', + 'q/w/3/5/feature/TEST-03', + 'q/w/3/10.0/feature/TEST-03', + 'q/w/3/10/feature/TEST-03', + 'q/w/3/11.3/feature/TEST-03', + 'q/w/22/11.3/feature/TEST-9997', + 'q/w/23/4.3.18/feature/TEST-9998', + 'q/w/23/4.3/feature/TEST-9998', + 'q/w/23/4/feature/TEST-9998', + 'q/w/23/5.1/feature/TEST-9998', + 'q/w/23/5/feature/TEST-9998', + 'q/w/23/10.0/feature/TEST-9998', + 'q/w/23/10/feature/TEST-9998', + 'q/w/23/11.3/feature/TEST-9998', ] self.gitrepo._get_remote_branches(force=True) for branch in expected_branches: @@ -6870,7 +7154,7 @@ def test_job_create_branch_dev_end(self): # merge everything so that branches advance # and also to allow creation of intermediary dest branches - sha1_middle = self.gitrepo._remote_branches['q/11.3'] + sha1_middle = self.gitrepo._remote_branches['q/10'] self.process_job(ForceMergeQueuesJob(bert_e=self.berte), 'Merged') # test a branch creation with source specified @@ -6885,7 +7169,7 @@ def test_job_create_branch_dev_end(self): CreateBranchJob( settings={ 'branch': 'development/10.2', - 'branch_from': 'development/11.3'}, + 'branch_from': 'development/10'}, bert_e=self.berte), 'JobSuccess') @@ -6897,7 +7181,7 @@ def test_job_create_branch_dev_end(self): ) self.assertEqual( self.gitrepo._remote_branches['development/10.2'], - self.gitrepo._remote_branches['development/11.3'] + self.gitrepo._remote_branches['development/10'] ) # one last PR to check the repo is in order @@ -6952,14 +7236,22 @@ def test_job_delete_branch(self): ('development/5.1', self.assertTrue), ('development/10.0', self.assertTrue), ('q/10.0', self.assertTrue), - ('q/1/5.1/feature/TEST-01', self.assertTrue), - ('q/1/10.0/feature/TEST-01', self.assertTrue), - ('q/2/5.1/feature/TEST-02', self.assertTrue), - ('q/2/10.0/feature/TEST-02', self.assertTrue), - ('q/3/5.1/feature/TEST-03', self.assertTrue), - ('q/3/10.0/feature/TEST-03', self.assertTrue), - ('q/7/5.1/feature/TEST-9998', self.assertTrue), - ('q/7/10.0/feature/TEST-9998', self.assertTrue), + ('q/w/1/5.1/feature/TEST-01', self.assertTrue), + ('q/w/1/5/feature/TEST-01', self.assertTrue), + ('q/w/1/10.0/feature/TEST-01', self.assertTrue), + ('q/w/1/10/feature/TEST-01', self.assertTrue), + ('q/w/2/5.1/feature/TEST-02', self.assertTrue), + ('q/w/2/5/feature/TEST-02', self.assertTrue), + ('q/w/2/10.0/feature/TEST-02', self.assertTrue), + ('q/w/2/10/feature/TEST-02', self.assertTrue), + ('q/w/3/5.1/feature/TEST-03', self.assertTrue), + ('q/w/3/5/feature/TEST-03', self.assertTrue), + ('q/w/3/10.0/feature/TEST-03', self.assertTrue), + ('q/w/3/10/feature/TEST-03', self.assertTrue), + ('q/w/13/5.1/feature/TEST-9998', self.assertTrue), + ('q/w/13/5/feature/TEST-9998', self.assertTrue), + ('q/w/13/10.0/feature/TEST-9998', self.assertTrue), + ('q/w/13/10/feature/TEST-9998', self.assertTrue), ] self.gitrepo._get_remote_branches(force=True) for branch, func in expected_branches: @@ -7066,13 +7358,16 @@ def test_job_create_branch_stab(self): 'development/10.0', 'q/4.3.18', 'q/4.3', - 'q/5.1', + 'q/4', 'q/5.1.4', + 'q/5.1', + 'q/5', 'q/10.0', - 'q/16/4.3.18/feature/TEST-9999', - 'q/16/4.3/feature/TEST-9999', - 'q/16/5.1/feature/TEST-9999', - 'q/16/10.0/feature/TEST-9999', + 'q/10', + 'q/w/30/4.3.18/feature/TEST-9999', + 'q/w/30/4.3/feature/TEST-9999', + 'q/w/30/5.1/feature/TEST-9999', + 'q/w/30/10.0/feature/TEST-9999', ] self.gitrepo._get_remote_branches(force=True) for branch in expected_branches: @@ -7196,7 +7491,7 @@ def test_job_force_merge_queues_with_hotfix(self): prs.append(self.create_pr('feature/TEST-666', 'hotfix/4.2.17')) for pr in prs: - self.process_pr_job(pr, 'Queued') + self.process_pr_job(pr) # put a mix of build statuses in the queue self.gitrepo._get_remote_branches() @@ -7242,15 +7537,15 @@ def test_job_delete_queues(self): 'q/4.3', 'q/5.1', 'q/10.0', - 'q/1/4.3/feature/TEST-01', - 'q/1/5.1/feature/TEST-01', - 'q/1/10.0/feature/TEST-01', - 'q/2/4.3/feature/TEST-02', - 'q/2/5.1/feature/TEST-02', - 'q/2/10.0/feature/TEST-02', - 'q/3/4.3/feature/TEST-03', - 'q/3/5.1/feature/TEST-03', - 'q/3/10.0/feature/TEST-03', + 'q/w/1/4.3/feature/TEST-01', + 'q/w/1/5.1/feature/TEST-01', + 'q/w/1/10.0/feature/TEST-01', + 'q/w/2/4.3/feature/TEST-02', + 'q/w/2/5.1/feature/TEST-02', + 'q/w/2/10.0/feature/TEST-02', + 'q/w/3/4.3/feature/TEST-03', + 'q/w/3/5.1/feature/TEST-03', + 'q/w/3/10.0/feature/TEST-03', ] # Check that all PRs are queued @@ -7290,23 +7585,14 @@ def test_job_delete_queues_with_hotfix(self): prs.append(self.create_pr('feature/TEST-666', 'hotfix/4.2.17')) for pr in prs: - self.process_pr_job(pr, 'Queued') + self.process_pr_job(pr) expected_branches = [ 'q/4.2.17.1', 'q/4.3', 'q/5.1', 'q/10.0', - 'q/1/4.3/feature/TEST-01', - 'q/1/5.1/feature/TEST-01', - 'q/1/10.0/feature/TEST-01', - 'q/2/4.3/feature/TEST-02', - 'q/2/5.1/feature/TEST-02', - 'q/2/10.0/feature/TEST-02', - 'q/3/4.3/feature/TEST-03', - 'q/3/5.1/feature/TEST-03', - 'q/3/10.0/feature/TEST-03', - 'q/4/4.2.17.1/feature/TEST-666', + ] # Check that all PRs are queued @@ -7349,15 +7635,15 @@ def test_job_rebuild_queues(self): 'q/4.3', 'q/5.1', 'q/10.0', - 'q/1/4.3/feature/TEST-01', - 'q/1/5.1/feature/TEST-01', - 'q/1/10.0/feature/TEST-01', - 'q/2/4.3/feature/TEST-02', - 'q/2/5.1/feature/TEST-02', - 'q/2/10.0/feature/TEST-02', - 'q/3/4.3/feature/TEST-03', - 'q/3/5.1/feature/TEST-03', - 'q/3/10.0/feature/TEST-03', + 'q/w/1/4.3/feature/TEST-01', + 'q/w/1/5.1/feature/TEST-01', + 'q/w/1/10.0/feature/TEST-01', + 'q/w/2/4.3/feature/TEST-02', + 'q/w/2/5.1/feature/TEST-02', + 'q/w/2/10.0/feature/TEST-02', + 'q/w/3/4.3/feature/TEST-03', + 'q/w/3/5.1/feature/TEST-03', + 'q/w/3/10.0/feature/TEST-03', ] # Check that all PRs are queued @@ -7387,18 +7673,18 @@ def test_job_rebuild_queues(self): 'q/4.3', 'q/5.1', 'q/10.0', - 'q/2/4.3/feature/TEST-02', - 'q/2/5.1/feature/TEST-02', - 'q/2/10.0/feature/TEST-02', - 'q/3/4.3/feature/TEST-03', - 'q/3/5.1/feature/TEST-03', - 'q/3/10.0/feature/TEST-03', + 'q/w/2/4.3/feature/TEST-02', + 'q/w/2/5.1/feature/TEST-02', + 'q/w/2/10.0/feature/TEST-02', + 'q/w/3/4.3/feature/TEST-03', + 'q/w/3/5.1/feature/TEST-03', + 'q/w/3/10.0/feature/TEST-03', ] excluded_branches = [ - 'q/1/4.3/feature/TEST-01', - 'q/1/5.1/feature/TEST-01', - 'q/1/10.0/feature/TEST-01', + 'q/w/1/4.3/feature/TEST-01', + 'q/w/1/5.1/feature/TEST-01', + 'q/w/1/10.0/feature/TEST-01', ] # Check that all 'requeued' PRs are queued again @@ -7425,8 +7711,8 @@ def test_job_rebuild_queues_with_hotfix(self): # Create a couple PRs and queue them prs = [ - self.create_pr('feature/TEST-{:02d}'.format(n), 'development/4.3') - for n in range(1, 4) + self.create_pr('feature/TEST-{:02d}'.format(n), 'development/10.0') + for n in range(1, 5) ] # Add a hotfix PR @@ -7437,19 +7723,16 @@ def test_job_rebuild_queues_with_hotfix(self): expected_branches = [ 'q/4.2.17.1', - 'q/4.3', - 'q/5.1', 'q/10.0', - 'q/1/4.3/feature/TEST-01', - 'q/1/5.1/feature/TEST-01', - 'q/1/10.0/feature/TEST-01', - 'q/2/4.3/feature/TEST-02', - 'q/2/5.1/feature/TEST-02', - 'q/2/10.0/feature/TEST-02', - 'q/3/4.3/feature/TEST-03', - 'q/3/5.1/feature/TEST-03', - 'q/3/10.0/feature/TEST-03', - 'q/4/4.2.17.1/feature/TEST-666', + 'q/10', + 'q/w/1/10.0/feature/TEST-01', + 'q/w/1/10/feature/TEST-01', + 'q/w/2/10.0/feature/TEST-02', + 'q/w/2/10/feature/TEST-02', + 'q/w/3/10.0/feature/TEST-03', + 'q/w/3/10/feature/TEST-03', + 'q/w/5/4.2.17.1/feature/TEST-666', + ] # Check that all PRs are queued @@ -7477,22 +7760,18 @@ def test_job_rebuild_queues_with_hotfix(self): expected_branches = [ 'q/4.2.17.1', - 'q/4.3', - 'q/5.1', 'q/10.0', - 'q/2/4.3/feature/TEST-02', - 'q/2/5.1/feature/TEST-02', - 'q/2/10.0/feature/TEST-02', - 'q/3/4.3/feature/TEST-03', - 'q/3/5.1/feature/TEST-03', - 'q/3/10.0/feature/TEST-03', - 'q/4/4.2.17.1/feature/TEST-666', + 'q/10', + 'q/w/2/10.0/feature/TEST-02', + 'q/w/2/10/feature/TEST-02', + 'q/w/3/10.0/feature/TEST-03', + 'q/w/3/10/feature/TEST-03', + 'q/w/5/4.2.17.1/feature/TEST-666', ] excluded_branches = [ - 'q/1/4.3/feature/TEST-01', - 'q/1/5.1/feature/TEST-01', - 'q/1/10.0/feature/TEST-01', + 'q/w/1/10.0/feature/TEST-01', + 'q/w/1/10/feature/TEST-01', ] # Check that all 'requeued' PRs are queued again @@ -7519,31 +7798,37 @@ def test_job_rebuild_queues_without_hotfix(self): # Create a couple PRs and queue them prs = [ - self.create_pr('feature/TEST-{:02d}'.format(n), 'development/4.3') + self.create_pr('feature/TEST-{:02d}'.format(n), 'development/5.1') for n in range(1, 4) ] + # create a 5.0.3 tag on hotfix/5.0.3 + self.gitrepo.cmd('git tag 5.0.3 origin/hotfix/5.0.3') + self.gitrepo.cmd('git push --tags') # Add a hotfix PR - prs.append(self.create_pr('feature/TEST-666', 'hotfix/4.2.17')) + prs.append(self.create_pr('feature/TEST-666', 'hotfix/5.0.3')) for pr in prs: self.process_pr_job(pr, 'Queued') expected_branches = [ - 'q/4.2.17.1', - 'q/4.3', + 'q/5.0.3.1', 'q/5.1', + 'q/5', 'q/10.0', - 'q/1/4.3/feature/TEST-01', - 'q/1/5.1/feature/TEST-01', - 'q/1/10.0/feature/TEST-01', - 'q/2/4.3/feature/TEST-02', - 'q/2/5.1/feature/TEST-02', - 'q/2/10.0/feature/TEST-02', - 'q/3/4.3/feature/TEST-03', - 'q/3/5.1/feature/TEST-03', - 'q/3/10.0/feature/TEST-03', - 'q/4/4.2.17.1/feature/TEST-666', + 'q/w/1/5.1/feature/TEST-01', + 'q/w/1/5/feature/TEST-01', + 'q/w/1/10.0/feature/TEST-01', + 'q/w/1/10/feature/TEST-01', + 'q/w/2/5.1/feature/TEST-02', + 'q/w/2/5/feature/TEST-02', + 'q/w/2/10.0/feature/TEST-02', + 'q/w/2/10/feature/TEST-02', + 'q/w/3/5.1/feature/TEST-03', + 'q/w/3/5/feature/TEST-03', + 'q/w/3/10.0/feature/TEST-03', + 'q/w/3/10/feature/TEST-03', + 'q/w/4/5.0.3.1/feature/TEST-666', ] # Check that all PRs are queued @@ -7565,20 +7850,27 @@ def test_job_rebuild_queues_without_hotfix(self): self.berte.process_task() expected_branches = [ - 'q/4.3', 'q/5.1', + 'q/5', 'q/10.0', - 'q/2/4.3/feature/TEST-02', - 'q/2/5.1/feature/TEST-02', - 'q/2/10.0/feature/TEST-02', - 'q/3/4.3/feature/TEST-03', - 'q/3/5.1/feature/TEST-03', - 'q/3/10.0/feature/TEST-03', + 'q/w/1/5.1/feature/TEST-01', + 'q/w/1/5/feature/TEST-01', + 'q/w/1/10.0/feature/TEST-01', + 'q/w/1/10/feature/TEST-01', + 'q/w/2/5.1/feature/TEST-02', + 'q/w/2/5/feature/TEST-02', + 'q/w/2/10.0/feature/TEST-02', + 'q/w/2/10/feature/TEST-02', + 'q/w/3/5.1/feature/TEST-03', + 'q/w/3/5/feature/TEST-03', + 'q/w/3/10.0/feature/TEST-03', + 'q/w/3/10/feature/TEST-03', ] excluded_branches = [ - 'q/4.2.17.1', - 'q/4/4.2.17.1/feature/TEST-666', + 'q/5.0.3.1', + 'q/w/4/5.0.3.1/feature/TEST-666', + ] # Check that all 'requeued' PRs are queued again @@ -7649,6 +7941,25 @@ def test_flakiness(self): last_comment = pr.get_comments()[-1].text self.assertTrue(FLAKINESS_MESSAGE_TITLE in last_comment) + def test_delete_queue_no_major(self): + self.init_berte(options=self.bypass_all) + # delete the development/10 branch + self.gitrepo.cmd('git push origin :development/10') + expected_branches = [ + 'q/10.0', + ] + for i in range(3): + pr = self.create_pr('feature/TEST-000%d' % i, 'development/10.0') + self.process_pr_job(pr, 'Queued') + expected_branches.append(f'q/w/{pr.id}/10.0/feature/TEST-000{i}') + for branch in expected_branches: + self.assertTrue(self.gitrepo.remote_branch_exists(branch), + 'branch %s not found' % branch) + self.process_job(DeleteQueuesJob(bert_e=self.berte), 'JobSuccess') + for branch in expected_branches: + self.assertFalse(self.gitrepo.remote_branch_exists(branch, True), + 'branch %s still exists' % branch) + def main(): parser = argparse.ArgumentParser(description='Launches Bert-E tests.') @@ -7701,7 +8012,8 @@ def main(): jira_api.JiraIssue = jira_api_mock.JiraIssue if RepositoryTests.args.verbose: - logging.basicConfig(level=logging.DEBUG) + # only the message in the format string will be displayed + logging.basicConfig(level=logging.DEBUG, format="%(message)s") else: # it is expected that Bert-E issues some warning # during the tests, only report critical stuff diff --git a/bert_e/tests/test_server.py b/bert_e/tests/test_server.py index 2358dd80..31edf0da 100644 --- a/bert_e/tests/test_server.py +++ b/bert_e/tests/test_server.py @@ -236,45 +236,54 @@ def set_status_cache(self, sha1, state, url, def test_merge_queue_print(self): server.BERTE.status['merge queue'] = OrderedDict([ (4472, [ + ('6', '4472/6'), ('6.4', '4472/6.4'), ('6.3', '4472/6.3'), ('6.2', '4472/6.2')]), ('5773', [ + ('6', '5773/6'), ('6.4', '5773/6.4')]), ('6050', [ + ('6', '6050/6'), ('6.4', '6050/6.4')]), ('6086', [ + ('6', '6086/6'), ('6.4', '6086/6.4'), ('6.3.0', '6086/6.3.0'), ('6.3', '6086/6.3')]), ('5095', [ + ('6', '5095/6'), ('6.4', '5095/6.4')]), ]) + self.set_status_cache('4472/6', 'SUCCESSFUL', '4472/6_url') self.set_status_cache('4472/6.4', 'SUCCESSFUL', '4472/6.4_url') self.set_status_cache('4472/6.3', 'SUCCESSFUL', '4472/6.3_url') self.set_status_cache('4472/6.2', 'INPROGRESS', '4472/6.2_url') + self.set_status_cache('5773/6', 'FAILED', '5773/6_url') self.set_status_cache('5773/6.4', 'FAILED', '5773/6.4_url') + self.set_status_cache('6050/6', 'SUCCESSFUL', '6050/6_url') self.set_status_cache('6050/6.4', 'SUCCESSFUL', '6050/6.4_url') + self.set_status_cache('6086/6', 'FAILED', '6086/6_url') self.set_status_cache('6086/6.4', 'FAILED', '6086/6.4_url') self.set_status_cache('6086/6.3.0', 'SUCCESSFUL', '6086/6.3.0_url') self.set_status_cache('6086/6.3', 'SUCCESSFUL', '6086/6.3_url') + self.set_status_cache('5095/6', 'SUCCESSFUL', '5095/6_urltoto') self.set_status_cache('5095/6.4', 'SUCCESSFUL', '5095/6.4_urltoto') expected = ( 'Merge queue status:', - ' 6.4 6.3.0 6.3 6.2', - ' #4472 SUCCESSFUL SUCCESSFUL INPROGRESS \n', # noqa - ' #5773 FAILED \n', # noqa - ' #6050 SUCCESSFUL \n', # noqa - ' #6086 FAILED SUCCESSFUL SUCCESSFUL \n', # noqa - ' #5095 SUCCESSFUL' # noqa + ' 6 6.4 6.3 6.3.0 6.2 \n', # noqa + ' #4472 SUCCESSFUL SUCCESSFUL SUCCESSFUL INPROGRESS \n', # noqa + ' #5773 FAILED FAILED \n', # noqa + ' #6050 SUCCESSFUL SUCCESSFUL \n', # noqa + ' #6086 FAILED FAILED SUCCESSFUL SUCCESSFUL \n', # noqa + ' #5095 SUCCESSFUL SUCCESSFUL' # noqa ) client = self.test_client() res = client.get('/?output=txt') data = res.data.decode() - for exp in expected: self.assertIn(exp, data) @@ -287,17 +296,22 @@ def test_merge_queue_print(self): ' \n' ' \n' ' \n' + ' \n' + ' FAILED\n' # noqa + ' \n' + ' \n' + ' \n' ' \n' ' FAILED\n' # noqa ' \n' ' \n' ' \n' - ' \n' + ' \n' ' SUCCESSFUL\n' # noqa ' \n' ' \n' ' \n' - ' \n' + ' \n' ' SUCCESSFUL\n' # noqa ' \n' ' \n' diff --git a/bert_e/tests/unit/test_reactor.py b/bert_e/tests/unit/test_reactor.py index a9e1f686..b3fc9084 100644 --- a/bert_e/tests/unit/test_reactor.py +++ b/bert_e/tests/unit/test_reactor.py @@ -43,12 +43,11 @@ def test_add_option(reactor_cls): default=False, privileged=True) options = reactor_cls.get_options() - assert 'my_option' in options assert 'other_option' in options assert 'privileged_option' in options - assert len(options) == 3 + assert len(options) == 4 my_option = options['my_option'] other_option = options['other_option'] @@ -99,7 +98,7 @@ def custom_key_option(job, *args): assert 'privileged_option' in options assert 'custom_key' in options - assert len(options) == 5 + assert len(options) == 6 assert options['my_option'].handler == my_option assert options['my_option'].help == my_option.__doc__ @@ -131,9 +130,10 @@ def test_options_behavior(reactor_cls, job): reactor.init_settings(job) assert job.settings == { + 'after_pull_request': set(), 'my_option': None, 'default_option': True, - 'privileged_option': None + 'privileged_option': None, } reactor.handle_options(job, '!set my_option', '!set') diff --git a/bert_e/tests/unit/test_sorted.py b/bert_e/tests/unit/test_sorted.py new file mode 100644 index 00000000..0a96e5bc --- /dev/null +++ b/bert_e/tests/unit/test_sorted.py @@ -0,0 +1,58 @@ + +from functools import cmp_to_key +from collections import OrderedDict +from bert_e.workflow.gitwaterflow.branches import ( + DevelopmentBranch, + StabilizationBranch, + HotfixBranch, + compare_branches +) +from pytest import fixture +from bert_e.lib.versions import version_key + + +@fixture(scope='function') +def branches(): + branches = OrderedDict() + branches[2, 0] = { + DevelopmentBranch: None, + StabilizationBranch: None, + HotfixBranch: None, + } + branches[1, None] = { + DevelopmentBranch: None, + StabilizationBranch: None, + HotfixBranch: None, + } + branches[1, 0] = { + DevelopmentBranch: None, + StabilizationBranch: None, + HotfixBranch: None, + } + branches[1, 1] = { + DevelopmentBranch: None, + StabilizationBranch: None, + HotfixBranch: None, + } + return branches + + +def test_sorted_with_branches(branches): + + sorted_branches = OrderedDict( + sorted(branches.items(), key=cmp_to_key(compare_branches))) + assert list(sorted_branches.keys()) == [(1, 0), (1, 1), (1, None), (2, 0)] + + +def test_sorted_versions(): + versions = [ + '1.0.0', '1.0.1', '1.1.0', '1.1.1', + '2.0.0', '2', '1.0', '3', '1.0.0.3' + ] + expected = [ + '3', '2', '2.0.0', '1.1.1', '1.1.0', + '1.0', '1.0.1', '1.0.0', '1.0.0.3' + ] + sorted_versions = sorted(versions, key=version_key, reverse=True) + + assert sorted_versions == expected diff --git a/bert_e/workflow/gitwaterflow/branches.py b/bert_e/workflow/gitwaterflow/branches.py index 238ec7ec..dbf29c45 100644 --- a/bert_e/workflow/gitwaterflow/branches.py +++ b/bert_e/workflow/gitwaterflow/branches.py @@ -29,6 +29,26 @@ LOG = logging.getLogger(__name__) +def compare_branches(branch1, branch2): + """Compare GitWaterflow branches for sorting. + + Important to note that when a branch has a minor version as None, + it will be considered as the latest version. + """ + + major1, minor1 = branch1[0][:2] + major2, minor2 = branch2[0][:2] + if major1 == major2: + if minor1 == minor2: + return 0 + if minor1 is None: + return 1 + if minor2 is None: + return -1 + return minor1 - minor2 + return major1 - major2 + + def compare_queues(version1, version2): # if we have a stab and its related dev, put the stab first. v1 = version1[0] @@ -39,13 +59,7 @@ def compare_queues(version1, version2): elif len(v2) == 3 and len(v1) == 2: return 1 - # normal sorting - if v1 < v2: - return -1 - elif v1 > v2: - return 1 - else: - return 0 + return compare_branches(version1, version2) class GWFBranch(git.Branch): @@ -151,12 +165,13 @@ def version_t(self): @total_ordering class DevelopmentBranch(GWFBranch): - pattern = r'^development/(?P(?P\d+)\.(?P\d+))$' + pattern = r'^development/(?P(?P\d+)(\.(?P\d+))?)$' cascade_producer = True cascade_consumer = True can_be_destination = True allow_prefixes = FeatureBranch.all_prefixes has_stabilization = False + latest_minor = -1 def __eq__(self, other): return (self.__class__ == other.__class__ and @@ -164,10 +179,21 @@ def __eq__(self, other): self.minor == other.minor) def __lt__(self, other): - return (self.__class__ == other.__class__ and - (self.major < other.major or - (self.major == other.major and - self.minor < other.minor))) + if self.__class__ != other.__class__: + return NotImplemented + if self.major != other.major: + return self.major < other.major + if self.minor is None: + # development/ is greater than development/. + return False + if other.minor is None: + # development/. is less than development/ + return True + return self.minor < other.minor + + @property + def has_minor(self) -> bool: + return self.minor is not None @property def version_t(self): @@ -201,7 +227,7 @@ def version_t(self): class IntegrationBranch(GWFBranch): - pattern = r'^w/(?P(?P\d+)\.(?P\d+)' \ + pattern = r'^w/(?P(?P\d+)(\.(?P\d+))?' \ r'(\.(?P\d+)(\.(?P\d+))?)?)/' + \ FeatureBranch.pattern[1:] dst_branch = '' @@ -267,7 +293,7 @@ def remove(self, do_push=False): class QueueBranch(GWFBranch): - pattern = r'^q/(?P(?P\d+)\.(?P\d+)' \ + pattern = r'^q/(?P(?P\d+)(\.(?P\d+))?' \ r'(\.(?P\d+)(\.(?P\d+))?)?)$' dst_branch = '' @@ -289,7 +315,7 @@ def __eq__(self, other): @total_ordering class QueueIntegrationBranch(GWFBranch): - pattern = r'^q/(?P\d+)/' + IntegrationBranch.pattern[3:] + pattern = r'^q/w/(?P\d+)/' + IntegrationBranch.pattern[3:] def __eq__(self, other): return self.__class__ == other.__class__ and \ @@ -801,8 +827,9 @@ def build(self, repo, dst_branch=None): self.add_branch(branch, dst_branch) for tag in repo.cmd('git tag').split('\n')[:-1]: - self.update_micro(tag) + self.update_versions(tag) + self._update_major_versions() if dst_branch: self.finalize(dst_branch) @@ -863,7 +890,9 @@ def add_branch(self, branch, dst_branch=None): HotfixBranch: None, } # Sort the cascade again - self._cascade = OrderedDict(sorted(self._cascade.items())) + self._cascade = OrderedDict( + sorted(self._cascade.items(), key=cmp_to_key(compare_branches)) + ) cur_branch = self._cascade[(major, minor)][branch.__class__] @@ -872,8 +901,8 @@ def add_branch(self, branch, dst_branch=None): self._cascade[(major, minor)][branch.__class__] = branch - def update_micro(self, tag): - """Update development branch latest micro based on tag.""" + def update_versions(self, tag): + """Update expected versions based on repository tags.""" pattern = r"^v?(?P\d+)\.(?P\d+)\.(?P\d+)" \ r"(\.(?P\d+)|)$" match = re.match(pattern, tag) @@ -888,15 +917,17 @@ def update_micro(self, tag): if match.groupdict()['hfrev'] is not None: hfrev = int(match.groupdict()['hfrev']) - try: - branches = self._cascade[(major, minor)] - except KeyError: + branches = self._cascade.get((major, minor), {}) + major_branches = self._cascade.get((major, None), {}) + + if not branches and not major_branches: LOG.debug("Ignore tag: %s", tag) return - hf_branch = branches[HotfixBranch] - stb_branch = branches[StabilizationBranch] - dev_branch = branches[DevelopmentBranch] + hf_branch: HotfixBranch = branches.get(HotfixBranch) + stb_branch: StabilizationBranch = branches.get(StabilizationBranch) + dev_branch: DevelopmentBranch = branches.get(DevelopmentBranch) + major_branch: DevelopmentBranch = major_branches.get(DevelopmentBranch) if hf_branch: if hf_branch.micro == micro: @@ -919,6 +950,9 @@ def update_micro(self, tag): if dev_branch: dev_branch.micro = max(micro, dev_branch.micro) + if major_branch: + major_branch.latest_minor = max(minor, major_branch.latest_minor) + def validate(self): previous_dev_branch = None for (major, minor), branch_set in self._cascade.items(): @@ -951,6 +985,27 @@ def validate(self): previous_dev_branch = dev_branch + def _update_major_versions(self): + """For every major dev branch, ensure the latest minor is set. + + This function is used on the case where we have a + dev/1 and dev/1.0 branch but no 1.0.0 tag. + In this case, when expecting the next version for dev/1 + we should return 1.1.0 instead of 1.0.0. + + """ + for (_, minor), branch_set in self._cascade.items(): + if minor is not None: + continue + major_branch: DevelopmentBranch = branch_set[DevelopmentBranch] + minors = [ + minor for (m, minor) in self._cascade.keys() + if m == major_branch.major and minor is not None + ] + minors.append(major_branch.latest_minor) + + major_branch.latest_minor = max(minors) + def _set_target_versions(self, dst_branch): """Compute list of expected Jira FixVersion/s. @@ -958,9 +1013,9 @@ def _set_target_versions(self, dst_branch): """ for (major, minor), branch_set in self._cascade.items(): - dev_branch = branch_set[DevelopmentBranch] - stb_branch = branch_set[StabilizationBranch] - hf_branch = branch_set[HotfixBranch] + dev_branch: DevelopmentBranch = branch_set[DevelopmentBranch] + stb_branch: StabilizationBranch = branch_set[StabilizationBranch] + hf_branch: HotfixBranch = branch_set[HotfixBranch] if hf_branch and dst_branch.name.startswith('hotfix/'): self.target_versions.append('%d.%d.%d.%d' % ( @@ -970,16 +1025,23 @@ def _set_target_versions(self, dst_branch): if stb_branch: self.target_versions.append('%d.%d.%d' % ( major, minor, stb_branch.micro)) - elif dev_branch: + elif dev_branch and dev_branch.has_minor is True: offset = 2 if dev_branch.has_stabilization else 1 + self.target_versions.append('%d.%d.%d' % ( major, minor, dev_branch.micro + offset)) + elif dev_branch and dev_branch.has_minor is False: + self.target_versions.append( + f"{major}." + f"{dev_branch.latest_minor + 1}." + f"{dev_branch.micro + 1}" + ) def finalize(self, dst_branch): """Finalize cascade considering given destination. Assumes the cascade has been populated by calls to add_branch - and update_micro. The local lists keeping track + and update_versions. The local lists keeping track Args: dst_branch: where the pull request wants to merge diff --git a/bert_e/workflow/gitwaterflow/queueing.py b/bert_e/workflow/gitwaterflow/queueing.py index 730380ee..ccc4afab 100644 --- a/bert_e/workflow/gitwaterflow/queueing.py +++ b/bert_e/workflow/gitwaterflow/queueing.py @@ -104,14 +104,14 @@ def get_queue_branch(job, dev_branch: DevelopmentBranch, create=True def get_queue_integration_branch(job, pr_id, wbranch: IntegrationBranch ) -> QueueIntegrationBranch: - """Get the q/pr_id/x.y/* branch corresponding to a w/x.y/* branch.""" + """Get the q/w/pr_id/x.y/* branch corresponding to a w/x.y/* branch.""" wbranch_version = None if len(job.git.cascade.dst_branches) == 1 and \ job.git.cascade.dst_branches[0].hfrev > 0: wbranch_version = job.git.cascade.dst_branches[0].version else: wbranch_version = wbranch.version - name = 'q/{}/{}/{}'.format( + name = 'q/w/{}/{}/{}'.format( pr_id, wbranch_version, job.pull_request.src_branch ) return branch_factory(job.git.repo, name) diff --git a/tox.ini b/tox.ini index 5636c105..43fae19f 100644 --- a/tox.ini +++ b/tox.ini @@ -30,6 +30,7 @@ commands = coverage run -am bert_e.tests.test_server [testenv:tests-noqueue] deps = pip==22.3.1 +passenv = CI commands = coverage run -am bert_e.tests.test_bert_e \ -v \ @@ -48,6 +49,7 @@ commands = [testenv:tests] deps = pip==22.3.1 +passenv = CI commands = coverage run -am bert_e.tests.test_bert_e \ -v \