Skip to content

Commit c341234

Browse files
abadgerMariatta
authored andcommitted
Validate a branch that we parse when running cherry_picker --continue (#266)
When running cherry_picker --continue we count on being able to get certain information from the branch's name which cherry_picker constructed earlier. Verify as best we can that the branch name is one which cherry_picker could have constructed.
1 parent ff8a587 commit c341234

File tree

2 files changed

+72
-16
lines changed

2 files changed

+72
-16
lines changed

cherry_picker/cherry_picker/cherry_picker.py

+50-11
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,7 @@ def upstream(self):
7474

7575
@property
7676
def sorted_branches(self):
77-
def version_from_branch(branch):
78-
try:
79-
return tuple(map(int, re.match(r'^.*(?P<version>\d+(\.\d+)+).*$', branch).groupdict()['version'].split('.')))
80-
except AttributeError as attr_err:
81-
raise ValueError(f'Branch {branch} seems to not have a version in its name.') from attr_err
82-
77+
"""Return the branches to cherry-pick to, sorted by version."""
8378
return sorted(
8479
self.branches,
8580
reverse=True,
@@ -354,12 +349,15 @@ def continue_cherry_pick(self):
354349
click.echo(f"Current branch ({cherry_pick_branch}) is not a backport branch. Will not continue. \U0001F61B")
355350

356351
def check_repo(self):
357-
# CPython repo has a commit with
358-
# SHA=7f777ed95a19224294949e1b4ce56bbffcb1fe9f
359-
cmd = ['git', 'log', '-r', self.config['check_sha']]
352+
"""
353+
Check that the repository is for the project we're configured to operate on.
354+
355+
This function performs the check by making sure that the sha specified in the config
356+
is present in the repository that we're operating on.
357+
"""
360358
try:
361-
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
362-
except subprocess.SubprocessError:
359+
validate_sha(self.config['check_sha'])
360+
except ValueError:
363361
raise InvalidRepoException()
364362

365363

@@ -421,11 +419,52 @@ def cherry_pick_cli(dry_run, pr_remote, abort, status, push, config_path,
421419
def get_base_branch(cherry_pick_branch):
422420
"""
423421
return '2.7' from 'backport-sha-2.7'
422+
423+
raises ValueError if the specified branch name is not of a form that
424+
cherry_picker would have created
424425
"""
425426
prefix, sha, base_branch = cherry_pick_branch.split('-', 2)
427+
428+
if prefix != 'backport':
429+
raise ValueError('branch name is not prefixed with "backport-". Is this a cherry_picker branch?')
430+
431+
if not re.match('[0-9a-f]{7,40}', sha):
432+
raise ValueError(f'branch name has an invalid sha: {sha}')
433+
434+
# Validate that the sha refers to a valid commit within the repo
435+
# Throws a ValueError if the sha is not present in the repo
436+
validate_sha(sha)
437+
438+
# Subject the parsed base_branch to the same tests as when we generated it
439+
# This throws a ValueError if the base_branch doesn't meet our requirements
440+
version_from_branch(base_branch)
441+
426442
return base_branch
427443

428444

445+
def validate_sha(sha):
446+
"""
447+
Validate that a hexdigest sha is a valid commit in the repo
448+
449+
raises ValueError if the sha does not reference a commit within the repo
450+
"""
451+
cmd = ['git', 'log', '-r', sha]
452+
try:
453+
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
454+
except subprocess.SubprocessError:
455+
raise ValueError(f'The sha listed in the branch name, {sha}, is not present in the repository')
456+
457+
458+
def version_from_branch(branch):
459+
"""
460+
return version information from a git branch name
461+
"""
462+
try:
463+
return tuple(map(int, re.match(r'^.*(?P<version>\d+(\.\d+)+).*$', branch).groupdict()['version'].split('.')))
464+
except AttributeError as attr_err:
465+
raise ValueError(f'Branch {branch} seems to not have a version in its name.') from attr_err
466+
467+
429468
def get_current_branch():
430469
"""
431470
Return the current branch

cherry_picker/cherry_picker/test.py

+22-5
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,36 @@ def changedir(d):
3232
os.chdir(cwd)
3333

3434

35-
def test_get_base_branch():
36-
# The format of cherry-pick branches we create are "backport-{SHA}-{base_branch}"
37-
cherry_pick_branch = 'backport-afc23f4-2.7'
35+
@mock.patch('subprocess.check_output')
36+
def test_get_base_branch(subprocess_check_output):
37+
# The format of cherry-pick branches we create are::
38+
# backport-{SHA}-{base_branch}
39+
subprocess_check_output.return_value = b'22a594a0047d7706537ff2ac676cdc0f1dcb329c'
40+
cherry_pick_branch = 'backport-22a594a-2.7'
3841
result = get_base_branch(cherry_pick_branch)
3942
assert result == '2.7'
4043

4144

42-
def test_get_base_branch_which_has_dashes():
43-
cherry_pick_branch ='backport-afc23f4-baseprefix-2.7-basesuffix'
45+
@mock.patch('subprocess.check_output')
46+
def test_get_base_branch_which_has_dashes(subprocess_check_output):
47+
subprocess_check_output.return_value = b'22a594a0047d7706537ff2ac676cdc0f1dcb329c'
48+
cherry_pick_branch = 'backport-22a594a-baseprefix-2.7-basesuffix'
4449
result = get_base_branch(cherry_pick_branch)
4550
assert result == 'baseprefix-2.7-basesuffix'
4651

4752

53+
@pytest.mark.parametrize('cherry_pick_branch', ['backport-22a594a', # Not enough fields
54+
'prefix-22a594a-2.7', # Not the prefix we were expecting
55+
'backport-22a594a-base', # No version info in the base branch
56+
]
57+
)
58+
@mock.patch('subprocess.check_output')
59+
def test_get_base_branch_invalid(subprocess_check_output, cherry_pick_branch):
60+
subprocess_check_output.return_value = b'22a594a0047d7706537ff2ac676cdc0f1dcb329c'
61+
with pytest.raises(ValueError):
62+
get_base_branch(cherry_pick_branch)
63+
64+
4865
@mock.patch('subprocess.check_output')
4966
def test_get_current_branch(subprocess_check_output):
5067
subprocess_check_output.return_value = b'master'

0 commit comments

Comments
 (0)