fix stable branch membership detection

The rules for stable branch releases need to be relaxed a bit to account
for projects that are late to create their branches and only do so after
the default branch has been updated. This change makes the rules more
flexible so that it is possible to release for a stable branch on master
as long as that stable branch does not exist. The primary use for this
is to release and then create the stable branch in the same patch.

Change-Id: I2d8f9abcf8ffa34e29eb7021830fd635e6e0f85f
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
Doug Hellmann
2017-05-22 13:35:05 -04:00
parent 8eea824289
commit d287e47cfc
2 changed files with 71 additions and 15 deletions

View File

@@ -114,24 +114,56 @@ def sha_for_tag(workdir, repo, version):
return actual_sha
def _filter_branches(output):
"Strip garbage from branch list output"
return [
n
for n in output.strip().split()
if '/' in n or n == 'master'
]
def check_branch_sha(workdir, repo, series, master, sha):
"Check if the SHA is in the targeted branch."
"""Check if the SHA is in the targeted branch.
The SHA must appear on a stable/$series branch (if it exists) or
master (if stable/$series does not exist). It is up to the
reviewer to verify that releases from master are in a sensible
location relative to other existing branches.
We do not compare $series against the existing branches ordering
because that would prevent us from retroactively creating a stable
branch for a project after a later stable branch is created (i.e.,
if stable/N exists we could not create stable/N-1).
"""
remote_match = 'remotes/origin/stable/%s' % series
if series == master:
existing = get_branches(workdir, repo)
if remote_match not in existing:
# The stable branch for the series on master does not
# exist in this repository, yet, so look for the commit on
# the master branch.
remote_match = 'master'
try:
output = subprocess.check_output(
['git', 'branch', '-a', '--contains', sha],
cwd=os.path.join(workdir, repo),
).decode('utf-8').strip()
for branch in output.split():
if branch == remote_match:
return True
containing_branches = _filter_branches(
subprocess.check_output(
['git', 'branch', '-a', '--contains', sha],
cwd=os.path.join(workdir, repo),
).decode('utf-8')
)
# If the patch is on the named branch, everything is fine.
if remote_match in containing_branches:
return True
# If the expected branch does not exist yet, this may be a
# late release attempt to create that branch or just a project
# that hasn't branched, yet, and is releasing from master for
# that series. Allow the release, as long as it is on the
# master branch.
all_branches = _filter_branches(
subprocess.check_output(
['git', 'branch', '-a'],
cwd=os.path.join(workdir, repo),
).decode('utf-8')
)
if (remote_match not in all_branches) and ('master' in containing_branches):
return True
# At this point we know the release is not from the required
# branch and it is not from master, which means it is the
# wrong branch and should not be allowed.
return False
except subprocess.CalledProcessError as e:
print('ERROR checking SHA on branch: %s [%s]' % (e, e.output.strip()))

View File

@@ -617,6 +617,30 @@ class TestValidateReleases(base.BaseTestCase):
self.assertEqual(0, len(warnings))
self.assertEqual(1, len(errors))
def test_hash_from_master_used_after_default_branch_should_exist_but_does_not(self):
deliverable_info = {
'artifact-link-mode': 'none',
'releases': [
{'version': '1.0.0',
'projects': [
{'repo': 'openstack/releases',
'hash': '8eea82428995b8f3354c0a75351fe95bbbb1135a'},
]}
],
}
warnings = []
errors = []
validate.validate_releases(
deliverable_info,
{'validate-projects-by-name': {}},
'austin',
self.tmpdir,
warnings.append,
errors.append,
)
self.assertEqual(0, len(warnings))
self.assertEqual(0, len(errors))
def test_not_descendent(self):
deliverable_info = {
'artifact-link-mode': 'none',