From 3e1f5d0925fc45e697631b2c63325fc840423f0e Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Thu, 18 Jan 2024 23:04:06 +0000 Subject: [PATCH 01/14] First draft to try skipping the queue --- bert_e/workflow/gitwaterflow/__init__.py | 2 +- bert_e/workflow/gitwaterflow/queueing.py | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/bert_e/workflow/gitwaterflow/__init__.py b/bert_e/workflow/gitwaterflow/__init__.py index a5ccd452..4bce3c14 100644 --- a/bert_e/workflow/gitwaterflow/__init__.py +++ b/bert_e/workflow/gitwaterflow/__init__.py @@ -207,7 +207,7 @@ def _handle_pull_request(job: PullRequestJob): # check for conflicts), and all builds were green, and we reached # this point without an error, then all conditions are met to enter # the queue. - if job.settings.use_queue: + if job.settings.use_queue and queueing.is_needed(job, wbranches) is False: # validate current state of queues try: queues = queueing.build_queue_collection(job) diff --git a/bert_e/workflow/gitwaterflow/queueing.py b/bert_e/workflow/gitwaterflow/queueing.py index 2653f406..39ad3109 100644 --- a/bert_e/workflow/gitwaterflow/queueing.py +++ b/bert_e/workflow/gitwaterflow/queueing.py @@ -23,11 +23,11 @@ from ..git_utils import clone_git_repo, consecutive_merge, robust_merge, push from ..pr_utils import send_comment -from .branches import (BranchCascade, DevelopmentBranch, IntegrationBranch, +from .branches import (BranchCascade, DevelopmentBranch, GWFBranch, IntegrationBranch, QueueBranch, QueueIntegrationBranch, branch_factory, build_queue_collection) from .integration import get_integration_branches - +from typing import List LOG = logging.getLogger(__name__) @@ -207,3 +207,23 @@ def close_queued_pull_request(job, pr_id, cascade): except git.RemoveFailedException: # not critical pass + +def is_needed(job, wbranches: List[GWFBranch]): + """This function will analyze the current state of the given PR and the queue + and return a boolean indicating if the PR should pass through the queue + in order to be merged.""" + + queues = build_queue_collection(job) + queues.validate() + + if already_in_queue(job, wbranches) or len(queues.queued_prs) > 0: + return True + + if not job.src_branch.includes_commit(job.dst_branch.get_latest_commit()): + return True + # Check if the wbranches all contain the commits in the dst branches + for branch, dst_branch in zip(wbranches, job.git.cascade.dst_branches): + if not branch.includes_commit(dst_branch.get_latest_commit()): + return True + + return False \ No newline at end of file From 8c406151614c0913c5e3a9f3a195708738d9523a Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Thu, 25 Jan 2024 22:31:54 +0000 Subject: [PATCH 02/14] BERTE-561 working state on skipping queue when not required --- bert_e/tests/test_bert_e.py | 34 ++++++++++++++++++++++ bert_e/workflow/gitwaterflow/__init__.py | 37 +++++++++++++++--------- bert_e/workflow/gitwaterflow/branches.py | 11 +++++++ bert_e/workflow/gitwaterflow/queueing.py | 24 ++++++++------- 4 files changed, 82 insertions(+), 24 deletions(-) diff --git a/bert_e/tests/test_bert_e.py b/bert_e/tests/test_bert_e.py index ecbfc5d9..dd47d3a9 100644 --- a/bert_e/tests/test_bert_e.py +++ b/bert_e/tests/test_bert_e.py @@ -7690,6 +7690,40 @@ def test_job_rebuild_queues_without_hotfix(self): self.assertFalse(self.gitrepo.remote_branch_exists(branch), "branch %s shouldn't exist" % branch) + def test_no_need_queuing(self): + """Expect Bert-E to skip the queue when there is no need to queue.""" + + # Two PRs created at the same time + # At the moment they were created they are both up to date with the + # destination branch + self.init_berte(options=self.bypass_all) + first_pr = self.create_pr('feature/TEST-1', 'development/4.3') + second_pr = self.create_pr('feature/TEST-2', 'development/4.3') + # The first PR is ready to merge, and is expected to merge directly + # without going through the queue. + self.process_pr_job(first_pr, 'SuccessMessage') + # When the second PR is merged we expect it to go through the queue + # as it is no longer up to date with the destination branch. + self.process_pr_job(second_pr, 'Queued') + # At this point the repository should now contain queue branches. + # We force the merge to get everything setup according for the next + # scenario. + self.process_job(ForceMergeQueuesJob(bert_e=self.berte), 'Merged') + # We expect the PR to be merged so there should be nothing left to do. + self.process_pr_job(second_pr, 'NothingToDo') + # We get the local repo setup for a third PR that should be up to + # date with the latest changes. + self.gitrepo.cmd('git checkout development/4.3') + self.gitrepo.cmd('git branch --set-upstream-to=origin/development/4.3') + self.gitrepo.cmd('git pull') + third_pr = self.create_pr('feature/TEST-3', 'development/4.3') + fourth_pr = self.create_pr('feature/TEST-4', 'development/4.3') + # Just like the first PR, we expect this one to be merged directly. + self.process_pr_job(third_pr, 'SuccessMessage') + # Now we want to know if when the queue is a bit late is Bert-E + # capable of reeastablishing the Queue in order, and queue PR number 4. + self.process_pr_job(fourth_pr, 'Queued') + def test_bypass_prefixes(self): self.init_berte() pr = self.create_pr('documentation/stuff', 'development/4.3') diff --git a/bert_e/workflow/gitwaterflow/__init__.py b/bert_e/workflow/gitwaterflow/__init__.py index 4bce3c14..7a0854a8 100644 --- a/bert_e/workflow/gitwaterflow/__init__.py +++ b/bert_e/workflow/gitwaterflow/__init__.py @@ -21,6 +21,7 @@ from bert_e import exceptions as messages from bert_e.job import handler, CommitJob, PullRequestJob, QueuesJob +from bert_e.jobs.delete_queues import delete_queues from bert_e.lib.cli import confirm from bert_e.reactor import Reactor, NotFound, NotPrivileged, NotAuthored from ..git_utils import push, clone_git_repo @@ -202,28 +203,36 @@ def _handle_pull_request(job: PullRequestJob): if interactive and not confirm('Do you want to merge/queue?'): return - # If the integration pull requests were already in sync with the - # feature branch before our last update (which serves as a final - # check for conflicts), and all builds were green, and we reached - # this point without an error, then all conditions are met to enter - # the queue. - if job.settings.use_queue and queueing.is_needed(job, wbranches) is False: + # Check if queueing is needed or if we can merge right away + if job.settings.use_queue: # validate current state of queues try: queues = queueing.build_queue_collection(job) - queues.validate() + is_needed = queueing.is_needed(job, wbranches, queues) except messages.IncoherentQueues as err: raise messages.QueueOutOfOrder( active_options=job.active_options) from err # Enter the merge queue! - queueing.add_to_queue(job, wbranches) + if is_needed: + queues.validate() + queueing.add_to_queue(job, wbranches) + message = messages.Queued( + branches=job.git.cascade.dst_branches, + ignored=job.git.cascade.ignored_branches, + issue=job.git.src_branch.jira_issue_key, + author=job.pull_request.author_display_name, + active_options=job.active_options) + else: + queues.delete() + merge_integration_branches(job, wbranches) + message = messages.SuccessMessage( + branches=job.git.cascade.dst_branches, + ignored=job.git.cascade.ignored_branches, + issue=job.git.src_branch.jira_issue_key, + author=job.pull_request.author_display_name, + active_options=job.active_options) job.git.cascade.validate() - raise messages.Queued( - branches=job.git.cascade.dst_branches, - ignored=job.git.cascade.ignored_branches, - issue=job.git.src_branch.jira_issue_key, - author=job.pull_request.author_display_name, - active_options=job.active_options) + raise message else: merge_integration_branches(job, wbranches) diff --git a/bert_e/workflow/gitwaterflow/branches.py b/bert_e/workflow/gitwaterflow/branches.py index c3587bb3..0555cb37 100644 --- a/bert_e/workflow/gitwaterflow/branches.py +++ b/bert_e/workflow/gitwaterflow/branches.py @@ -743,6 +743,17 @@ def has_version_queued_prs(self, version): return (self._queues.get(version, {}).get(QueueIntegrationBranch) is not None) + def delete(self): + """Delete the queues entirely.""" + + if len(self._queues) > 0: + for branch in self._queues.values(): + # TODO: Review if QueueIntegrationBranch should be removed as well + queue: QueueBranch = branch[QueueBranch] + queue.dst_branch.checkout() + queue.remove(do_push=True) + + class BranchCascade(object): def __init__(self): diff --git a/bert_e/workflow/gitwaterflow/queueing.py b/bert_e/workflow/gitwaterflow/queueing.py index 39ad3109..129815a1 100644 --- a/bert_e/workflow/gitwaterflow/queueing.py +++ b/bert_e/workflow/gitwaterflow/queueing.py @@ -18,13 +18,13 @@ from bert_e import exceptions from bert_e.job import handler as job_handler -from bert_e.job import QueuesJob +from bert_e.job import QueuesJob, PullRequestJob from bert_e.lib import git from ..git_utils import clone_git_repo, consecutive_merge, robust_merge, push from ..pr_utils import send_comment from .branches import (BranchCascade, DevelopmentBranch, GWFBranch, IntegrationBranch, - QueueBranch, QueueIntegrationBranch, + QueueBranch, QueueCollection, QueueIntegrationBranch, branch_factory, build_queue_collection) from .integration import get_integration_branches from typing import List @@ -208,22 +208,26 @@ def close_queued_pull_request(job, pr_id, cascade): # not critical pass -def is_needed(job, wbranches: List[GWFBranch]): - """This function will analyze the current state of the given PR and the queue - and return a boolean indicating if the PR should pass through the queue - in order to be merged.""" +def is_needed(job: PullRequestJob, wbranches: List[GWFBranch], queues: QueueCollection): + """Determine if queuing is required to merge the given PR. - queues = build_queue_collection(job) - queues.validate() + Queuing a pull request should only be done if: + - The PR or the integration branches are not up to date with the destination branch. + - Other PRs are already in the queue. + + Returns: + - True if the PR should be queued. + - False otherwise. + """ if already_in_queue(job, wbranches) or len(queues.queued_prs) > 0: return True - if not job.src_branch.includes_commit(job.dst_branch.get_latest_commit()): + if not job.git.src_branch.includes_commit(job.git.dst_branch.get_latest_commit()): return True # Check if the wbranches all contain the commits in the dst branches for branch, dst_branch in zip(wbranches, job.git.cascade.dst_branches): if not branch.includes_commit(dst_branch.get_latest_commit()): return True - return False \ No newline at end of file + return False From d79bcbb84113117a711b6471e18dafb4323dd255 Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Thu, 25 Jan 2024 22:50:27 +0000 Subject: [PATCH 03/14] quick review --- bert_e/workflow/gitwaterflow/__init__.py | 6 +++++- bert_e/workflow/gitwaterflow/branches.py | 11 +++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/bert_e/workflow/gitwaterflow/__init__.py b/bert_e/workflow/gitwaterflow/__init__.py index 7a0854a8..dbf0bded 100644 --- a/bert_e/workflow/gitwaterflow/__init__.py +++ b/bert_e/workflow/gitwaterflow/__init__.py @@ -203,7 +203,11 @@ def _handle_pull_request(job: PullRequestJob): if interactive and not confirm('Do you want to merge/queue?'): return - # Check if queueing is needed or if we can merge right away + # If the integration pull requests were already in sync with the + # feature branch before our last update (which serves as a final + # check for conflicts), and all builds were green, and we reached + # this point without an error, then all conditions are met to enter + # the queue. if job.settings.use_queue: # validate current state of queues try: diff --git a/bert_e/workflow/gitwaterflow/branches.py b/bert_e/workflow/gitwaterflow/branches.py index 0555cb37..e0d209e5 100644 --- a/bert_e/workflow/gitwaterflow/branches.py +++ b/bert_e/workflow/gitwaterflow/branches.py @@ -746,12 +746,11 @@ def has_version_queued_prs(self, version): def delete(self): """Delete the queues entirely.""" - if len(self._queues) > 0: - for branch in self._queues.values(): - # TODO: Review if QueueIntegrationBranch should be removed as well - queue: QueueBranch = branch[QueueBranch] - queue.dst_branch.checkout() - queue.remove(do_push=True) + for branch in self._queues.values(): + # TODO: Review if QueueIntegrationBranch should be removed as well + queue: QueueBranch = branch[QueueBranch] + queue.dst_branch.checkout() + queue.remove(do_push=True) From 866f32c2b78c7b90fd0443927f4374fba8de453f Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:58:23 +0000 Subject: [PATCH 04/14] Add feature flag to behavior --- bert_e/settings.py | 1 + bert_e/tests/test_bert_e.py | 2 +- bert_e/workflow/gitwaterflow/queueing.py | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/bert_e/settings.py b/bert_e/settings.py index 123a0854..69950059 100644 --- a/bert_e/settings.py +++ b/bert_e/settings.py @@ -184,6 +184,7 @@ class Meta: quiet = fields.Bool(required=False, load_default=False) disable_queues = fields.Bool(required=False, load_default=False) use_queues = fields.Bool(required=False, load_default=True) + skip_queue_when_possible = fields.Bool(required=False, load_default=False) cmd_line_options = fields.List(fields.Str(), load_default=[]) client_id = fields.Str(required=False, load_default='') diff --git a/bert_e/tests/test_bert_e.py b/bert_e/tests/test_bert_e.py index dd47d3a9..477b75a3 100644 --- a/bert_e/tests/test_bert_e.py +++ b/bert_e/tests/test_bert_e.py @@ -7696,7 +7696,7 @@ def test_no_need_queuing(self): # Two PRs created at the same time # At the moment they were created they are both up to date with the # destination branch - self.init_berte(options=self.bypass_all) + self.init_berte(options=self.bypass_all, skip_queue_when_possible=True) first_pr = self.create_pr('feature/TEST-1', 'development/4.3') second_pr = self.create_pr('feature/TEST-2', 'development/4.3') # The first PR is ready to merge, and is expected to merge directly diff --git a/bert_e/workflow/gitwaterflow/queueing.py b/bert_e/workflow/gitwaterflow/queueing.py index 129815a1..70d2c482 100644 --- a/bert_e/workflow/gitwaterflow/queueing.py +++ b/bert_e/workflow/gitwaterflow/queueing.py @@ -220,7 +220,9 @@ def is_needed(job: PullRequestJob, wbranches: List[GWFBranch], queues: QueueColl - False otherwise. """ - if already_in_queue(job, wbranches) or len(queues.queued_prs) > 0: + if (job.settings.skip_queue_when_possible is False or + already_in_queue(job, wbranches) or + len(queues.queued_prs) > 0): return True if not job.git.src_branch.includes_commit(job.git.dst_branch.get_latest_commit()): From fd04a4bb5fbbd38b2f3b49eb61fa9dbeb07877d9 Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:58:40 +0000 Subject: [PATCH 05/14] Fix lint issues --- bert_e/workflow/gitwaterflow/__init__.py | 1 - bert_e/workflow/gitwaterflow/branches.py | 1 - bert_e/workflow/gitwaterflow/queueing.py | 19 +++++++++++++------ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/bert_e/workflow/gitwaterflow/__init__.py b/bert_e/workflow/gitwaterflow/__init__.py index dbf0bded..baa8f8c0 100644 --- a/bert_e/workflow/gitwaterflow/__init__.py +++ b/bert_e/workflow/gitwaterflow/__init__.py @@ -21,7 +21,6 @@ from bert_e import exceptions as messages from bert_e.job import handler, CommitJob, PullRequestJob, QueuesJob -from bert_e.jobs.delete_queues import delete_queues from bert_e.lib.cli import confirm from bert_e.reactor import Reactor, NotFound, NotPrivileged, NotAuthored from ..git_utils import push, clone_git_repo diff --git a/bert_e/workflow/gitwaterflow/branches.py b/bert_e/workflow/gitwaterflow/branches.py index e0d209e5..3c566886 100644 --- a/bert_e/workflow/gitwaterflow/branches.py +++ b/bert_e/workflow/gitwaterflow/branches.py @@ -753,7 +753,6 @@ def delete(self): queue.remove(do_push=True) - class BranchCascade(object): def __init__(self): self._cascade = OrderedDict() diff --git a/bert_e/workflow/gitwaterflow/queueing.py b/bert_e/workflow/gitwaterflow/queueing.py index 70d2c482..ffa0e060 100644 --- a/bert_e/workflow/gitwaterflow/queueing.py +++ b/bert_e/workflow/gitwaterflow/queueing.py @@ -23,9 +23,10 @@ from ..git_utils import clone_git_repo, consecutive_merge, robust_merge, push from ..pr_utils import send_comment -from .branches import (BranchCascade, DevelopmentBranch, GWFBranch, IntegrationBranch, - QueueBranch, QueueCollection, QueueIntegrationBranch, - branch_factory, build_queue_collection) +from .branches import (BranchCascade, DevelopmentBranch, GWFBranch, + IntegrationBranch, QueueBranch, QueueCollection, + QueueIntegrationBranch, branch_factory, + build_queue_collection) from .integration import get_integration_branches from typing import List LOG = logging.getLogger(__name__) @@ -208,11 +209,16 @@ def close_queued_pull_request(job, pr_id, cascade): # not critical pass -def is_needed(job: PullRequestJob, wbranches: List[GWFBranch], queues: QueueCollection): + +def is_needed( + job: PullRequestJob, + wbranches: List[GWFBranch], + queues: QueueCollection): """Determine if queuing is required to merge the given PR. Queuing a pull request should only be done if: - - The PR or the integration branches are not up to date with the destination branch. + - The PR or the integration branches are not up to date + with the destination branch. - Other PRs are already in the queue. Returns: @@ -225,7 +231,8 @@ def is_needed(job: PullRequestJob, wbranches: List[GWFBranch], queues: QueueColl len(queues.queued_prs) > 0): return True - if not job.git.src_branch.includes_commit(job.git.dst_branch.get_latest_commit()): + if not job.git.src_branch.includes_commit( + job.git.dst_branch.get_latest_commit()): return True # Check if the wbranches all contain the commits in the dst branches for branch, dst_branch in zip(wbranches, job.git.cascade.dst_branches): From 6755b41f2796f29d488fe679c8f4d2a673f63344 Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:11:29 +0000 Subject: [PATCH 06/14] Ensure integration queue branch is deleted as well --- bert_e/workflow/gitwaterflow/branches.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bert_e/workflow/gitwaterflow/branches.py b/bert_e/workflow/gitwaterflow/branches.py index 3c566886..0848b924 100644 --- a/bert_e/workflow/gitwaterflow/branches.py +++ b/bert_e/workflow/gitwaterflow/branches.py @@ -747,10 +747,13 @@ def delete(self): """Delete the queues entirely.""" for branch in self._queues.values(): - # TODO: Review if QueueIntegrationBranch should be removed as well queue: QueueBranch = branch[QueueBranch] queue.dst_branch.checkout() queue.remove(do_push=True) + queue_integration: QueueIntegrationBranch | None = branch.get( + QueueIntegrationBranch) + if queue_integration: + queue_integration.remove(do_push=True) class BranchCascade(object): From f86a5bd3f72fa1ecf6c37eb3b017b7dc6fbf645c Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:23:15 +0000 Subject: [PATCH 07/14] Ensure queue validation exception is catched --- bert_e/workflow/gitwaterflow/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bert_e/workflow/gitwaterflow/__init__.py b/bert_e/workflow/gitwaterflow/__init__.py index baa8f8c0..4bd21b1d 100644 --- a/bert_e/workflow/gitwaterflow/__init__.py +++ b/bert_e/workflow/gitwaterflow/__init__.py @@ -209,15 +209,15 @@ def _handle_pull_request(job: PullRequestJob): # the queue. if job.settings.use_queue: # validate current state of queues - try: - queues = queueing.build_queue_collection(job) - is_needed = queueing.is_needed(job, wbranches, queues) - except messages.IncoherentQueues as err: - raise messages.QueueOutOfOrder( - active_options=job.active_options) from err + queues = queueing.build_queue_collection(job) + is_needed = queueing.is_needed(job, wbranches, queues) # Enter the merge queue! if is_needed: - queues.validate() + try: + queues.validate() + except messages.IncoherentQueues as err: + raise messages.QueueOutOfOrder( + active_options=job.active_options) from err queueing.add_to_queue(job, wbranches) message = messages.Queued( branches=job.git.cascade.dst_branches, From f2122740b9d1184d2b2ce88ee645a31ee1f3136e Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:36:59 +0000 Subject: [PATCH 08/14] Try to reduce duplicated code --- bert_e/workflow/gitwaterflow/__init__.py | 47 +++++++++--------------- bert_e/workflow/gitwaterflow/queueing.py | 2 +- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/bert_e/workflow/gitwaterflow/__init__.py b/bert_e/workflow/gitwaterflow/__init__.py index 4bd21b1d..4945587b 100644 --- a/bert_e/workflow/gitwaterflow/__init__.py +++ b/bert_e/workflow/gitwaterflow/__init__.py @@ -207,37 +207,26 @@ def _handle_pull_request(job: PullRequestJob): # check for conflicts), and all builds were green, and we reached # this point without an error, then all conditions are met to enter # the queue. - if job.settings.use_queue: - # validate current state of queues - queues = queueing.build_queue_collection(job) - is_needed = queueing.is_needed(job, wbranches, queues) - # Enter the merge queue! - if is_needed: - try: - queues.validate() - except messages.IncoherentQueues as err: - raise messages.QueueOutOfOrder( - active_options=job.active_options) from err - queueing.add_to_queue(job, wbranches) - message = messages.Queued( - branches=job.git.cascade.dst_branches, - ignored=job.git.cascade.ignored_branches, - issue=job.git.src_branch.jira_issue_key, - author=job.pull_request.author_display_name, - active_options=job.active_options) - else: - queues.delete() - merge_integration_branches(job, wbranches) - message = messages.SuccessMessage( - branches=job.git.cascade.dst_branches, - ignored=job.git.cascade.ignored_branches, - issue=job.git.src_branch.jira_issue_key, - author=job.pull_request.author_display_name, - active_options=job.active_options) - job.git.cascade.validate() - raise message + queues = queueing.build_queue_collection(job) if job.settings.use_queue \ + else None + is_needed = queueing.is_needed(job, wbranches, queues) + if is_needed: + try: + queues.validate() + except messages.IncoherentQueues as err: + raise messages.QueueOutOfOrder( + active_options=job.active_options) from err + queueing.add_to_queue(job, wbranches) + raise messages.Queued( + branches=job.git.cascade.dst_branches, + ignored=job.git.cascade.ignored_branches, + issue=job.git.src_branch.jira_issue_key, + author=job.pull_request.author_display_name, + active_options=job.active_options) else: + if queues is not None: + queues.delete() merge_integration_branches(job, wbranches) job.bert_e.add_merged_pr(job.pull_request.id) job.git.cascade.validate() diff --git a/bert_e/workflow/gitwaterflow/queueing.py b/bert_e/workflow/gitwaterflow/queueing.py index ffa0e060..b1c93792 100644 --- a/bert_e/workflow/gitwaterflow/queueing.py +++ b/bert_e/workflow/gitwaterflow/queueing.py @@ -213,7 +213,7 @@ def close_queued_pull_request(job, pr_id, cascade): def is_needed( job: PullRequestJob, wbranches: List[GWFBranch], - queues: QueueCollection): + queues: QueueCollection | None): """Determine if queuing is required to merge the given PR. Queuing a pull request should only be done if: From 72c117644e403ce2bdf23b14ee36ebad2729015e Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:43:39 +0000 Subject: [PATCH 09/14] Add comments and clarity to the code logic --- bert_e/workflow/gitwaterflow/__init__.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bert_e/workflow/gitwaterflow/__init__.py b/bert_e/workflow/gitwaterflow/__init__.py index 4945587b..f6e809be 100644 --- a/bert_e/workflow/gitwaterflow/__init__.py +++ b/bert_e/workflow/gitwaterflow/__init__.py @@ -209,9 +209,11 @@ def _handle_pull_request(job: PullRequestJob): # the queue. queues = queueing.build_queue_collection(job) if job.settings.use_queue \ else None - is_needed = queueing.is_needed(job, wbranches, queues) - if is_needed: + # If we need to go through the queue, we need to check if we can + # merge the integration branches right away, or if we need to add + # the pull request to the queue. + if queueing.is_needed(job, wbranches, queues): try: queues.validate() except messages.IncoherentQueues as err: @@ -225,6 +227,11 @@ def _handle_pull_request(job: PullRequestJob): author=job.pull_request.author_display_name, active_options=job.active_options) else: + # If we don't need to go through the queue, we can merge the + # integration branches right away. + # But if the bot is configured with the 'use_queue' option, we + # still need to delete the queue to ensure that we don't raise + # IncohorentQueues in the next runs. if queues is not None: queues.delete() merge_integration_branches(job, wbranches) From 4ce2bf08a9a9cde4c5e2d926b92b805da30f5920 Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:48:51 +0000 Subject: [PATCH 10/14] Safety net to verify if queueing is needed --- bert_e/workflow/gitwaterflow/queueing.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bert_e/workflow/gitwaterflow/queueing.py b/bert_e/workflow/gitwaterflow/queueing.py index b1c93792..28f8e579 100644 --- a/bert_e/workflow/gitwaterflow/queueing.py +++ b/bert_e/workflow/gitwaterflow/queueing.py @@ -226,6 +226,9 @@ def is_needed( - False otherwise. """ + if queues is None or job.settings.use_queue is False: + return False + if (job.settings.skip_queue_when_possible is False or already_in_queue(job, wbranches) or len(queues.queued_prs) > 0): From be77e43ccdcc75e24d4a53a33b81c83ffb6b0f33 Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 19:15:04 +0000 Subject: [PATCH 11/14] BERTE-561 Add changelog entry --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index d053a89b..a9edbd29 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ # Change Log All notable changes to this project will be documented in this file. +## [3.11.0] - 2024-01-26 +# Added +- Support of merging a PR and skip the queue when it is not needed. + ## [3.10.0] - 2023-11-14 # Added - Support of tags created with `v` prefix. From 86953fce965aaf7997726b6b66fb0802832e1c2b Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 19:17:38 +0000 Subject: [PATCH 12/14] Add back code accidently removed --- bert_e/workflow/gitwaterflow/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bert_e/workflow/gitwaterflow/__init__.py b/bert_e/workflow/gitwaterflow/__init__.py index f6e809be..0341ce09 100644 --- a/bert_e/workflow/gitwaterflow/__init__.py +++ b/bert_e/workflow/gitwaterflow/__init__.py @@ -219,7 +219,9 @@ def _handle_pull_request(job: PullRequestJob): except messages.IncoherentQueues as err: raise messages.QueueOutOfOrder( active_options=job.active_options) from err + # Enter the merge queue! queueing.add_to_queue(job, wbranches) + job.git.cascade.validate() raise messages.Queued( branches=job.git.cascade.dst_branches, ignored=job.git.cascade.ignored_branches, From 60a73c3923813424c2558dfe71c9c4bb8a2331a8 Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 11:18:40 -0800 Subject: [PATCH 13/14] Fix typo in comments --- bert_e/workflow/gitwaterflow/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bert_e/workflow/gitwaterflow/__init__.py b/bert_e/workflow/gitwaterflow/__init__.py index 0341ce09..7056f593 100644 --- a/bert_e/workflow/gitwaterflow/__init__.py +++ b/bert_e/workflow/gitwaterflow/__init__.py @@ -233,7 +233,7 @@ def _handle_pull_request(job: PullRequestJob): # integration branches right away. # But if the bot is configured with the 'use_queue' option, we # still need to delete the queue to ensure that we don't raise - # IncohorentQueues in the next runs. + # IncoherentQueues in the next runs. if queues is not None: queues.delete() merge_integration_branches(job, wbranches) From 8ee3fad6c753e358b9b2b312f949d44400e92e07 Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 26 Jan 2024 19:34:28 +0000 Subject: [PATCH 14/14] Rename flag for consistency --- bert_e/settings.py | 3 ++- bert_e/tests/test_bert_e.py | 3 ++- bert_e/workflow/gitwaterflow/queueing.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bert_e/settings.py b/bert_e/settings.py index 69950059..b50e29fd 100644 --- a/bert_e/settings.py +++ b/bert_e/settings.py @@ -184,7 +184,8 @@ class Meta: quiet = fields.Bool(required=False, load_default=False) disable_queues = fields.Bool(required=False, load_default=False) use_queues = fields.Bool(required=False, load_default=True) - skip_queue_when_possible = fields.Bool(required=False, load_default=False) + skip_queue_when_not_needed = fields.Bool( + required=False, load_default=False) cmd_line_options = fields.List(fields.Str(), load_default=[]) client_id = fields.Str(required=False, load_default='') diff --git a/bert_e/tests/test_bert_e.py b/bert_e/tests/test_bert_e.py index 477b75a3..44d4e616 100644 --- a/bert_e/tests/test_bert_e.py +++ b/bert_e/tests/test_bert_e.py @@ -7696,7 +7696,8 @@ def test_no_need_queuing(self): # Two PRs created at the same time # At the moment they were created they are both up to date with the # destination branch - self.init_berte(options=self.bypass_all, skip_queue_when_possible=True) + self.init_berte( + options=self.bypass_all, skip_queue_when_not_needed=True) first_pr = self.create_pr('feature/TEST-1', 'development/4.3') second_pr = self.create_pr('feature/TEST-2', 'development/4.3') # The first PR is ready to merge, and is expected to merge directly diff --git a/bert_e/workflow/gitwaterflow/queueing.py b/bert_e/workflow/gitwaterflow/queueing.py index 28f8e579..08c08c0d 100644 --- a/bert_e/workflow/gitwaterflow/queueing.py +++ b/bert_e/workflow/gitwaterflow/queueing.py @@ -229,7 +229,7 @@ def is_needed( if queues is None or job.settings.use_queue is False: return False - if (job.settings.skip_queue_when_possible is False or + if (job.settings.skip_queue_when_not_needed is False or already_in_queue(job, wbranches) or len(queues.queued_prs) > 0): return True