Skip to content

Commit d188778

Browse files
committed
chore: wip
1 parent 495a706 commit d188778

File tree

3 files changed

+139
-17
lines changed

3 files changed

+139
-17
lines changed

src/pr/pr-generator.ts

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ export class PullRequestGenerator {
111111
// Fetch package information for package.json updates only
112112
const packageInfos = new Map<string, { packageInfo: PackageInfo, releaseNotes: ReleaseNote[], compareUrl?: string }>()
113113

114+
// Fetch Composer package information
115+
const composerPackageInfos = new Map<string, PackageInfo>()
116+
114117
// Package.json updates table (with full badges)
115118
if (packageJsonUpdates.length > 0) {
116119
body += `### npm Dependencies\n\n`
@@ -177,33 +180,49 @@ export class PullRequestGenerator {
177180
body += `\n`
178181
}
179182

180-
// Composer dependencies table (simplified, without badges)
183+
// Composer dependencies table (with full badges like npm)
181184
if (uniqueComposerUpdates.length > 0) {
185+
// Fetch Composer package information first
186+
for (const update of uniqueComposerUpdates) {
187+
try {
188+
const packageInfo = await this.releaseNotesFetcher.fetchComposerPackageInfo(update.name)
189+
composerPackageInfos.set(update.name, packageInfo)
190+
}
191+
catch (error) {
192+
console.warn(`Failed to fetch Composer info for ${update.name}:`, error)
193+
}
194+
}
195+
182196
body += `### PHP/Composer Dependencies\n\n`
183-
body += `| Package | Change | File | Status |\n`
184-
body += `|---|---|---|---|\n`
197+
body += `| Package | Change | Age | Adoption | Passing | Confidence |\n`
198+
body += `|---|---|---|---|---|---|\n`
185199

186200
for (const update of uniqueComposerUpdates) {
187-
// Generate enhanced package link with source repository (like npm packages)
201+
const packageInfo = composerPackageInfos.get(update.name) || { name: update.name }
202+
203+
// Generate enhanced package link to match npm format exactly
188204
let packageCell: string
189-
if (update.metadata?.repository) {
190-
const sourceUrl = this.getComposerSourceUrl(update.metadata.repository, update.name)
191-
packageCell = `[${update.name}](https://packagist.org/packages/${encodeURIComponent(update.name)}) ([source](${sourceUrl}))`
205+
if (packageInfo.repository?.url) {
206+
const sourceUrl = this.getComposerSourceUrl(packageInfo.repository.url, update.name)
207+
// Format: [packageName](repoUrl) ([source](sourceUrl)) - matching npm format
208+
packageCell = `[${update.name}](${sourceUrl}) ([source](${sourceUrl}))`
192209
} else {
193210
// Fallback to Packagist page only
194211
packageCell = `[${update.name}](https://packagist.org/packages/${encodeURIComponent(update.name)})`
195212
}
196213

197-
// Simple version change display
198-
const change = `\`${update.currentVersion}\` -> \`${update.newVersion}\``
199-
200-
// File reference
201-
const fileName = update.file.split('/').pop() || update.file
214+
// Generate version change with diff link (Renovate style, similar to npm)
215+
const diffUrl = `https://renovatebot.com/diffs/packagist/${encodeURIComponent(update.name)}/${update.currentVersion}/${update.newVersion}`
216+
const change = `[\`${update.currentVersion}\` -> \`${update.newVersion}\`](${diffUrl})`
202217

203-
// Status (simple)
204-
const status = '✅ Available'
218+
// Generate Composer confidence badges
219+
const badges = this.releaseNotesFetcher.generateComposerBadges(
220+
packageInfo,
221+
update.currentVersion,
222+
update.newVersion,
223+
)
205224

206-
body += `| ${packageCell} | ${change} | ${fileName} | ${status} |\n`
225+
body += `| ${packageCell} | ${change} | ${badges.age} | ${badges.adoption} | ${badges.passing} | ${badges.confidence} |\n`
207226
}
208227

209228
body += `\n`

src/services/release-notes-fetcher.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,68 @@ export class ReleaseNotesFetcher {
358358
confidence: `[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/${packageName}/${encodedCurrent}/${encodedNew}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,
359359
}
360360
}
361+
362+
/**
363+
* Fetch Composer package information from Packagist
364+
*/
365+
async fetchComposerPackageInfo(packageName: string): Promise<PackageInfo> {
366+
try {
367+
const response = await fetch(`https://packagist.org/packages/${encodeURIComponent(packageName)}.json`, {
368+
headers: { 'User-Agent': this.userAgent },
369+
})
370+
371+
if (!response.ok) {
372+
throw new Error(`Packagist responded with ${response.status}`)
373+
}
374+
375+
const data = await response.json() as any
376+
const packageData = data.package
377+
378+
if (!packageData) {
379+
return { name: packageName }
380+
}
381+
382+
// Get the latest stable version info
383+
const versions = Object.keys(packageData.versions || {})
384+
const latestVersion = versions.find(v => !v.includes('dev') && !v.includes('alpha') && !v.includes('beta')) || versions[0]
385+
const versionData = packageData.versions[latestVersion] || {}
386+
387+
return {
388+
name: packageData.name,
389+
description: versionData.description,
390+
homepage: versionData.homepage,
391+
repository: versionData.source ? { type: 'git', url: versionData.source.url } : undefined,
392+
license: versionData.license?.[0] || versionData.license,
393+
author: versionData.authors?.[0],
394+
keywords: versionData.keywords,
395+
// Packagist doesn't provide download stats like npm, so we'll skip weeklyDownloads
396+
lastPublish: versionData.time,
397+
}
398+
}
399+
catch (error) {
400+
console.warn(`Failed to fetch Packagist info for ${packageName}:`, error)
401+
return { name: packageName }
402+
}
403+
}
404+
405+
/**
406+
* Generate Composer-specific confidence badges and metrics
407+
*/
408+
generateComposerBadges(packageInfo: PackageInfo, currentVersion: string, newVersion: string): {
409+
age: string
410+
adoption: string
411+
passing: string
412+
confidence: string
413+
} {
414+
const packageName = encodeURIComponent(packageInfo.name)
415+
const encodedCurrent = encodeURIComponent(currentVersion)
416+
const encodedNew = encodeURIComponent(newVersion)
417+
418+
return {
419+
age: `[![age](https://developer.mend.io/api/mc/badges/age/packagist/${packageName}/${encodedNew}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,
420+
adoption: `[![adoption](https://developer.mend.io/api/mc/badges/adoption/packagist/${packageName}/${encodedNew}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,
421+
passing: `[![passing](https://developer.mend.io/api/mc/badges/compatibility/packagist/${packageName}/${encodedCurrent}/${encodedNew}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,
422+
confidence: `[![confidence](https://developer.mend.io/api/mc/badges/confidence/packagist/${packageName}/${encodedCurrent}/${encodedNew}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,
423+
}
424+
}
361425
}

test/composer-non-major-pr.test.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ describe('Composer Non-Major PR', () => {
158158
expect(prBody).toContain('phpstan/phpstan')
159159
expect(prBody).toContain('### PHP/Composer Dependencies')
160160

161-
// Should show file references
162-
expect(prBody).toContain('composer.json')
161+
// Should have the same format as npm dependencies (no file column)
162+
expect(prBody).toContain('| Package | Change | Age | Adoption | Passing | Confidence |')
163163
})
164164

165165
it('should deduplicate composer packages and show enhanced links', async () => {
@@ -222,4 +222,43 @@ describe('Composer Non-Major PR', () => {
222222
// Should still show phpunit
223223
expect(prBody).toContain('phpunit/phpunit')
224224
})
225+
226+
it('should format links exactly like npm packages', async () => {
227+
const testGroup: UpdateGroup = {
228+
name: 'Non-Major Updates',
229+
updates: [
230+
{
231+
name: 'monolog/monolog',
232+
currentVersion: '3.7.0',
233+
newVersion: '3.8.0',
234+
updateType: 'minor',
235+
dependencyType: 'require',
236+
file: 'composer.json',
237+
metadata: {
238+
name: 'monolog/monolog',
239+
repository: 'https://github.com/Seldaek/monolog',
240+
description: 'Sends your logs to files, sockets, inboxes, databases and various web services'
241+
}
242+
},
243+
],
244+
updateType: 'minor',
245+
title: 'chore(deps): update all non-major dependencies',
246+
body: '',
247+
}
248+
249+
const prBody = await prGenerator.generateBody(testGroup)
250+
251+
// Should format like npm: [packageName](repoUrl) ([source](sourceUrl))
252+
expect(prBody).toContain('[monolog/monolog](https://github.com/Seldaek/monolog/tree/master) ([source](https://github.com/Seldaek/monolog/tree/master))')
253+
254+
// Should have the same columns as npm dependencies
255+
expect(prBody).toContain('| Package | Change | Age | Adoption | Passing | Confidence |')
256+
expect(prBody).toContain('|---|---|---|---|---|---|')
257+
258+
// Should have confidence badges
259+
expect(prBody).toContain('[![age](https://developer.mend.io/api/mc/badges/age/packagist/')
260+
expect(prBody).toContain('[![adoption](https://developer.mend.io/api/mc/badges/adoption/packagist/')
261+
expect(prBody).toContain('[![passing](https://developer.mend.io/api/mc/badges/compatibility/packagist/')
262+
expect(prBody).toContain('[![confidence](https://developer.mend.io/api/mc/badges/confidence/packagist/')
263+
})
225264
})

0 commit comments

Comments
 (0)