Skip to content

Commit

Permalink
Handle branch names containing hyphen separators
Browse files Browse the repository at this point in the history
  • Loading branch information
tspencer244 committed Apr 24, 2024
1 parent 518993c commit a44a9df
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 6 deletions.
25 changes: 23 additions & 2 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

101 changes: 100 additions & 1 deletion src/dependabot/update_metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ test('it supports multiple dependencies within a single fragment', async () => {
return Promise.resolve(0)
}

const updatedDependencies = await updateMetadata.parse(commitMessage, body, 'dependabot/nuget/api/main/coffee-rails', 'main', getAlert, getScore)
const updatedDependencies = await updateMetadata.parse(commitMessage, body, 'dependabot/nuget/api/main/coffee-rails/and/coffeescript', 'main', getAlert, getScore)

expect(updatedDependencies).toHaveLength(2)

Expand Down Expand Up @@ -299,6 +299,105 @@ test('it properly handles dependencies which contain slashes', async () => {
expect(updatedDependencies[0].dependencyGroup).toEqual('')
})

test('it handles branch names with hyphen separator', async () => {
const commitMessage =
'- [Release notes](https://github.com/fsevents/fsevents/releases)\n' +
'- [Commits](fsevents/fsevents@v1.2.9...v1.2.13)\n' +
'\n' +
'---\n' +
'updated-dependencies:\n' +
'- dependency-name: fsevents\n' +
' dependency-type: indirect\n' +
'...\n' +
'\n' +
'Signed-off-by: dependabot[bot] <support@github.com>'

const getAlert = async () => Promise.resolve({ alertState: '', ghsaId: '', cvss: 0 })
const getScore = async () => Promise.resolve(0)
const updatedDependencies = await updateMetadata.parse(commitMessage, '', 'dependabot-npm_and_yarn-fsevents-1.2.13', 'master', getAlert, getScore)

expect(updatedDependencies[0].directory).toEqual('/')
})

test('it handles branch names with hyphen separator and manifest files in nested directories', async () => {
const commitMessage =
'- [Release notes](https://github.com/fsevents/fsevents/releases)\n' +
'- [Commits](fsevents/fsevents@v1.2.9...v1.2.13)\n' +
'\n' +
'---\n' +
'updated-dependencies:\n' +
'- dependency-name: fsevents\n' +
' dependency-type: indirect\n' +
'...\n' +
'\n' +
'Signed-off-by: dependabot[bot] <support@github.com>'

const getAlert = async () => Promise.resolve({ alertState: '', ghsaId: '', cvss: 0 })
const getScore = async () => Promise.resolve(0)
const updatedDependencies = await updateMetadata.parse(commitMessage, '', 'dependabot-npm_and_yarn-nested-nested-fsevents-1.2.13', 'master', getAlert, getScore)

expect(updatedDependencies[0].directory).toEqual('/nested/nested')
})

test('it handles branch names with hyphen separator and dependency names with forward slashes', async () => {
const commitMessage =
'- [Release notes](https://github.com/composer/composer/releases)\n' +
'- [Changelog](https://github.com/composer/composer/blob/main/CHANGELOG.md)\n' +
'- [Commits](composer/composer@1.10.26...2.6.5)\n' +
'\n' +
'---\n' +
'updated-dependencies:\n' +
'- dependency-name: composer/composer\n' +
' dependency-type: indirect\n' +
'...\n' +
'\n' +
'Signed-off-by: dependabot[bot] <support@github.com>'

const getAlert = async () => Promise.resolve({ alertState: '', ghsaId: '', cvss: 0 })
const getScore = async () => Promise.resolve(0)
const updatedDependencies = await updateMetadata.parse(commitMessage, '', 'dependabot-composer-composer-composer-2.6.5', 'master', getAlert, getScore)

expect(updatedDependencies[0].directory).toEqual('/')
})

test('it handles branch names with hyphen separator and multiple dependencies', async () => {
const commitMessage =
'Updates `twilio-video` from 2.7.0 to 2.28.1\n' +
'- [Release notes](https://github.com/twilio/twilio-video.js/releases)\n' +
'- [Changelog](https://github.com/twilio/twilio-video.js/blob/master/CHANGELOG.md)\n' +
'- [Commits](twilio/twilio-video.js@2.7.0...2.28.1)\n' +
'\n' +
'Updates `@types/twilio-video` from 2.7.0 to 2.11.0\n' +
'- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)\n' +
'- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/twilio-video)\n' +
'\n' +
'---\n' +
'updated-dependencies:\n' +
'- dependency-name: twilio-video\n' +
' dependency-type: direct:production\n' +
' update-type: version-update:semver-minor\n' +
'- dependency-name: "@types/twilio-video"\n' +
' dependency-type: direct:development\n' +
' update-type: version-update:semver-minor\n' +
'...\n' +
'\n' +
'Signed-off-by: dependabot[bot] <support@github.com>'

const getAlert = async () => Promise.resolve({ alertState: '', ghsaId: '', cvss: 0 })
const getScore = async () => Promise.resolve(0)

const updatedDependencies = await updateMetadata.parse(
commitMessage,
'',
'dependabot-npm_and_yarn-twilio-video-and-types-twilio-video-2.28.1',
'master',
getAlert,
getScore
)

expect(updatedDependencies[0].directory).toEqual('/')
})

test('calculateUpdateType should handle all paths', () => {
expect(updateMetadata.calculateUpdateType('', '')).toEqual('')
expect(updateMetadata.calculateUpdateType('', '1')).toEqual('')
Expand Down
31 changes: 29 additions & 2 deletions src/dependabot/update_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,32 @@ export interface scoreLookup {
(dependencyName: string, previousVersion: string, newVersion: string, ecosystem: string): Promise<number>;
}

function branchNameToDirectoryName (chunks: string[], delimiter: string, updatedDependencies: any): string {
// We can always slice after the first 2 pieces, because they will always contain "dependabot" followed by the name
// of the package ecosystem. e.g. "dependabot/npm_and_yarn".
const sliceStart = 2
let sliceEnd = chunks.length

// If the delimiter is "-", we assume the last piece of the branch name is a version number.
if (delimiter === '-') {
sliceEnd -= 1
}

// If there is more than 1 dependency name being updated, we assume 1 piece of the branch name will be "and".
if (updatedDependencies.length > 1) {
sliceEnd -= 1
}

updatedDependencies.forEach(dependency => {
// After replacing "/" in the dependency name with the delimiter, which could also be "/", we count how many pieces
// the dependency name would split into by the delimiter, and slicing that amount off the end of the branch name.
// e.g. "@types/twilio-video" and a delimiter of "-" would show up in the branch name as "types-twilio-video".
sliceEnd -= dependency['dependency-name'].replace('/', delimiter).split(delimiter).length
})

return `/${chunks.slice(sliceStart, sliceEnd).join('/')}`
}

export async function parse (commitMessage: string, body: string, branchName: string, mainBranch: string, lookup?: alertLookup, getScore?: scoreLookup): Promise<Array<updatedDependency>> {
const bumpFragment = commitMessage.match(/^Bumps .* from (?<from>v?\d[^ ]*) to (?<to>v?\d[^ ]*)\.$/m)
const updateFragment = commitMessage.match(/^Update .* requirement from \S*? ?(?<from>v?\d\S*) to \S*? ?(?<to>v?\d\S*)$/m)
Expand All @@ -48,8 +74,9 @@ export async function parse (commitMessage: string, body: string, branchName: st
const dependencyGroup = groupName?.groups?.name ?? ''

if (data['updated-dependencies']) {
const dirname = branchNameToDirectoryName(chunks, delim, data['updated-dependencies'])

return await Promise.all(data['updated-dependencies'].map(async (dependency, index) => {
const dirname = `/${chunks.slice(2, -1 * (1 + (dependency['dependency-name'].match(/\//g) || []).length)).join(delim) || ''}`
const lastVersion = index === 0 ? prev : ''
const nextVersion = index === 0 ? next : ''
const updateType = dependency['update-type'] || calculateUpdateType(lastVersion, nextVersion)
Expand All @@ -64,7 +91,7 @@ export async function parse (commitMessage: string, body: string, branchName: st
newVersion: nextVersion,
compatScore: await scoreFn(dependency['dependency-name'], lastVersion, nextVersion, chunks[1]),
maintainerChanges: newMaintainer,
dependencyGroup: dependencyGroup,
dependencyGroup,
...await lookupFn(dependency['dependency-name'], lastVersion, dirname)
}
}))
Expand Down
1 change: 1 addition & 0 deletions src/dry-run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@ require('yargs')(hideBin(process.argv))
})
.demandCommand(1)
.help()
.strict()
.argv
2 changes: 1 addition & 1 deletion src/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ test('if there are multiple dependencies, it summarizes them', async () => {
const mockAlert = { alertState: '', ghsaId: '', cvss: 0 }

jest.spyOn(core, 'getInput').mockReturnValue('mock-token')
jest.spyOn(util, 'getBranchNames').mockReturnValue({ headName: 'dependabot/npm_and_yarn/api/main/feature1', baseName: 'trunk' })
jest.spyOn(util, 'getBranchNames').mockReturnValue({ headName: 'dependabot/npm_and_yarn/api/main/coffee-rails/and/coffeescript', baseName: 'trunk' })
jest.spyOn(dependabotCommits, 'getMessage').mockImplementation(jest.fn(
() => Promise.resolve(mockCommitMessage)
))
Expand Down

0 comments on commit a44a9df

Please sign in to comment.