@@ -1663,7 +1663,7 @@ def _prepare_get_commits(dbs, source, args):
return info, None
-def prepare_apply(dbs, source, branch, args=None):
+def prepare_apply(dbs, source, branch, args=None, info=None):
"""Prepare for applying commits from a source branch
Get the next commits, set up the branch name and prints info about
@@ -1676,15 +1676,18 @@ def prepare_apply(dbs, source, branch, args=None):
branch (str): Branch name to use, or None to auto-generate
args (Namespace): Parsed arguments with 'push', 'remote', 'target'
(needed for subtree updates)
+ info (NextCommitsInfo): Pre-fetched commit info from
+ _prepare_get_commits(), or None to fetch it here
Returns:
tuple: (ApplyInfo, return_code) where ApplyInfo is set if there are
commits to apply, or None with return_code indicating the result
(0 for no commits, 1 for error)
"""
- info, ret = _prepare_get_commits(dbs, source, args)
- if ret is not None:
- return None, ret
+ if info is None:
+ info, ret = _prepare_get_commits(dbs, source, args)
+ if ret is not None:
+ return None, ret
commits = info.commits
@@ -1889,18 +1892,20 @@ def execute_apply(dbs, source, commits, branch_name, args, advance_to=None): #
return ret, success, conv_log
-def do_apply(args, dbs):
+def do_apply(args, dbs, info=None):
"""Apply the next set of commits using Claude agent
Args:
args (Namespace): Parsed arguments with 'source' and 'branch' attributes
dbs (Database): Database instance
+ info (NextCommitsInfo): Pre-fetched commit info from
+ _prepare_get_commits(), or None to fetch during prepare
Returns:
int: 0 on success, 1 on failure
"""
source = args.source
- info, ret = prepare_apply(dbs, source, args.branch, args)
+ info, ret = prepare_apply(dbs, source, args.branch, args, info=info)
if not info:
return ret
@@ -2851,6 +2856,14 @@ def do_step(args, dbs):
process_pipeline_failures(remote, active_mrs, dbs,
args.target, args.fix_retries)
+ # Process subtree updates and advance past fully-processed merges
+ # regardless of MR count, since these don't create MRs
+ info, ret = _prepare_get_commits(dbs, source, args)
+ if ret is not None:
+ if ret:
+ return ret
+ return 0
+
# Only block new MR creation if we've reached the max allowed open MRs
max_mrs = args.max_mrs
if len(active_mrs) >= max_mrs:
@@ -2869,7 +2882,7 @@ def do_step(args, dbs):
tout.info('No pending pickman MRs, creating new one...')
args.push = True
args.branch = None # Let do_apply generate branch name
- return do_apply(args, dbs)
+ return do_apply(args, dbs, info=info)
def do_poll(args, dbs):
@@ -2061,16 +2061,21 @@ class TestStep(unittest.TestCase):
source_branch='cherry-test',
description='Test',
)
+ mock_info = control.NextCommitsInfo(
+ commits=['fake'], merge_found=True, advance_to='abc123')
with mock.patch.object(control, 'run_git'):
with mock.patch.object(gitlab, 'get_merged_pickman_mrs',
return_value=[]):
with mock.patch.object(gitlab, 'get_open_pickman_mrs',
return_value=[mock_mr]):
- args = argparse.Namespace(cmd='step', source='us/next',
- remote='ci', target='master',
- max_mrs=1, fix_retries=3)
- with terminal.capture():
- ret = control.do_step(args, None)
+ with mock.patch.object(
+ control, '_prepare_get_commits',
+ return_value=(mock_info, None)):
+ args = argparse.Namespace(
+ cmd='step', source='us/next', remote='ci',
+ target='master', max_mrs=1, fix_retries=3)
+ with terminal.capture():
+ ret = control.do_step(args, None)
self.assertEqual(ret, 0)
@@ -2109,18 +2114,23 @@ class TestStep(unittest.TestCase):
source_branch='cherry-test',
description='Test',
)
+ mock_info = control.NextCommitsInfo(
+ commits=['fake'], merge_found=True, advance_to='abc123')
with mock.patch.object(control, 'run_git'):
with mock.patch.object(gitlab, 'get_merged_pickman_mrs',
return_value=[]):
with mock.patch.object(gitlab, 'get_open_pickman_mrs',
return_value=[mock_mr]):
- with mock.patch.object(control, 'do_apply',
- return_value=0) as mock_apply:
- args = argparse.Namespace(cmd='step', source='us/next',
- remote='ci', target='master',
- max_mrs=5, fix_retries=3)
- with terminal.capture():
- ret = control.do_step(args, None)
+ with mock.patch.object(
+ control, '_prepare_get_commits',
+ return_value=(mock_info, None)):
+ with mock.patch.object(control, 'do_apply',
+ return_value=0) as mock_apply:
+ args = argparse.Namespace(
+ cmd='step', source='us/next', remote='ci',
+ target='master', max_mrs=5, fix_retries=3)
+ with terminal.capture():
+ ret = control.do_step(args, None)
# With 1 open MR and max_mrs=5, it should try to create a new one
self.assertEqual(ret, 0)
@@ -2138,17 +2148,23 @@ class TestStep(unittest.TestCase):
)
for i in range(3)
]
+ mock_info = control.NextCommitsInfo(
+ commits=['fake'], merge_found=True, advance_to='abc123')
with mock.patch.object(control, 'run_git'):
with mock.patch.object(gitlab, 'get_merged_pickman_mrs',
return_value=[]):
with mock.patch.object(gitlab, 'get_open_pickman_mrs',
return_value=mock_mrs):
- with mock.patch.object(control, 'do_apply') as mock_apply:
- args = argparse.Namespace(cmd='step', source='us/next',
- remote='ci', target='master',
- max_mrs=3, fix_retries=3)
- with terminal.capture():
- ret = control.do_step(args, None)
+ with mock.patch.object(
+ control, '_prepare_get_commits',
+ return_value=(mock_info, None)):
+ with mock.patch.object(control, 'do_apply') \
+ as mock_apply:
+ args = argparse.Namespace(
+ cmd='step', source='us/next', remote='ci',
+ target='master', max_mrs=3, fix_retries=3)
+ with terminal.capture():
+ ret = control.do_step(args, None)
# With 3 open MRs and max_mrs=3, should not create new MR
self.assertEqual(ret, 0)
@@ -6120,19 +6136,25 @@ class TestProcessPipelineFailures(unittest.TestCase):
pipeline_status='failed',
pipeline_id=100,
)
+ mock_info = control.NextCommitsInfo(
+ commits=['fake'], merge_found=True, advance_to='abc123')
with mock.patch.object(control, 'run_git'):
with mock.patch.object(gitlab, 'get_merged_pickman_mrs',
return_value=[]):
with mock.patch.object(gitlab, 'get_open_pickman_mrs',
return_value=[mock_mr]):
with mock.patch.object(
- control, 'process_pipeline_failures') as mock_ppf:
- args = argparse.Namespace(
- cmd='step', source='us/next',
- remote='ci', target='master',
- max_mrs=1, fix_retries=0)
- with terminal.capture():
- control.do_step(args, None)
+ control, '_prepare_get_commits',
+ return_value=(mock_info, None)):
+ with mock.patch.object(
+ control,
+ 'process_pipeline_failures') as mock_ppf:
+ args = argparse.Namespace(
+ cmd='step', source='us/next',
+ remote='ci', target='master',
+ max_mrs=1, fix_retries=0)
+ with terminal.capture():
+ control.do_step(args, None)
mock_ppf.assert_not_called()