Skip to content

Commit 25f8108

Browse files
committed
chore: wip
1 parent 074ba77 commit 25f8108

File tree

5 files changed

+153
-32
lines changed

5 files changed

+153
-32
lines changed

src/pr/pr-generator.ts

Lines changed: 73 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -194,27 +194,29 @@ export class PullRequestGenerator {
194194
}
195195

196196
body += `### PHP/Composer Dependencies\n\n`
197-
body += `| Package | Change | Age | Adoption | Passing | Confidence |\n`
198-
body += `|---|---|---|---|---|---|\n`
197+
body += `| Package | Change | Age | Adoption | Passing | Confidence | Type | Update |\n`
198+
body += `|---|---|---|---|---|---|---|---|\n`
199199

200200
for (const update of uniqueComposerUpdates) {
201201
const packageInfo = composerPackageInfos.get(update.name) || { name: update.name }
202202

203-
// Generate enhanced package link to match npm format exactly
203+
// Generate package link: homepage first, then source
204204
let packageCell: string
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}))`
209-
}
210-
else {
211-
// Fallback to Packagist page only
205+
if (packageInfo.homepage && packageInfo.repository?.url) {
206+
const sourceUrl = this.getComposerRedirectSourceUrl(packageInfo.repository.url, update.name)
207+
packageCell = `[${update.name}](${packageInfo.homepage}) ([source](${sourceUrl}))`
208+
} else if (packageInfo.repository?.url) {
209+
const sourceUrl = this.getComposerRedirectSourceUrl(packageInfo.repository.url, update.name)
210+
packageCell = `[${update.name}](${sourceUrl})`
211+
} else {
212+
// Fallback to Packagist page
212213
packageCell = `[${update.name}](https://packagist.org/packages/${encodeURIComponent(update.name)})`
213214
}
214215

215-
// Generate version change with diff link (Renovate style, similar to npm)
216+
// Generate constraint-style version change (e.g., ^3.0 -> ^3.10.0)
217+
const constraintChange = this.getConstraintStyleChange(update.currentVersion, update.newVersion)
216218
const diffUrl = `https://renovatebot.com/diffs/packagist/${encodeURIComponent(update.name)}/${update.currentVersion}/${update.newVersion}`
217-
const change = `[\`${update.currentVersion}\` -> \`${update.newVersion}\`](${diffUrl})`
219+
const change = `[\`${constraintChange}\`](${diffUrl})`
218220

219221
// Generate Composer confidence badges
220222
const badges = this.releaseNotesFetcher.generateComposerBadges(
@@ -223,7 +225,11 @@ export class PullRequestGenerator {
223225
update.newVersion,
224226
)
225227

226-
body += `| ${packageCell} | ${change} | ${badges.age} | ${badges.adoption} | ${badges.passing} | ${badges.confidence} |\n`
228+
// Dependency type and update type
229+
const dependencyType = update.dependencyType || 'require'
230+
const updateType = update.updateType || 'minor'
231+
232+
body += `| ${packageCell} | ${change} | ${badges.age} | ${badges.adoption} | ${badges.passing} | ${badges.confidence} | ${dependencyType} | ${updateType} |\n`
227233
}
228234

229235
body += `\n`
@@ -556,6 +562,60 @@ export class PullRequestGenerator {
556562
}
557563
}
558564

