Skip to content

Commit 653081a

Browse files
authored
BERTE-561 skip the merge through the queue when not needed (#158)
1 parent b20cd32 commit 653081a

File tree

6 files changed

+108
-9
lines changed

6 files changed

+108
-9
lines changed

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Change Log
22
All notable changes to this project will be documented in this file.
33

4+
## [3.11.0] - 2024-01-26
5+
# Added
6+
- Support of merging a PR and skip the queue when it is not needed.
7+
48
## [3.10.0] - 2023-11-14
59
# Added
610
- Support of tags created with `v` prefix.

bert_e/settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ class Meta:
184184
quiet = fields.Bool(required=False, load_default=False)
185185
disable_queues = fields.Bool(required=False, load_default=False)
186186
use_queues = fields.Bool(required=False, load_default=True)
187+
skip_queue_when_not_needed = fields.Bool(
188+
required=False, load_default=False)
187189
cmd_line_options = fields.List(fields.Str(), load_default=[])
188190

189191
client_id = fields.Str(required=False, load_default='')

bert_e/tests/test_bert_e.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7690,6 +7690,41 @@ def test_job_rebuild_queues_without_hotfix(self):
76907690
self.assertFalse(self.gitrepo.remote_branch_exists(branch),
76917691
"branch %s shouldn't exist" % branch)
76927692

7693+
def test_no_need_queuing(self):
7694+
"""Expect Bert-E to skip the queue when there is no need to queue."""
7695+
7696+
# Two PRs created at the same time
7697+
# At the moment they were created they are both up to date with the
7698+
# destination branch
7699+
self.init_berte(
7700+
options=self.bypass_all, skip_queue_when_not_needed=True)
7701+
first_pr = self.create_pr('feature/TEST-1', 'development/4.3')
7702+
second_pr = self.create_pr('feature/TEST-2', 'development/4.3')
7703+
# The first PR is ready to merge, and is expected to merge directly
7704+
# without going through the queue.
7705+
self.process_pr_job(first_pr, 'SuccessMessage')
7706+
# When the second PR is merged we expect it to go through the queue
7707+
# as it is no longer up to date with the destination branch.
7708+
self.process_pr_job(second_pr, 'Queued')
7709+
# At this point the repository should now contain queue branches.
7710+
# We force the merge to get everything setup according for the next
7711+
# scenario.
7712+
self.process_job(ForceMergeQueuesJob(bert_e=self.berte), 'Merged')
7713+
# We expect the PR to be merged so there should be nothing left to do.
7714+
self.process_pr_job(second_pr, 'NothingToDo')
7715+
# We get the local repo setup for a third PR that should be up to
7716+
# date with the latest changes.
7717+
self.gitrepo.cmd('git checkout development/4.3')
7718+
self.gitrepo.cmd('git branch --set-upstream-to=origin/development/4.3')
7719+
self.gitrepo.cmd('git pull')
7720+
third_pr = self.create_pr('feature/TEST-3', 'development/4.3')
7721+
fourth_pr = self.create_pr('feature/TEST-4', 'development/4.3')
7722+
# Just like the first PR, we expect this one to be merged directly.
7723+
self.process_pr_job(third_pr, 'SuccessMessage')
7724+
# Now we want to know if when the queue is a bit late is Bert-E
7725+
# capable of reeastablishing the Queue in order, and queue PR number 4.
7726+
self.process_pr_job(fourth_pr, 'Queued')
7727+
76937728
def test_bypass_prefixes(self):
76947729
self.init_berte()
76957730
pr = self.create_pr('documentation/stuff', 'development/4.3')

bert_e/workflow/gitwaterflow/__init__.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,14 @@ def _handle_pull_request(job: PullRequestJob):
207207
# check for conflicts), and all builds were green, and we reached
208208
# this point without an error, then all conditions are met to enter
209209
# the queue.
210-
if job.settings.use_queue:
211-
# validate current state of queues
210+
queues = queueing.build_queue_collection(job) if job.settings.use_queue \
211+
else None
212+
213+
# If we need to go through the queue, we need to check if we can
214+
# merge the integration branches right away, or if we need to add
215+
# the pull request to the queue.
216+
if queueing.is_needed(job, wbranches, queues):
212217
try:
213-
queues = queueing.build_queue_collection(job)
214218
queues.validate()
215219
except messages.IncoherentQueues as err:
216220
raise messages.QueueOutOfOrder(
@@ -224,8 +228,14 @@ def _handle_pull_request(job: PullRequestJob):
224228
issue=job.git.src_branch.jira_issue_key,
225229
author=job.pull_request.author_display_name,
226230
active_options=job.active_options)
227-
228231
else:
232+
# If we don't need to go through the queue, we can merge the
233+
# integration branches right away.
234+
# But if the bot is configured with the 'use_queue' option, we
235+
# still need to delete the queue to ensure that we don't raise
236+
# IncoherentQueues in the next runs.
237+
if queues is not None:
238+
queues.delete()
229239
merge_integration_branches(job, wbranches)
230240
job.bert_e.add_merged_pr(job.pull_request.id)
231241
job.git.cascade.validate()

bert_e/workflow/gitwaterflow/branches.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,18 @@ def has_version_queued_prs(self, version):
743743
return (self._queues.get(version, {}).get(QueueIntegrationBranch)
744744
is not None)
745745

746+
def delete(self):
747+
"""Delete the queues entirely."""
748+
749+
for branch in self._queues.values():
750+
queue: QueueBranch = branch[QueueBranch]
751+
queue.dst_branch.checkout()
752+
queue.remove(do_push=True)
753+
queue_integration: QueueIntegrationBranch | None = branch.get(
754+
QueueIntegrationBranch)
755+
if queue_integration:
756+
queue_integration.remove(do_push=True)
757+
746758

747759
class BranchCascade(object):
748760
def __init__(self):

bert_e/workflow/gitwaterflow/queueing.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@
1818

1919
from bert_e import exceptions
2020
from bert_e.job import handler as job_handler
21-
from bert_e.job import QueuesJob
21+
from bert_e.job import QueuesJob, PullRequestJob
2222
from bert_e.lib import git
2323

2424
from ..git_utils import clone_git_repo, consecutive_merge, robust_merge, push
2525
from ..pr_utils import send_comment
26-
from .branches import (BranchCascade, DevelopmentBranch, IntegrationBranch,
27-
QueueBranch, QueueIntegrationBranch,
28-
branch_factory, build_queue_collection)
26+
from .branches import (BranchCascade, DevelopmentBranch, GWFBranch,
27+
IntegrationBranch, QueueBranch, QueueCollection,
28+
QueueIntegrationBranch, branch_factory,
29+
build_queue_collection)
2930
from .integration import get_integration_branches
30-
31+
from typing import List
3132
LOG = logging.getLogger(__name__)
3233

3334

@@ -207,3 +208,38 @@ def close_queued_pull_request(job, pr_id, cascade):
207208
except git.RemoveFailedException:
208209
# not critical
209210
pass
211+
212+
213+
def is_needed(
214+
job: PullRequestJob,
215+
wbranches: List[GWFBranch],
216+
queues: QueueCollection | None):
217+
"""Determine if queuing is required to merge the given PR.
218+
219+
Queuing a pull request should only be done if:
220+
- The PR or the integration branches are not up to date
221+
with the destination branch.
222+
- Other PRs are already in the queue.
223+
224+
Returns:
225+
- True if the PR should be queued.
226+
- False otherwise.
227+
"""
228+
229+
if queues is None or job.settings.use_queue is False:
230+
return False
231+
232+
if (job.settings.skip_queue_when_not_needed is False or
233+
already_in_queue(job, wbranches) or
234+
len(queues.queued_prs) > 0):
235+
return True
236+
237+
if not job.git.src_branch.includes_commit(
238+
job.git.dst_branch.get_latest_commit()):
239+
return True
240+
# Check if the wbranches all contain the commits in the dst branches
241+
for branch, dst_branch in zip(wbranches, job.git.cascade.dst_branches):
242+
if not branch.includes_commit(dst_branch.get_latest_commit()):
243+
return True
244+
245+
return False

0 commit comments

Comments
 (0)