Skip to content

Commit da1322b

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. Signed-off-by: Sebastian Schuberth <[email protected]>
1 parent feaf969 commit da1322b

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)