Skip to content

Commit 2c65a53

Browse files
authored
Merge pull request #110 from advanced-security/juxtin/file-centric-manifests
Stop aggregating manifests in multi-module projects
2 parents fe8d4d6 + aa342c1 commit 2c65a53

File tree

11 files changed

+214
-211
lines changed

11 files changed

+214
-211
lines changed

README.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@
22

33
This is a GitHub Action that will generate a complete dependency graph for a Maven project and submit the graph to the GitHub repository so that the graph is complete and includes all the transitive dependencies.
44

5-
The action will invoke maven using the `com.github.ferstl:depgraph-maven-plugin:4.0.2` plugin to generate JSON output of the complete dependency graph, which is then processed and submitted using the [Dependency Submission Toolkit](https://github.com/github/dependency-submission-toolkit) to the GitHub repository.
5+
The action will invoke maven using the `com.github.ferstl:depgraph-maven-plugin:4.0.3` plugin to generate JSON output of the complete dependency graph, which is then processed and submitted using the [Dependency Submission Toolkit](https://github.com/github/dependency-submission-toolkit) to the GitHub repository.
66

77

88
## Usage
99

10-
As of version `3.0.0` this action now support Maven multi-module projects as well as additional Maven configuration parameters.
10+
As of version `3.0.0` this action now supports Maven multi-module projects as well as additional Maven configuration parameters. As of version `5.0.0`, multi-module projects report dependencies as coming from their respective `pom.xml` files.
1111

1212

1313
### Pre-requisites
1414
For this action to work properly, you must have the Maven available on PATH (`mvn`) or using a `mvnw` Maven wrapper in your maven project directory. Maven will need to be configured to be able to access and pull your dependencies from whatever sources you have defined (i.e. a properly configured `settings.xml` or all details provided in the POM).
1515

1616
Custom maven `settings.xml` can now be specified as an input parameter to the action.
1717

18-
This action writes informations in the repository dependency graph, so if you are using the default token, you need to set the `contents: write` permission to the workflow or job. If you are using a personal access token, this token must have the `repo` scope. ([API used by this action](https://docs.github.com/en/rest/dependency-graph/dependency-submission#create-a-snapshot-of-dependencies-for-a-repository))
18+
This action writes information in the repository dependency graph, so if you are using the default token, you need to set the `contents: write` permission to the workflow or job. If you are using a personal access token, this token must have the `repo` scope. ([API used by this action](https://docs.github.com/en/rest/dependency-graph/dependency-submission#create-a-snapshot-of-dependencies-for-a-repository))
1919

2020
### Inputs
2121

@@ -29,10 +29,6 @@ This action writes informations in the repository dependency graph, so if you ar
2929

3030
* `maven-args` - An optional string value (space separated) options to pass to the maven command line when generating the dependency snapshot. This is empty by default.
3131

32-
* `snapshot-include-file-name`: Optional flag to control whether or no the path and file name of the pom.xml is provided with the snapshot submission. Defaults to `true` so as to create a link to the repository file from the dependency tree view, but at the cost of losing the POM `artifactId` when it renders.
33-
34-
* `snapshot-dependency-file-name`: An optional user control file path to the POM file, requires `snapshot-include-file-name` to be `true` for the value to be submitted.
35-
3632
* `correlator`: An optional identifier to distinguish between multiple dependency snapshots of the same type. Defaults to the [job_id](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_id) of the current job.
3733

3834
## Examples
@@ -41,7 +37,7 @@ Generating and submitting a dependency snapshot using the defaults:
4137

4238
```
4339
- name: Submit Dependency Snapshot
44-
uses: advanced-security/maven-dependency-submission-action@v4
40+
uses: advanced-security/maven-dependency-submission-action@v5
4541
```
4642

4743
Upon success it will generate a snapshot captured from Maven POM like;

action.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,6 @@ inputs:
2525
type: string
2626
default: ''
2727

28-
snapshot-include-file-name:
29-
description: Optionally include the file name in the dependency snapshot report to GitHub. This is required to be true if you want the results in the dependency tree to have a working link.
30-
type: boolean
31-
default: true
32-
33-
snapshot-dependency-file-name:
34-
description: An optional override to specify the path to the file in the repository that the snapshot should be associated with.
35-
type: string
36-
required: false
37-
3828
token:
3929
description: The GitHub token to use to submit the depedency snapshot to the repository
4030
type: string

dist/index.js

Lines changed: 76 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ require('./sourcemap-register.js');/******/ (() => { // webpackBootstrap
77
"use strict";
88

99
Object.defineProperty(exports, "__esModule", ({ value: true }));
10-
exports.artifactToPackageURL = exports.parseDependencyJson = exports.MavenDependencyGraph = void 0;
10+
exports.artifactToPackageURL = exports.parseDependencyJson = exports.MavenDependencyGraph = exports.depgraphfilename = void 0;
1111
const packageurl_js_1 = __nccwpck_require__(8915);
1212
const dependency_submission_toolkit_1 = __nccwpck_require__(3415);
1313
const file_utils_1 = __nccwpck_require__(799);
14+
exports.depgraphfilename = 'maven-dependency-submission-action-depgraph.json';
1415
class MavenDependencyGraph {
1516
constructor(graph) {
1617
this.depGraph = graph;
@@ -119,20 +120,20 @@ class MavenDependencyGraph {
119120
}
120121
}
121122
exports.MavenDependencyGraph = MavenDependencyGraph;
122-
function parseDependencyJson(file, isMultiModule = false) {
123+
function parseDependencyJson(file) {
123124
const data = (0, file_utils_1.loadFileContents)(file);
125+
const pomXmlFilepath = file.replace(`target/${exports.depgraphfilename}`, 'pom.xml');
124126
if (!data) {
125127
return {
128+
filePath: pomXmlFilepath,
126129
graphName: 'empty',
127130
artifacts: [],
128131
dependencies: [],
129-
isMultiModule: isMultiModule
130132
};
131133
}
132134
try {
133135
const depGraph = JSON.parse(data);
134-
depGraph.isMultiModule = isMultiModule;
135-
return depGraph;
136+
return Object.assign(Object.assign({}, depGraph), { filePath: pomXmlFilepath });
136137
}
137138
catch (err) {
138139
throw new Error(`Failed to parse JSON dependency data: ${err.message}`);
@@ -252,8 +253,6 @@ function run() {
252253
mavenArgs: core.getInput('maven-args') || '',
253254
};
254255
const snapshotConfig = {
255-
includeManifestFile: core.getBooleanInput('snapshot-include-file-name'),
256-
manifestFile: core.getInput('snapshot-dependency-file-name'),
257256
sha: core.getInput('snapshot-sha'),
258257
ref: core.getInput('snapshot-ref'),
259258
};
@@ -482,56 +481,45 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
482481
});
483482
};
484483
Object.defineProperty(exports, "__esModule", ({ value: true }));
485-
exports.generateDependencyGraph = exports.generateSnapshot = void 0;
484+
exports.generateDependencyGraphs = exports.generateSnapshot = void 0;
486485
const core = __importStar(__nccwpck_require__(2186));
487486
const path = __importStar(__nccwpck_require__(1017));
488487
const dependency_submission_toolkit_1 = __nccwpck_require__(3415);
489488
const depgraph_1 = __nccwpck_require__(8047);
490489
const maven_runner_1 = __nccwpck_require__(7433);
491-
const file_utils_1 = __nccwpck_require__(799);
490+
const fs_1 = __nccwpck_require__(7147);
492491
const packageData = __nccwpck_require__(2876);
493492
const DEPGRAPH_MAVEN_PLUGIN_VERSION = '4.0.3';
494493
function generateSnapshot(directory, mvnConfig, snapshotConfig) {
495494
return __awaiter(this, void 0, void 0, function* () {
496495
var _a, _b;
497-
const depgraph = yield generateDependencyGraph(directory, mvnConfig);
496+
const depgraphs = yield generateDependencyGraphs(directory, mvnConfig);
497+
const detector = (_a = snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.detector) !== null && _a !== void 0 ? _a : getDetector();
498+
let snapshot = new dependency_submission_toolkit_1.Snapshot(detector, snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.context, snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.job);
499+
snapshot.job.correlator = (snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.correlator)
500+
? snapshotConfig.correlator
501+
: (_b = snapshot.job) === null || _b === void 0 ? void 0 : _b.correlator;
502+
const specifiedRef = getNonEmptyValue(snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.ref);
503+
if (specifiedRef) {
504+
snapshot.ref = specifiedRef;
505+
}
506+
const specifiedSha = getNonEmptyValue(snapshot === null || snapshot === void 0 ? void 0 : snapshot.sha);
507+
if (specifiedSha) {
508+
snapshot.sha = specifiedSha;
509+
}
498510
try {
499-
const mavenDependencies = new depgraph_1.MavenDependencyGraph(depgraph);
500-
let manifest;
501-
if (snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.includeManifestFile) {
502-
let pomFile;
503-
if (snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.manifestFile) {
504-
pomFile = snapshotConfig.manifestFile;
505-
}
506-
else {
507-
// The filepath to the POM needs to be relative to the root of the GitHub repository for the links to work once uploaded
508-
pomFile = getRepositoryRelativePath(path.join(directory, 'pom.xml'));
509-
}
510-
manifest = mavenDependencies.createManifest(pomFile);
511-
}
512-
else {
513-
manifest = mavenDependencies.createManifest();
511+
for (const depgraph of depgraphs) {
512+
const mavenDependencies = new depgraph_1.MavenDependencyGraph(depgraph);
513+
const pomFile = getRepositoryRelativePath(depgraph.filePath);
514+
const manifest = mavenDependencies.createManifest(pomFile);
515+
snapshot.addManifest(manifest);
514516
}
515-
const detector = (_a = snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.detector) !== null && _a !== void 0 ? _a : getDetector();
516-
const snapshot = new dependency_submission_toolkit_1.Snapshot(detector, snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.context, snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.job);
517-
snapshot.addManifest(manifest);
518-
snapshot.job.correlator = (snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.correlator)
519-
? snapshotConfig.correlator
520-
: (_b = snapshot.job) === null || _b === void 0 ? void 0 : _b.correlator;
521-
const specifiedRef = getNonEmptyValue(snapshotConfig === null || snapshotConfig === void 0 ? void 0 : snapshotConfig.ref);
522-
if (specifiedRef) {
523-
snapshot.ref = specifiedRef;
524-
}
525-
const specifiedSha = getNonEmptyValue(snapshot === null || snapshot === void 0 ? void 0 : snapshot.sha);
526-
if (specifiedSha) {
527-
snapshot.sha = specifiedSha;
528-
}
529-
return snapshot;
530517
}
531518
catch (err) {
532519
core.error(err);
533520
throw new Error(`Could not generate a snapshot of the dependencies; ${err.message}`);
534521
}
522+
return snapshot;
535523
});
536524
}
537525
exports.generateSnapshot = generateSnapshot;
@@ -542,71 +530,44 @@ function getDetector() {
542530
version: packageData.version
543531
};
544532
}
545-
function generateDependencyGraph(directory, config) {
533+
function generateDependencyGraphs(directory, config) {
546534
return __awaiter(this, void 0, void 0, function* () {
547535
try {
548536
const mvn = new maven_runner_1.MavenRunner(directory, config === null || config === void 0 ? void 0 : config.settingsFile, config === null || config === void 0 ? void 0 : config.ignoreMavenWrapper, config === null || config === void 0 ? void 0 : config.mavenArgs);
549-
core.startGroup('depgraph-maven-plugin:reactor');
550-
const mavenReactorArguments = [
551-
`com.github.ferstl:depgraph-maven-plugin:${DEPGRAPH_MAVEN_PLUGIN_VERSION}:reactor`,
552-
'-DgraphFormat=json',
553-
'-DoutputFileName=reactor.json'
554-
];
555-
const reactorResults = yield mvn.exec(directory, mavenReactorArguments);
556-
core.info(reactorResults.stdout);
557-
core.info(reactorResults.stderr);
558-
core.endGroup();
559-
if (reactorResults.exitCode !== 0) {
560-
throw new Error(`Failed to successfully generate reactor results with Maven, exit code: ${reactorResults.exitCode}`);
561-
}
562537
core.startGroup('depgraph-maven-plugin:aggregate');
563-
const mavenAggregateArguments = [
564-
`com.github.ferstl:depgraph-maven-plugin:${DEPGRAPH_MAVEN_PLUGIN_VERSION}:aggregate`,
538+
const mavenGraphArguments = [
539+
`com.github.ferstl:depgraph-maven-plugin:${DEPGRAPH_MAVEN_PLUGIN_VERSION}:graph`,
565540
'-DgraphFormat=json',
566-
'-DoutputDirectory=target',
567-
'-DoutputFileName=aggregate-depgraph.json'
541+
`-DoutputFileName=${depgraph_1.depgraphfilename}`,
568542
];
569-
const aggregateResults = yield mvn.exec(directory, mavenAggregateArguments);
570-
core.info(aggregateResults.stdout);
571-
core.info(aggregateResults.stderr);
543+
const graphResults = yield mvn.exec(directory, mavenGraphArguments);
544+
core.info(graphResults.stdout);
545+
core.info(graphResults.stderr);
572546
core.endGroup();
573-
if (aggregateResults.exitCode !== 0) {
574-
throw new Error(`Failed to successfully dependency results with Maven, exit code: ${aggregateResults.exitCode}`);
547+
if (graphResults.exitCode !== 0) {
548+
throw new Error(`Failed to successfully dependency results with Maven, exit code: ${graphResults.exitCode}`);
575549
}
576550
}
577551
catch (err) {
578552
core.error(err);
579553
throw new Error(`A problem was encountered generating dependency files, please check execution logs for details; ${err.message}`);
580554
}
581-
const targetPath = path.join(directory, 'target');
582-
const isMultiModule = checkForMultiModule(path.join(targetPath, 'reactor.json'));
583-
// Now we have the aggregate dependency graph file to process
584-
const aggregateGraphFile = path.join(targetPath, 'aggregate-depgraph.json');
585-
try {
586-
return (0, depgraph_1.parseDependencyJson)(aggregateGraphFile, isMultiModule);
587-
}
588-
catch (err) {
589-
core.error(err);
590-
throw new Error(`Could not parse maven dependency file, '${aggregateGraphFile}': ${err.message}`);
555+
const graphFiles = getDepgraphFiles(directory, depgraph_1.depgraphfilename);
556+
let results = [];
557+
for (const graphFile of graphFiles) {
558+
core.debug(`Found depgraph file: ${graphFile}`);
559+
try {
560+
const depgraph = (0, depgraph_1.parseDependencyJson)(graphFile);
561+
results.push(depgraph);
562+
}
563+
catch (err) {
564+
core.error(`Could not parse depgraph file, '${graphFile}': ${err.message}`);
565+
}
591566
}
567+
return results;
592568
});
593569
}
594-
exports.generateDependencyGraph = generateDependencyGraph;
595-
function checkForMultiModule(reactorJsonFile) {
596-
const data = (0, file_utils_1.loadFileContents)(reactorJsonFile);
597-
if (data) {
598-
try {
599-
const reactor = JSON.parse(data);
600-
// The reactor file will have an array of artifacts making up the parent and child modules if it is a multi module project
601-
return reactor.artifacts && reactor.artifacts.length > 0;
602-
}
603-
catch (err) {
604-
throw new Error(`Failed to parse reactor JSON payload: ${err.message}`);
605-
}
606-
}
607-
// If no data report that it is not a multi module project
608-
return false;
609-
}
570+
exports.generateDependencyGraphs = generateDependencyGraphs;
610571
// TODO this is assuming the checkout was made into the base path of the workspace...
611572
function getRepositoryRelativePath(file) {
612573
const workspaceDirectory = path.resolve(process.env.GITHUB_WORKSPACE || '.');
@@ -631,6 +592,31 @@ function getNonEmptyValue(str) {
631592
}
632593
return undefined;
633594
}
595+
// getDepgraphFiles recursively finds all files that match the filename within the directory
596+
function getDepgraphFiles(directory, filename) {
597+
let files = [];
598+
// debug only
599+
files = (0, fs_1.readdirSync)(directory);
600+
try {
601+
files = (0, fs_1.readdirSync)(directory)
602+
.filter((f) => f === filename)
603+
.map((f) => path.join(directory, f));
604+
}
605+
catch (err) {
606+
core.error(`Could not read depgraphs directory: ${err.message}`);
607+
return [];
608+
}
609+
// recursively find all files that match the filename within the directory
610+
const subdirs = (0, fs_1.readdirSync)(directory, { withFileTypes: true })
611+
.filter(dirent => dirent.isDirectory())
612+
.map(dirent => dirent.name);
613+
for (const subdir of subdirs) {
614+
const subdirPath = path.join(directory, subdir);
615+
const subdirFiles = getDepgraphFiles(subdirPath, filename);
616+
files = files.concat(subdirFiles);
617+
}
618+
return files;
619+
}
634620
//# sourceMappingURL=snapshot-generator.js.map
635621

636622
/***/ }),
@@ -33301,7 +33287,7 @@ exports.submitSnapshot = L;
3330133287
/***/ ((module) => {
3330233288

3330333289
"use strict";
33304-
module.exports = JSON.parse('{"name":"maven-dependency-submission-action","version":"4.1.2","description":"Submit Maven dependencies to GitHub dependency submission API","main":"index.js","scripts":{"base-build":"npm ci && tsc","build":"npm run base-build && npm exec -- @vercel/ncc build --source-map lib/src/index.js","build-exe":"npm run build && pkg package.json --compress Gzip","test":"vitest --run"},"repository":{"type":"git","url":"git+https://github.com/advanced-security/maven-dependency-submission-action.git"},"keywords":[],"author":"GitHub, Inc","license":"MIT","bugs":{"url":"https://github.com/advanced-security/maven-dependency-submission-action/issues"},"homepage":"https://github.com/advanced-security/maven-dependency-submission-action","dependencies":{"@actions/core":"^1.10.1","@actions/exec":"^1.1.1","@github/dependency-submission-toolkit":"^2.0.0","commander":"^12.0.0","packageurl-js":"^1.2.0"},"devDependencies":{"@types/chai":"^4.3.1","@vercel/ncc":"^0.38.1","chai":"^4.3.6","@yao-pkg/pkg":"^5.11.5","ts-node":"^10.9.2","typescript":"^5.3.3","vitest":"^1.6.1"},"bin":{"cli":"lib/src/executable/cli.js"},"pkg":{"targets":["node20-linux-x64","node20-win-x64","node20-macos-x64"],"assets":["package.json"],"publicPackages":"*","outputPath":"cli"}}');
33290+
module.exports = JSON.parse('{"name":"maven-dependency-submission-action","version":"5.0.0","description":"Submit Maven dependencies to GitHub dependency submission API","main":"index.js","scripts":{"base-build":"npm ci && tsc","build":"npm run base-build && npm exec -- @vercel/ncc build --source-map lib/src/index.js","build-exe":"npm run build && pkg package.json --compress Gzip","test":"vitest --run"},"repository":{"type":"git","url":"git+https://github.com/advanced-security/maven-dependency-submission-action.git"},"keywords":[],"author":"GitHub, Inc","license":"MIT","bugs":{"url":"https://github.com/advanced-security/maven-dependency-submission-action/issues"},"homepage":"https://github.com/advanced-security/maven-dependency-submission-action","dependencies":{"@actions/core":"^1.10.1","@actions/exec":"^1.1.1","@github/dependency-submission-toolkit":"^2.0.0","commander":"^12.0.0","packageurl-js":"^1.2.0"},"devDependencies":{"@types/chai":"^4.3.1","@vercel/ncc":"^0.38.1","chai":"^4.3.6","@yao-pkg/pkg":"^5.11.5","ts-node":"^10.9.2","typescript":"^5.3.3","vitest":"^1.6.1"},"bin":{"cli":"lib/src/executable/cli.js"},"pkg":{"targets":["node20-linux-x64","node20-win-x64","node20-macos-x64"],"assets":["package.json"],"publicPackages":"*","outputPath":"cli"}}');
3330533291

3330633292
/***/ })
3330733293

dist/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)