Skip to content
This repository was archived by the owner on Feb 21, 2025. It is now read-only.

Commit 842c587

Browse files
authored
Merge pull request #911 - Improve dependency review support
2 parents d1b726d + 4241e05 commit 842c587

File tree

7 files changed

+231
-73
lines changed

7 files changed

+231
-73
lines changed

README.md

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -546,8 +546,6 @@ You enable GitHub Dependency Graph support by setting the `dependency-graph` act
546546
| `generate-and-submit` | As per `generate`, but any generated dependency graph snapshots will be submitted at the end of the job. |
547547
| `download-and-submit` | Download any previously saved dependency graph snapshots, submitting them via the Dependency Submission API. This can be useful to collect all snapshots in a matrix of builds and submit them in one step. |
548548

549-
Dependency Graph _submission_ (but not generation) requires the `contents: write` permission, which may need to be explicitly enabled in the workflow file.
550-
551549
Example of a simple workflow that generates and submits a dependency graph:
552550
```yaml
553551
name: Submit dependency graph
@@ -566,14 +564,62 @@ jobs:
566564
uses: gradle/gradle-build-action@v2
567565
with:
568566
dependency-graph: generate-and-submit
569-
- name: Run a build, generating the dependency graph snapshot which will be submitted
567+
- name: Run a build and generate the dependency graph which will be submitted post-job
570568
run: ./gradlew build
571569
```
572570

