Skip to content

Commit aca53d4

Browse files
CDRIVER-4422 calc_release_version.py: remove --merged, plus improvements (#1060)
Detail of changes: * remove --merged option for compatibility with older versions of 'git' * fix bug that considered pre-release tags as higher than their succeeding releases (e.g., 1.22.0-beta0 was treated as a higher version tag than 1.22.0) * refactor to reduce code duplication, increase readability, and improve maintainability Co-authored-by: Kevin Albertson <[email protected]>
1 parent ea14e26 commit aca53d4

File tree

1 file changed

+120
-57
lines changed

1 file changed

+120
-57
lines changed

build/calc_release_version.py

Lines changed: 120 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -72,28 +72,29 @@ def check_head_tag():
7272
"""
7373
Checks the current HEAD to see if it has been tagged with a tag that matches
7474
the pattern for a release tag. Returns release version calculated from the
75-
tag, or None if there is no matching tag associated with HEAD. If there are
76-
multiple release tags associated with HEAD, the one with the highest version
77-
is returned.
75+
tag, or None if there is no matching tag associated with HEAD.
7876
"""
7977

8078
found_tag = False
8179
version_loose = LooseVersion('0.0.0')
8280

83-
tags = check_output(['git', 'tag', '-l']).split()
84-
head_commit = check_output(['git', 'rev-parse', '--revs-only',
85-
'HEAD^{commit}']).strip()
86-
for tag in tags:
87-
release_tag_match = RELEASE_TAG_RE.match(tag)
88-
tag_commit = check_output(['git', 'rev-parse', '--revs-only',
89-
tag + '^{commit}']).strip()
90-
if tag_commit == head_commit and release_tag_match:
91-
new_version_loose = LooseVersion(release_tag_match.group('ver'))
92-
if new_version_loose > version_loose:
93-
if DEBUG:
94-
print('HEAD release tag: ' + release_tag_match.group('ver'))
95-
version_loose = new_version_loose
96-
found_tag = True
81+
# have git tell us if any tags that look like release tags point at HEAD;
82+
# based on our policy, a commit should never have more than one release tag
83+
tags = check_output(['git', 'tag', '--points-at', 'HEAD', '--list', '1.*']).split()
84+
tag = ''
85+
if len(tags) == 1:
86+
tag = tags[0]
87+
elif len(tags) > 1:
88+
raise Exception ('Expected 1 or 0 tags on HEAD, got: {}'.format(tags))
89+
90+
release_tag_match = RELEASE_TAG_RE.match(tag)
91+
if release_tag_match:
92+
new_version_loose = LooseVersion(release_tag_match.group('ver'))
93+
if new_version_loose > version_loose:
94+
if DEBUG:
95+
print('HEAD release tag: ' + release_tag_match.group('ver'))
96+
version_loose = new_version_loose
97+
found_tag = True
9798

9899
if found_tag:
99100
if DEBUG:
@@ -102,13 +103,15 @@ def check_head_tag():
102103

103104
return None
104105

105-
def get_next_minor (prerelease_marker):
106+
def get_next_minor(prerelease_marker):
106107
"""
107108
get_next_minor does the following:
108-
Inspect the branches that fit the convention for a release branch.
109-
Choose the latest increment the minor version. Append .0 to form the new version (e.g., r1.21 becomes 1.22.0)
110-
Append a pre-release marker. (e.g. 1.22.0 becomes 1.22.0-20220201+gitf6e6a7025d)
109+
- Inspect the branches that fit the convention for a release branch.
110+
- Choose the latest and increment the minor version.
111+
- Append .0 to form the new version (e.g., r1.21 becomes 1.22.0)
112+
- Append a pre-release marker. (e.g. 1.22.0 becomes 1.22.0-20220201+gitf6e6a7025d)
111113
"""
114+
112115
version_loose = LooseVersion('0.0.0')
113116

114117
version_new = {}
@@ -123,9 +126,9 @@ def get_next_minor (prerelease_marker):
123126
version_new['patch'] = 0
124127
version_new['prerelease'] = prerelease_marker
125128
new_version_loose = LooseVersion(str(version_new['major']) + '.' +
126-
str(version_new['minor']) + '.' +
127-
str(version_new['patch']) + '-' +
128-
version_new['prerelease'])
129+
str(version_new['minor']) + '.' +
130+
str(version_new['patch']) + '-' +
131+
version_new['prerelease'])
129132
if new_version_loose > version_loose:
130133
version_loose = new_version_loose
131134
if DEBUG:
@@ -134,6 +137,69 @@ def get_next_minor (prerelease_marker):
134137
+ release_branch_match.group('brname') + '"')
135138
return str(version_loose)
136139

