Skip to content

Commit 01fb950

Browse files
Add output version argument to merge command (#388)
Copy of #366 --------- Signed-off-by: andreas hilti <[email protected]> Co-authored-by: andreas hilti <[email protected]>
1 parent 3bebe12 commit 01fb950

17 files changed

+95
-23
lines changed

.markdownlint.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"MD013": {
3+
"code_blocks": false
4+
}
5+
}

README.md

+9-8
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,15 @@ Usage:
192192
cyclonedx merge [options]
193193
194194
Options:
195-
--input-files <input-files> Input BOM filenames (separate filenames with a space).
196-
--output-file <output-file> Output BOM filename, will write to stdout if no value provided.
197-
--input-format <autodetect|json|protobuf|xml> Specify input file format.
198-
--output-format <autodetect|json|protobuf|xml> Specify output file format.
199-
--hierarchical Perform a hierarchical merge.
200-
--group <group> Provide the group of software the merged BOM describes.
201-
--name <name> Provide the name of software the merged BOM describes (required for hierarchical merging).
202-
--version <version> Provide the version of software the merged BOM describes (required for hierarchical merging).
195+
--input-files <input-files> Input BOM filenames (separate filenames with a space).
196+
--output-file <output-file> Output BOM filename, will write to stdout if no value provided.
197+
--input-format <autodetect|json|protobuf|xml> Specify input file format.
198+
--output-format <autodetect|json|protobuf|xml> Specify output file format.
199+
--output-version <v1_0|v1_1|v1_2|v1_3|v1_4|v1_5> Specify output BOM specification version.
200+
--hierarchical Perform a hierarchical merge.
201+
--group <group> Provide the group of software the merged BOM describes.
202+
--name <name> Provide the name of software the merged BOM describes (required for hierarchical merging).
203+
--version <version> Provide the version of software the merged BOM describes (required for hierarchical merging).
203204
```
204205

205206
Note: To perform a hierarchical merge all BOMs need the subject of the BOM

src/cyclonedx/Commands/MergeCommand.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public static void Configure(RootCommand rootCommand)
3535
subCommand.Add(new Option<string>("--output-file", "Output BOM filename, will write to stdout if no value provided."));
3636
subCommand.Add(new Option<CycloneDXBomFormat>("--input-format", "Specify input file format."));
3737
subCommand.Add(new Option<CycloneDXBomFormat>("--output-format", "Specify output file format."));
38+
subCommand.Add(new Option<SpecificationVersion>("--output-version", "Specify output BOM specification version."));
3839
subCommand.Add(new Option<bool>("--hierarchical", "Perform a hierarchical merge."));
3940
subCommand.Add(new Option<string>("--group", "Provide the group of software the merged BOM describes."));
4041
subCommand.Add(new Option<string>("--name", "Provide the name of software the merged BOM describes (required for hierarchical merging)."));
@@ -110,7 +111,7 @@ public static async Task<int> Merge(MergeCommandOptions options)
110111
Console.WriteLine($" Total {outputBom.Components?.Count ?? 0} components");
111112
}
112113

113-
return await CliUtils.OutputBomHelper(outputBom, options.OutputFormat, options.OutputFile).ConfigureAwait(false);
114+
return await CliUtils.OutputBomHelper(outputBom, (ConvertFormat)options.OutputFormat, options.OutputVersion, options.OutputFile).ConfigureAwait(false);
114115
}
115116

116117
private static async Task<IEnumerable<Bom>> InputBoms(IEnumerable<string> inputFilenames, CycloneDXBomFormat inputFormat, bool outputToConsole)

src/cyclonedx/Commands/MergeCommandOptions.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// This file is part of CycloneDX CLI Tool
1+
// This file is part of CycloneDX CLI Tool
22
//
33
// Licensed under the Apache License, Version 2.0 (the “License”);
44
// you may not use this file except in compliance with the License.
@@ -24,9 +24,10 @@ public class MergeCommandOptions
2424
public string OutputFile { get; set; }
2525
public CycloneDXBomFormat InputFormat { get; set; }
2626
public CycloneDXBomFormat OutputFormat { get; set; }
27+
public SpecificationVersion? OutputVersion { get; set; }
2728
public bool Hierarchical { get; set; }
2829
public string Group { get; set; }
2930
public string Name { get; set; }
3031
public string Version { get; set; }
3132
}
32-
}
33+
}

tests/cyclonedx.tests/MergeTests.cs

+16-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// This file is part of CycloneDX CLI Tool
1+
// This file is part of CycloneDX CLI Tool
22
//
33
// Licensed under the Apache License, Version 2.0 (the “License”);
44
// you may not use this file except in compliance with the License.
@@ -29,20 +29,23 @@ namespace CycloneDX.Cli.Tests
2929
public class MergeTests
3030
{
3131
[Theory]
32-
[InlineData(new string[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.autodetect, "sbom.json", CycloneDXBomFormat.autodetect, true, null, "Thing", "1")]
33-
[InlineData(new string[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.autodetect, "sbom.json", CycloneDXBomFormat.autodetect, false, null, null, null)]
34-
[InlineData(new string[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.autodetect, "sbom.xml", CycloneDXBomFormat.autodetect, false, null, null, null)]
35-
[InlineData(new string[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.json, "sbom.json", CycloneDXBomFormat.autodetect, false, null, null, null)]
36-
[InlineData(new string[] { "sbom1.xml", "sbom2.xml"}, CycloneDXBomFormat.autodetect, "sbom.xml", CycloneDXBomFormat.autodetect, false, null, null, null)]
37-
[InlineData(new string[] { "sbom1.xml", "sbom2.xml"}, CycloneDXBomFormat.autodetect, "sbom.json", CycloneDXBomFormat.autodetect, false, null, null, null)]
38-
[InlineData(new string[] { "sbom1.xml", "sbom2.xml"}, CycloneDXBomFormat.xml, "sbom.xml", CycloneDXBomFormat.autodetect, false, null, null, null)]
39-
[InlineData(new string[] { "sbom1.json", "sbom2.xml"}, CycloneDXBomFormat.autodetect, "sbom.xml", CycloneDXBomFormat.autodetect, false, null, null, null)]
40-
[InlineData(new string[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.autodetect, "sbom.json", CycloneDXBomFormat.json, false, null, null, null)]
41-
[InlineData(new string[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.autodetect, "sbom.xml", CycloneDXBomFormat.xml, false, null, null, null)]
32+
[InlineData(new[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.autodetect, "sbom.json", CycloneDXBomFormat.autodetect, null, true, null, "Thing", "1")]
33+
[InlineData(new[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.autodetect, "sbom.json", CycloneDXBomFormat.autodetect, null, false, null, null, null)]
34+
[InlineData(new[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.autodetect, "sbom.xml", CycloneDXBomFormat.autodetect, null, false, null, null, null)]
35+
[InlineData(new[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.json, "sbom.json", CycloneDXBomFormat.autodetect, null, false, null, null, null)]
36+
[InlineData(new[] { "sbom1.json", "sbom2.json" }, CycloneDXBomFormat.json, "sbom.json", CycloneDXBomFormat.autodetect, SpecificationVersion.v1_4, false, null, null, null)]
37+
[InlineData(new[] { "sbom1.xml", "sbom2.xml"}, CycloneDXBomFormat.autodetect, "sbom.xml", CycloneDXBomFormat.autodetect, null, false, null, null, null)]
38+
[InlineData(new[] { "sbom1.xml", "sbom2.xml" }, CycloneDXBomFormat.autodetect, "sbom.xml", CycloneDXBomFormat.autodetect, SpecificationVersion.v1_4, false, null, null, null)]
39+
[InlineData(new[] { "sbom1.xml", "sbom2.xml"}, CycloneDXBomFormat.autodetect, "sbom.json", CycloneDXBomFormat.autodetect, null, false, null, null, null)]
40+
[InlineData(new[] { "sbom1.xml", "sbom2.xml"}, CycloneDXBomFormat.xml, "sbom.xml", CycloneDXBomFormat.autodetect, null, false, null, null, null)]
41+
[InlineData(new[] { "sbom1.json", "sbom2.xml"}, CycloneDXBomFormat.autodetect, "sbom.xml", CycloneDXBomFormat.autodetect, null, false, null, null, null)]
42+
[InlineData(new[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.autodetect, "sbom.json", CycloneDXBomFormat.json, null, false, null, null, null)]
43+
[InlineData(new[] { "sbom1.json", "sbom2.json"}, CycloneDXBomFormat.autodetect, "sbom.xml", CycloneDXBomFormat.xml, null, false, null, null, null)]
4244
public async Task Merge(
4345
string[] inputFilenames,
4446
CycloneDXBomFormat inputFormat,
4547
string outputFilename, CycloneDXBomFormat outputFormat,
48+
SpecificationVersion? outputVersion,
4649
bool hierarchical,
4750
string group, string name, string version
4851
)
@@ -57,6 +60,7 @@ public async Task Merge(
5760
InputFormat = inputFormat,
5861
OutputFile = fullOutputPath,
5962
OutputFormat = outputFormat,
63+
OutputVersion = outputVersion,
6064
Hierarchical = hierarchical,
6165
Group = group,
6266
Name = name,
@@ -73,7 +77,7 @@ public async Task Merge(
7377
var bom = File.ReadAllText(fullOutputPath);
7478
bom = Regex.Replace(bom, @"\s*""serialNumber"": "".*?"",\r?\n", ""); // json
7579
bom = Regex.Replace(bom, @"\s+serialNumber="".*?""", ""); // xml
76-
Snapshot.Match(bom, SnapshotNameExtension.Create(hierarchical ? "Hierarchical" : "Flat", snapshotInputFilenames, inputFormat, outputFilename, outputFormat));
80+
Snapshot.Match(bom, SnapshotNameExtension.Create(hierarchical ? "Hierarchical" : "Flat", snapshotInputFilenames, inputFormat, outputFilename, outputFormat, outputVersion));
7781
}
7882
}
7983
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"bomFormat": "CycloneDX",
3+
"specVersion": "1.4", "version": 1,
4+
"metadata": {
5+
"component": {
6+
"type": "application",
7+
"name": "thing1",
8+
"version": "1"
9+
}
10+
},
11+
"components": [
12+
{
13+
"type": "library",
14+
"name": "acme-library",
15+
"version": "1.0.0"
16+
},
17+
{
18+
"type": "application",
19+
"name": "thing1",
20+
"version": "1"
21+
},
22+
{
23+
"type": "framework",
24+
"name": "acme-framework",
25+
"version": "1.0.0"
26+
},
27+
{
28+
"type": "application",
29+
"name": "thing2",
30+
"version": "1"
31+
}
32+
]
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<bom xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1" xmlns="http://cyclonedx.org/schema/bom/1.4">
3+
<metadata>
4+
<component type="application">
5+
<name>thing1</name>
6+
<version>1</version>
7+
</component>
8+
</metadata>
9+
<components>
10+
<component type="library">
11+
<name>acme-library</name>
12+
<version>1.0.0</version>
13+
</component>
14+
<component type="application">
15+
<name>thing1</name>
16+
<version>1</version>
17+
</component>
18+
<component type="framework">
19+
<name>acme-framework</name>
20+
<version>1.0.0</version>
21+
</component>
22+
<component type="application">
23+
<name>thing2</name>
24+
<version>1</version>
25+
</component>
26+
</components>
27+
</bom>

0 commit comments

Comments
 (0)