From 9fc7c32a68ae29c0192f136abb1a8a32bf5e8e33 Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Tue, 21 Nov 2023 10:56:40 +0100 Subject: [PATCH] Stream java versions --- .../acceptance-test.tests.actions.yml | 4 +- .github/workflows/api.tests.actions.yml | 4 +- .../workflows/quarkus-ecosystem.actions.yml | 2 +- .quarkus/cli/plugins/quarkus-cli-catalog.json | 5 ++ acceptance-test/pom.xml | 6 +-- .../src/main/docker/Dockerfile.multistage | 4 +- api/pom.xml | 10 ++-- api/src/main/docker/Dockerfile.multistage | 4 +- .../quarkus/code/model/ProjectDefinition.kt | 4 +- .../code/model/ProjectDefinitionQuery.kt | 9 +--- .../kotlin/io/quarkus/code/model/Stream.kt | 10 +++- .../io/quarkus/code/rest/AnalyticsFilter.kt | 15 +++--- .../quarkus/code/rest/CodeQuarkusResource.kt | 4 +- .../io/quarkus/code/service/PlatformInfo.kt | 3 +- .../quarkus/code/service/PlatformService.kt | 45 +++++++++++----- .../code/service/QuarkusProjectService.kt | 24 ++++++--- .../kotlin/io/quarkus/code/CodeQuarkusIT.kt | 9 ++-- .../code/rest/CodeQuarkusResourceTest.kt | 42 ++++++++++++++- .../code/service/QuarkusProjectServiceTest.kt | 53 +++++++++++++------ .../dir-tree.snapshot | 20 ++++--- library/.bitmap | 8 ++- .../__tests__/launcher-quarkus.spec.tsx | 4 +- .../quarkus-project-utils.spec.ts.snap | 6 +-- library/components/api/model.ts | 9 +++- .../components/api/quarkus-project-utils.ts | 25 ++++++++- library/components/header/stream-picker.tsx | 18 +------ .../components/info-picker/info-picker.tsx | 4 +- .../info-picker/java-version-select.tsx | 12 +++-- .../quarkus-project-edition-form.tsx | 7 ++- 29 files changed, 252 insertions(+), 118 deletions(-) create mode 100644 .quarkus/cli/plugins/quarkus-cli-catalog.json rename api/src/test/resources/__snapshots__/QuarkusProjectServiceTest/{testQuteYaml => testQuinoaYaml}/dir-tree.snapshot (59%) diff --git a/.github/workflows/acceptance-test.tests.actions.yml b/.github/workflows/acceptance-test.tests.actions.yml index 119299730..cc19a4232 100644 --- a/.github/workflows/acceptance-test.tests.actions.yml +++ b/.github/workflows/acceptance-test.tests.actions.yml @@ -25,11 +25,11 @@ jobs: - uses: n1hility/cancel-previous-runs@v2 with: token: ${{ secrets.GITHUB_TOKEN }} - - name: Set up JDK 17 + - name: Set up JDK 21 # Uses sha for added security since tags can be updated uses: joschi/setup-jdk@e87a7cec853d2dd7066adf837fe12bf0f3d45e52 with: - java-version: openjdk17 + java-version: openjdk21 - name: Cache Maven Repository id: cache-maven uses: actions/cache@v1 diff --git a/.github/workflows/api.tests.actions.yml b/.github/workflows/api.tests.actions.yml index fb800d781..09ef0dca1 100644 --- a/.github/workflows/api.tests.actions.yml +++ b/.github/workflows/api.tests.actions.yml @@ -25,11 +25,11 @@ jobs: - uses: n1hility/cancel-previous-runs@v2 with: token: ${{ secrets.GITHUB_TOKEN }} - - name: Set up JDK 17 + - name: Set up JDK 21 # Uses sha for added security since tags can be updated uses: joschi/setup-jdk@e87a7cec853d2dd7066adf837fe12bf0f3d45e52 with: - java-version: openjdk17 + java-version: openjdk21 - name: Cache Maven Repository id: cache-maven uses: actions/cache@v1 diff --git a/.github/workflows/quarkus-ecosystem.actions.yml b/.github/workflows/quarkus-ecosystem.actions.yml index 181abeac1..302445139 100644 --- a/.github/workflows/quarkus-ecosystem.actions.yml +++ b/.github/workflows/quarkus-ecosystem.actions.yml @@ -9,7 +9,7 @@ on: env: ECOSYSTEM_CI_REPO: quarkusio/quarkus-ecosystem-ci ECOSYSTEM_CI_REPO_FILE: context.yaml - JAVA_VERSION: 17 + JAVA_VERSION: 21 ######################### # Repo specific setting # diff --git a/.quarkus/cli/plugins/quarkus-cli-catalog.json b/.quarkus/cli/plugins/quarkus-cli-catalog.json new file mode 100644 index 000000000..bd139adbf --- /dev/null +++ b/.quarkus/cli/plugins/quarkus-cli-catalog.json @@ -0,0 +1,5 @@ +{ + "version" : "v1", + "lastUpdate" : "21/11/2023 09:29:31", + "plugins" : { } +} \ No newline at end of file diff --git a/acceptance-test/pom.xml b/acceptance-test/pom.xml index e6724bf15..0ebe639f1 100644 --- a/acceptance-test/pom.xml +++ b/acceptance-test/pom.xml @@ -8,8 +8,8 @@ 3.10.0 true - 17 - 17 + 21 + 21 UTF-8 UTF-8 @@ -18,7 +18,7 @@ io.quarkus quarkus-bom - 3.5.1 + 3.6.0 diff --git a/acceptance-test/src/main/docker/Dockerfile.multistage b/acceptance-test/src/main/docker/Dockerfile.multistage index 036f903a2..b6a55f8b2 100644 --- a/acceptance-test/src/main/docker/Dockerfile.multistage +++ b/acceptance-test/src/main/docker/Dockerfile.multistage @@ -1,5 +1,5 @@ ## Stage 1 : build with maven builder image -FROM registry.access.redhat.com/ubi8/openjdk-17:1.14 AS build +FROM registry.access.redhat.com/ubi8/openjdk-21:1.18 AS build ARG MAVEN_BUILD_EXTRA_ARGS= RUN echo "$MAVEN_BUILD_EXTRA_ARGS" COPY pom.xml mvnw /usr/src/app/ @@ -14,7 +14,7 @@ RUN cd /usr/src/app/ && ./mvnw clean package -DskipTests $MAVEN_BUILD_EXTRA_ARGS ## Stage 2 : create the docker final image FROM mcr.microsoft.com/playwright:v1.29.2-focal -ARG JAVA_PACKAGE=openjdk-17-jre-headless +ARG JAVA_PACKAGE=openjdk-21-jre-headless ARG MAVEN_EXTRA_ARGS= RUN echo "$MAVEN_EXTRA_ARGS" diff --git a/api/pom.xml b/api/pom.xml index edb120f3b..be08febfd 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,8 +9,8 @@ 17 true - 17 - 17 + 21 + 21 4.3.2 3.0.0-M7 @@ -27,11 +27,11 @@ io.quarkus quarkus-bom - 3.5.1 + 3.6.0 - 3.5.1 - 3.5.1 + 3.6.0 + 3.6.0 2.3.1 diff --git a/api/src/main/docker/Dockerfile.multistage b/api/src/main/docker/Dockerfile.multistage index 344195dd6..8f8d3d070 100644 --- a/api/src/main/docker/Dockerfile.multistage +++ b/api/src/main/docker/Dockerfile.multistage @@ -1,5 +1,5 @@ ## Stage 1 : build with maven builder image -FROM registry.access.redhat.com/ubi8/openjdk-17:1.17 AS build +FROM registry.access.redhat.com/ubi8/openjdk-21:1.18 AS build ARG MAVEN_BUILD_EXTRA_ARGS= RUN echo "$MAVEN_BUILD_EXTRA_ARGS" COPY mvnw pom.xml maven-settings.xml* /usr/src/app/ @@ -12,7 +12,7 @@ USER 185 RUN cd /usr/src/app/ && ./mvnw clean package $MAVEN_BUILD_EXTRA_ARGS ## Stage 2 : create the docker final image -FROM registry.access.redhat.com/ubi8/openjdk-17:1.17 +FROM registry.access.redhat.com/ubi8/openjdk-21:1.18 ARG MAVEN_EXTRA_ARGS= RUN echo "$MAVEN_EXTRA_ARGS" diff --git a/api/src/main/kotlin/io/quarkus/code/model/ProjectDefinition.kt b/api/src/main/kotlin/io/quarkus/code/model/ProjectDefinition.kt index d492972db..c3bb02873 100644 --- a/api/src/main/kotlin/io/quarkus/code/model/ProjectDefinition.kt +++ b/api/src/main/kotlin/io/quarkus/code/model/ProjectDefinition.kt @@ -16,7 +16,7 @@ data class ProjectDefinition(val streamKey: String? = null, val className: String? = null, val path: String? = null, val buildTool: String = DEFAULT_BUILDTOOL, - val javaVersion: String = DEFAULT_JAVA_VERSION, + val javaVersion: Int? = null, val noCode: Boolean = DEFAULT_NO_CODE, val noExamples: Boolean = DEFAULT_NO_CODE, val extensions: Set = setOf()) { @@ -27,9 +27,7 @@ data class ProjectDefinition(val streamKey: String? = null, const val DEFAULT_VERSION = "1.0.0-SNAPSHOT" const val DEFAULT_BUILDTOOL = "MAVEN" const val DEFAULT_NO_CODE = false - const val DEFAULT_JAVA_VERSION = "17" - const val JAVA_VERSION_PATTERN = "^(?:1\\.)?(\\d+)(?:\\..*)?\$"; const val GROUPID_PATTERN = "^([a-zA-Z_\$][a-zA-Z\\d_\$]*\\.)*[a-zA-Z_\$][a-zA-Z\\d_\$]*\$" const val ARTIFACTID_PATTERN = "^[a-z][a-z0-9-._]*\$" const val CLASSNAME_PATTERN = GROUPID_PATTERN diff --git a/api/src/main/kotlin/io/quarkus/code/model/ProjectDefinitionQuery.kt b/api/src/main/kotlin/io/quarkus/code/model/ProjectDefinitionQuery.kt index e0f440aed..4ca9b56f7 100644 --- a/api/src/main/kotlin/io/quarkus/code/model/ProjectDefinitionQuery.kt +++ b/api/src/main/kotlin/io/quarkus/code/model/ProjectDefinitionQuery.kt @@ -7,11 +7,9 @@ import io.quarkus.code.model.ProjectDefinition.Companion.CLASSNAME_PATTERN import io.quarkus.code.model.ProjectDefinition.Companion.DEFAULT_ARTIFACTID import io.quarkus.code.model.ProjectDefinition.Companion.DEFAULT_BUILDTOOL import io.quarkus.code.model.ProjectDefinition.Companion.DEFAULT_GROUPID -import io.quarkus.code.model.ProjectDefinition.Companion.DEFAULT_JAVA_VERSION import io.quarkus.code.model.ProjectDefinition.Companion.DEFAULT_NO_CODE import io.quarkus.code.model.ProjectDefinition.Companion.DEFAULT_VERSION import io.quarkus.code.model.ProjectDefinition.Companion.GROUPID_PATTERN -import io.quarkus.code.model.ProjectDefinition.Companion.JAVA_VERSION_PATTERN import io.quarkus.code.model.ProjectDefinition.Companion.PATH_PATTERN import org.eclipse.microprofile.openapi.annotations.media.Schema import org.eclipse.microprofile.openapi.annotations.parameters.Parameter @@ -94,12 +92,9 @@ class ProjectDefinitionQuery { private set @QueryParam("j") - @NotEmpty - @DefaultValue(DEFAULT_JAVA_VERSION) - @Pattern(regexp = JAVA_VERSION_PATTERN) @Parameter(name = "j", description = "The Java version for the generation application", required = false) - @Schema(description = "The Java version for the generation application", required = false, pattern = JAVA_VERSION_PATTERN) - var javaVersion: String = DEFAULT_JAVA_VERSION + @Schema(description = "The Java version for the generation application", required = false) + var javaVersion: Int? = null private set diff --git a/api/src/main/kotlin/io/quarkus/code/model/Stream.kt b/api/src/main/kotlin/io/quarkus/code/model/Stream.kt index fa11ca3f5..f75dfc9a3 100644 --- a/api/src/main/kotlin/io/quarkus/code/model/Stream.kt +++ b/api/src/main/kotlin/io/quarkus/code/model/Stream.kt @@ -1,9 +1,15 @@ package io.quarkus.code.model +import java.util.SortedSet + data class Stream( val key: String, val quarkusCoreVersion: String, + val javaCompatibility: JavaCompatibility, val platformVersion: String, val recommended: Boolean, - val status: String -) + val status: String, + val lts: Boolean +) { + data class JavaCompatibility(val versions: SortedSet, val recommended: Int) +} diff --git a/api/src/main/kotlin/io/quarkus/code/rest/AnalyticsFilter.kt b/api/src/main/kotlin/io/quarkus/code/rest/AnalyticsFilter.kt index e5b4068c6..d98215247 100644 --- a/api/src/main/kotlin/io/quarkus/code/rest/AnalyticsFilter.kt +++ b/api/src/main/kotlin/io/quarkus/code/rest/AnalyticsFilter.kt @@ -127,8 +127,9 @@ class AnalyticsFilter : ContainerRequestFilter { val extensions: Set val buildTool: String val streamKey: String? - val javaVersion: String + val javaVersion: Int? val noCode: Boolean + val recommendedPlatformInfo = platformService.get().recommendedPlatformInfo if (context.method == "POST") { val text = context.entityStream.bufferedReader().use(BufferedReader::readText) context.entityStream = text.byteInputStream() @@ -136,27 +137,27 @@ class AnalyticsFilter : ContainerRequestFilter { val json = JsonObject(text) val rawExtensions = json.getJsonArray("extensions", JsonArray()).stream().map { it.toString() } .collect(Collectors.toSet()) - extensions = platformService.get().recommendedPlatformInfo.checkAndMergeExtensions(rawExtensions) + extensions = recommendedPlatformInfo.checkAndMergeExtensions(rawExtensions) buildTool = json.getString("buildTool", ProjectDefinition.DEFAULT_BUILDTOOL) - javaVersion = json.getString("javaVersion", ProjectDefinition.DEFAULT_JAVA_VERSION) + javaVersion = json.getInteger("javaVersion") streamKey = json.getString("streamKey") noCode = json.getBoolean("noCode", ProjectDefinition.DEFAULT_NO_CODE) } else { extensions = emptySet() buildTool = ProjectDefinition.DEFAULT_BUILDTOOL streamKey = null - javaVersion = ProjectDefinition.DEFAULT_JAVA_VERSION + javaVersion = null noCode = ProjectDefinition.DEFAULT_NO_CODE } } else { extensions = - platformService.get().recommendedPlatformInfo.checkAndMergeExtensions(queryParams["e"]?.toSet()) + recommendedPlatformInfo.checkAndMergeExtensions(queryParams["e"]?.toSet()) buildTool = queryParams.getFirst("b") ?: ProjectDefinition.DEFAULT_BUILDTOOL streamKey = queryParams.getFirst("S") - javaVersion = queryParams.getFirst("j") ?: ProjectDefinition.DEFAULT_JAVA_VERSION + javaVersion = queryParams.getFirst("j")?.toInt() noCode = queryParams.getFirst("nc")?.toBoolean() ?: ProjectDefinition.DEFAULT_NO_CODE } - val resolvedStreamKey = platformService.get().getPlatformInfo(streamKey).streamKey + val resolvedStreamKey = platformService.get().getPlatformInfo(streamKey).stream.key return mapOf( "buildTool" to buildTool, "extensions" to extensions, diff --git a/api/src/main/kotlin/io/quarkus/code/rest/CodeQuarkusResource.kt b/api/src/main/kotlin/io/quarkus/code/rest/CodeQuarkusResource.kt index eb7bef67c..13f8e76f3 100644 --- a/api/src/main/kotlin/io/quarkus/code/rest/CodeQuarkusResource.kt +++ b/api/src/main/kotlin/io/quarkus/code/rest/CodeQuarkusResource.kt @@ -216,8 +216,8 @@ class CodeQuarkusResource @Inject constructor( if (projectDefinition.buildTool != ProjectDefinition.DEFAULT_BUILDTOOL) { params.add(BasicNameValuePair("b", projectDefinition.buildTool)) } - if (projectDefinition.javaVersion != ProjectDefinition.DEFAULT_JAVA_VERSION) { - params.add(BasicNameValuePair("j", projectDefinition.javaVersion)) + if (projectDefinition.javaVersion != null) { + params.add(BasicNameValuePair("j", projectDefinition.javaVersion.toString())) } if (projectDefinition.noCode != ProjectDefinition.DEFAULT_NO_CODE || projectDefinition.noExamples != ProjectDefinition.DEFAULT_NO_CODE) { params.add(BasicNameValuePair("nc", (!ProjectDefinition.DEFAULT_NO_CODE).toString())) diff --git a/api/src/main/kotlin/io/quarkus/code/service/PlatformInfo.kt b/api/src/main/kotlin/io/quarkus/code/service/PlatformInfo.kt index f1fcf2fc4..aa9417c6d 100644 --- a/api/src/main/kotlin/io/quarkus/code/service/PlatformInfo.kt +++ b/api/src/main/kotlin/io/quarkus/code/service/PlatformInfo.kt @@ -3,6 +3,7 @@ package io.quarkus.code.service import com.fasterxml.jackson.annotation.JsonIgnoreProperties import io.quarkus.code.misc.QuarkusExtensionUtils import io.quarkus.code.model.CodeQuarkusExtension +import io.quarkus.code.model.Stream import io.quarkus.registry.catalog.ExtensionCatalog import java.util.logging.Logger import java.util.stream.Collectors @@ -10,7 +11,7 @@ import java.util.stream.Collectors @JsonIgnoreProperties(ignoreUnknown = true) class PlatformInfo( val platformKey: String, - val streamKey: String, + val stream: Stream, val quarkusCoreVersion: String, val platformVersion: String, val recommended: Boolean, diff --git a/api/src/main/kotlin/io/quarkus/code/service/PlatformService.kt b/api/src/main/kotlin/io/quarkus/code/service/PlatformService.kt index 04a11d38a..5aaf11dd6 100644 --- a/api/src/main/kotlin/io/quarkus/code/service/PlatformService.kt +++ b/api/src/main/kotlin/io/quarkus/code/service/PlatformService.kt @@ -6,6 +6,11 @@ import io.quarkus.devtools.project.QuarkusProjectHelper import io.quarkus.code.model.CodeQuarkusExtension import io.quarkus.code.model.ProjectDefinition import io.quarkus.code.model.Stream +import io.quarkus.devtools.project.JavaVersion +import io.quarkus.devtools.project.JavaVersion.getCompatibleLTSVersions +import io.quarkus.platform.catalog.processor.CatalogProcessor +import io.quarkus.platform.catalog.processor.CatalogProcessor.getMinimumJavaVersion +import io.quarkus.platform.catalog.processor.CatalogProcessor.getRecommendedJavaVersion import io.quarkus.registry.Constants import java.util.HashMap import io.quarkus.registry.catalog.PlatformCatalog @@ -130,22 +135,36 @@ class PlatformService { val platformKey = platform.platformKey val streamId = stream.id val streamKey = createStreamKey(platformKey, streamId) + val lts = stream.metadata["lts"] as Boolean + val minimumJavaVersion = getMinimumJavaVersion(extensionCatalog) + + val compatibleJavaLTSVersions = getCompatibleLTSVersions(JavaVersion(minimumJavaVersion)) + if (platformKey.equals("com.redhat.quarkus.platform")) { + // Hack to remove 21 support from code.quarkus.redhat.com + compatibleJavaLTSVersions.remove(21) + } + val recommendedJavaVersion = getRecommendedJavaVersion(extensionCatalog)?.toInt() ?: compatibleJavaLTSVersions.first() + val quarkusCoreVersion = stream.recommendedRelease.quarkusCoreVersion + val recommended = stream.id == platform.recommendedStream.id + val streamInfo = Stream( + key = streamKey, + quarkusCoreVersion = quarkusCoreVersion, + platformVersion = stream.recommendedRelease.version.toString(), + recommended = recommended, + status = getStreamStatus(quarkusCoreVersion), + lts = lts, + javaCompatibility = Stream.JavaCompatibility(compatibleJavaLTSVersions, recommendedJavaVersion) + ) val platformInfo = PlatformInfo( platformKey = platformKey, - streamKey = streamKey, - quarkusCoreVersion = stream.recommendedRelease.quarkusCoreVersion, + quarkusCoreVersion = quarkusCoreVersion, platformVersion = stream.recommendedRelease.version.toString(), - recommended = (stream.id == platform.recommendedStream.id), + recommended = recommended, codeQuarkusExtensions = codeQuarkusExtensions, - extensionCatalog = extensionCatalog + extensionCatalog = extensionCatalog, + stream = streamInfo ) - streams.add(Stream( - key = streamKey, - quarkusCoreVersion = platformInfo.quarkusCoreVersion, - platformVersion = stream.recommendedRelease.version.toString(), - recommended = platformInfo.recommended, - status = getStreamStatus(platformInfo) - )) + streams.add(streamInfo) updatedStreamCatalogMap[streamKey] = platformInfo } } @@ -218,8 +237,8 @@ class PlatformService { } - private fun getStreamStatus(platformInfo: PlatformInfo): String { - val qualifier = DefaultArtifactVersion(platformInfo.quarkusCoreVersion).qualifier?.uppercase() + private fun getStreamStatus(quarkusCoreVersion: String): String { + val qualifier = DefaultArtifactVersion(quarkusCoreVersion).qualifier?.uppercase() return if (qualifier.isNullOrBlank()) "FINAL" else qualifier } diff --git a/api/src/main/kotlin/io/quarkus/code/service/QuarkusProjectService.kt b/api/src/main/kotlin/io/quarkus/code/service/QuarkusProjectService.kt index 2a31e18bf..0cb794a18 100644 --- a/api/src/main/kotlin/io/quarkus/code/service/QuarkusProjectService.kt +++ b/api/src/main/kotlin/io/quarkus/code/service/QuarkusProjectService.kt @@ -3,19 +3,19 @@ package io.quarkus.code.service import io.quarkus.code.model.ProjectDefinition import io.quarkus.devtools.codestarts.CodestartException import io.quarkus.devtools.commands.CreateProject -import io.quarkus.devtools.commands.data.QuarkusCommandException import io.quarkus.devtools.messagewriter.MessageWriter import io.quarkus.devtools.project.BuildTool import io.quarkus.devtools.project.JavaVersion import io.quarkus.devtools.project.QuarkusProjectHelper +import io.quarkus.devtools.project.SourceType import io.quarkus.devtools.project.compress.QuarkusProjectCompress +import jakarta.inject.Singleton import java.io.IOException import java.io.OutputStream import java.io.PrintStream import java.nio.file.Files import java.nio.file.Path import java.util.logging.Logger -import jakarta.inject.Singleton @Singleton open class QuarkusProjectService { @@ -50,9 +50,19 @@ open class QuarkusProjectService { platformInfo.checkAndMergeExtensions(projectDefinition.extensions) val buildTool = BuildTool.valueOf(projectDefinition.buildTool) val codestarts = HashSet() - /**if (gitHub) { - codestarts.add("github-action") - }**/ + val javaVersionString = (projectDefinition.javaVersion ?: platformInfo.stream.javaCompatibility.recommended).toString() + if (gitHub) { + codestarts.add("tooling-github-action") + } + val javaVersion = JavaVersion(javaVersionString) + if (javaVersion.isPresent && !platformInfo.stream.javaCompatibility.versions.contains(javaVersion.asInt)) { + throw IllegalArgumentException("This Java version is not compatible with this stream (${platformInfo.stream.javaCompatibility.versions}): $javaVersionString"); + } + val isJava = extensions.stream() + .noneMatch{ it.startsWith("io.quarkus:quarkus-kotlin") || it.startsWith("io.quarkus:quarkus-scala") } + if (javaVersion.isPresent && !isJava && javaVersion.asInt > JavaVersion.MAX_LTS_SUPPORTED_BY_KOTLIN) { + throw IllegalArgumentException("This Java version is not yet compatible with Kotlin and Scala using Quarkus (max:${JavaVersion.MAX_LTS_SUPPORTED_BY_KOTLIN}): $javaVersionString"); + } val messageWriter = if (silent) MessageWriter.info(PrintStream(OutputStream.nullOutputStream())) else MessageWriter.info() try { @@ -61,7 +71,7 @@ open class QuarkusProjectService { projectFolderPath, platformInfo.extensionCatalog, buildTool, - JavaVersion(projectDefinition.javaVersion), + javaVersion, messageWriter ) val projectDefinition = CreateProject(project) @@ -70,7 +80,7 @@ open class QuarkusProjectService { .version(projectDefinition.version) .resourcePath(projectDefinition.path) .extraCodestarts(codestarts) - .javaVersion(projectDefinition.javaVersion) + .javaVersion(javaVersion.version) .resourceClassName(projectDefinition.className) .extensions(extensions) .noCode(projectDefinition.noCode || projectDefinition.noExamples) diff --git a/api/src/test/kotlin/io/quarkus/code/CodeQuarkusIT.kt b/api/src/test/kotlin/io/quarkus/code/CodeQuarkusIT.kt index 156ffd5bb..9231888e2 100644 --- a/api/src/test/kotlin/io/quarkus/code/CodeQuarkusIT.kt +++ b/api/src/test/kotlin/io/quarkus/code/CodeQuarkusIT.kt @@ -15,13 +15,13 @@ import io.quarkus.devtools.testing.WrapperRunner class CodeQuarkusIT { @ParameterizedTest - @ValueSource(strings = ["java", "kotlin", "scala"]) + @ValueSource(strings = ["java"]) @DisplayName("Should generate a maven project and run it in different language") fun testMaven(language: String) { val languageExt = if(language != "java") "io.quarkus:quarkus-$language" else "" val appName = "test-app-maven-$language" val result = given() - .`when`().get("/api/download?a=$appName&e=neo4j&e=amazon-lambda-http&e=$languageExt") + .`when`().get("/api/download?a=$appName&e=neo4j&e=resteasy-reactive&e=$languageExt&j=21") .then() .log().ifValidationFails() .statusCode(200) @@ -36,13 +36,13 @@ class CodeQuarkusIT { } @ParameterizedTest - @ValueSource(strings = ["java", "kotlin", "scala"]) + @ValueSource(strings = ["java"]) @DisplayName("Should generate a gradle project and run it in different language") fun testGradle(language: String) { val languageExt = if(language != "java") "io.quarkus:quarkus-$language" else "" val appName = "test-app-gradle-$language" val result = given() - .`when`().get("/api/download?b=GRADLE&a=$appName&v=1.0.0&e=neo4j&e=amazon-lambda-http&e=$languageExt") + .`when`().get("/api/download?b=GRADLE&a=$appName&v=1.0.0&e=neo4j&e=resteasy-reactive&e=$languageExt&j=21") .then() .log().ifValidationFails() .statusCode(200) @@ -54,4 +54,5 @@ class CodeQuarkusIT { val run = WrapperRunner.run(dir.toPath().resolve(appName), WrapperRunner.Wrapper.GRADLE) assertThat(run, `is`(0)) } + } diff --git a/api/src/test/kotlin/io/quarkus/code/rest/CodeQuarkusResourceTest.kt b/api/src/test/kotlin/io/quarkus/code/rest/CodeQuarkusResourceTest.kt index e3aba0345..a19539c7f 100644 --- a/api/src/test/kotlin/io/quarkus/code/rest/CodeQuarkusResourceTest.kt +++ b/api/src/test/kotlin/io/quarkus/code/rest/CodeQuarkusResourceTest.kt @@ -127,7 +127,6 @@ class CodeQuarkusResourceTest { artifactId = "my-awesome-app", version = "2.0.0", noCode = true, - javaVersion = "17", extensions = setOf("io.quarkus:quarkus-resteasy", "io.quarkus:quarkus-resteasy-jackson") ) val path = given() @@ -155,7 +154,7 @@ class CodeQuarkusResourceTest { groupId = "io.awesome", artifactId = "my-awesome-app", version = "2.0.0", - javaVersion = "11", + javaVersion = 11, noCode = true, extensions = setOf("io.quarkus:quarkus-resteasy", "io.quarkus:quarkus-resteasy-jackson") ) @@ -248,6 +247,45 @@ class CodeQuarkusResourceTest { .statusCode(400) } + @Test + @DisplayName("Should fail when using invalid javaVersion") + fun testWithInvalidJavaVersion() { + given() + .`when`() + .get("/api/download?j=550") + .then() + .log().ifValidationFails() + .statusCode(400) + } + + @Test + @DisplayName("Should fail when using incompatible java version with kotlin & Scala") + fun testWithInvalidJavaVersionKotlinScala() { + given() + .`when`() + .get("/api/download?j=21&e=kotlin") + .then() + .log().ifValidationFails() + .statusCode(400) + given() + .`when`() + .get("/api/download?j=21&e=scala") + .then() + .log().ifValidationFails() + .statusCode(400) + } + + @Test + @DisplayName("Should fail when using javaVersion as text") + fun testWithInvalidJavaVersionString() { + given() + .`when`() + .get("/api/download?j=text") + .then() + .log().ifValidationFails() + .statusCode(404) + } + @Test @DisplayName("Should return a project with specified configuration when a few parameters are specified") fun testWithAFewParams() { diff --git a/api/src/test/kotlin/io/quarkus/code/service/QuarkusProjectServiceTest.kt b/api/src/test/kotlin/io/quarkus/code/service/QuarkusProjectServiceTest.kt index 53742b0d4..afc72ae73 100644 --- a/api/src/test/kotlin/io/quarkus/code/service/QuarkusProjectServiceTest.kt +++ b/api/src/test/kotlin/io/quarkus/code/service/QuarkusProjectServiceTest.kt @@ -69,7 +69,7 @@ internal class QuarkusProjectServiceTest { .satisfies(checkContains("${platformService.recommendedPlatformInfo.extensionCatalog.bom.artifactId}")) .satisfies(checkContains("${platformService.recommendedPlatformInfo.extensionCatalog.bom.version}")).satisfies(checkContains("io.quarkus")) .satisfies(checkContains("quarkus-resteasy-reactive")) - .satisfies(checkContains("${ProjectDefinition.DEFAULT_JAVA_VERSION}")) + .satisfies(checkContains("${platformService.recommendedPlatformInfo.stream.javaCompatibility.recommended}")) .satisfies(checkContains("rest-assured")) assertThatMatchSnapshot(info, projDir, "src/main/java/org/acme/GreetingResource.java") @@ -95,7 +95,7 @@ internal class QuarkusProjectServiceTest { .satisfies(checkContains("${platformInfo.extensionCatalog.bom.artifactId}")) .satisfies(checkContains("${platformInfo.extensionCatalog.bom.version}")).satisfies(checkContains("io.quarkus")) .satisfies(checkContains("quarkus-resteasy-reactive")) - .satisfies(checkContains("${ProjectDefinition.DEFAULT_JAVA_VERSION}")) + .satisfies(checkContains("${platformService.recommendedPlatformInfo.stream.javaCompatibility.recommended}")) .satisfies(checkContains("rest-assured")) assertThatMatchSnapshot(info, projDir, "src/main/java/org/acme/GreetingResource.java") @@ -116,7 +116,7 @@ internal class QuarkusProjectServiceTest { version = "2.0.0", className = "com.test.TestResource", path = "/test/it", - javaVersion = "17", + javaVersion = platformService.recommendedPlatformInfo.stream.javaCompatibility.recommended, extensions = setOf( "io.quarkus:quarkus-resteasy-reactive", "io.quarkus:quarkus-resteasy-reactive-jsonb", @@ -144,7 +144,7 @@ internal class QuarkusProjectServiceTest { .satisfies(checkContains("quarkus-hibernate-validator")) .satisfies(checkContains("quarkus-neo4j")) .satisfies(checkContains("rest-assured")) - .satisfies(checkContains("17")) + .satisfies(checkContains("${platformService.recommendedPlatformInfo.stream.javaCompatibility.recommended}")) assertThatMatchSnapshot(info, projDir, "src/main/java/com/test/TestResource.java") @@ -199,7 +199,7 @@ internal class QuarkusProjectServiceTest { groupId = "com.gr", artifactId = "test-gradle-17-app", buildTool = "GRADLE", - javaVersion = "17" + javaVersion = 17 ) ) val testDir = QuarkusProjectServiceTestUtils.extractProject(proj) @@ -212,30 +212,53 @@ internal class QuarkusProjectServiceTest { } @Test - @DisplayName("Create a project with RESTEasy-Qute and YAML config") - fun testQuteYaml(info: TestInfo) { + @DisplayName("Create a Gradle project with java 21") + fun testGradle21(info: TestInfo) { // When val creator = getProjectService() val proj = creator.create( platformService.recommendedPlatformInfo, ProjectDefinition( - groupId = "my.qute.yaml.app", - artifactId = "test-qute-yaml-app", + groupId = "com.gr", + artifactId = "test-gradle-21-app", + buildTool = "GRADLE", + javaVersion = 21 + ) + ) + val testDir = QuarkusProjectServiceTestUtils.extractProject(proj) + val projDir = Paths.get(testDir.first.path, "test-gradle-21-app") + + // Then + assertThat(projDir.resolve("build.gradle")) + .satisfies(checkContains("sourceCompatibility = JavaVersion.VERSION_21")) + .satisfies(checkContains("targetCompatibility = JavaVersion.VERSION_21")) + } + + @Test + @DisplayName("Create a project with quinoa and YAML config") + fun testQuinoaYaml(info: TestInfo) { + // When + val creator = getProjectService() + + val proj = creator.create( + platformService.recommendedPlatformInfo, + ProjectDefinition( + groupId = "my.quinoa.yaml.app", + artifactId = "test-quinoa-yaml-app", buildTool = "MAVEN", - extensions = setOf("resteasy-reactive-qute", "config-yaml") + extensions = setOf("quinoa", "config-yaml") ) ) val testDir = QuarkusProjectServiceTestUtils.extractProject(proj) - val projDir = Paths.get(testDir.first.path, "test-qute-yaml-app") + val projDir = Paths.get(testDir.first.path, "test-quinoa-yaml-app") // Then assertThatDirectoryTreeMatchSnapshots(info, projDir) .contains( - "src/main/resources/templates/page.qute.html", - "src/main/java/my/qute/yaml/app/GreetingConfig.java", - "src/main/resources/application.yml", - "src/main/java/my/qute/yaml/app/SomePage.java" + "src/main/webui/package.json", + "src/main/java/my/quinoa/yaml/app/GreetingConfig.java", + "src/main/resources/application.yml" ) } diff --git a/api/src/test/resources/__snapshots__/QuarkusProjectServiceTest/testQuteYaml/dir-tree.snapshot b/api/src/test/resources/__snapshots__/QuarkusProjectServiceTest/testQuinoaYaml/dir-tree.snapshot similarity index 59% rename from api/src/test/resources/__snapshots__/QuarkusProjectServiceTest/testQuteYaml/dir-tree.snapshot rename to api/src/test/resources/__snapshots__/QuarkusProjectServiceTest/testQuinoaYaml/dir-tree.snapshot index 3c722a54a..19576e483 100644 --- a/api/src/test/resources/__snapshots__/QuarkusProjectServiceTest/testQuteYaml/dir-tree.snapshot +++ b/api/src/test/resources/__snapshots__/QuarkusProjectServiceTest/testQuinoaYaml/dir-tree.snapshot @@ -19,15 +19,21 @@ src/main/docker/Dockerfile.native src/main/docker/Dockerfile.native-micro src/main/java/ src/main/java/my/ -src/main/java/my/qute/ -src/main/java/my/qute/yaml/ -src/main/java/my/qute/yaml/app/ -src/main/java/my/qute/yaml/app/GreetingConfig.java -src/main/java/my/qute/yaml/app/SomePage.java +src/main/java/my/quinoa/ +src/main/java/my/quinoa/yaml/ +src/main/java/my/quinoa/yaml/app/ +src/main/java/my/quinoa/yaml/app/GreetingConfig.java src/main/resources/ src/main/resources/META-INF/ src/main/resources/META-INF/resources/ src/main/resources/META-INF/resources/index.html src/main/resources/application.yml -src/main/resources/templates/ -src/main/resources/templates/page.qute.html \ No newline at end of file +src/main/webui/ +src/main/webui/main.js +src/main/webui/package-lock.json +src/main/webui/package.json +src/main/webui/public/ +src/main/webui/public/vite.svg +src/main/webui/quinoa.css +src/main/webui/quinoa.html +src/main/webui/vite.config.js \ No newline at end of file diff --git a/library/.bitmap b/library/.bitmap index 00f05db5d..41c6a9b16 100644 --- a/library/.bitmap +++ b/library/.bitmap @@ -13,7 +13,13 @@ "scope": "quarkusio.code-quarkus", "version": "0.0.54", "mainFile": "index.ts", - "rootDir": "components" + "rootDir": "components", + "nextVersion": { + "version": "patch", + "message": "", + "username": "ia3andy", + "email": "ia3andy@gmail.com" + } }, "core/analytics": { "scope": "quarkusio.code-quarkus", diff --git a/library/components/__tests__/launcher-quarkus.spec.tsx b/library/components/__tests__/launcher-quarkus.spec.tsx index 73b35117f..591420f48 100644 --- a/library/components/__tests__/launcher-quarkus.spec.tsx +++ b/library/components/__tests__/launcher-quarkus.spec.tsx @@ -12,7 +12,9 @@ const fetchPlatform = async (api: Api, streamKey?: string) => ( 'key': 'io.quarkus.platform:2.0', 'recommended': true, 'quarkusCoreVersion': '2.0.0.Final', - 'status': 'FINAL' + 'status': 'FINAL', + 'lts': true, + 'javaCompatiblity': { 'versions': [11, 17, 21], 'recommended': 17} } ], 'extensions': [ diff --git a/library/components/api/__tests__/__snapshots__/quarkus-project-utils.spec.ts.snap b/library/components/api/__tests__/__snapshots__/quarkus-project-utils.spec.ts.snap index 33b0adf68..84eca7293 100644 --- a/library/components/api/__tests__/__snapshots__/quarkus-project-utils.spec.ts.snap +++ b/library/components/api/__tests__/__snapshots__/quarkus-project-utils.spec.ts.snap @@ -59,7 +59,7 @@ Object { "artifactId": "code-test", "buildTool": "GRADLE", "groupId": "org.test", - "javaVersion": "17", + "javaVersion": undefined, "noCode": false, "version": "1.0.0-SNAPSHOT", }, @@ -80,7 +80,7 @@ Object { "artifactId": "code-test", "buildTool": "MAVEN", "groupId": "org.test", - "javaVersion": "17", + "javaVersion": undefined, "noCode": false, "version": "1.0.0-SNAPSHOT", }, @@ -97,7 +97,7 @@ Object { "artifactId": "code-with-quarkus", "buildTool": "MAVEN", "groupId": "org.acme", - "javaVersion": "17", + "javaVersion": undefined, "noCode": false, "version": "1.0", }, diff --git a/library/components/api/model.ts b/library/components/api/model.ts index c0e36456d..53231497b 100644 --- a/library/components/api/model.ts +++ b/library/components/api/model.ts @@ -7,7 +7,7 @@ export interface QuarkusProject { name?: string; noCode?: boolean; buildTool: string; - javaVersion: string; + javaVersion?: string; }; extensions: string[]; streamKey?: string; @@ -53,12 +53,19 @@ export interface Platform { tagsDef: Tag[]; } +export interface JavaCompatibility { + versions: number[]; + recommended: number; +} + export interface Stream { key: string; quarkusCoreVersion: string; platformVersion: string; recommended: boolean; status: string; + lts: boolean; + javaCompatibility: JavaCompatibility; } export interface Config { diff --git a/library/components/api/quarkus-project-utils.ts b/library/components/api/quarkus-project-utils.ts index 19b53dd65..867e6c8be 100644 --- a/library/components/api/quarkus-project-utils.ts +++ b/library/components/api/quarkus-project-utils.ts @@ -1,9 +1,31 @@ import {createGitHubProject} from './code-quarkus-github-api'; -import {Extension, PlatformMappedExtensions, QuarkusProject} from './model'; +import {Extension, PlatformMappedExtensions, QuarkusProject, Platform, Stream } from './model'; import _ from 'lodash'; import {Api} from './code-quarkus-api'; +const ERROR_STREAM: Stream = { + key: 'recommended.not.found:stream', + quarkusCoreVersion: 'error', + recommended: true, + status: 'NOT_FOUND', + platformVersion: 'error', + lts: false, + javaCompatibility: { versions: [], recommended: -1 } +} + +export function getRecommendedStream(platform: Platform) { + return platform.streams.find(s => s.recommended) || ERROR_STREAM; +} + +export function getProjectStream(platform: Platform, streamKey?: string): Stream { + const recommendedStream = getRecommendedStream(platform); + if (!streamKey) { + return recommendedStream; + } + const normalizedStreamKey = normalizeStreamKey(recommendedStream.key.split(':')[0], streamKey); + return platform.streams.find(s => s.key === normalizedStreamKey) || recommendedStream; +} export function parse(str): object { const decode = decodeURIComponent; return (str + '') @@ -186,7 +208,6 @@ export function newDefaultProject(): QuarkusProject { artifactId: 'code-with-quarkus', version: '1.0.0-SNAPSHOT', buildTool: 'MAVEN', - javaVersion: '17', noCode: false }, extensions: [], diff --git a/library/components/header/stream-picker.tsx b/library/components/header/stream-picker.tsx index b03c07f42..1dd8bcf57 100644 --- a/library/components/header/stream-picker.tsx +++ b/library/components/header/stream-picker.tsx @@ -1,27 +1,13 @@ import React from 'react'; import './stream-picker.scss'; import { Platform, Stream } from '../api/model'; -import { normalizeStreamKey } from '../api/quarkus-project-utils'; +import { getRecommendedStream, getProjectStream } from '../api/quarkus-project-utils'; import { Dropdown } from 'react-bootstrap'; import { FaAngleDown, FaCheck } from 'react-icons/fa'; import { useAnalytics } from '@quarkusio/code-quarkus.core.analytics'; import classNames from 'classnames'; -const ERROR_STREAM: Stream = { key: 'recommended.not.found:stream', quarkusCoreVersion: 'error', recommended: true, status: 'NOT_FOUND', platformVersion: 'error' } - -function getRecommendedStream(platform: Platform) { - return platform.streams.find(s => s.recommended) || ERROR_STREAM; -} - -function getProjectStream(platform: Platform, streamKey?: string) { - if (!streamKey) { - return null; - } - const recommendedStream = getRecommendedStream(platform); - const normalizedStreamKey = normalizeStreamKey(recommendedStream.key.split(':')[0], streamKey); - return platform.streams.find(s => s.key === normalizedStreamKey); -} function formatStreamStatus(status?: string, quarkusCoreVersion?: string) { let s = status?.toLowerCase(); @@ -84,7 +70,7 @@ function StreamItem(props: { streamKey: string; quarkusCoreVersion?: string; pla export function StreamPicker(props: StreamPickerProps) { const analytics = useAnalytics(); const recommendedStream = getRecommendedStream(props.platform); - const stream = getProjectStream(props.platform, props.streamKey) || recommendedStream; + const stream = getProjectStream(props.platform, props.streamKey); function setStreamKey(s: Stream) { props.setStreamKey(s.key, props.platformOnly); analytics.event('Switch stream', { stream: s.key, element: 'stream-picker' }); diff --git a/library/components/info-picker/info-picker.tsx b/library/components/info-picker/info-picker.tsx index 8ef7fbab6..b3369a0a0 100644 --- a/library/components/info-picker/info-picker.tsx +++ b/library/components/info-picker/info-picker.tsx @@ -5,6 +5,7 @@ import './info-picker.scss'; import { BuildToolSelect } from './build-tool-select'; import { NoCodeSelect } from './no-code-select'; import { JavaVersionSelect } from './java-version-select'; +import { Stream } from '../api/model'; export interface InfoPickerValue { groupId?: string; @@ -17,6 +18,7 @@ export interface InfoPickerValue { interface InfoPickerProps extends InputProps { showMoreOptions?: boolean; + currentStream: Stream; } const ARTIFACTID_PATTERN = /^[a-z][a-z0-9-._]*$/; @@ -73,7 +75,7 @@ export const InfoPicker = (props: InfoPickerProps) => { onChange={onVersionChange} isValid={!!props.value.version} /> - + diff --git a/library/components/info-picker/java-version-select.tsx b/library/components/info-picker/java-version-select.tsx index 4dacf46d2..57024ff79 100644 --- a/library/components/info-picker/java-version-select.tsx +++ b/library/components/info-picker/java-version-select.tsx @@ -2,8 +2,13 @@ import * as React from 'react'; import { useAnalyticsEditionField } from '@quarkusio/code-quarkus.core.analytics'; import { InputProps } from '@quarkusio/code-quarkus.core.types'; import { FaAngleDown } from 'react-icons/fa'; +import { JavaCompatibility } from '../api/model'; -export const JavaVersionSelect = (props: InputProps) => { +interface JavaVersionSelectProps extends InputProps { + javaCompatibility?: JavaCompatibility; +} + +export const JavaVersionSelect = (props: JavaVersionSelectProps) => { const onChangeWithDirty = useAnalyticsEditionField('buildTool', props.onChange)[1]; const adaptedOnChange = (e: React.ChangeEvent) => { onChangeWithDirty(e.currentTarget.value); @@ -12,9 +17,8 @@ export const JavaVersionSelect = (props: InputProps) => {
- + {props.javaCompatibility?.versions.map((version) => )}
diff --git a/library/components/quarkus-project/quarkus-project-edition-form.tsx b/library/components/quarkus-project/quarkus-project-edition-form.tsx index 55f28abde..d63816f7e 100644 --- a/library/components/quarkus-project/quarkus-project-edition-form.tsx +++ b/library/components/quarkus-project/quarkus-project-edition-form.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import './quarkus-project-edition-form.scss'; import { ExtensionEntry, ExtensionsPicker } from '../extensions-picker/extensions-picker'; +import { getProjectStream } from '../api/quarkus-project-utils'; import { InfoPicker, isValidInfo } from '../info-picker/info-picker'; import { GenerateButton } from '../generate-project/generate-button'; import { Config, Extension, Platform, QuarkusProject } from '../api/model'; @@ -60,7 +61,9 @@ export function CodeQuarkusForm(props: CodeQuarkusFormProps) { React.useEffect(() => { debouncedSyncParamsQuery(props.api, props.project, filter); - }, [ filter, props.project ]) + }, [ filter, props.project ]); + + const currentStream = getProjectStream(props.platform, props.project.streamKey); return (
@@ -71,7 +74,7 @@ export function CodeQuarkusForm(props: CodeQuarkusFormProps) { Configure your application
- +