Adds option to write summary into a pr comment
This commit is contained in:
@@ -66,22 +66,22 @@ 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 |
|
||||||
| `fail-on-scopes`† | Contains a list of strings of the build environments you want to support. The action will fail on pull requests that introduce vulnerabilities in the scopes that match the list. |`runtime`, `development`, `unknown` | `runtime` |
|
| `fail-on-scopes`† | Contains a list of strings of the build environments you want to support. The action will fail on pull requests that introduce vulnerabilities in the scopes that match the list. | `runtime`, `development`, `unknown` | `runtime` |
|
||||||
| `allow-ghsas` | Contains a list of GitHub Advisory Database IDs that can be skipped during detection. | Any GHSAs from the [GitHub Advisory Database](https://github.com/advisories) | none |
|
| `allow-ghsas` | Contains a list of GitHub Advisory Database IDs that can be skipped during detection. | Any GHSAs from the [GitHub Advisory Database](https://github.com/advisories) | none |
|
||||||
| `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.
|
||||||
@@ -112,10 +112,10 @@ 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. |
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
|
|||||||
@@ -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 => {
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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
@@ -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(
|
||||||
|
|||||||
@@ -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
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user