@@ -72,28 +72,29 @@ def check_head_tag():
72
72
"""
73
73
Checks the current HEAD to see if it has been tagged with a tag that matches
74
74
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.
78
76
"""
79
77
80
78
found_tag = False
81
79
version_loose = LooseVersion ('0.0.0' )
82
80
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
97
98
98
99
if found_tag :
99
100
if DEBUG :
@@ -102,13 +103,15 @@ def check_head_tag():
102
103
103
104
return None
104
105
105
- def get_next_minor (prerelease_marker ):
106
+ def get_next_minor (prerelease_marker ):
106
107
"""
107
108
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)
111
113
"""
114
+
112
115
version_loose = LooseVersion ('0.0.0' )
113
116
114
117
version_new = {}
@@ -123,9 +126,9 @@ def get_next_minor (prerelease_marker):
123
126
version_new ['patch' ] = 0
124
127
version_new ['prerelease' ] = prerelease_marker
125
128
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' ])
129
132
if new_version_loose > version_loose :
130
133
version_loose = new_version_loose
131
134
if DEBUG :
@@ -134,6 +137,69 @@ def get_next_minor (prerelease_marker):
134
137
+ release_branch_match .group ('brname' ) + '"' )
135
138
return str (version_loose )
136
139
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
+
137
203
def main ():
138
204
"""
139
205
The algorithm is roughly:
@@ -151,53 +217,50 @@ def main():
151
217
patch version, and append a new pre-release marker
152
218
"""
153
219
154
-
155
220
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 ()
159
223
prerelease_marker = datetime .date .today ().strftime ('%Y%m%d' ) \
160
224
+ '+git' + head_commit_short
161
225
162
226
if NEXT_MINOR :
163
227
if DEBUG :
164
228
print ('Calculating next minor release' )
165
- return get_next_minor (prerelease_marker )
229
+ return get_next_minor (prerelease_marker )
166
230
167
231
head_tag_ver = check_head_tag ()
168
232
if head_tag_ver :
169
233
return head_tag_ver
170
234
171
235
active_branch_name = check_output (['git' , 'rev-parse' ,
172
- '--abbrev-ref' , 'HEAD' ]).strip ()
236
+ '--abbrev-ref' , 'HEAD' ]).strip ()
173
237
if DEBUG :
174
238
print ('Calculating release version for branch: ' + active_branch_name )
175
239
if active_branch_name == 'master' :
176
- return get_next_minor (prerelease_marker )
240
+ return get_next_minor (prerelease_marker )
177
241
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' ) + '"' )
201
264
202
265
return str (version_loose )
203
266
@@ -210,9 +273,9 @@ def previous(rel_ver):
210
273
print ('Calculating previous release version (option -p was specified).' )
211
274
version_loose = LooseVersion ('0.0.0' )
212
275
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 :
216
279
previous_tag_match = PREVIOUS_TAG_RE .match (tag )
217
280
if previous_tag_match :
218
281
version_new = {}
0 commit comments