573-
The `contents: write` permission is not required to generate the dependency graph, but is required in order to submit the graph via the GitHub API.
571+
The `contents: write` permission is not required to generate the dependency graph, but is required in order to submit the graph via the GitHub API. This permission will need to be explicitly enabled in the workflow file for dependency graph submission to succeed.
572+
573+
> [!IMPORTANT]
574+
> The above configuration will work for workflows that run as a result of commits to a repository branch,
575+
> but not when a workflow is triggered by a PR from a repository fork.
576+
> This is because the `contents: write` permission is not available when executing a workflow
577+
> for a PR submitted from a forked repository.
578+
> For a configuration that supports this setup, see [Dependency Graphs for pull request workflows](#dependency-graphs-for-pull-request-workflows).
579+
580+
### Integrating the `dependency-review-action`
581+
582+
The GitHub [dependency-review-action](https://github.com/actions/dependency-review-action) helps you
583+
understand dependency changes (and the security impact of these changes) for a pull request.
584+
For the `dependency-review-action` to succeed, it must run _after_ the dependency graph has been submitted for a PR.
585+
586+
When using `generate-and-submit`, dependency graph files are submitted at the end of the job, after all steps have been
587+
executed. For this reason, the `dependency-review-action` must be executed in a dependent job,
588+
and not as a subsequent step in the job that generates the dependency graph.
589+
590+
Example of a pull request workflow that executes a build for a pull request and runs the `dependency-review-action`:
574591

575-
The above configuration will work for workflows that run as a result of commits to a repository branch, but not when a workflow is triggered by a PR from a repository fork.
576-
For a configuration that supports this setup, see [Dependency Graphs for pull request workflows](#dependency-graphs-for-pull-request-workflows).
592+
```yaml
593+
name: PR check
594+
595+
on:
596+
pull_request:
597+
598+
permissions:
599+
contents: write
600+
# Note that this permission will not be available if the PR is from a forked repository
601+
602+
jobs:
603+
build:
604+
runs-on: ubuntu-latest
605+
steps:
606+
- uses: actions/checkout@v3
607+
- name: Setup Gradle to generate and submit dependency graphs
608+
uses: gradle/gradle-build-action@v2
609+
with:
610+
dependency-graph: generate-and-submit
611+
- name: Run a build and generate the dependency graph which will be submitted post-job
612+
run: ./gradlew build
613+
614+
dependency-review:
615+
needs: build
616+
runs-on: ubuntu-latest
617+
- name: Perform dependency review
618+
uses: actions/dependency-review-action@v3
619+
```
620+
621+
See [Dependency Graphs for pull request workflows](#dependency-graphs-for-pull-request-workflows) for a more complex
622+
(and less functional) example that will work for pull requests submitted from forked repositories.
577623

578624
## Limiting the scope of the dependency graph
579625

@@ -682,6 +728,9 @@ Note: when `download-and-submit` is used in a workflow triggered via [workflow_r
682728
```yaml
683729
name: run-build-and-generate-dependency-snapshot
684730
731+
on:
732+
pull_request:
733+
685734
jobs:
686735
build:
687736
runs-on: ubuntu-latest
@@ -693,6 +742,13 @@ jobs:
693742
dependency-graph: generate # Only generate in this job
694743
- name: Run a build, generating the dependency graph snapshot which will be submitted
695744
run: ./gradlew build
745+
746+
dependency-review:
747+
needs: build
748+
runs-on: ubuntu-latest
749+
- name: Perform dependency review
750+
uses: actions/dependency-review-action@v3
751+
696752
```
697753

698754
***Dependent workflow file***
@@ -705,15 +761,48 @@ on:
705761
types: [completed]
706762
707763
jobs:
708-
submit-snapshots:
764+
submit-dependency-graph:
709765
runs-on: ubuntu-latest
710766
steps:
711-
- name: Retrieve dependency graph artifact and submit
712-
uses: gradle/gradle-build-action@v2
767+
- name: Retrieve dependency graph artifact and submit
768+
uses: gradle/gradle-build-action@v2
713769
with:
714770
dependency-graph: download-and-submit
715771
```
716772

773+
### Integrating `dependency-review-action` for pull request workflows
774+
775+
The GitHub [dependency-review-action](https://github.com/actions/dependency-review-action) helps you
776+
understand dependency changes (and the security impact of these changes) for a pull request.
777+
778+
To integrate the `dependency-review-action` into the pull request workflows above, a separate workflow should be added.
779+
This workflow will be triggered directly on `pull_request`, but will need to wait until the dependency graph results are
780+
submitted before the dependency review can complete. How long to wait is controlled by the `retry-on-snapshot-warnings` input parameters.
781+
782+
Here's an example of a separate "Dependency Review" workflow that will wait for 10 minutes for the PR check workflow to complete.
783+
784+
```yaml
785+
name: dependency-review
786+
on:
787+
pull_request:
788+
789+
permissions:
790+
contents: read
791+
pull-requests: write
792+
793+
jobs:
794+
dependency-review:
795+
runs-on: ubuntu-latest
796+
steps:
797+
- name: 'Dependency Review'
798+
uses: actions/dependency-review-action@v3
799+
with:
800+
retry-on-snapshot-warnings: true
801+
retry-on-snapshot-warnings-timeout: 600
802+
```
803+
804+
The `retry-on-snapshot-warnings-timeout` (in seconds) needs to be long enough to allow the entire `run-build-and-generate-dependency-snapshot` and `submit-dependency-snapshot` workflows (above) to complete.
805+
717806
## Gradle version compatibility
718807

719808
The GitHub Dependency Graph plugin should be compatible with all versions of Gradle >= 5.0, and has been tested against

dist/main/index.js

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -70674,38 +70674,44 @@ const artifact = __importStar(__nccwpck_require__(2605));
7067470674
const github = __importStar(__nccwpck_require__(5438));
7067570675
const glob = __importStar(__nccwpck_require__(8090));
7067670676
const toolCache = __importStar(__nccwpck_require__(7784));
70677+
const request_error_1 = __nccwpck_require__(537);
7067770678
const path = __importStar(__nccwpck_require__(1017));
7067870679
const fs_1 = __importDefault(__nccwpck_require__(7147));
7067970680
const layout = __importStar(__nccwpck_require__(8182));
7068070681
const input_params_1 = __nccwpck_require__(3885);
7068170682
const DEPENDENCY_GRAPH_ARTIFACT = 'dependency-graph';
7068270683
function setup(option) {
70683-
if (option === input_params_1.DependencyGraphOption.Disabled || option === input_params_1.DependencyGraphOption.DownloadAndSubmit) {
70684-
return;
70685-
}
70686-
core.info('Enabling dependency graph generation');
70687-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_ENABLED', 'true');
70688-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR', getJobCorrelator());
70689-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_ID', github.context.runId);
70690-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_REF', github.context.ref);
70691-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_SHA', getShaFromContext());
70692-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_WORKSPACE', layout.workspaceDirectory());
70693-
core.exportVariable('DEPENDENCY_GRAPH_REPORT_DIR', path.resolve(layout.workspaceDirectory(), 'dependency-graph-reports'));
70684+
return __awaiter(this, void 0, void 0, function* () {
70685+
if (option === input_params_1.DependencyGraphOption.Disabled) {
70686+
return;
70687+
}
70688+
if (option === input_params_1.DependencyGraphOption.DownloadAndSubmit) {
70689+
yield downloadAndSubmitDependencyGraphs();
70690+
return;
70691+
}
70692+
core.info('Enabling dependency graph generation');
70693+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_ENABLED', 'true');
70694+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR', getJobCorrelator());
70695+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_ID', github.context.runId);
70696+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_REF', github.context.ref);
70697+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_SHA', getShaFromContext());
70698+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_WORKSPACE', layout.workspaceDirectory());
70699+
core.exportVariable('DEPENDENCY_GRAPH_REPORT_DIR', path.resolve(layout.workspaceDirectory(), 'dependency-graph-reports'));
70700+
});
7069470701
}
7069570702
exports.setup = setup;
7069670703
function complete(option) {
7069770704
return __awaiter(this, void 0, void 0, function* () {
7069870705
switch (option) {
7069970706
case input_params_1.DependencyGraphOption.Disabled:
70707+
case input_params_1.DependencyGraphOption.DownloadAndSubmit:
7070070708
return;
7070170709
case input_params_1.DependencyGraphOption.Generate:
7070270710
yield uploadDependencyGraphs();
7070370711
return;
7070470712
case input_params_1.DependencyGraphOption.GenerateAndSubmit:
7070570713
yield submitDependencyGraphs(yield uploadDependencyGraphs());
7070670714
return;
70707-
case input_params_1.DependencyGraphOption.DownloadAndSubmit:
70708-
yield downloadAndSubmitDependencyGraphs();
7070970715
}
7071070716
});
7071170717
}
@@ -70729,18 +70735,36 @@ function downloadAndSubmitDependencyGraphs() {
7072970735
}
7073070736
function submitDependencyGraphs(dependencyGraphFiles) {
7073170737
return __awaiter(this, void 0, void 0, function* () {
70732-
const octokit = getOctokit();
7073370738
for (const jsonFile of dependencyGraphFiles) {
70734-
const jsonContent = fs_1.default.readFileSync(jsonFile, 'utf8');
70735-
const jsonObject = JSON.parse(jsonContent);
70736-
jsonObject.owner = github.context.repo.owner;
70737-
jsonObject.repo = github.context.repo.repo;
70738-
const response = yield octokit.request('POST /repos/{owner}/{repo}/dependency-graph/snapshots', jsonObject);
70739-
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile);
70740-
core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`);
70739+
try {
70740+
yield submitDependencyGraphFile(jsonFile);
70741+
}
70742+
catch (error) {
70743+
if (error instanceof request_error_1.RequestError) {
70744+
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile);
70745+
core.warning(`Failed to submit dependency graph ${relativeJsonFile}.\n` +
70746+
"Please ensure that the 'contents: write' permission is available for the workflow job.\n" +
70747+
"Note that this permission is never available for a 'pull_request' trigger from a repository fork.");
70748+
}
70749+
else {
70750+
throw error;
70751+
}
70752+
}
7074170753
}
7074270754
});
7074370755
}
70756+
function submitDependencyGraphFile(jsonFile) {
70757+
return __awaiter(this, void 0, void 0, function* () {
70758+
const octokit = getOctokit();
70759+
const jsonContent = fs_1.default.readFileSync(jsonFile, 'utf8');
70760+
const jsonObject = JSON.parse(jsonContent);
70761+
jsonObject.owner = github.context.repo.owner;
70762+
jsonObject.repo = github.context.repo.repo;
70763+
const response = yield octokit.request('POST /repos/{owner}/{repo}/dependency-graph/snapshots', jsonObject);
70764+
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile);
70765+
core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`);
70766+
});
70767+
}
7074470768
function retrieveDependencyGraphs(workspaceDirectory) {
7074570769
return __awaiter(this, void 0, void 0, function* () {
7074670770
if (github.context.payload.workflow_run) {
@@ -71657,7 +71681,7 @@ function setup() {
7165771681
const cacheListener = new cache_reporting_1.CacheListener();
7165871682
yield caches.restore(gradleUserHome, cacheListener);
7165971683
core.saveState(CACHE_LISTENER, cacheListener.stringify());
71660-
dependencyGraph.setup(params.getDependencyGraphOption());
71684+
yield dependencyGraph.setup(params.getDependencyGraphOption());
7166171685
});
7166271686
}
7166371687
exports.setup = setup;
@@ -71679,7 +71703,7 @@ function complete() {
7167971703
else {
7168071704
(0, job_summary_1.logJobSummary)(buildResults, cacheListener);
7168171705
}
71682-
dependencyGraph.complete(params.getDependencyGraphOption());
71706+
yield dependencyGraph.complete(params.getDependencyGraphOption());
7168371707
});
7168471708
}
7168571709
exports.complete = complete;

dist/main/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/post/index.js

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -70674,38 +70674,44 @@ const artifact = __importStar(__nccwpck_require__(2605));
7067470674
const github = __importStar(__nccwpck_require__(5438));
7067570675
const glob = __importStar(__nccwpck_require__(8090));
7067670676
const toolCache = __importStar(__nccwpck_require__(7784));
70677+
const request_error_1 = __nccwpck_require__(537);
7067770678
const path = __importStar(__nccwpck_require__(1017));
7067870679
const fs_1 = __importDefault(__nccwpck_require__(7147));
7067970680
const layout = __importStar(__nccwpck_require__(8182));
7068070681
const input_params_1 = __nccwpck_require__(3885);
7068170682
const DEPENDENCY_GRAPH_ARTIFACT = 'dependency-graph';
7068270683
function setup(option) {
70683-
if (option === input_params_1.DependencyGraphOption.Disabled || option === input_params_1.DependencyGraphOption.DownloadAndSubmit) {
70684-
return;
70685-
}
70686-
core.info('Enabling dependency graph generation');
70687-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_ENABLED', 'true');
70688-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR', getJobCorrelator());
70689-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_ID', github.context.runId);
70690-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_REF', github.context.ref);
70691-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_SHA', getShaFromContext());
70692-
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_WORKSPACE', layout.workspaceDirectory());
70693-
core.exportVariable('DEPENDENCY_GRAPH_REPORT_DIR', path.resolve(layout.workspaceDirectory(), 'dependency-graph-reports'));
70684+
return __awaiter(this, void 0, void 0, function* () {
70685+
if (option === input_params_1.DependencyGraphOption.Disabled) {
70686+
return;
70687+
}
70688+
if (option === input_params_1.DependencyGraphOption.DownloadAndSubmit) {
70689+
yield downloadAndSubmitDependencyGraphs();
70690+
return;
70691+
}
70692+
core.info('Enabling dependency graph generation');
70693+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_ENABLED', 'true');
70694+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR', getJobCorrelator());
70695+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_ID', github.context.runId);
70696+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_REF', github.context.ref);
70697+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_SHA', getShaFromContext());
70698+
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_WORKSPACE', layout.workspaceDirectory());
70699+
core.exportVariable('DEPENDENCY_GRAPH_REPORT_DIR', path.resolve(layout.workspaceDirectory(), 'dependency-graph-reports'));
70700+
});
7069470701
}
7069570702
exports.setup = setup;
7069670703
function complete(option) {
7069770704
return __awaiter(this, void 0, void 0, function* () {
7069870705
switch (option) {
7069970706
case input_params_1.DependencyGraphOption.Disabled:
70707+
case input_params_1.DependencyGraphOption.DownloadAndSubmit:
7070070708
return;
7070170709
case input_params_1.DependencyGraphOption.Generate:
7070270710
yield uploadDependencyGraphs();
7070370711
return;
7070470712
case input_params_1.DependencyGraphOption.GenerateAndSubmit:
7070570713
yield submitDependencyGraphs(yield uploadDependencyGraphs());
7070670714
return;
70707-
case input_params_1.DependencyGraphOption.DownloadAndSubmit:
70708-
yield downloadAndSubmitDependencyGraphs();
7070970715
}
7071070716
});
7071170717
}
@@ -70729,18 +70735,36 @@ function downloadAndSubmitDependencyGraphs() {
7072970735
}
7073070736
function submitDependencyGraphs(dependencyGraphFiles) {
7073170737
return __awaiter(this, void 0, void 0, function* () {
70732-
const octokit = getOctokit();
7073370738
for (const jsonFile of dependencyGraphFiles) {
70734-
const jsonContent = fs_1.default.readFileSync(jsonFile, 'utf8');
70735-
const jsonObject = JSON.parse(jsonContent);
70736-
jsonObject.owner = github.context.repo.owner;
70737-
jsonObject.repo = github.context.repo.repo;
70738-
const response = yield octokit.request('POST /repos/{owner}/{repo}/dependency-graph/snapshots', jsonObject);
70739-
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile);
70740-
core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`);
70739+
try {
70740+
yield submitDependencyGraphFile(jsonFile);
70741+
}
70742+
catch (error) {
70743+
if (error instanceof request_error_1.RequestError) {
70744+
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile);
70745+
core.warning(`Failed to submit dependency graph ${relativeJsonFile}.\n` +
70746+
"Please ensure that the 'contents: write' permission is available for the workflow job.\n" +
70747+
"Note that this permission is never available for a 'pull_request' trigger from a repository fork.");
70748+
}
70749+
else {
70750+
throw error;
70751+
}
70752+
}
7074170753
}
7074270754
});
7074370755
}
70756+
function submitDependencyGraphFile(jsonFile) {
70757+
return __awaiter(this, void 0, void 0, function* () {
70758+
const octokit = getOctokit();
70759+
const jsonContent = fs_1.default.readFileSync(jsonFile, 'utf8');
70760+
const jsonObject = JSON.parse(jsonContent);
70761+
jsonObject.owner = github.context.repo.owner;
70762+
jsonObject.repo = github.context.repo.repo;
70763+
const response = yield octokit.request('POST /repos/{owner}/{repo}/dependency-graph/snapshots', jsonObject);
70764+
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile);
70765+
core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`);
70766+
});
70767+
}
7074470768
function retrieveDependencyGraphs(workspaceDirectory) {
7074570769
return __awaiter(this, void 0, void 0, function* () {
7074670770
if (github.context.payload.workflow_run) {
@@ -71289,7 +71313,7 @@ function setup() {
7128971313
const cacheListener = new cache_reporting_1.CacheListener();
7129071314
yield caches.restore(gradleUserHome, cacheListener);
7129171315
core.saveState(CACHE_LISTENER, cacheListener.stringify());
71292-
dependencyGraph.setup(params.getDependencyGraphOption());
71316+
yield dependencyGraph.setup(params.getDependencyGraphOption());
7129371317
});
7129471318
}
7129571319
exports.setup = setup;
@@ -71311,7 +71335,7 @@ function complete() {
7131171335
else {
7131271336
(0, job_summary_1.logJobSummary)(buildResults, cacheListener);
7131371337
}
71314-
dependencyGraph.complete(params.getDependencyGraphOption());
71338+
yield dependencyGraph.complete(params.getDependencyGraphOption());
7131571339
});
7131671340
}
7131771341
exports.complete = complete;

dist/post/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy