Dokumentations-Tasks

This commit is contained in:
stefan 2025-09-01 14:32:23 +02:00
parent 00407d4ec6
commit 4fcb4bb48c
7 changed files with 172 additions and 165 deletions

View File

@ -1,4 +1,3 @@
import java.util.Locale
plugins { plugins {
alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kotlin.jvm) apply false
@ -23,7 +22,7 @@ subprojects {
} }
// Configure CDS in auto-mode to prevent bootstrap classpath warnings // Configure CDS in auto-mode to prevent bootstrap classpath warnings
jvmArgs("-Xshare:auto", "-Djdk.instrument.traceUsage=false") jvmArgs("-Xshare:auto", "-Djdk.instrument.traceUsage=false")
// Increase test JVM memory with stable configuration // Increase test JVM memory with a stable configuration
maxHeapSize = "2g" maxHeapSize = "2g"
// Removed byte-buddy-agent configuration to fix Gradle 9.0.0 deprecation warning // Removed byte-buddy-agent configuration to fix Gradle 9.0.0 deprecation warning
// The agent configuration was causing Task.project access at execution time // The agent configuration was causing Task.project access at execution time
@ -62,84 +61,14 @@ subprojects {
} }
// ################################################################## // ##################################################################
// ### IHRE DOKUMENTATIONS-TASKS ### // ### DOKU-AGGREGATOR ###
// ################################################################## // ##################################################################
// Abstrakte Klasse für die Custom Task (Best Practice) // Leichter Aggregator im Root-Projekt, ruft die eigentlichen Tasks im :docs Subprojekt auf
abstract class ValidateDocumentationTask @Inject constructor( tasks.register("docs") {
private val execOperations: ExecOperations description = "Aggregates documentation tasks from :docs"
) : DefaultTask() {
@TaskAction
fun validate() {
println("🔍 Validating documentation...")
execOperations.exec {
commandLine("./scripts/validation/validate-docs.sh")
}
}
}
// Registrierung der Tasks
tasks.register<ValidateDocumentationTask>("validateDocumentation") {
description = "Validates documentation completeness and consistency"
group = "documentation" group = "documentation"
} dependsOn(":docs:generateAllDocs")
tasks.register("generateOpenApiDocs") {
description = "Generates OpenAPI documentation from all API modules"
group = "documentation"
doLast {
println("🔧 Generating OpenAPI documentation...")
val apiModules = listOf(
"members:members-api",
"horses:horses-api",
"events:events-api",
"masterdata:masterdata-api"
)
val outputDir = file("docs/api/generated")
outputDir.mkdirs()
apiModules.forEach { module ->
val moduleName = module.split(":").last().replace("-api", "")
println("📝 Processing $moduleName API...")
val specFile = file("$outputDir/${moduleName}-openapi.json")
specFile.writeText(
"""
{
"openapi": "3.0.3",
"info": {
"title": "${moduleName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }} API",
"description": "REST API für $moduleName Verwaltung",
"version": "1.0.0",
"contact": {
"name": "Meldestelle Development Team"
}
},
"servers": [
{ "url": "http://localhost:8080", "description": "Entwicklungs-Server" },
{ "url": "https://api.meldestelle.at", "description": "Produktions-Server" }
],
"paths": {},
"components": {
"securitySchemes": {
"bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" }
}
},
"security": [ { "bearerAuth": [] } ]
}
""".trimIndent()
)
}
println("✅ OpenAPI documentation generated in docs/api/generated/")
}
}
tasks.register("generateAllDocs") {
description = "Generates all documentation (API docs + validation)"
group = "documentation"
dependsOn("generateOpenApiDocs", "validateDocumentation")
} }
// Wrapper-Konfiguration // Wrapper-Konfiguration

View File

@ -2,35 +2,19 @@
"openapi": "3.0.3", "openapi": "3.0.3",
"info": { "info": {
"title": "Events API", "title": "Events API",
"description": "REST API for events management", "description": "REST API für events Verwaltung",
"version": "1.0.0", "version": "1.0.0",
"contact": { "contact": { "name": "Meldestelle Development Team" }
"name": "Meldestelle Development Team"
}
}, },
"servers": [ "servers": [
{ { "url": "http://localhost:8080", "description": "Entwicklungs-Server" },
"url": "http://localhost:8080", { "url": "https://api.meldestelle.at", "description": "Produktions-Server" }
"description": "Development server"
},
{
"url": "https://api.meldestelle.at",
"description": "Production server"
}
], ],
"paths": {}, "paths": {},
"components": { "components": {
"securitySchemes": { "securitySchemes": {
"bearerAuth": { "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" }
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
} }
}, },
"security": [ "security": [ { "bearerAuth": [] } ]
{
"bearerAuth": []
}
]
} }

View File

@ -2,35 +2,19 @@
"openapi": "3.0.3", "openapi": "3.0.3",
"info": { "info": {
"title": "Horses API", "title": "Horses API",
"description": "REST API for horses management", "description": "REST API für horses Verwaltung",
"version": "1.0.0", "version": "1.0.0",
"contact": { "contact": { "name": "Meldestelle Development Team" }
"name": "Meldestelle Development Team"
}
}, },
"servers": [ "servers": [
{ { "url": "http://localhost:8080", "description": "Entwicklungs-Server" },
"url": "http://localhost:8080", { "url": "https://api.meldestelle.at", "description": "Produktions-Server" }
"description": "Development server"
},
{
"url": "https://api.meldestelle.at",
"description": "Production server"
}
], ],
"paths": {}, "paths": {},
"components": { "components": {
"securitySchemes": { "securitySchemes": {
"bearerAuth": { "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" }
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
} }
}, },
"security": [ "security": [ { "bearerAuth": [] } ]
{
"bearerAuth": []
}
]
} }

View File

@ -2,35 +2,19 @@
"openapi": "3.0.3", "openapi": "3.0.3",
"info": { "info": {
"title": "Masterdata API", "title": "Masterdata API",
"description": "REST API for masterdata management", "description": "REST API für masterdata Verwaltung",
"version": "1.0.0", "version": "1.0.0",
"contact": { "contact": { "name": "Meldestelle Development Team" }
"name": "Meldestelle Development Team"
}
}, },
"servers": [ "servers": [
{ { "url": "http://localhost:8080", "description": "Entwicklungs-Server" },
"url": "http://localhost:8080", { "url": "https://api.meldestelle.at", "description": "Produktions-Server" }
"description": "Development server"
},
{
"url": "https://api.meldestelle.at",
"description": "Production server"
}
], ],
"paths": {}, "paths": {},
"components": { "components": {
"securitySchemes": { "securitySchemes": {
"bearerAuth": { "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" }
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
} }
}, },
"security": [ "security": [ { "bearerAuth": [] } ]
{
"bearerAuth": []
}
]
} }

View File

@ -2,35 +2,19 @@
"openapi": "3.0.3", "openapi": "3.0.3",
"info": { "info": {
"title": "Members API", "title": "Members API",
"description": "REST API for members management", "description": "REST API für members Verwaltung",
"version": "1.0.0", "version": "1.0.0",
"contact": { "contact": { "name": "Meldestelle Development Team" }
"name": "Meldestelle Development Team"
}
}, },
"servers": [ "servers": [
{ { "url": "http://localhost:8080", "description": "Entwicklungs-Server" },
"url": "http://localhost:8080", { "url": "https://api.meldestelle.at", "description": "Produktions-Server" }
"description": "Development server"
},
{
"url": "https://api.meldestelle.at",
"description": "Production server"
}
], ],
"paths": {}, "paths": {},
"components": { "components": {
"securitySchemes": { "securitySchemes": {
"bearerAuth": { "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" }
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
} }
}, },
"security": [ "security": [ { "bearerAuth": [] } ]
{
"bearerAuth": []
}
]
} }

139
docs/build.gradle.kts Normal file
View File

@ -0,0 +1,139 @@
import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.*
import java.util.Locale
import javax.inject.Inject
plugins {
// no special plugins required for these custom tasks
}
abstract class ValidateDocumentationTask @Inject constructor(
private val execOperations: ExecOperations
) : DefaultTask() {
@get:InputFile
abstract val scriptFile: RegularFileProperty
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val docsSources: ConfigurableFileCollection
// When true, a non-zero exit from the validation script will fail the task
@get:Input
abstract val strict: Property<Boolean>
@TaskAction
fun validate() {
logger.lifecycle("Validating documentation…")
val result = execOperations.exec {
workingDir(project.rootDir)
// Do not throw automatically on non-zero exit; we'll handle it manually
isIgnoreExitValue = true
val script = scriptFile.get().asFile
val os = System.getProperty("os.name").lowercase(Locale.getDefault())
if (os.contains("win")) {
commandLine("bash", script.absolutePath)
} else {
commandLine(script.absolutePath)
}
}
val code = result.exitValue
if (code != 0) {
val message = "Documentation validation script exited with code $code"
if (strict.getOrElse(false)) {
throw GradleException(message)
} else {
logger.warn("$message — continuing because strict mode is disabled. Use -PdocsValidateStrict=true to enforce failures.")
}
}
}
}
@CacheableTask
abstract class GenerateOpenApiDocsTask : DefaultTask() {
@get:Input
abstract val apiModules: ListProperty<String>
@get:Input
abstract val apiVersion: Property<String>
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
@TaskAction
fun generate() {
val out = outputDir.get().asFile.apply { mkdirs() }
apiModules.get().forEach { module ->
val moduleName = module.substringAfterLast(":").removeSuffix("-api")
val specFile = out.resolve("${moduleName}-openapi.json")
specFile.writeText(
"""
{
"openapi": "3.0.3",
"info": {
"title": "${moduleName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }} API",
"description": "REST API für $moduleName Verwaltung",
"version": "${apiVersion.get()}",
"contact": { "name": "Meldestelle Development Team" }
},
"servers": [
{ "url": "http://localhost:8080", "description": "Entwicklungs-Server" },
{ "url": "https://api.meldestelle.at", "description": "Produktions-Server" }
],
"paths": {},
"components": {
"securitySchemes": {
"bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" }
}
},
"security": [ { "bearerAuth": [] } ]
}
""".trimIndent()
)
logger.lifecycle("Generated ${specFile.relativeTo(project.rootDir)}")
}
}
}
val generatedDir = layout.projectDirectory.dir("api/generated")
tasks.register<GenerateOpenApiDocsTask>("generateOpenApiDocs") {
group = "documentation"
description = "Generates OpenAPI docs for all API modules"
apiModules.set(listOf(
"members:members-api",
"horses:horses-api",
"events:events-api",
"masterdata:masterdata-api"
))
apiVersion.set("1.0.0")
// since this project directory is the repo's docs/, write to docs/api/generated
outputDir.set(generatedDir)
}
tasks.register<ValidateDocumentationTask>("validateDocumentation") {
group = "documentation"
description = "Validates documentation completeness and consistency (use -PdocsValidateStrict=true to fail on issues)"
// Ensure generated OpenAPI docs are available before validation
dependsOn(tasks.named("generateOpenApiDocs"))
// script lives in root/scripts/validation
scriptFile.set(layout.projectDirectory.file("../scripts/validation/validate-docs.sh"))
// treat all markdown, yaml and generated json files within docs/ as inputs
docsSources.from(
layout.projectDirectory.asFileTree.matching { include("**/*.md") },
layout.projectDirectory.asFileTree.matching { include("**/*.yml", "**/*.yaml") },
layout.projectDirectory.dir("api/generated").asFileTree.matching { include("**/*.json") }
)
// strict mode from Gradle property (default: false)
strict.set(
providers.gradleProperty("docsValidateStrict")
.map { it.equals("true", ignoreCase = true) }
.orElse(false)
)
}
tasks.register("generateAllDocs") {
group = "documentation"
description = "Generates all documentation (API + validation)"
dependsOn("generateOpenApiDocs", "validateDocumentation")
}

View File

@ -58,6 +58,9 @@ include(":client:common-ui")
include(":client:web-app") include(":client:web-app")
include(":client:desktop-app") include(":client:desktop-app")
// Documentation module
include(":docs")
/* /*
// Temporär deaktivierte Fach-Module // Temporär deaktivierte Fach-Module
// Members modules // Members modules