@@ -161,6 +161,7 @@ export const generateReleaseNotes = async (args: Args = {}): Promise<ChangelogRe
161
161
// Helper functions
162
162
163
163
async function createContributorSection ( commits : GitCommit [ ] ) : Promise < string > {
164
+ console . log ( 'Fetching contributors...' )
164
165
const contributors = await getContributors ( commits )
165
166
if ( ! contributors . length ) {
166
167
return ''
@@ -179,18 +180,21 @@ async function getContributors(commits: GitCommit[]): Promise<Contributor[]> {
179
180
const contributors : Contributor [ ] = [ ]
180
181
const emails = new Set < string > ( )
181
182
183
+ const headers = {
184
+ Accept : 'application/vnd.github.v3+json' ,
185
+ Authorization : `token ${ process . env . GITHUB_TOKEN } ` ,
186
+ }
187
+
182
188
for ( const commit of commits ) {
183
- if ( emails . has ( commit . author . email ) || commit . author . name === 'dependabot[bot]' ) {
189
+ console . log ( `Fetching details for ${ commit . message } - ${ commit . shortHash } ` )
190
+ if ( emails . has ( commit . author . email ) || commit . author . name . includes ( '[bot]' ) ) {
184
191
continue
185
192
}
186
193
187
194
const res = await fetch (
188
195
`https://api.github.com/repos/payloadcms/payload/commits/${ commit . shortHash } ` ,
189
196
{
190
- headers : {
191
- Accept : 'application/vnd.github.v3+json' ,
192
- Authorization : `token ${ process . env . GITHUB_TOKEN } ` ,
193
- } ,
197
+ headers,
194
198
} ,
195
199
)
196
200
@@ -202,12 +206,73 @@ async function getContributors(commits: GitCommit[]): Promise<Contributor[]> {
202
206
203
207
const { author } = ( await res . json ( ) ) as { author : { login : string ; email : string } }
204
208
205
- // TODO: Handle co-authors
206
-
207
209
if ( ! contributors . some ( ( c ) => c . username === author . login ) ) {
208
210
contributors . push ( { name : commit . author . name , username : author . login } )
209
211
}
210
212
emails . add ( author . email )
213
+
214
+ // Check git commit for 'Co-authored-by:' lines
215
+ const coAuthorPattern = / C o - a u t h o r e d - b y : (?< name > [ ^ < ] + ) < (?< email > [ ^ > ] + ) > / g
216
+ const coAuthors = Array . from (
217
+ commit . body . matchAll ( coAuthorPattern ) ,
218
+ ( match ) => match . groups ,
219
+ ) . filter ( ( e ) => ! e . email . includes ( '[bot]' ) )
220
+
221
+ if ( ! coAuthors . length ) {
222
+ continue
223
+ }
224
+
225
+ console . log (
226
+ `Fetching co-author details for hash: ${ commit . shortHash } . Co-authors:` ,
227
+ coAuthors . map ( ( c ) => c . email ) . join ( ', ' ) ,
228
+ )
229
+
230
+ // Attempt to co-authors by email
231
+ await Promise . all (
232
+ ( coAuthors || [ ] )
233
+ . map ( async ( { name, email } ) => {
234
+ // Check if this co-author has already been added
235
+ if ( emails . has ( email ) ) {
236
+ return null
237
+ }
238
+
239
+ // Get co-author's GitHub username by email
240
+ try {
241
+ const response = await fetch (
242
+ `https://api.github.com/search/users?q=${ encodeURIComponent ( email ) } +in:email` ,
243
+ {
244
+ headers,
245
+ } ,
246
+ )
247
+
248
+ if ( ! response . ok ) {
249
+ console . log ( 'Bad response from GitHub API fetching co-author by email' )
250
+ console . error ( response . status )
251
+ return null
252
+ }
253
+
254
+ const data = ( await response . json ( ) ) as { items ?: { login : string } [ ] }
255
+ const user = data . items ?. [ 0 ]
256
+
257
+ if ( ! user ) {
258
+ return null
259
+ }
260
+
261
+ console . log ( `Found co-author by email: ${ user . login } ` )
262
+
263
+ if ( ! contributors . some ( ( c ) => c . username === user . login ) ) {
264
+ contributors . push ( { name, username : user . login } )
265
+ }
266
+ emails . add ( email )
267
+ return user . login
268
+ } catch ( error ) {
269
+ console . log ( `ERROR: Failed to fetch co-author by email` )
270
+ console . error ( error )
271
+ return null
272
+ }
273
+ } )
274
+ . filter ( Boolean ) ,
275
+ )
211
276
}
212
277
return contributors
213
278
}
0 commit comments