Adds option to write summary into a pr comment

This commit is contained in:
David Losert
2023-02-16 10:03:16 +00:00
committed by GitHub
parent 5c771993de
commit 1c85e9db8d
7 changed files with 116 additions and 21 deletions
+3 -3
View File
@@ -67,7 +67,7 @@ jobs:
Configure this action by either inlining these options in your workflow file, or by using an external configuration file. All configuration options are optional. Configure this action by either inlining these options in your workflow file, or by using an external configuration file. All configuration options are optional.
| Option | Usage | Possible values | Default value | | Option | Usage | Possible values | Default value |
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|---------------| | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------- |
| `fail-on-severity` | Defines the threshold for the level of severity. The action will fail on any pull requests that introduce vulnerabilities of the specified severity level or higher. | `low`, `moderate`, `high`, `critical` | `low` | | `fail-on-severity` | Defines the threshold for the level of severity. The action will fail on any pull requests that introduce vulnerabilities of the specified severity level or higher. | `low`, `moderate`, `high`, `critical` | `low` |
| `allow-licenses`* | Contains a list of allowed licenses. The action will fail on pull requests that introduce dependencies with licenses that do not match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none | | `allow-licenses`* | Contains a list of allowed licenses. The action will fail on pull requests that introduce dependencies with licenses that do not match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
| `deny-licenses`* | Contains a list of prohibited licenses. The action will fail on pull requests that introduce dependencies with licenses that match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none | | `deny-licenses`* | Contains a list of prohibited licenses. The action will fail on pull requests that introduce dependencies with licenses that match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
@@ -76,12 +76,12 @@ Configure this action by either inlining these options in your workflow file, or
| `license-check` | Enable or disable the license check performed by the action. | `true`, `false` | `true` | | `license-check` | Enable or disable the license check performed by the action. | `true`, `false` | `true` |
| `vulnerability-check` | Enable or disable the vulnerability check performed by the action. | `true`, `false` | `true` | | `vulnerability-check` | Enable or disable the vulnerability check performed by the action. | `true`, `false` | `true` |
| `base-ref`/`head-ref` | Provide custom git references for the git base/head when performing the comparison check. This is only used for event types other than `pull_request` and `pull_request_target`. | Any valid git ref(s) in your project | none | | `base-ref`/`head-ref` | Provide custom git references for the git base/head when performing the comparison check. This is only used for event types other than `pull_request` and `pull_request_target`. | Any valid git ref(s) in your project | none |
| `comment-summary-in-pr` | Enable or disable reporting the review summary as a comment in the pull request. If enabled, you must give the workflow or job permission `pull-requests: write`. | `true`, `false` | `false` |
*not supported for use with GitHub Enterprise Server *not supported for use with GitHub Enterprise Server
†will be supported with GitHub Enterprise Server 3.8 †will be supported with GitHub Enterprise Server 3.8
### Inline Configuration ### Inline Configuration
You can pass options to the Dependency Review GitHub Action using your workflow file. You can pass options to the Dependency Review GitHub Action using your workflow file.
@@ -113,7 +113,7 @@ jobs:
You can use an external configuration file to specify the settings for this action. It can be a local file or a file in an external repository. Refer to the following options for the specification. You can use an external configuration file to specify the settings for this action. It can be a local file or a file in an external repository. Refer to the following options for the specification.
| Option | Usage | Possible values | | Option | Usage | Possible values |
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `config-file` | A path to a file in the current repository or an external repository. Use this syntax for external files: `OWNER/REPOSITORY/FILENAME@BRANCH` | **Local file**: `./.github/dependency-review-config.yml` <br> **External repo**: `github/octorepo/dependency-review-config.yml@main` | | `config-file` | A path to a file in the current repository or an external repository. Use this syntax for external files: `OWNER/REPOSITORY/FILENAME@BRANCH` | **Local file**: `./.github/dependency-review-config.yml` <br> **External repo**: `github/octorepo/dependency-review-config.yml@main` |
| `external-repo-token` | Specifies a token for fetching the configuration file. It is required if the file resides in a private external repository and for all GitHub Enterprise Server repositories. Create a token in [developer settings](https://github.com/settings/tokens). | Any token with `read` permissions to the repository hosting the config file. | | `external-repo-token` | Specifies a token for fetching the configuration file. It is required if the file resides in a private external repository and for all GitHub Enterprise Server repositories. Create a token in [developer settings](https://github.com/settings/tokens). | Any token with `read` permissions to the repository hosting the config file. |
+2 -1
View File
@@ -22,7 +22,8 @@ function clearInputs() {
'VULNERABILITY-CHECK', 'VULNERABILITY-CHECK',
'CONFIG-FILE', 'CONFIG-FILE',
'BASE-REF', 'BASE-REF',
'HEAD-REF' 'HEAD-REF',
'COMMENT-SUMMARY-IN-PR'
] ]
allowedOptions.forEach(option => { allowedOptions.forEach(option => {
+3
View File
@@ -41,6 +41,9 @@ inputs:
vulnerability-check: vulnerability-check:
description: A boolean to determine if vulnerability checks should be performed description: A boolean to determine if vulnerability checks should be performed
required: false required: false
comment-summary-in-pr:
description: A boolean to determine if the report should be posted as a comment in the PR itself. Setting this to true requires you to give the workflow the write permissions for pull-requests
required: false
runs: runs:
using: 'node16' using: 'node16'
main: 'dist/index.js' main: 'dist/index.js'
+84
View File
@@ -0,0 +1,84 @@
import * as github from '@actions/github'
import * as core from '@actions/core'
import * as githubUtils from '@actions/github/lib/utils'
import * as retry from '@octokit/plugin-retry'
import {RequestError} from '@octokit/request-error'
const retryingOctokit = githubUtils.GitHub.plugin(retry.retry)
const octo = new retryingOctokit(
githubUtils.getOctokitOptions(core.getInput('repo-token', {required: true}))
)
// Comment Marker to identify an existing comment to update, so we don't spam the PR with comments
const COMMENT_MARKER = '<!-- dependency-review-pr-comment-marker -->'
export async function commentPr(summary: typeof core.summary): Promise<void> {
if (!github.context.payload.pull_request) {
core.warning(
'Not in the context of a pull request. Skipping comment creation.'
)
return
}
const commentBody = `${summary.stringify()}\n\n${COMMENT_MARKER}`
try {
const existingCommentId = await findCommentByMarker(COMMENT_MARKER)
if (existingCommentId) {
await octo.rest.issues.updateComment({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
comment_id: existingCommentId,
body: commentBody
})
} else {
await octo.rest.issues.createComment({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: github.context.payload.pull_request.number,
body: commentBody
})
}
} catch (error) {
if (error instanceof RequestError && error.status === 403) {
core.warning(
`Unable to write summary to pull-request. Make sure you are giving this workflow the permission 'pull-requests: write'.`
)
} else {
if (error instanceof Error) {
core.warning(
`Unable to comment summary to pull-request, received error: ${error.message}`
)
} else {
core.warning(
'Unable to comment summary to pull-request: Unexpected fatal error'
)
}
}
}
}
async function findCommentByMarker(
commentBodyIncludes: string
): Promise<number | undefined> {
const commentsIterator = octo.paginate.iterator(
octo.rest.issues.listComments,
{
owner: github.context.repo.owner,
repo: github.context.repo.repo,
// We are already checking if we are in the context of a pull request in the caller
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
issue_number: github.context.payload.pull_request!.number
}
)
for await (const {data: comments} of commentsIterator) {
const existingComment = comments.find(comment =>
comment.body?.includes(commentBodyIncludes)
)
if (existingComment) return existingComment.id
}
return undefined
}
+3 -1
View File
@@ -34,6 +34,7 @@ function readInlineConfig(): ConfigurationOptionsPartial {
const vulnerability_check = getOptionalBoolean('vulnerability-check') const vulnerability_check = getOptionalBoolean('vulnerability-check')
const base_ref = getOptionalInput('base-ref') const base_ref = getOptionalInput('base-ref')
const head_ref = getOptionalInput('head-ref') const head_ref = getOptionalInput('head-ref')
const comment_summary_in_pr = getOptionalBoolean('comment-summary-in-pr')
validateLicenses('allow-licenses', allow_licenses) validateLicenses('allow-licenses', allow_licenses)
validateLicenses('deny-licenses', deny_licenses) validateLicenses('deny-licenses', deny_licenses)
@@ -47,7 +48,8 @@ function readInlineConfig(): ConfigurationOptionsPartial {
license_check, license_check,
vulnerability_check, vulnerability_check,
base_ref, base_ref,
head_ref head_ref,
comment_summary_in_pr
} }
return Object.fromEntries( return Object.fromEntries(
+4
View File
@@ -15,6 +15,7 @@ import * as summary from './summary'
import {getRefs} from './git-refs' import {getRefs} from './git-refs'
import {groupDependenciesByManifest} from './utils' import {groupDependenciesByManifest} from './utils'
import {commentPr} from './comment-pr'
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
@@ -69,6 +70,9 @@ async function run(): Promise<void> {
summary.addScannedDependencies(changes) summary.addScannedDependencies(changes)
printScannedDependencies(changes) printScannedDependencies(changes)
if (config.comment_summary_in_pr) {
await commentPr(core.summary)
}
} catch (error) { } catch (error) {
if (error instanceof RequestError && error.status === 404) { if (error instanceof RequestError && error.status === 404) {
core.setFailed( core.setFailed(
+2 -1
View File
@@ -45,7 +45,8 @@ export const ConfigurationOptionsSchema = z
vulnerability_check: z.boolean().default(true), vulnerability_check: z.boolean().default(true),
config_file: z.string().optional(), config_file: z.string().optional(),
base_ref: z.string().optional(), base_ref: z.string().optional(),
head_ref: z.string().optional() head_ref: z.string().optional(),
comment_summary_in_pr: z.boolean().default(false)
}) })
.superRefine((config, context) => { .superRefine((config, context) => {
if (config.allow_licenses && config.deny_licenses) { if (config.allow_licenses && config.deny_licenses) {