1919# Value of the mode flag for a v2 release
2020V2_MODE = 'v2-release'
2121
22+ SOURCE_BRANCH_FOR_MODE = { V1_MODE : 'releases/v2' , V2_MODE : 'main' }
23+ TARGET_BRANCH_FOR_MODE = { V1_MODE : 'releases/v1' , V2_MODE : 'releases/v2' }
24+
2225# Name of the remote
2326ORIGIN = 'origin'
2427
2528# Runs git with the given args and returns the stdout.
26- # Raises an error if git does not exit successfully.
27- def run_git (* args ):
29+ # Raises an error if git does not exit successfully (unless passed
30+ # allow_non_zero_exit_code=True).
31+ def run_git (* args , allow_non_zero_exit_code = False ):
2832 cmd = ['git' , * args ]
2933 p = subprocess .run (cmd , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
30- if ( p .returncode != 0 ) :
34+ if not allow_non_zero_exit_code and p .returncode != 0 :
3135 raise Exception ('Call to ' + ' ' .join (cmd ) + ' exited with code ' + str (p .returncode ) + ' stderr:' + p .stderr .decode ('ascii' ))
3236 return p .stdout .decode ('ascii' )
3337
@@ -36,7 +40,9 @@ def branch_exists_on_remote(branch_name):
3640 return run_git ('ls-remote' , '--heads' , ORIGIN , branch_name ).strip () != ''
3741
3842# Opens a PR from the given branch to the target branch
39- def open_pr (repo , all_commits , source_branch_short_sha , new_branch_name , source_branch , target_branch , conductor , is_v2_release , labels ):
43+ def open_pr (
44+ repo , all_commits , source_branch_short_sha , new_branch_name , source_branch , target_branch ,
45+ conductor , is_v2_release , labels , conflicted_files ):
4046 # Sort the commits into the pull requests that introduced them,
4147 # and any commits that don't have a pull request
4248 pull_requests = []
@@ -81,6 +87,12 @@ def open_pr(repo, all_commits, source_branch_short_sha, new_branch_name, source_
8187
8288 body .append ('' )
8389 body .append ('Please review the following:' )
90+ if len (conflicted_files ) > 0 :
91+ body .append (' - [ ] You have added commits to this branch that resolve the merge conflicts ' +
92+ 'in the following files:' )
93+ body .extend ([f' - [ ] `{ file } `' for file in conflicted_files ])
94+ body .append (' - [ ] Another maintainer has reviewed the additional commits you added to this ' +
95+ 'branch to resolve the merge conflicts.' )
8496 body .append (' - [ ] The CHANGELOG displays the correct version and date.' )
8597 body .append (' - [ ] The CHANGELOG includes all relevant, user-facing changes since the last release.' )
8698 body .append (' - [ ] There are no unexpected commits being merged into the ' + target_branch + ' branch.' )
@@ -191,8 +203,10 @@ def main():
191203 type = str ,
192204 required = True ,
193205 choices = [V2_MODE , V1_MODE ],
194- help = f"Which release to perform. '{ V2_MODE } ' uses main as the source branch and v2 as the target branch. " +
195- f"'{ V1_MODE } ' uses v2 as the source branch and v1 as the target branch."
206+ help = f"Which release to perform. '{ V2_MODE } ' uses { SOURCE_BRANCH_FOR_MODE [V2_MODE ]} as the source " +
207+ f"branch and { TARGET_BRANCH_FOR_MODE [V2_MODE ]} as the target branch. " +
208+ f"'{ V1_MODE } ' uses { SOURCE_BRANCH_FOR_MODE [V1_MODE ]} as the source branch and " +
209+ f"{ TARGET_BRANCH_FOR_MODE [V1_MODE ]} as the target branch."
196210 )
197211 parser .add_argument (
198212 '--conductor' ,
@@ -203,14 +217,8 @@ def main():
203217
204218 args = parser .parse_args ()
205219
206- if args .mode == V2_MODE :
207- source_branch = 'main'
208- target_branch = 'v2'
209- elif args .mode == V1_MODE :
210- source_branch = 'v2'
211- target_branch = 'v1'
212- else :
213- raise ValueError (f"Unexpected value for release mode: '{ args .mode } '" )
220+ source_branch = SOURCE_BRANCH_FOR_MODE [args .mode ]
221+ target_branch = TARGET_BRANCH_FOR_MODE [args .mode ]
214222
215223 repo = Github (args .github_token ).get_repo (args .repository_nwo )
216224 version = get_current_version ()
@@ -246,10 +254,15 @@ def main():
246254 # Create the new branch and push it to the remote
247255 print ('Creating branch ' + new_branch_name )
248256
257+ # The process of creating the v1 release can run into merge conflicts. We commit the unresolved
258+ # conflicts so a maintainer can easily resolve them (vs erroring and requiring maintainers to
259+ # reconstruct the release manually)
260+ conflicted_files = []
261+
249262 if args .mode == V1_MODE :
250- # If we're performing a backport, start from the v1 branch
251- print (f'Creating { new_branch_name } from the { ORIGIN } /v1 branch' )
252- run_git ('checkout' , '-b' , new_branch_name , f'{ ORIGIN } /v1 ' )
263+ # If we're performing a backport, start from the target branch
264+ print (f'Creating { new_branch_name } from the { ORIGIN } /{ target_branch } branch' )
265+ run_git ('checkout' , '-b' , new_branch_name , f'{ ORIGIN } /{ target_branch } ' )
253266
254267 # Revert the commit that we made as part of the last release that updated the version number and
255268 # changelog to refer to 1.x.x variants. This avoids merge conflicts in the changelog and
@@ -274,7 +287,12 @@ def main():
274287 print (' Nothing to revert.' )
275288
276289 print (f'Merging { ORIGIN } /{ source_branch } into the release prep branch' )
277- run_git ('merge' , f'{ ORIGIN } /{ source_branch } ' , '--no-edit' )
290+ # Commit any conflicts (see the comment for `conflicted_files`)
291+ run_git ('merge' , f'{ ORIGIN } /{ source_branch } ' , allow_non_zero_exit_code = True )
292+ conflicted_files = run_git ('diff' , '--name-only' , '--diff-filter' , 'U' ).splitlines ()
293+ if len (conflicted_files ) > 0 :
294+ run_git ('add' , '.' )
295+ run_git ('commit' , '--no-edit' )
278296
279297 # Migrate the package version number from a v2 version number to a v1 version number
280298 print (f'Setting version number to { version } ' )
@@ -317,6 +335,7 @@ def main():
317335 conductor = args .conductor ,
318336 is_v2_release = args .mode == V2_MODE ,
319337 labels = ['Update dependencies' ] if args .mode == V1_MODE else [],
338+ conflicted_files = conflicted_files
320339 )
321340
322341if __name__ == '__main__' :
0 commit comments