565+
/**
566+
* Generate a constraint-style version change (e.g., ^3.0 -> ^3.10.0)
567+
*/
568+
private getConstraintStyleChange(currentVersion: string, newVersion: string): string {
569+
// Extract base versions (remove any v prefix)
570+
const cleanCurrent = currentVersion.replace(/^v/, '')
571+
const cleanNew = newVersion.replace(/^v/, '')
572+
573+
// For constraint updates, we want to show the constraint form
574+
// e.g., 3.0 -> 3.10.0 becomes ^3.0 -> ^3.10.0
575+
const currentConstraint = `^${cleanCurrent}`
576+
const newConstraint = `^${cleanNew}`
577+
578+
return `${currentConstraint} -> ${newConstraint}`
579+
}
580+
581+
/**
582+
* Generate a redirect source URL for Composer packages (like npm)
583+
*/
584+
private getComposerRedirectSourceUrl(repositoryUrl: string, packageName: string): string {
585+
try {
586+
const cleanUrl = repositoryUrl
587+
.replace(/^git\+/, '')
588+
.replace(/\.git$/, '')
589+
.replace(/^git:\/\//, 'https://')
590+
.replace(/^ssh:\/\/git@/, 'https://')
591+
.replace(/^git@github\.com:/, 'https://github.com/')
592+
593+
const url = new URL(cleanUrl)
594+
595+
// For DefinitelyTyped packages, use the types subdirectory
596+
if (packageName.startsWith('@types/') && url.pathname.includes('DefinitelyTyped')) {
597+
const typeName = packageName.replace('@types/', '')
598+
return `https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/${typeName}`
599+
}
600+
601+
// For regular GitHub repositories, use redirect.github.com
602+
if (url.hostname === 'github.com') {
603+
const pathParts = url.pathname.split('/').filter(p => p)
604+
if (pathParts.length >= 2) {
605+
const owner = pathParts[0]
606+
const repo = pathParts[1]
607+
return `https://redirect.github.com/${owner}/${repo}`
608+
}
609+
}
610+
611+
// Fallback to repository URL
612+
return cleanUrl
613+
}
614+
catch {
615+
return repositoryUrl
616+
}
617+
}
618+
559619
/**
560620
* Clean and truncate release body content
561621
*/

src/services/release-notes-fetcher.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,13 @@ export class ReleaseNotesFetcher {
348348
confidence: string
349349
} {
350350
const packageName = encodeURIComponent(packageInfo.name)
351-
const encodedCurrent = encodeURIComponent(currentVersion)
352-
const encodedNew = encodeURIComponent(newVersion)
351+
352+
// Normalize versions: remove v prefix and ensure proper semver format
353+
const normalizedCurrent = this.normalizeVersionForBadges(currentVersion)
354+
const normalizedNew = this.normalizeVersionForBadges(newVersion)
355+
356+
const encodedCurrent = encodeURIComponent(normalizedCurrent)
357+
const encodedNew = encodeURIComponent(normalizedNew)
353358

354359
return {
355360
age: `[![age](https://developer.mend.io/api/mc/badges/age/npm/${packageName}/${encodedNew}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,
@@ -412,8 +417,13 @@ export class ReleaseNotesFetcher {
412417
confidence: string
413418
} {
414419
const packageName = encodeURIComponent(packageInfo.name)
415-
const encodedCurrent = encodeURIComponent(currentVersion)
416-
const encodedNew = encodeURIComponent(newVersion)
420+
421+
// Normalize versions: remove v prefix and ensure proper semver format
422+
const normalizedCurrent = this.normalizeVersionForBadges(currentVersion)
423+
const normalizedNew = this.normalizeVersionForBadges(newVersion)
424+
425+
const encodedCurrent = encodeURIComponent(normalizedCurrent)
426+
const encodedNew = encodeURIComponent(normalizedNew)
417427

418428
return {
419429
age: `[![age](https://developer.mend.io/api/mc/badges/age/packagist/${packageName}/${encodedNew}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,
@@ -422,4 +432,20 @@ export class ReleaseNotesFetcher {
422432
confidence: `[![confidence](https://developer.mend.io/api/mc/badges/confidence/packagist/${packageName}/${encodedCurrent}/${encodedNew}?slim=true)](https://docs.renovatebot.com/merge-confidence/)`,
423433
}
424434
}
435+
436+
/**
437+
* Normalize version for badge URLs (remove v prefix, ensure proper format)
438+
*/
439+
private normalizeVersionForBadges(version: string): string {
440+
// Remove v prefix
441+
let normalized = version.replace(/^v/, '')
442+
443+
// If version is just major.minor (e.g., "3.0"), add .0 to make it "3.0.0"
444+
const parts = normalized.split('.')
445+
if (parts.length === 2) {
446+
normalized = `${normalized}.0`
447+
}
448+
449+
return normalized
450+
}
425451
}

test/composer-individual-pr-simulation.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ describe('Composer Individual PR Simulation', () => {
2424
beforeEach(() => {
2525
const mockConfig = {
2626
repository: {
27+
provider: 'github' as const,
2728
owner: 'test-owner',
2829
name: 'test-repo',
2930
baseBranch: 'main',

test/composer-mixed-updates.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ describe('Composer Mixed Updates (Major + Minor)', () => {
4242
{ name: 'another/package', constraint: '*', current: '1.5.0', latest: '2.1.0' },
4343
]
4444

45-
const includedUpdates = []
46-
const excludedUpdates = []
45+
const includedUpdates: Array<{ name: string; constraint: string; current: string; latest: string; updateType: string; shouldGetIndividualPR: boolean }> = []
46+
const excludedUpdates: Array<{ name: string; constraint: string; current: string; latest: string }> = []
4747

4848
packages.forEach((pkg) => {
4949
const shouldInclude = shouldIncludeUpdate(pkg.constraint, pkg.current, pkg.latest)

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

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('Composer Non-Major PR', () => {
1313
baseBranch: 'main',
1414
},
1515
}
16-
prGenerator = new PullRequestGenerator(mockConfig)
16+
prGenerator = new PullRequestGenerator()
1717
})
1818

1919
it('should include Composer updates in non-major grouped PR', async () => {
@@ -86,13 +86,13 @@ describe('Composer Non-Major PR', () => {
8686
expect(prBody).toContain('### GitHub Actions')
8787
expect(prBody).toContain('actions/checkout')
8888

89-
// Verify Composer package links
90-
expect(prBody).toContain('https://packagist.org/packages/monolog%2Fmonolog')
91-
expect(prBody).toContain('https://packagist.org/packages/phpunit%2Fphpunit')
89+
// Verify Composer package links - they now go to homepage/source, not packagist
90+
expect(prBody).toContain('([source](https://redirect.github.com/Seldaek/monolog))')
91+
expect(prBody).toContain('([source](https://redirect.github.com/sebastianbergmann/phpunit))')
9292

93-
// Verify version changes in Composer section
94-
expect(prBody).toContain('`3.7.0` -> `3.8.0`')
95-
expect(prBody).toContain('`10.5.0` -> `10.5.2`')
93+
// Verify constraint-style version changes in Composer section
94+
expect(prBody).toContain('`^3.7.0 -> ^3.8.0`')
95+
expect(prBody).toContain('`^10.5.0 -> ^10.5.2`')
9696
})
9797

9898
it('should handle non-major PR with only Composer updates', async () => {
@@ -160,6 +160,28 @@ describe('Composer Non-Major PR', () => {
160160

161161
// Should have the same format as npm dependencies (no file column)
162162
expect(prBody).toContain('| Package | Change | Age | Adoption | Passing | Confidence |')
163+
164+
// Should have the same columns as npm dependencies
165+
expect(prBody).toContain('| Package | Change | Age | Adoption | Passing | Confidence |')
166+
expect(prBody).toContain('|---|---|---|---|---|---|')
167+
168+
// Should have the new table format with dependency and update type columns
169+
expect(prBody).toContain('| Package | Change | Age | Adoption | Passing | Confidence | Type | Update |')
170+
expect(prBody).toContain('|---|---|---|---|---|---|---|---|')
171+
172+
// Should show constraint-style changes for actual test packages
173+
expect(prBody).toContain('^6.4.0 -> ^6.4.12')
174+
expect(prBody).toContain('^1.10.0 -> ^1.12.0')
175+
176+
// Should have confidence badges
177+
expect(prBody).toContain('[![age](https://developer.mend.io/api/mc/badges/age/packagist/')
178+
expect(prBody).toContain('[![adoption](https://developer.mend.io/api/mc/badges/adoption/packagist/')
179+
expect(prBody).toContain('[![passing](https://developer.mend.io/api/mc/badges/compatibility/packagist/')
180+
expect(prBody).toContain('[![confidence](https://developer.mend.io/api/mc/badges/confidence/packagist/')
181+
182+
// Should show dependency types
183+
expect(prBody).toContain('require')
184+
expect(prBody).toContain('require-dev')
163185
})
164186

165187
it('should deduplicate composer packages and show enhanced links', async () => {
@@ -178,6 +200,8 @@ describe('Composer Non-Major PR', () => {
178200
name: 'monolog/monolog',
179201
repository: 'https://github.com/Seldaek/monolog',
180202
description: 'Sends your logs to files, sockets, inboxes, databases and various web services',
203+
latestVersion: '3.8.0',
204+
versions: ['3.7.0', '3.8.0'],
181205
},
182206
},
183207
{
@@ -191,6 +215,8 @@ describe('Composer Non-Major PR', () => {
191215
name: 'monolog/monolog',
192216
repository: 'https://github.com/Seldaek/monolog',
193217
description: 'Sends your logs to files, sockets, inboxes, databases and various web services',
218+
latestVersion: '3.8.0',
219+
versions: ['3.7.0', '3.8.0'],
194220
},
195221
},
196222
{
@@ -238,6 +264,8 @@ describe('Composer Non-Major PR', () => {
238264
name: 'monolog/monolog',
239265
repository: 'https://github.com/Seldaek/monolog',
240266
description: 'Sends your logs to files, sockets, inboxes, databases and various web services',
267+
latestVersion: '3.8.0',
268+
versions: ['3.7.0', '3.8.0'],
241269
},
242270
},
243271
],
@@ -248,17 +276,23 @@ describe('Composer Non-Major PR', () => {
248276

249277
const prBody = await prGenerator.generateBody(testGroup)
250278

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))')
279+
// Should format like Renovate: [packageName](homepage) ([source](redirect.github.com))
280+
expect(prBody).toContain('[monolog/monolog](https://github.com/Seldaek/monolog) ([source](https://redirect.github.com/Seldaek/monolog))')
253281

254-
// Should have the same columns as npm dependencies
255-
expect(prBody).toContain('| Package | Change | Age | Adoption | Passing | Confidence |')
256-
expect(prBody).toContain('|---|---|---|---|---|---|')
282+
// Should have the new table format with dependency and update type columns
283+
expect(prBody).toContain('| Package | Change | Age | Adoption | Passing | Confidence | Type | Update |')
284+
expect(prBody).toContain('|---|---|---|---|---|---|---|---|')
257285

258-
// Should have confidence badges
286+
// Should show constraint-style changes
287+
expect(prBody).toContain('^3.7.0 -> ^3.8.0')
288+
289+
// Should have confidence badges with normalized versions
259290
expect(prBody).toContain('[![age](https://developer.mend.io/api/mc/badges/age/packagist/')
260291
expect(prBody).toContain('[![adoption](https://developer.mend.io/api/mc/badges/adoption/packagist/')
261292
expect(prBody).toContain('[![passing](https://developer.mend.io/api/mc/badges/compatibility/packagist/')
262293
expect(prBody).toContain('[![confidence](https://developer.mend.io/api/mc/badges/confidence/packagist/')
294+
295+
// Should show dependency type
296+
expect(prBody).toContain('| require | minor |')
263297
})
264298
})

0 commit comments

Comments
 (0)