140+
def get_branch_tags(active_branch_name):
141+
"""
142+
Returns the tag or tags (as a single string with newlines between tags)
143+
corresponding to the current branch, which must not be master. If the
144+
specified branch is a release branch then return all tags based on the
145+
major/minor X.Y release version. If the specified branch is neither master
146+
nor a release branch, then walk backwards in history until the first tag
147+
matching the glob '1.*' and return that tag.
148+
"""
149+
150+
if active_branch_name == 'master':
151+
raise Exception('this method is not meant to be called while on "master"')
152+
tags = ''
153+
release_branch_match = RELEASE_BRANCH_RE.match(active_branch_name)
154+
if release_branch_match:
155+
# This is a release branch, so look for tags only on this branch
156+
tag_glob = release_branch_match.group('vermaj') + '.' \
157+
+ release_branch_match.group('vermin') + '.*'
158+
tags = check_output(['git', 'tag', '--list', tag_glob])
159+
else:
160+
# Not a release branch, so look for the most recent tag in history
161+
commits = check_output(['git', 'log', '--pretty=format:%H',
162+
'--no-merges'])
163+
if len(commits) > 0:
164+
for commit in commits.splitlines():
165+
tags = check_output(['git', 'tag', '--points-at',
166+
commit, '--list', '1.*'])
167+
if len(tags) > 0:
168+
# found a tag, we should be done
169+
break
170+
171+
return tags
172+
173+
def process_and_sort_tags(tags):
174+
"""
175+
Given a string (as returned from get_branch_tags), return a sorted list of
176+
zero or more tags (sorted based on the LooseVersion comparison) which meet
177+
the following criteria:
178+
- a final release tag (i.e., 1.x.y without any pre-release suffix)
179+
- a pre-release tag which is not superseded by a release tag (i.e.,
180+
1.x.y-preX iff 1.x.y does not already exist)
181+
"""
182+
183+
processed_and_sorted_tags = []
184+
if not tags or len(tags) == 0:
185+
return processed_and_sorted_tags
186+
187+
raw_tags = tags.splitlines()
188+
# find all the final release tags
189+
for tag in raw_tags:
190+
release_tag_match = RELEASE_TAG_RE.match(tag)
191+
if release_tag_match and not release_tag_match.group('verpre'):
192+
processed_and_sorted_tags.append(tag)
193+
# collect together final release tags and pre-release tags for
194+
# versions that have not yet had a final release
195+
for tag in raw_tags:
196+
tag_parts = tag.split('-')
197+
if len(tag_parts) >= 2 and tag_parts[0] not in processed_and_sorted_tags:
198+
processed_and_sorted_tags.append(tag)
199+
processed_and_sorted_tags.sort(key=LooseVersion)
200+
201+
return processed_and_sorted_tags
202+
137203
def main():
138204
"""
139205
The algorithm is roughly:
@@ -151,53 +217,50 @@ def main():
151217
patch version, and append a new pre-release marker
152218
"""
153219

154-
155220
version_loose = LooseVersion('0.0.0')
156-
head_commit_short = check_output(['git', 'rev-parse',
157-
'--revs-only', '--short=10',
158-
'HEAD^{commit}']).strip()
221+
head_commit_short = check_output(['git', 'rev-parse', '--revs-only',
222+
'--short=10', 'HEAD^{commit}']).strip()
159223
prerelease_marker = datetime.date.today().strftime('%Y%m%d') \
160224
+ '+git' + head_commit_short
161225

162226
if NEXT_MINOR:
163227
if DEBUG:
164228
print('Calculating next minor release')
165-
return get_next_minor (prerelease_marker)
229+
return get_next_minor(prerelease_marker)
166230

167231
head_tag_ver = check_head_tag()
168232
if head_tag_ver:
169233
return head_tag_ver
170234

171235
active_branch_name = check_output(['git', 'rev-parse',
172-
'--abbrev-ref', 'HEAD']).strip()
236+
'--abbrev-ref', 'HEAD']).strip()
173237
if DEBUG:
174238
print('Calculating release version for branch: ' + active_branch_name)
175239
if active_branch_name == 'master':
176-
return get_next_minor (prerelease_marker)
240+
return get_next_minor(prerelease_marker)
177241

178-
else:
179-
tags = check_output(['git', 'tag',
180-
'--merged', 'HEAD',
181-
'--list', '1.*'])
182-
if len(tags) > 0:
183-
sorted_tags = tags.splitlines()
184-
sorted_tags.sort(key=LooseVersion)
185-
release_tag_match = RELEASE_TAG_RE.match(sorted_tags[-1])
186-
if release_tag_match:
187-
version_new = {}
188-
version_new['major'] = int(release_tag_match.group('vermaj'))
189-
version_new['minor'] = int(release_tag_match.group('vermin'))
190-
version_new['patch'] = int(release_tag_match.group('verpatch')) + 1
191-
version_new['prerelease'] = prerelease_marker
192-
new_version_loose = LooseVersion(str(version_new['major']) + '.' +
193-
str(version_new['minor']) + '.' +
194-
str(version_new['patch']) + '-' +
195-
version_new['prerelease'])
196-
if new_version_loose > version_loose:
197-
version_loose = new_version_loose
198-
if DEBUG:
199-
print('Found new best version "' + str(version_loose) \
200-
+ '" from tag "' + release_tag_match.group('ver') + '"')
242+
branch_tags = get_branch_tags(active_branch_name)
243+
tags = process_and_sort_tags(branch_tags)
244+
245+
tag = tags[-1] if len(tags) > 0 else ''
246+
# at this point the RE match is redundant, but convenient for accessing
247+
# the components of the version string
248+
release_tag_match = RELEASE_TAG_RE.match(tag)
249+
if release_tag_match:
250+
version_new = {}
251+
version_new['major'] = int(release_tag_match.group('vermaj'))
252+
version_new['minor'] = int(release_tag_match.group('vermin'))
253+
version_new['patch'] = int(release_tag_match.group('verpatch')) + 1
254+
version_new['prerelease'] = prerelease_marker
255+
new_version_loose = LooseVersion(str(version_new['major']) + '.' +
256+
str(version_new['minor']) + '.' +
257+
str(version_new['patch']) + '-' +
258+
version_new['prerelease'])
259+
if new_version_loose > version_loose:
260+
version_loose = new_version_loose
261+
if DEBUG:
262+
print('Found new best version "' + str(version_loose) \
263+
+ '" from tag "' + release_tag_match.group('ver') + '"')
201264

202265
return str(version_loose)
203266

@@ -210,9 +273,9 @@ def previous(rel_ver):
210273
print('Calculating previous release version (option -p was specified).')
211274
version_loose = LooseVersion('0.0.0')
212275
rel_ver_loose = LooseVersion(rel_ver)
213-
tags = check_output(['git', 'tag',
214-
'--list', '1.*'])
215-
for tag in tags.splitlines():
276+
tags = check_output(['git', 'tag', '--list', '1.*'])
277+
processed_and_sorted_tags = process_and_sort_tags(tags)
278+
for tag in processed_and_sorted_tags:
216279
previous_tag_match = PREVIOUS_TAG_RE.match(tag)
217280
if previous_tag_match:
218281
version_new = {}

0 commit comments

Comments
 (0)