Merge pull request #632 from ppkarwasz/feat/multi-versions

feat: Parse versions from metadata links
This commit is contained in:
Thomas Ruggeri
2025-12-22 13:00:31 -08:00
committed by GitHub
4 changed files with 128 additions and 13 deletions

34
dist/index.js generated vendored
View File

@@ -10243,10 +10243,13 @@ async function parse(commitMessage, body, branchName, mainBranch, lookup, getSco
const next = bumpFragment?.groups?.to ?? (updateFragment?.groups?.to ?? '');
const dependencyGroup = groupName?.groups?.name ?? '';
if (data['updated-dependencies']) {
const updatedVersions = parseMetadataLinks(commitMessage);
const dirname = branchNameToDirectoryName(chunks, delim, data['updated-dependencies'], dependencyGroup);
return await Promise.all(data['updated-dependencies'].map(async (dependency, index) => {
const lastVersion = index === 0 ? prev : '';
const nextVersion = index === 0 ? next : '';
const dependencyName = dependency['dependency-name'];
const updatedVersion = updatedVersions.get(dependencyName);
const lastVersion = updatedVersion?.prevVersion || (index === 0 ? prev : '');
const nextVersion = dependency['dependency-version'] || updatedVersion?.newVersion || (index === 0 ? next : '');
const updateType = dependency['update-type'] || calculateUpdateType(lastVersion, nextVersion);
return {
dependencyName: dependency['dependency-name'],
@@ -10267,6 +10270,33 @@ async function parse(commitMessage, body, branchName, mainBranch, lookup, getSco
}
return Promise.resolve([]);
}
/**
* Parses the human-readable metadata links from a commit message.
*
* See `Dependabot::PullRequestCreator::MessageBuilder#metadata_links` in the Ruby codebase for more details
* on the current format.
*
* **NOTE**: This data is only available if more than one dependency is updated in a single PR.
*
* @param commitMessage - The commit message containing metadata links.
* @returns A map from the name of the dependency to an updatedDependency object containing the old and new versions.
*/
function parseMetadataLinks(commitMessage) {
const updates = new Map();
const updatesExpr = /^Updates `(?<dependencyName>\S+)` (from (?<from>\S+) )?to (?<to>\S+)$/gm;
let match;
while ((match = updatesExpr.exec(commitMessage)) !== null) {
const groups = match.groups;
if (groups) {
const dependencyName = groups.dependencyName;
updates.set(dependencyName, {
prevVersion: groups.from ?? '',
newVersion: groups.to
});
}
}
return updates;
}
function calculateUpdateType(lastVersion, nextVersion) {
if (!lastVersion || !nextVersion || lastVersion === nextVersion) {
return '';

View File

@@ -507,3 +507,54 @@ test('calculateUpdateType should handle all paths', () => {
expect(updateMetadata.calculateUpdateType('1.1.1', '1.1.2')).toEqual('version-update:semver-patch')
expect(updateMetadata.calculateUpdateType('1.1.1.1', '1.1.1.2')).toEqual('version-update:semver-patch')
})
test('it handles versions from `metadataLinks`', async () => {
const commitMessage = `Bump the non-breaking group in /log4j-parent with 2 updates
Bumps the non-breaking group in /log4j-parent with 2 updates:
Updates \`commons-codec:commons-codec\` from 1.17.0 to 1.18.0
- [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt)
- [Commits](apache/commons-codec@rel/commons-codec-1.17.0...rel/commons-codec-1.18.0)
Updates \`org.apache.commons:commons-compress\` to 1.27.1
---
updated-dependencies:
- dependency-name: commons-codec:commons-codec
- dependency-name: org.apache.commons:commons-compress
...
`
const updatedDependencies = await updateMetadata.parse(commitMessage, '', 'dependabot/maven/non-breaking-cc60d48967', '2.x')
expect(updatedDependencies).toHaveLength(2)
expect(updatedDependencies[0].dependencyName).toEqual('commons-codec:commons-codec')
expect(updatedDependencies[0].prevVersion).toEqual('1.17.0')
expect(updatedDependencies[0].newVersion).toEqual('1.18.0')
expect(updatedDependencies[1].dependencyName).toEqual('org.apache.commons:commons-compress')
expect(updatedDependencies[1].prevVersion).toEqual('')
expect(updatedDependencies[1].newVersion).toEqual('1.27.1')
})
test('it handles new versions from YAML', async () => {
const commitMessage = `Bump the non-breaking group in /log4j-parent with 2 updates
Bumps the non-breaking group in /log4j-parent with 2 updates:
---
updated-dependencies:
- dependency-name: commons-codec:commons-codec
dependency-version: 1.18.0
- dependency-name: org.apache.commons:commons-compress
dependency-version: 1.27.1
...
`
const updatedDependencies = await updateMetadata.parse(commitMessage, '', 'dependabot/maven/non-breaking-cc60d48967', '2.x')
expect(updatedDependencies).toHaveLength(2)
expect(updatedDependencies[0].dependencyName).toEqual('commons-codec:commons-codec')
expect(updatedDependencies[0].prevVersion).toEqual('')
expect(updatedDependencies[0].newVersion).toEqual('1.18.0')
expect(updatedDependencies[1].dependencyName).toEqual('org.apache.commons:commons-compress')
expect(updatedDependencies[1].prevVersion).toEqual('')
expect(updatedDependencies[1].newVersion).toEqual('1.27.1')
})

View File

@@ -6,15 +6,18 @@ export interface dependencyAlert {
cvss: number
}
export interface updatedDependency extends dependencyAlert {
interface dependencyVersions {
prevVersion: string,
newVersion: string
}
export interface updatedDependency extends dependencyAlert, dependencyVersions {
dependencyName: string,
dependencyType: string,
updateType: string,
directory: string,
packageEcosystem: string,
targetBranch: string,
prevVersion: string,
newVersion: string,
compatScore: number,
maintainerChanges: boolean,
dependencyGroup: string
@@ -83,11 +86,14 @@ export async function parse (commitMessage: string, body: string, branchName: st
const dependencyGroup = groupName?.groups?.name ?? ''
if (data['updated-dependencies']) {
const updatedVersions = parseMetadataLinks(commitMessage)
const dirname = branchNameToDirectoryName(chunks, delim, data['updated-dependencies'], dependencyGroup)
return await Promise.all(data['updated-dependencies'].map(async (dependency, index) => {
const lastVersion = index === 0 ? prev : ''
const nextVersion = index === 0 ? next : ''
const dependencyName = dependency['dependency-name']
const updatedVersion = updatedVersions.get(dependencyName)
const lastVersion = updatedVersion?.prevVersion || (index === 0 ? prev : '')
const nextVersion = dependency['dependency-version'] || updatedVersion?.newVersion || (index === 0 ? next : '')
const updateType = dependency['update-type'] || calculateUpdateType(lastVersion, nextVersion)
return {
dependencyName: dependency['dependency-name'],
@@ -110,6 +116,34 @@ export async function parse (commitMessage: string, body: string, branchName: st
return Promise.resolve([])
}
/**
* Parses the human-readable metadata links from a commit message.
*
* See `Dependabot::PullRequestCreator::MessageBuilder#metadata_links` in the Ruby codebase for more details
* on the current format.
*
* **NOTE**: This data is only available if more than one dependency is updated in a single PR.
*
* @param commitMessage - The commit message containing metadata links.
* @returns A map from the name of the dependency to an updatedDependency object containing the old and new versions.
*/
function parseMetadataLinks(commitMessage: string): Map<string, dependencyVersions> {
const updates: Map<string, dependencyVersions> = new Map()
const updatesExpr: RegExp = /^Updates `(?<dependencyName>\S+)` (from (?<from>\S+) )?to (?<to>\S+)$/gm
let match: RegExpExecArray | null
while ((match = updatesExpr.exec(commitMessage)) !== null) {
const groups = match.groups
if (groups) {
const dependencyName = groups.dependencyName
updates.set(dependencyName, {
prevVersion: groups.from ?? '',
newVersion: groups.to
})
}
}
return updates
}
export function calculateUpdateType (lastVersion: string, nextVersion: string) {
if (!lastVersion || !nextVersion || lastVersion === nextVersion) {
return ''

View File

@@ -286,8 +286,8 @@ test('it supports returning information about grouped updates', async () => {
directory: '/gh-base-image',
packageEcosystem: 'docker',
targetBranch: 'trunk',
prevVersion: '',
newVersion: '',
prevVersion: '24.0.1+incompatible',
newVersion: '24.0.2+incompatible',
compatScore: 34,
maintainerChanges: false,
dependencyGroup: 'docker',
@@ -302,8 +302,8 @@ test('it supports returning information about grouped updates', async () => {
directory: '/gh-base-image',
packageEcosystem: 'docker',
targetBranch: 'trunk',
prevVersion: '',
newVersion: '',
prevVersion: '24.0.1+incompatible',
newVersion: '24.0.2+incompatible',
compatScore: 34,
maintainerChanges: false,
dependencyGroup: 'docker',
@@ -318,8 +318,8 @@ test('it supports returning information about grouped updates', async () => {
directory: '/gh-base-image',
packageEcosystem: 'docker',
targetBranch: 'trunk',
prevVersion: '',
newVersion: '',
prevVersion: '24.0.1+incompatible',
newVersion: '24.0.2+incompatible',
compatScore: 34,
maintainerChanges: false,
dependencyGroup: 'docker',