Skip to content

Commit bf33639

Browse files
committed
feat: Add a reporter that writes a JSON schema for ORT results
This is mostly to assist the ORT community with writing external tooling. But it could also be used to detect breaking ORT results changes in the future, and introduce / bump an output file format version. Signed-off-by: Sebastian Schuberth <[email protected]>
1 parent feaf969 commit bf33639

File tree

6 files changed

+179
-1
lines changed

6 files changed

+179
-1
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", ver
121121
jackson-dataformat-yaml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml", version.ref = "jackson" }
122122
jackson-datatype-jsr310 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", version.ref = "jackson" }
123123
jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
124+
jackson-module-jsonSchema = { module = "com.fasterxml.jackson.module:jackson-module-jsonSchema", version.ref = "jackson" }
124125
jakartaMail = { module = "com.sun.mail:jakarta.mail", version.ref = "jakartaMail" }
125126
jakartaRestApi = { module = "jakarta.ws.rs:jakarta.ws.rs-api", version.ref = "jakartaRestApi" }
126127
jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version.ref = "jgit" }

integrations/completions/ort-completion.fish

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ complete -c ort -f -n __fish_use_subcommand -a report -d 'Present Analyzer, Scan
149149
## Options for report
150150
complete -c ort -n "__fish_seen_subcommand_from report" -l ort-file -s i -r -F -d 'The ORT result file to use.'
151151
complete -c ort -n "__fish_seen_subcommand_from report" -l output-dir -s o -r -F -d 'The output directory to store the generated reports in.'
152-
complete -c ort -n "__fish_seen_subcommand_from report" -l report-formats -s f -r -d 'A comma-separated list of report formats to generate, any of [AOSD2.0, AOSD2.1, CtrlXAutomation, CycloneDX, DocBookTemplate, EvaluatedModel, FossID, FossIdSnippet, HtmlTemplate, ManPageTemplate, Opossum, PdfTemplate, PlainTextTemplate, SpdxDocument, StaticHTML, TrustSource, WebApp].'
152+
complete -c ort -n "__fish_seen_subcommand_from report" -l report-formats -s f -r -d 'A comma-separated list of report formats to generate, any of [AOSD2.0, AOSD2.1, CtrlXAutomation, CycloneDX, DocBookTemplate, EvaluatedModel, FossID, FossIdSnippet, HtmlTemplate, ManPageTemplate, Opossum, OrtResultSchema, PdfTemplate, PlainTextTemplate, SpdxDocument, StaticHTML, TrustSource, WebApp].'
153153
complete -c ort -n "__fish_seen_subcommand_from report" -l copyright-garbage-file -r -F -d 'A file containing copyright statements which are marked as garbage. This can make the output inconsistent with the evaluator output but is useful when testing copyright garbage.'
154154
complete -c ort -n "__fish_seen_subcommand_from report" -l custom-license-texts-dir -r -F -d 'A directory which maps custom license IDs to license texts. It should contain one text file per license with the license ID as the filename. A custom license text is used only if its ID has a \'LicenseRef-\' prefix and if the respective license text is not known by ORT.'
155155
complete -c ort -n "__fish_seen_subcommand_from report" -l how-to-fix-text-provider-script -r -F -d 'The path to a Kotlin script which returns an instance of a \'HowToFixTextProvider\'. That provider injects how-to-fix texts in Markdown format for ORT issues.'
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (C) 2025 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
plugins {
21+
// Apply precompiled plugins.
22+
id("ort-plugin-conventions")
23+
}
24+
25+
dependencies {
26+
api(projects.model)
27+
api(projects.reporter)
28+
29+
ksp(projects.reporter)
30+
31+
implementation(libs.jackson.module.jsonSchema)
32+
33+
funTestImplementation(testFixtures(projects.reporter))
34+
funTestImplementation(libs.jsonSchemaValidator)
35+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (C) 2025 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
package org.ossreviewtoolkit.plugins.reporters.ortresultschema
21+
22+
import com.fasterxml.jackson.databind.JsonNode
23+
24+
import com.networknt.schema.JsonSchemaFactory
25+
import com.networknt.schema.SpecVersion
26+
import com.networknt.schema.serialization.JsonNodeReader
27+
28+
import io.kotest.core.spec.style.WordSpec
29+
import io.kotest.engine.spec.tempdir
30+
import io.kotest.matchers.collections.beEmpty
31+
import io.kotest.matchers.collections.shouldBeSingleton
32+
import io.kotest.matchers.file.aFile
33+
import io.kotest.matchers.result.shouldBeSuccess
34+
import io.kotest.matchers.should
35+
import io.kotest.matchers.shouldBe
36+
37+
import org.ossreviewtoolkit.model.jsonMapper
38+
import org.ossreviewtoolkit.reporter.ORT_RESULT
39+
import org.ossreviewtoolkit.reporter.ReporterInput
40+
41+
class OrtResultSchemaReporterFunTest : WordSpec({
42+
"The generated schema" should {
43+
"successfully validate the ORT_RESULT constant" {
44+
val nodeReader = JsonNodeReader.builder().jsonMapper(jsonMapper).build()
45+
val schemaV4 = JsonSchemaFactory
46+
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
47+
.jsonNodeReader(nodeReader)
48+
.build()
49+
50+
val outputDir = tempdir()
51+
val reportFiles = OrtResultSchemaReporter().generateReport(ReporterInput(ORT_RESULT), outputDir)
52+
53+
reportFiles.shouldBeSingleton { result ->
54+
result shouldBeSuccess { file ->
55+
file shouldBe aFile()
56+
57+
val schema = file.readText()
58+
val node = jsonMapper.valueToTree<JsonNode>(ORT_RESULT)
59+
val errors = schemaV4.getSchema(schema).validate(node)
60+
61+
errors should beEmpty()
62+
}
63+
}
64+
}
65+
}
66+
})
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (C) 2025 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
package org.ossreviewtoolkit.plugins.reporters.ortresultschema
21+
22+
import com.fasterxml.jackson.databind.ObjectMapper
23+
import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator
24+
25+
import java.io.File
26+
27+
import org.ossreviewtoolkit.model.OrtResult
28+
import org.ossreviewtoolkit.plugins.api.OrtPlugin
29+
import org.ossreviewtoolkit.plugins.api.PluginDescriptor
30+
import org.ossreviewtoolkit.reporter.Reporter
31+
import org.ossreviewtoolkit.reporter.ReporterFactory
32+
import org.ossreviewtoolkit.reporter.ReporterInput
33+
34+
@OrtPlugin(
35+
id = "OrtResultSchema",
36+
displayName = "ORT result JSON schema",
37+
description = "A reporter to serialize the JSON schema for ORT result files.",
38+
factory = ReporterFactory::class
39+
)
40+
class OrtResultSchemaReporter(
41+
override val descriptor: PluginDescriptor = OrtResultSchemaReporterFactory.descriptor
42+
) : Reporter {
43+
private val nullableRuns = setOf("analyzer", "scanner", "advisor", "evaluator")
44+
45+
override fun generateReport(input: ReporterInput, outputDir: File): List<Result<File>> {
46+
val mapper = ObjectMapper()
47+
val generator = JsonSchemaGenerator(mapper)
48+
49+
val schemeFileResult = runCatching {
50+
val schema = generator.generateSchema(OrtResult::class.java)
51+
52+
outputDir.resolve("ort-result-schema.json").also { schemaFile ->
53+
val schemaJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema)
54+
55+
// Fixup the V3 schema generated by Jackson to be V4 validatable.
56+
val finalSchemaJson = nullableRuns.fold(schemaJson) { updatedSchemaJson, tool ->
57+
updatedSchemaJson.replace(
58+
"""
59+
| "$tool" : {
60+
| "type" : "object",
61+
""".trimMargin(),
62+
"""
63+
| "$tool" : {
64+
| "type" : ["object", "null"],
65+
""".trimMargin()
66+
)
67+
}
68+
69+
schemaFile.writeText(finalSchemaJson)
70+
}
71+
}
72+
73+
return listOf(schemeFileResult)
74+
}
75+
}

website/docs/tools/reporter.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Currently, the following formats are supported (reporter names are case-insensit
2828
* Summarize all license texts and copyrights (`-f PlainTextTemplate -O PlainTextTemplate=template.id=NOTICE_SUMMARY`)
2929
* Customizable with [Apache Freemarker](https://freemarker.apache.org/) templates
3030
* [OpossumUI](https://github.com/opossum-tool/opossumUI) input (`-f Opossum`)
31+
* [ORT Result Schema](https://github.com/oss-review-toolkit/ort/blob/main/model/src/main/kotlin/OrtResult.kt) input (`-f OrtResultSchema`)
3132
* [SPDX Document](https://spdx.dev/specifications/), version 2.2 (`-f SpdxDocument`)
3233
* Static HTML (`-f StaticHtml`)
3334
* [TrustSource](https://www.trustsource.io/) JSON file (`-f TrustSource`)

0 commit comments

Comments
 (0)