Skip to content

Commit

Permalink
Added flag if "Maintainer changes" appears in the PR body (#174)
Browse files Browse the repository at this point in the history
Ideally this would get passed over in the commit message instead of being part of the (editable) PR body. For now though we can just use the PR body.
  • Loading branch information
mwaddell committed Apr 24, 2023
1 parent 5a033be commit 32fd3a6
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 15 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ Subsequent actions will have access to the following outputs:
- If this PR is associated with a security alert and `alert-lookup` is `true`, this contains the CVSS value of that alert (otherwise it contains 0).
- `steps.dependabot-metadata.outputs.compatibility-score`
- If this PR has a known compatibility score and `compat-lookup` is `true`, this contains the compatibility score (otherwise it contains 0).
- `steps.dependabot-metadata.outputs.maintainer-changes`
- Whether or not the the body of this PR contains the phrase "Maintainer changes" which is an indicator of whether or not any maintainers have changed.

**Note:** By default, these outputs will only be populated if the target Pull Request was opened by Dependabot and contains
**only** Dependabot-created commits. To override, see `skip-commit-verification` / `skip-verification`.
Expand Down
2 changes: 2 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ outputs:
description: 'If this PR is associated with a security alert and `alert-lookup` is `true`, this contains the CVSS value of that alert (otherwise it contains 0).'
compatibility-score:
description: 'If this PR has a known compatibility score and `compat-lookup` is `true`, this contains the compatibility score (otherwise it contains 0).'
maintainer-changes:
description: 'Whether or not the the body of this PR contains the phrase "Maintainer changes" which is an indicator of whether or not any maintainers have changed.'
runs:
using: 'node16'
main: 'dist/index.js'
18 changes: 14 additions & 4 deletions dist/index.js

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

2 changes: 2 additions & 0 deletions src/dependabot/output.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const baseDependency = {
prevVersion: '',
newVersion: '',
compatScore: 0,
maintainerChanges: false,
alertState: '',
ghsaId: '',
cvss: 0
Expand All @@ -36,6 +37,7 @@ test('when given a single dependency it sets its values', async () => {
prevVersion: '1.0.2',
newVersion: '1.1.3-beta',
compatScore: 43,
maintainerChanges: true,
alertState: 'FIXED',
ghsaId: 'VERY_LONG_ID',
cvss: 4.6
Expand Down
3 changes: 3 additions & 0 deletions src/dependabot/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export function set (updatedDependencies: Array<updatedDependency>): void {
const prevVersion = firstDependency?.prevVersion
const newVersion = firstDependency?.newVersion
const compatScore = firstDependency?.compatScore
const maintainerChanges = firstDependency?.maintainerChanges
const alertState = firstDependency?.alertState
const ghsaId = firstDependency?.ghsaId
const cvss = firstDependency?.cvss
Expand All @@ -41,6 +42,7 @@ export function set (updatedDependencies: Array<updatedDependency>): void {
core.info(`outputs.previous-version: ${prevVersion}`)
core.info(`outputs.new-version: ${newVersion}`)
core.info(`outputs.compatibility-score: ${compatScore}`)
core.info(`outputs.maintainer-changes: ${maintainerChanges}`)
core.info(`outputs.alert-state: ${alertState}`)
core.info(`outputs.ghsa-id: ${ghsaId}`)
core.info(`outputs.cvss: ${cvss}`)
Expand All @@ -56,6 +58,7 @@ export function set (updatedDependencies: Array<updatedDependency>): void {
core.setOutput('previous-version', prevVersion)
core.setOutput('new-version', newVersion)
core.setOutput('compatibility-score', compatScore)
core.setOutput('maintainer-changes', maintainerChanges)
core.setOutput('alert-state', alertState)
core.setOutput('ghsa-id', ghsaId)
core.setOutput('cvss', cvss)
Expand Down
34 changes: 27 additions & 7 deletions src/dependabot/update_metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as updateMetadata from './update_metadata'
test('it returns an empty array for a blank string', async () => {
const getAlert = async () => Promise.resolve({ alertState: 'DISMISSED', ghsaId: 'GHSA-III-BBB', cvss: 4.6 })
const getScore = async () => Promise.resolve(43)
expect(updateMetadata.parse('', 'dependabot/nuget/coffee-rails', 'main', getAlert, getScore)).resolves.toEqual([])
expect(updateMetadata.parse('', '', 'dependabot/nuget/coffee-rails', 'main', getAlert, getScore)).resolves.toEqual([])
})

test('it returns an empty array for commit message with no dependabot yaml fragment', async () => {
Expand All @@ -16,7 +16,7 @@ test('it returns an empty array for commit message with no dependabot yaml fragm

const getAlert = async () => Promise.resolve({ alertState: 'DISMISSED', ghsaId: 'GHSA-III-BBB', cvss: 4.6 })
const getScore = async () => Promise.resolve(43)
expect(updateMetadata.parse(commitMessage, 'dependabot/nuget/coffee-rails', 'main', getAlert, getScore)).resolves.toEqual([])
expect(updateMetadata.parse(commitMessage, '', 'dependabot/nuget/coffee-rails', 'main', getAlert, getScore)).resolves.toEqual([])
})

test('it returns the updated dependency information when there is a yaml fragment', async () => {
Expand All @@ -33,10 +33,18 @@ test('it returns the updated dependency information when there is a yaml fragmen
'...\n' +
'\n' +
'Signed-off-by: dependabot[bot] <support@github.com>'
const body =
'Bumps [coffee-rails](https://github.com/rails/coffee-rails) from 4.0.1 to 4.2.2.\n' +
'- [Release notes](https://github.com/rails/coffee-rails/releases)\n' +
'- [Changelog](https://github.com/rails/coffee-rails/blob/master/CHANGELOG.md)\n' +
'- [Commits](rails/coffee-rails@v4.0.1...v4.2.2)\n' +
'\n' +
'Maintainer changes:\n' +
'The maintainer changed!'

const getAlert = async () => Promise.resolve({ alertState: 'DISMISSED', ghsaId: 'GHSA-III-BBB', cvss: 4.6 })
const getScore = async () => Promise.resolve(43)
const updatedDependencies = await updateMetadata.parse(commitMessage, 'dependabot/nuget/coffee-rails', 'main', getAlert, getScore)
const updatedDependencies = await updateMetadata.parse(commitMessage, body, 'dependabot/nuget/coffee-rails', 'main', getAlert, getScore)

expect(updatedDependencies).toHaveLength(1)

Expand All @@ -49,6 +57,7 @@ test('it returns the updated dependency information when there is a yaml fragmen
expect(updatedDependencies[0].prevVersion).toEqual('4.0.1')
expect(updatedDependencies[0].newVersion).toEqual('4.2.2')
expect(updatedDependencies[0].compatScore).toEqual(43)
expect(updatedDependencies[0].maintainerChanges).toEqual(true)
expect(updatedDependencies[0].alertState).toEqual('DISMISSED')
expect(updatedDependencies[0].ghsaId).toEqual('GHSA-III-BBB')
expect(updatedDependencies[0].cvss).toEqual(4.6)
Expand All @@ -72,6 +81,13 @@ test('it supports multiple dependencies within a single fragment', async () => {
'...\n' +
'\n' +
'Signed-off-by: dependabot[bot] <support@github.com>'
const body =
'Bumps [coffee-rails](https://github.com/rails/coffee-rails) from 4.0.1 to 4.2.2.\n' +
'- [Release notes](https://github.com/rails/coffee-rails/releases)\n' +
'- [Changelog](https://github.com/rails/coffee-rails/blob/master/CHANGELOG.md)\n' +
'- [Commits](rails/coffee-rails@v4.0.1...v4.2.2)\n' +
'\n' +
'Has the maintainer changed?'

const getAlert = async (name: string) => {
if (name === 'coffee-rails') {
Expand All @@ -89,7 +105,7 @@ test('it supports multiple dependencies within a single fragment', async () => {
return Promise.resolve(0)
}

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

expect(updatedDependencies).toHaveLength(2)

Expand All @@ -102,6 +118,7 @@ test('it supports multiple dependencies within a single fragment', async () => {
expect(updatedDependencies[0].prevVersion).toEqual('4.0.1')
expect(updatedDependencies[0].newVersion).toEqual('4.2.2')
expect(updatedDependencies[0].compatScore).toEqual(34)
expect(updatedDependencies[0].maintainerChanges).toEqual(false)
expect(updatedDependencies[0].alertState).toEqual('DISMISSED')
expect(updatedDependencies[0].ghsaId).toEqual('GHSA-III-BBB')
expect(updatedDependencies[0].cvss).toEqual(4.6)
Expand All @@ -114,6 +131,7 @@ test('it supports multiple dependencies within a single fragment', async () => {
expect(updatedDependencies[1].targetBranch).toEqual('main')
expect(updatedDependencies[1].prevVersion).toEqual('')
expect(updatedDependencies[1].compatScore).toEqual(0)
expect(updatedDependencies[1].maintainerChanges).toEqual(false)
expect(updatedDependencies[1].alertState).toEqual('')
expect(updatedDependencies[1].ghsaId).toEqual('')
expect(updatedDependencies[1].cvss).toEqual(0)
Expand All @@ -136,7 +154,7 @@ test('it returns the updated dependency information when there is a leading v in

const getAlert = async () => Promise.resolve({ alertState: 'DISMISSED', ghsaId: 'GHSA-III-BBB', cvss: 4.6 })
const getScore = async () => Promise.resolve(43)
const updatedDependencies = await updateMetadata.parse(commitMessage, 'dependabot/nuget/coffee-rails', 'main', getAlert, getScore)
const updatedDependencies = await updateMetadata.parse(commitMessage, '', 'dependabot/nuget/coffee-rails', 'main', getAlert, getScore)

expect(updatedDependencies).toHaveLength(1)

Expand Down Expand Up @@ -176,7 +194,7 @@ test('it only returns information within the first fragment if there are multipl
'\n' +
'Signed-off-by: dependabot[bot] <support@github.com>'

const updatedDependencies = await updateMetadata.parse(commitMessage, 'dependabot|nuget|coffee-rails', 'main', undefined, undefined)
const updatedDependencies = await updateMetadata.parse(commitMessage, '', 'dependabot|nuget|coffee-rails', 'main', undefined, undefined)

expect(updatedDependencies).toHaveLength(1)

Expand All @@ -189,6 +207,7 @@ test('it only returns information within the first fragment if there are multipl
expect(updatedDependencies[0].prevVersion).toEqual('')
expect(updatedDependencies[0].newVersion).toEqual('')
expect(updatedDependencies[0].compatScore).toEqual(0)
expect(updatedDependencies[0].maintainerChanges).toEqual(false)
expect(updatedDependencies[0].alertState).toEqual('')
expect(updatedDependencies[0].ghsaId).toEqual('')
expect(updatedDependencies[0].cvss).toEqual(0)
Expand All @@ -211,7 +230,7 @@ test('it properly handles dependencies which contain slashes', async () => {

const getAlert = async () => Promise.resolve({ alertState: '', ghsaId: '', cvss: 0 })
const getScore = async () => Promise.resolve(0)
const updatedDependencies = await updateMetadata.parse(commitMessage, 'dependabot/nuget/api/rails/coffee', 'main', getAlert, getScore)
const updatedDependencies = await updateMetadata.parse(commitMessage, '', 'dependabot/nuget/api/rails/coffee', 'main', getAlert, getScore)

expect(updatedDependencies).toHaveLength(1)

Expand All @@ -224,6 +243,7 @@ test('it properly handles dependencies which contain slashes', async () => {
expect(updatedDependencies[0].prevVersion).toEqual('')
expect(updatedDependencies[0].newVersion).toEqual('')
expect(updatedDependencies[0].compatScore).toEqual(0)
expect(updatedDependencies[0].maintainerChanges).toEqual(false)
expect(updatedDependencies[0].alertState).toEqual('')
expect(updatedDependencies[0].ghsaId).toEqual('')
expect(updatedDependencies[0].cvss).toEqual(0)
Expand Down
7 changes: 5 additions & 2 deletions src/dependabot/update_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export interface updatedDependency extends dependencyAlert {
targetBranch: string,
prevVersion: string,
newVersion: string,
compatScore: number
compatScore: number,
maintainerChanges: boolean
}

export interface alertLookup {
Expand All @@ -26,10 +27,11 @@ export interface scoreLookup {
(dependencyName: string, previousVersion: string, newVersion: string, ecosystem: string): Promise<number>;
}

export async function parse (commitMessage: string, branchName: string, mainBranch: string, lookup?: alertLookup, getScore?: scoreLookup): Promise<Array<updatedDependency>> {
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[^ ]*) to \S*? ?(?<to>v?\d[^ ]*)$/m)
const yamlFragment = commitMessage.match(/^-{3}\n(?<dependencies>[\S|\s]*?)\n^\.{3}\n/m)
const newMaintainer = !!body.match(/Maintainer changes/m)
const lookupFn = lookup ?? (() => Promise.resolve({ alertState: '', ghsaId: '', cvss: 0 }))
const scoreFn = getScore ?? (() => Promise.resolve(0))

Expand Down Expand Up @@ -58,6 +60,7 @@ export async function parse (commitMessage: string, branchName: string, mainBran
prevVersion: lastVersion,
newVersion: nextVersion,
compatScore: await scoreFn(dependency['dependency-name'], lastVersion, nextVersion, chunks[1]),
maintainerChanges: newMaintainer,
...await lookupFn(dependency['dependency-name'], lastVersion, dirname)
}
}))
Expand Down
5 changes: 5 additions & 0 deletions src/dependabot/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ export function getBranchNames (context: Context): branchNames {
const { pull_request: pr } = context.payload
return { headName: pr?.head.ref || '', baseName: pr?.base.ref }
}

export function getBody (context: Context): string {
const { pull_request: pr } = context.payload
return pr?.body || ''
}
2 changes: 1 addition & 1 deletion src/dry-run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async function check (args: any): Promise<void> {
const branchNames = getBranchNames(newContext)
const lookupFn = (name, version, directory) => getAlert(name, version, directory, githubClient, actionContext)

const updatedDependencies = await parse(commitMessage, branchNames.headName, branchNames.baseName, lookupFn, getCompatibility)
const updatedDependencies = await parse(commitMessage, pullRequest.body, branchNames.headName, branchNames.baseName, lookupFn, getCompatibility)

if (updatedDependencies.length > 0) {
console.log('Updated dependencies:')
Expand Down

0 comments on commit 32fd3a6

Please sign in to comment.