@@ -114,7 +114,8 @@ def get_changeset_metadata(changeset_path: Path) -> dict:
114114 commit_hash = result .stdout .strip ().split ("\n " )[0 ]
115115 metadata ["commit_hash" ] = commit_hash
116116
117- # Get the commit message to extract PR number
117+
118+ # Get the commit message to extract PR number and co-authors
118119 msg_result = subprocess .run (
119120 ["git" , "log" , "-1" , "--format=%B" , commit_hash ],
120121 capture_output = True ,
@@ -149,6 +150,35 @@ def get_changeset_metadata(changeset_path: Path) -> dict:
149150 )
150151 if gh_result .stdout .strip ():
151152 metadata ["pr_author" ] = gh_result .stdout .strip ()
153+
154+ # Also try to get co-authors from PR commits
155+ try :
156+ # Get all commits in the PR
157+ commits_result = subprocess .run (
158+ [
159+ "gh" ,
160+ "api" ,
161+ f"repos/{ git_info .get ('owner' , '' )} /"
162+ f"{ git_info .get ('repo' , '' )} /pulls/{ pr_number } /commits" ,
163+ "--jq" ,
164+ ".[].author.login" ,
165+ ],
166+ capture_output = True ,
167+ text = True ,
168+ check = True ,
169+ )
170+ if commits_result .stdout .strip ():
171+ # Get unique commit authors (excluding the PR author)
172+ commit_authors = set (
173+ commits_result .stdout .strip ().split ('\n ' )
174+ )
175+ commit_authors .discard (metadata .get ("pr_author" ))
176+ commit_authors .discard ('' ) # Remove empty strings
177+ if commit_authors :
178+ metadata ["co_authors" ] = list (commit_authors )
179+ except Exception :
180+ pass
181+
152182 except Exception :
153183 # If gh command fails, try to extract from commit author
154184 author_result = subprocess .run (
@@ -168,6 +198,25 @@ def get_changeset_metadata(changeset_path: Path) -> dict:
168198 if author_result .stdout .strip ():
169199 metadata ["pr_author" ] = author_result .stdout .strip ()
170200
201+ # Extract co-authors from commit message if we don't already have
202+ # them from GitHub API
203+ if "co_authors" not in metadata :
204+ co_authors = []
205+ for line in commit_msg .split ('\n ' ):
206+ co_author_match = re .match (
207+ r'^Co-authored-by:\s*(.+?)\s*<.*>$' , line .strip ()
208+ )
209+ if co_author_match :
210+ co_author_name = co_author_match .group (1 ).strip ()
211+ if (
212+ co_author_name
213+ and co_author_name != metadata .get ("pr_author" )
214+ ):
215+ co_authors .append (co_author_name )
216+
217+ if co_authors :
218+ metadata ["co_authors" ] = co_authors
219+
171220 except subprocess .CalledProcessError :
172221 # If git commands fail, return empty metadata
173222 pass
@@ -190,6 +239,7 @@ def format_changelog_entry(entry: dict, config: dict, pr_metadata: dict) -> str:
190239 description = entry ["description" ]
191240 pr_number = pr_metadata .get ("pr_number" )
192241 pr_author = pr_metadata .get ("pr_author" )
242+ co_authors = pr_metadata .get ("co_authors" , [])
193243 commit_hash = pr_metadata .get ("commit_hash" , "" )[:7 ]
194244 repo_url = pr_metadata .get ("repo_url" , "" )
195245
@@ -205,8 +255,28 @@ def format_changelog_entry(entry: dict, config: dict, pr_metadata: dict) -> str:
205255 parts .append (f"[`{ commit_hash } `]({ repo_url } /commit/{ commit_hash } )" )
206256
207257 # Add author thanks if available
258+ authors_to_thank = []
208259 if pr_author :
209- parts .append (f"Thanks @{ pr_author } !" )
260+ # Check if pr_author already contains @ symbol
261+ if pr_author .startswith ("@" ):
262+ authors_to_thank .append (pr_author )
263+ else :
264+ authors_to_thank .append (f"@{ pr_author } " )
265+
266+ # Add co-authors
267+ for co_author in co_authors :
268+ if co_author .startswith ("@" ):
269+ authors_to_thank .append (co_author )
270+ else :
271+ authors_to_thank .append (f"@{ co_author } " )
272+
273+ if authors_to_thank :
274+ if len (authors_to_thank ) == 1 :
275+ parts .append (f"Thanks { authors_to_thank [0 ]} !" )
276+ else :
277+ # Format multiple authors nicely
278+ all_but_last = ", " .join (authors_to_thank [:- 1 ])
279+ parts .append (f"Thanks { all_but_last } and { authors_to_thank [- 1 ]} !" )
210280
211281 # Add description
212282 parts .append (f"- { description } " )
0 commit comments