diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml new file mode 100644 index 0000000..4fdd333 --- /dev/null +++ b/.github/workflows/master.yml @@ -0,0 +1,33 @@ +name: Test CI +on: [ push, fork ] + +jobs: + TEST_ALL: + runs-on: ubuntu-latest + strategy: + matrix: + java: [ '11', '17' ] + steps: + - name: Checkout + uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: 🪜 Setup java ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java }} + distribution: temurin + + - name: 🦞 chmod /gradlew + run: chmod +x ./gradlew + + - name: 🔦 Test + run: ./gradlew test --info diff --git a/.run/TEST.run.xml b/.run/TEST.run.xml new file mode 100644 index 0000000..7376a7c --- /dev/null +++ b/.run/TEST.run.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 65f2ee1..fc5d941 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Gradle Groovy DSL ```groovy plugins { id "org.springframework.boot" version "2.7.0" - id "org.springdoc.openapi-gradle-plugin" version "1.5.0" + id "org.springdoc.openapi-gradle-plugin" version "1.6.0" } ``` @@ -35,7 +35,7 @@ Gradle Kotlin DSL ```groovy plugins { id("org.springframework.boot") version "2.7.0" - id("org.springdoc.openapi-gradle-plugin") version "1.5.0" + id("org.springdoc.openapi-gradle-plugin") version "1.6.0" } ``` @@ -133,7 +133,7 @@ The `groupedApiMappings` customization allows you to specify multiple URLs/file 2. Update the version for the plugin to match the current version found in `build.gradle.kts` ``` - id("org.springdoc.openapi-gradle-plugin") version "1.5.0" + id("org.springdoc.openapi-gradle-plugin") version "1.6.0" ``` 3. Add the following to the spring boot apps `settings.gradle` diff --git a/build.gradle.kts b/build.gradle.kts index f35ed5c..3a9b4f2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ plugins { } group = "org.springdoc" -version = "1.5.0" +version = "1.6.0" sonarqube { properties { diff --git a/settings.gradle.kts b/settings.gradle.kts index 626792f..1012b6d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,2 +1,8 @@ rootProject.name = "springdoc-openapi-gradle-plugin" +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} diff --git a/src/main/kotlin/org/springdoc/openapi/gradle/plugin/Constants.kt b/src/main/kotlin/org/springdoc/openapi/gradle/plugin/Constants.kt index f3afed4..95dae65 100644 --- a/src/main/kotlin/org/springdoc/openapi/gradle/plugin/Constants.kt +++ b/src/main/kotlin/org/springdoc/openapi/gradle/plugin/Constants.kt @@ -6,6 +6,7 @@ const val OPEN_API_TASK_NAME = "generateOpenApiDocs" const val OPEN_API_TASK_DESCRIPTION = "Generates the spring doc openapi file" const val SPRING_BOOT_RUN_TASK_NAME = "bootRun" const val SPRING_BOOT_RUN_MAIN_CLASS_NAME_TASK_NAME = "bootRunMainClassName" +const val SPRING_BOOT_3_RUN_MAIN_CLASS_NAME_TASK_NAME = "bootRun" const val FORKED_SPRING_BOOT_RUN_TASK_NAME = "forkedSpringBootRun" const val DEFAULT_API_DOCS_URL = "http://localhost:8080/v3/api-docs" diff --git a/src/main/kotlin/org/springdoc/openapi/gradle/plugin/OpenApiGradlePlugin.kt b/src/main/kotlin/org/springdoc/openapi/gradle/plugin/OpenApiGradlePlugin.kt index 9e10aaf..5899e2c 100644 --- a/src/main/kotlin/org/springdoc/openapi/gradle/plugin/OpenApiGradlePlugin.kt +++ b/src/main/kotlin/org/springdoc/openapi/gradle/plugin/OpenApiGradlePlugin.kt @@ -1,84 +1,95 @@ -@file:Suppress("unused") - package org.springdoc.openapi.gradle.plugin import com.github.psxpaul.task.JavaExecFork import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.logging.Logging +import org.gradle.api.tasks.TaskProvider +import org.gradle.internal.jvm.Jvm import org.springframework.boot.gradle.tasks.run.BootRun open class OpenApiGradlePlugin : Plugin { - private val logger = Logging.getLogger(OpenApiGradlePlugin::class.java) + private val logger = Logging.getLogger(OpenApiGradlePlugin::class.java) + + override fun apply(project: Project) { + with(project) { + // Run time dependency on the following plugins + plugins.apply(SPRING_BOOT_PLUGIN) + plugins.apply(EXEC_FORK_PLUGIN) + + extensions.create(EXTENSION_NAME, OpenApiExtension::class.java, this) + + afterEvaluate { generate(this) } + } + } - override fun apply(project: Project) { - // Run time dependency on the following plugins - project.plugins.apply(SPRING_BOOT_PLUGIN) - project.plugins.apply(EXEC_FORK_PLUGIN) + private fun generate(project: Project) = project.run { + springBoot3CompatibilityCheck() - project.extensions.create(EXTENSION_NAME, OpenApiExtension::class.java, project) + // The task, used to run the Spring Boot application (`bootRun`) + val bootRunTask = tasks.named(SPRING_BOOT_RUN_TASK_NAME) + // The task, used to resolve the application's main class (`bootRunMainClassName`) + val bootRunMainClassNameTask = tasks.find { it.name == SPRING_BOOT_RUN_MAIN_CLASS_NAME_TASK_NAME} + ?:tasks.named(SPRING_BOOT_3_RUN_MAIN_CLASS_NAME_TASK_NAME) - project.afterEvaluate { - // The task, used to run the Spring Boot application (`bootRun`) - val bootRunTask = project.tasks.named(SPRING_BOOT_RUN_TASK_NAME) - // The task, used to resolve the application's main class (`bootRunMainClassName`) - val bootRunMainClassNameTask = - project.tasks.named(SPRING_BOOT_RUN_MAIN_CLASS_NAME_TASK_NAME) + val extension = extensions.findByName(EXTENSION_NAME) as OpenApiExtension + val customBootRun = extension.customBootRun + // Create a forked version spring boot run task + val forkedSpringBoot = tasks.register(FORKED_SPRING_BOOT_RUN_TASK_NAME, JavaExecFork::class.java) { fork -> + fork.dependsOn(bootRunMainClassNameTask) + fork.onlyIf { needToFork(bootRunTask, customBootRun, fork) } + } - val extension = project.extensions.findByName(EXTENSION_NAME) as OpenApiExtension - val customBootRun = extension.customBootRun - // Create a forked version spring boot run task - val forkedSpringBoot = - project.tasks.register( - FORKED_SPRING_BOOT_RUN_TASK_NAME, - JavaExecFork::class.java - ) { fork -> - fork.dependsOn(bootRunMainClassNameTask) + // This is my task. Before I can run it, I have to run the dependent tasks + tasks.register(OPEN_API_TASK_NAME, OpenApiGeneratorTask::class.java) { + it.dependsOn(forkedSpringBoot) + } - fork.onlyIf { - val bootRun = bootRunTask.get() as BootRun + // The forked task need to be terminated as soon as my task is finished + forkedSpringBoot.get().stopAfter = tasks.named(OPEN_API_TASK_NAME) + } - val baseSystemProperties = customBootRun.systemProperties.orNull?.takeIf { it.isNotEmpty() } - ?: bootRun.systemProperties - // copy all system properties, excluding those starting with `java.class.path` - fork.systemProperties = baseSystemProperties.filter { - !it.key.startsWith( - CLASS_PATH_PROPERTY_NAME - ) - } + private fun Project.springBoot3CompatibilityCheck() { + val tasksNames = tasks.names + val boot2TaskName = "bootRunMainClassName" + val boot3TaskName = "resolveMainClassName" + if (!tasksNames.contains(boot2TaskName) && tasksNames.contains(boot3TaskName)) + tasks.register(boot2TaskName) { it.dependsOn(tasks.named(boot3TaskName)) } + } - // use original bootRun parameter if the list-type customBootRun properties is empty - fork.workingDir = customBootRun.workingDir.asFile.orNull - ?: bootRun.workingDir - fork.args = customBootRun.args.orNull?.takeIf { it.isNotEmpty() }?.toMutableList() - ?: bootRun.args?.toMutableList() ?: mutableListOf() - fork.classpath = customBootRun.classpath.takeIf { !it.isEmpty } - ?: bootRun.classpath - fork.main = customBootRun.mainClass.orNull - ?: bootRun.mainClass.get() - fork.jvmArgs = customBootRun.jvmArgs.orNull?.takeIf { it.isNotEmpty() } - ?: bootRun.jvmArgs - fork.environment = customBootRun.environment.orNull?.takeIf { it.isNotEmpty() } - ?: bootRun.environment - if (org.gradle.internal.jvm.Jvm.current().toString() - .startsWith("1.8") - ) { - fork.killDescendants = false - } - true - } - } + private fun needToFork( + bootRunTask: TaskProvider, + customBootRun: CustomBootRunAction, + fork: JavaExecFork + ): Boolean { + val bootRun = bootRunTask.get() as BootRun - // This is my task. Before I can run it I have to run the dependent tasks - project.tasks.register( - OPEN_API_TASK_NAME, - OpenApiGeneratorTask::class.java - ) { openApiGenTask -> - openApiGenTask.dependsOn(forkedSpringBoot) - } + val baseSystemProperties = customBootRun.systemProperties.orNull?.takeIf { it.isNotEmpty() } + ?: bootRun.systemProperties + with(fork) { + // copy all system properties, excluding those starting with `java.class.path` + systemProperties = baseSystemProperties.filter { + !it.key.startsWith(CLASS_PATH_PROPERTY_NAME) + } - // The forked task need to be terminated as soon as my task is finished - forkedSpringBoot.get().stopAfter = project.tasks.named(OPEN_API_TASK_NAME) - } - } + // use original bootRun parameter if the list-type customBootRun properties are empty + workingDir = customBootRun.workingDir.asFile.orNull + ?: bootRun.workingDir + args = customBootRun.args.orNull?.takeIf { it.isNotEmpty() }?.toMutableList() + ?: bootRun.args?.toMutableList() ?: mutableListOf() + classpath = customBootRun.classpath.takeIf { !it.isEmpty } + ?: bootRun.classpath + main = customBootRun.mainClass.orNull + ?: bootRun.mainClass.get() + jvmArgs = customBootRun.jvmArgs.orNull?.takeIf { it.isNotEmpty() } + ?: bootRun.jvmArgs + environment = customBootRun.environment.orNull?.takeIf { it.isNotEmpty() } + ?: bootRun.environment + if (Jvm.current().toString().startsWith("1.8")) { + killDescendants = false + } + } + return true + } } diff --git a/src/test/kotlin/org/springdoc/openapi/gradle/plugin/OpenApiGradlePluginTest.kt b/src/test/kotlin/org/springdoc/openapi/gradle/plugin/OpenApiGradlePluginTest.kt index 4aae9dd..b1912a3 100644 --- a/src/test/kotlin/org/springdoc/openapi/gradle/plugin/OpenApiGradlePluginTest.kt +++ b/src/test/kotlin/org/springdoc/openapi/gradle/plugin/OpenApiGradlePluginTest.kt @@ -28,8 +28,8 @@ class OpenApiGradlePluginTest { private val baseBuildGradle = """plugins { id 'java' - id 'org.springframework.boot' version '2.4.5' - id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'org.springframework.boot' version '2.7.6' + id 'io.spring.dependency-management' version '1.1.15.RELEASE' id 'org.springdoc.openapi-gradle-plugin' } @@ -43,7 +43,7 @@ class OpenApiGradlePluginTest { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springdoc:springdoc-openapi-webmvc-core:1.6.12' + implementation 'org.springdoc:springdoc-openapi-webmvc-core:1.6.13' } """.trimIndent() diff --git a/src/test/resources/acceptance-project/src/main/java/com/example/demo/DemoApplication.java b/src/test/resources/acceptance-project/src/main/java/com/example/demo/DemoApplication.java index 322b2b6..0f77539 100644 --- a/src/test/resources/acceptance-project/src/main/java/com/example/demo/DemoApplication.java +++ b/src/test/resources/acceptance-project/src/main/java/com/example/demo/DemoApplication.java @@ -1,14 +1,15 @@ package com.example.demo; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import javax.annotation.PostConstruct; import java.time.Duration; @SpringBootApplication -public class DemoApplication { +public class DemoApplication implements ApplicationRunner{ @Value("${slower:false}") boolean slower; @@ -16,9 +17,9 @@ public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } - - @PostConstruct - public void afterBeanStuff() throws InterruptedException { + @Override + public void run(ApplicationArguments arg0) throws Exception { + System.out.println("Hello World from Application Runner"); if (slower) { Duration waitTime = Duration.ofSeconds(40); System.out.println("Waiting for " + waitTime + " before starting");