@@ -53,10 +53,43 @@ def version(version_file):
5353 return ver
5454
5555
56+ def get_merged_prs_since_last_tag ():
57+ """Get list of PR numbers and titles merged since the last tag."""
58+ try :
59+ previous_tag = (
60+ subprocess
61+ .check_output (
62+ ["git" , "describe" , "--abbrev=0" , "--tags" ],
63+ stderr = subprocess .DEVNULL ,
64+ )
65+ .decode ()
66+ .strip ()
67+ )
68+ except subprocess .CalledProcessError :
69+ return []
70+
71+ log = subprocess .check_output (["git" , "log" , "--merges" , "--oneline" , "{}..HEAD" .format (previous_tag )]).decode ()
72+
73+ prs = re .findall (r"(.+\(#(\d+)\))" , log )
74+ seen = set ()
75+ result = []
76+ for line , num in prs :
77+ if num not in seen :
78+ seen .add (num )
79+ result .append (num )
80+ return result
81+
82+
5683def commit_for_release (version_file , ver ):
84+ pr_numbers = get_merged_prs_since_last_tag ()
85+ pr_list = ""
86+ if pr_numbers :
87+ pr_list = "\n \n " + "\n " .join ("- #{}" .format (n ) for n in pr_numbers )
88+
89+ message = "Releasing version {}{}" .format (ver , pr_list )
5790 run_step ("git" , "reset" )
5891 run_step ("git" , "add" , "-u" )
59- run_step ("git" , "commit" , "--message" , "Releasing version {}" . format ( ver ) )
92+ run_step ("git" , "commit" , "--message" , message )
6093
6194
6295def create_git_tag (tag_name ):
@@ -80,6 +113,67 @@ def push_tags_to_github():
80113 run_step ("git" , "push" , "--tags" , "origin" )
81114
82115
116+ def check_tag (ver ):
117+ """Verify that HEAD is on the expected tag."""
118+ tag = "v{}" .format (ver )
119+ try :
120+ current_tag = (
121+ subprocess
122+ .check_output (
123+ ["git" , "describe" , "--exact-match" , "--tags" , "HEAD" ],
124+ stderr = subprocess .DEVNULL ,
125+ )
126+ .decode ()
127+ .strip ()
128+ )
129+ except subprocess .CalledProcessError :
130+ print ("ERROR: HEAD is not on any tag. Expected tag '{}'." .format (tag ))
131+ sys .exit (1 )
132+ if current_tag != tag :
133+ print ("ERROR: HEAD is on tag '{}', expected '{}'." .format (current_tag , tag ))
134+ sys .exit (1 )
135+ print ("OK: on tag '{}'" .format (tag ))
136+
137+
138+ def comment_on_released_prs (ver ):
139+ """Post a comment on all PRs included in this release."""
140+ tag = "v{}" .format (ver )
141+ try :
142+ previous_tag = (
143+ subprocess
144+ .check_output (
145+ ["git" , "describe" , "--abbrev=0" , "--tags" , "{}^" .format (tag )],
146+ stderr = subprocess .DEVNULL ,
147+ )
148+ .decode ()
149+ .strip ()
150+ )
151+ except subprocess .CalledProcessError :
152+ print ("WARNING: Could not find previous tag. Skipping PR comments." )
153+ return
154+
155+ log = subprocess .check_output (["git" , "log" , "--merges" , "--oneline" , "{}..{}" .format (previous_tag , tag )]).decode ()
156+
157+ pr_numbers = re .findall (r"#(\d+)" , log )
158+ pr_numbers = list (set (pr_numbers ))
159+
160+ if not pr_numbers :
161+ print ("No PRs found between {} and {}." .format (previous_tag , tag ))
162+ return
163+
164+ print ("Found PRs: {}" .format (", " .join ("#" + n for n in pr_numbers )))
165+ message = "Released as part of {}." .format (ver )
166+
167+ print ("gh pr comment --body '{}' {}" .format (message , " " .join (pr_numbers )))
168+ if skip_step ():
169+ print ("--- Skipping..." )
170+ elif DRY_RUN :
171+ print ("--- Pretending to run..." )
172+ else :
173+ for pr in pr_numbers :
174+ subprocess .check_output (["gh" , "pr" , "comment" , pr , "--body" , message ])
175+
176+
83177def checklist (questions ):
84178 for question in questions :
85179 if not click .confirm ("--- {}" .format (question ), default = False ):
@@ -126,7 +220,9 @@ def checklist(questions):
126220
127221 commit_for_release ("pgcli/__init__.py" , ver )
128222 create_git_tag ("v{}" .format (ver ))
129- create_distribution_files ()
130223 push_to_github ()
131224 push_tags_to_github ()
225+ check_tag (ver )
226+ create_distribution_files ()
132227 upload_distribution_files ()
228+ comment_on_released_prs (ver )
0 commit comments