@@ -42,10 +42,10 @@ export class DashboardGenerator {
42
42
/**
43
43
* Generate the default header section
44
44
*/
45
- private generateDefaultHeader ( data : DashboardData ) : string {
46
- const portalUrl = `https://developer.mend.io/github/${ data . repository . owner } /${ data . repository . name } `
45
+ private generateDefaultHeader ( _data : DashboardData ) : string {
46
+ // const portalUrl = `https://developer.mend.io/github/${data.repository.owner}/${data.repository.name}`
47
47
48
- return `This issue lists Buddy Bot updates and detected dependencies. Read the [Dependency Dashboard](https://buddy-bot.sh/features/dependency-dashboard) docs to learn more.<br/>[View this repository on the Mend.io Web Portal]( ${ portalUrl } ).
48
+ return `This issue lists Buddy Bot updates and detected dependencies. Read the [Dependency Dashboard](https://buddy-bot.sh/features/dependency-dashboard) docs to learn more.
49
49
50
50
`
51
51
}
@@ -72,6 +72,7 @@ The following updates have all been created. To force a retry/rebase of any, cli
72
72
73
73
section += ` - [ ] <!-- rebase-branch=${ rebaseBranch } -->[${ pr . title } ](${ relativeUrl } )`
74
74
75
+ // Show clean package names like Renovate does (without version info)
75
76
if ( packageInfo . length > 0 ) {
76
77
section += ` (\`${ packageInfo . join ( '`, `' ) } \`)`
77
78
}
@@ -247,58 +248,150 @@ The following updates have all been created. To force a retry/rebase of any, cli
247
248
}
248
249
249
250
/**
250
- * Extract package names from PR title or body
251
+ * Extract package names from PR title or body (like Renovate does)
251
252
*/
252
253
private extractPackageInfo ( pr : PullRequest ) : string [ ] {
253
254
const packages : string [ ] = [ ]
254
255
255
- // Try to extract from title patterns like "update dependency react to v18"
256
- const titleMatch = pr . title . match ( / u p d a t e .* ?d e p e n d e n c y \s + ( \w + ) / i)
257
- if ( titleMatch ) {
258
- packages . push ( titleMatch [ 1 ] )
256
+ // Pattern 1: Extract from common dependency update titles
257
+ // Examples: "chore(deps): update dependency react to v18"
258
+ // "chore(deps): update all non-major dependencies"
259
+ // "update @types/node to v20"
260
+ const titlePatterns = [
261
+ / u p d a t e .* ?d e p e n d e n c y \s + ( \S + ) / i,
262
+ / u p d a t e \s + ( \S + ) \s + t o \s + v ? \d + / i,
263
+ / b u m p \s + ( \S + ) \s + f r o m / i,
264
+ ]
265
+
266
+ for ( const pattern of titlePatterns ) {
267
+ const match = pr . title . match ( pattern )
268
+ if ( match && match [ 1 ] && ! packages . includes ( match [ 1 ] ) ) {
269
+ packages . push ( match [ 1 ] )
270
+ }
259
271
}
260
272
261
- // Extract from the enhanced PR body format
262
- // Look for table entries like: | [package-name](url) | version change | badges |
263
- const tableMatches = pr . body . match ( / \| \s * \[ ( [ ^ \] ] + ) \] / g)
264
- if ( tableMatches ) {
265
- for ( const match of tableMatches ) {
266
- const packageMatch = match . match ( / \| \s * \[ ( [ ^ \] ] + ) \] / )
267
- if ( packageMatch ) {
268
- const packageName = packageMatch [ 1 ]
269
- // Skip if it's a URL, badge, or contains special characters that indicate it's not a package name
270
- if ( ! packageName . includes ( '://' )
271
- && ! packageName . includes ( 'Compare Source' )
272
- && ! packageName . includes ( 'badge' )
273
- && ! packageName . includes ( '!' )
274
- && ! packageName . startsWith ( '[![' )
275
- && ! packages . includes ( packageName ) ) {
276
- packages . push ( packageName )
273
+ // Pattern 2: Extract from table format in PR body - handle all table sections
274
+ // Look for different table types: npm Dependencies, Launchpad/pkgx Dependencies, GitHub Actions
275
+
276
+ // Split the body into sections and process each table
277
+ const tableSections = [
278
+ // npm Dependencies table
279
+ { name : 'npm' , pattern : / # # # n p m D e p e n d e n c i e s [ \s \S ] * ?(? = # # # | \n \n - - - | z ) / i } ,
280
+ // Launchpad/pkgx Dependencies table
281
+ { name : 'pkgx' , pattern : / # # # L a u n c h p a d \/ p k g x D e p e n d e n c i e s [ \s \S ] * ?(? = # # # | \n \n - - - | z ) / i } ,
282
+ // GitHub Actions table
283
+ { name : 'actions' , pattern : / # # # G i t H u b A c t i o n s [ \s \S ] * ?(? = # # # | \n \n - - - | z ) / i } ,
284
+ ]
285
+
286
+ for ( const section of tableSections ) {
287
+ const sectionMatch = pr . body . match ( section . pattern )
288
+ if ( sectionMatch ) {
289
+ const sectionContent = sectionMatch [ 0 ]
290
+
291
+ // Extract package names from this section's table
292
+ const tableRowMatches = sectionContent . match ( / \| \s * \[ ( [ ^ \] ] + ) \] \( [ ^ ) ] + \) \s * \| / g)
293
+ if ( tableRowMatches ) {
294
+ for ( const match of tableRowMatches ) {
295
+ const packageMatch = match . match ( / \| \s * \[ ( [ ^ \] ] + ) \] / )
296
+ if ( packageMatch && packageMatch [ 1 ] ) {
297
+ const packageName = packageMatch [ 1 ] . trim ( )
298
+
299
+ // Check if this looks like a version string - if so, try to extract package name from URL
300
+ if ( packageName . includes ( '`' ) && packageName . includes ( '->' ) ) {
301
+ // This is a version string like "`1.2.17` -> `1.2.19`"
302
+ // Try to extract the package name from the URL
303
+ const urlMatch = match . match ( / \] \( ( [ ^ ) ] + ) \) / )
304
+ if ( urlMatch && urlMatch [ 1 ] ) {
305
+ const url = urlMatch [ 1 ]
306
+
307
+ // Extract package name from Renovate diff URLs like:
308
+ // https://renovatebot.com/diffs/npm/%40types%2Fbun/1.2.17/1.2.19
309
+ // https://renovatebot.com/diffs/npm/cac/6.7.13/6.7.14
310
+ const diffUrlMatch = url . match ( / \/ d i f f s \/ n p m \/ ( [ ^ / ] + ) \/ / )
311
+ if ( diffUrlMatch && diffUrlMatch [ 1 ] ) {
312
+ // Decode URL encoding like %40types%2Fbun -> @types/bun
313
+ const extractedPackage = decodeURIComponent ( diffUrlMatch [ 1 ] )
314
+
315
+ if ( extractedPackage && extractedPackage . length > 1 && ! packages . includes ( extractedPackage ) ) {
316
+ packages . push ( extractedPackage )
317
+ }
318
+ }
319
+ }
320
+ continue // Skip the normal processing for version strings
321
+ }
322
+
323
+ // Normal processing for direct package names
324
+ // Skip if it's a URL, badge, or contains special characters
325
+ // CRITICAL: Skip version strings like "`1.2.17` -> `1.2.19`"
326
+ if ( ! packageName . includes ( '://' )
327
+ && ! packageName . includes ( 'Compare Source' )
328
+ && ! packageName . includes ( 'badge' )
329
+ && ! packageName . includes ( '!' )
330
+ && ! packageName . startsWith ( '[![' )
331
+ && ! packageName . includes ( '`' ) // Skip anything with backticks (version strings)
332
+ && ! packageName . includes ( '->' ) // Skip version arrows
333
+ && ! packageName . includes ( ' -> ' ) // Skip spaced version arrows
334
+ && ! packageName . match ( / ^ \d + \. \d + / ) // Skip version numbers
335
+ && ! packageName . includes ( ' ' ) // Package names shouldn't have spaces
336
+ && packageName . length > 0
337
+ && ! packages . includes ( packageName ) ) {
338
+ packages . push ( packageName )
339
+ }
340
+ }
277
341
}
278
342
}
279
343
}
280
344
}
281
345
282
- // Fallback: try to extract from simple backtick patterns
283
- if ( packages . length === 0 ) {
346
+ // Pattern 3: Extract from PR body - look for package names in backticks (avoid table content)
347
+ // This handles cases where the title doesn't contain specific package names
348
+ // but the body lists them like: `@types/bun`, `cac`, `ts-pkgx`
349
+ if ( packages . length < 3 ) { // Allow backtick extraction to supplement table extraction
284
350
const bodyMatches = pr . body . match ( / ` ( [ ^ ` ] + ) ` / g)
285
351
if ( bodyMatches ) {
286
352
for ( const match of bodyMatches ) {
287
- const content = match . replace ( / ` / g, '' )
288
- // Only extract if it looks like a package name (no version arrows, URLs, or special chars)
289
- if ( ! content . includes ( '->' )
290
- && ! content . includes ( '://' )
291
- && ! content . includes ( ' ' )
292
- && ! content . includes ( 'Compare Source' )
293
- && content . length > 0
294
- && ! packages . includes ( content ) ) {
295
- packages . push ( content )
353
+ let packageName = match . replace ( / ` / g, '' ) . trim ( )
354
+
355
+ // Skip anything that looks like version information
356
+ if ( packageName . includes ( '->' )
357
+ || packageName . includes ( ' -> ' )
358
+ || packageName . includes ( '` -> `' )
359
+ || packageName . match ( / ^ \d + \. \d + / ) // Starts with version number
360
+ || packageName . match ( / ^ v \d + / ) // Version tags
361
+ || packageName . match ( / [ \d . ] + \s * - > \s * [ \d . ] + / ) // Version arrows
362
+ || packageName . match ( / ^ [ \d . ] + $ / ) // Pure version numbers like "1.2.17"
363
+ || packageName . match ( / ^ \d + \. \d + \. \d + / ) // Semver patterns
364
+ || packageName . match ( / ^ \d + \. \d + \. \d + \. / ) // Longer version patterns
365
+ || packageName . match ( / ^ \^ ? \d + \. \d + / ) // Version ranges like "^1.2.3"
366
+ || packageName . match ( / ^ ~ \d + \. \d + / ) // Tilde version ranges
367
+ || packageName . includes ( '://' ) // URLs with protocol
368
+ || packageName . includes ( 'Compare Source' )
369
+ || packageName . includes ( 'badge' )
370
+ || packageName . includes ( ' ' ) ) { // Package names shouldn't have spaces
371
+ continue
372
+ }
373
+
374
+ // Clean up the package name - take first part only
375
+ packageName = packageName . split ( ',' ) [ 0 ] . trim ( )
376
+
377
+ // Only include if it looks like a valid package/dependency name
378
+ if ( packageName
379
+ && packageName . length > 1
380
+ && ! packages . includes ( packageName )
381
+ && (
382
+ // Must match one of these patterns for valid package names:
383
+ packageName . startsWith ( '@' ) // Scoped packages like @types /node
384
+ || packageName . includes ( '/' ) // GitHub actions like actions/checkout
385
+ || packageName . match ( / ^ [ a - z ] [ a - z 0 - 9 . - ] * $ / i) // Simple package names like lodash, ts-pkgx, bun.com
386
+ ) ) {
387
+ packages . push ( packageName )
296
388
}
297
389
}
298
390
}
299
391
}
300
392
301
- return packages . slice ( 0 , 5 ) // Limit to first 5 packages to keep it clean
393
+ // NO LIMIT - return all packages like Renovate does
394
+ return packages
302
395
}
303
396
304
397
/**
0 commit comments