Extension codestart

Description

“扩展 Codestart”是我们赋予 Quarkus 扩展“开始”代码生成系统的名称。它旨在提供使用 Quarkus 的个性化入门体验。Quarkus 扩展可以提供一个或多个明确定义的 Codestart,其中将包含使用特定扩展所需的/推荐的资源和代码。

使用 Quarkus 工具时,默认情况下将应用扩展 Codestart(如果选定的扩展包含任何 Codestart):

  • code.quarkus.io (找出标记为 [code] 的扩展)

  • The Quarkus Maven plugin:[source, bash]

mvn {quarkus-platform-groupid}:quarkus-maven-plugin:create
  • The Quarkus CLI:[source, bash]

quarkus create app

How it works

开始一个项目时,您选择语言、构建工具、框架,然后添加 Dockerfile、CI、依赖关系和代码。

在为项目的生成做出贡献时,Codestart 的工作方式相同,它们分为两类:

The "Base" codestarts (you choose a combination of those):

  • project:项目框架(例如 Quarkus 项目)

  • buildtool:构建工具(例如 Maven、Gradle、使用 Kotlin DSL 的 Gradle)

  • language:编码语言(例如 Java、Kotlin、Scala)

  • config:配置类型(例如 yaml、properties)

Extra codestarts (as much as wanted, added on top of the base ones):

  • 工具:任何可添加以改善项目的内容(例如 Dockerfile、GitHub Actions 工作流)

  • 代码:任何 Quarkus 扩展都可以提供启动代码。用户可以决定是否激活它。

每个 codestart 包含:

  1. 一个 codestart 唯一名称,即 my-codestart

  2. 一个 codestart 文件目录,即 my-codestart/

  3. A codestart.yml file

  4. 一些可选模板,遵循通用的结构和命名约定

Where are the Quarkus Extension Codestarts located

  • 在 Quarkus 核心存储库中,扩展 codestart 全部位于相同的 module 中。

  • Quarkus REST(以前称为 RESTEasy Reactive)、RESTEasy 和 Spring Web 扩展 codestart 属于 the base codestarts 的一部分。

  • 对于其他扩展,codestart 通常将位于运行时模块中(带有 pom.xml 中的特殊指令来生成一个单独的 codestart 工件)。

Base codestarts

base codestarts 包含用于创建项目、构建工具、语言、配置和工具文件的模板。

此外,Quarkus 还提供了以下方法来使用 Codestart 初始化新扩展项目:

CLI

要使用 Codestart 框架创建新扩展,请为 create extension 命令提供 --codestart 标志:

quarkus create extension --codestart org.acme:greeting-extension
Detected layout type is 'standalone'
Generated runtime artifactId is 'greeting-extension'


applying codestarts...
📚  java
🔨  maven
📦  quarkus-extension
🚀  devmode-test
🚀  extension-base
🚀  extension-codestart
🚀  integration-tests
🚀  unit-test

-----------
 👍  extension has been successfully generated in:
--> /Users/.../greeting-extension
-----------
Navigate into this directory and get started: quarkus build

For more information about how to install the Quarkus CLI and use it, please refer to the Quarkus CLI guide.

Maven

Quarkus 提供 create-extension Maven Mojo 来初始化扩展项目。 要使用 Codestart 框架生成新扩展,请为此 Mojo 提供 -DwithCodestart 标志:

mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create-extension -N \
    -DgroupId=org.acme \
    -DextensionId=greeting-extension \
    -DwithCodestart
[INFO] --- quarkus-maven-plugin:{quarkus-version}:create-extension (default-cli) @ standalone-pom ---

Detected layout type is 'standalone'
Generated runtime artifactId is 'greeting-extension'


applying codestarts...
📚  java
🔨  maven
📦  quarkus-extension
🚀  devmode-test
🚀  extension-base
🚀  extension-codestart
🚀  integration-tests
🚀  unit-test

-----------
 👍  extension has been successfully generated in:
--> /Users/.../greeting-extension
-----------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.638 s
[INFO] Finished at: 2022-10-24T21:27:51+02:00
[INFO] ------------------------------------------------------------------------

Writing an Extension Codestart

以下是一份分步指南,用于编写扩展 codestart。你还可以观看带有现场编码环节的 Quarkus Insight #99

如之前所述,基本项目文件(pom.xml、Dockerfile 等)已由 Quarkus 核心提供的基本 codestart 生成。得益于此,我们只需关注特定于扩展的启动代码即可。

让我们以 io.quarkiverse.aloha:quarkus-aloha 为例扩展 GAV(不要寻找此扩展,它不存在)。

The code

Codestart 是用于构建新项目的模板。

本教程中,通过 Quarkus 项目创建一个 Codestart 项目并添加所需的模板。

因此,转到 code.quarkus.io,使用 aloha 扩展创建一个新项目,并将 org.acme 作为组(如 org.acme placeholder for package name)。准备一个不错的启动器。它不应包含任何业务逻辑,而应包含一些可编译且概述如何使用扩展名的存根数据/hello world。这个想法是引入一些最为常见的扩展名起始点代码。

对代码满意?让我们用它来创建一个 Codestart。

The Codestart (Quarkiverse or Standalone extensions)

在您的扩展中:

  • Create the runtime/src/main/codestarts/quarkus/aloha-codestart directory

  • src/main/java 从生成的项目移到 runtime/src/main/codestarts/quarkus/aloha-codestart/java/src/main/java

  • (可选)使用此约定移动配置:application config application.yml

  • runtime/src/main/codestarts/quarkus/aloha-codestart 中创建一个 codestart.yml 文件:[source, yaml]

name: aloha-codestart
ref: aloha
type: code
tags: extension-codestart
metadata:
  title: Aloha
  description: Start to code with the Aloha extension.
  related-guide-section: https://quarkiverse.github.io/quarkiverse-docs/quarkus-aloha/dev/
  path: /aloha # (optional) for web extensions providing HTTP resources
  • runtime/pom.xml 中添加 Maven 构建插件配置(生成 codestart 工件: /target/quarkus-aloha-VERSION-codestarts.jar):[source, xml]

      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <executions>
          <execution>
            <id>generate-codestart-jar</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>jar</goal>
            </goals>
            <configuration>
              <classesDirectory>${project.basedir}/src/main</classesDirectory>
              <includes>
                <include>codestarts/**</include>
              </includes>
              <classifier>codestarts</classifier>
              <skipIfEmpty>true</skipIfEmpty>
            </configuration>
          </execution>
        </executions>
      </plugin>
  • 在扩展元数据 runtime/src/main/resources/META-INF/quarkus-extension.yaml 中添加 codestart 绑定。 Without this, your codestart won&#8217;t be added when your extension is picked:[source, yaml]

name: ...
description: ...
metadata:
  ...
  codestart:
    name: "aloha"
    languages:
    - "java"
    artifact: "io.quarkiverse.aloha:quarkus-aloha:codestarts:jar:${project.version}"
  • base/README.tpl.qute.md 中添加自述文件 README.md 部分模板:[source, html]

{#include readme-header /}
  • 在扩展根目录中运行 mvn clean install (或仅运行 runtime )。

  • 现在,我们可以检查 codestart 的实际工作情况,方法是创建一个使用我们扩展名的项目(确保快照版本正确):[source, bash]

quarkus create app aloha-app -x=io.quarkiverse.aloha:quarkus-aloha:999-SNAPSHOT

...
applying codestarts...
📚  java
🔨  maven
📦  quarkus
📝  config-properties
🔧  dockerfiles
🔧  maven-wrapper
🚀  aloha-codestart <<<<<<<<<<<<<<<<
...

Testing

  • 将此依赖项添加到 integration-tests:[source, xml]

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-devtools-testing</artifactId>
  <scope>test</scope>
</dependency>
  • integration-tests 中创建一个 AlohaCodestartTest :[source, java]

public class AlohaCodestartTest {

    @RegisterExtension
    public static QuarkusCodestartTest codestartTest = QuarkusCodestartTest.builder()
            .languages(Language.JAVA)
            .setupStandaloneExtensionTest("io.quarkiverse.aloha:quarkus-aloha")
            .build();

    @Test
    void testContent() throws Throwable {
        codestartTest.checkGeneratedSource("org.acme.AlohaResource");
    }

    @Test
    void buildAllProjects() throws Throwable {
        codestartTest.buildAllProjects();
    }
}

Going further

  • 如果该扩展提供一些 Web 资源,请添加 base/src/main/resources/META-INF/resources/index.entry.qute.html 模板 (index.html and web extension codestarts)。

  • 添加另一种语言(建议同时提供 Java 和 Kotlin)。

  • 您可以添加一些其他资源(如果它们不是特定于语言的,则添加到 ./base 目录中)。

Extensions codestarts in Quarkus Core

  • Codestart 均分组在 specific module 中。

  • 无需其他 Maven 配置。

  • extension metadata引用包含所有核心代码启动项的工件。

  • 测试也https://github.com/quarkusio/quarkus/tree/main/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/quarkus[grouped, window=_blank]。您不需要测试构建,因为有一个特定的分组https://github.com/quarkusio/quarkus/blob/main/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartBuildIT.java[test, window=_blank]用于它,例如:[source, java]

public class ConfigYamlCodestartTest {

    @RegisterExtension
    public static QuarkusCodestartTest codestartTest = QuarkusCodestartTest.builder()
            .codestarts("config-yaml")
            .languages(JAVA, KOTLIN)
            .build();

    @Test
    void testContent() throws Throwable {
        codestartTest.checkGeneratedSource("org.acme.GreetingConfig");
        codestartTest.assertThatGeneratedFileMatchSnapshot(JAVA, "src/main/resources/application.yml");
    }

    @Test
    @EnabledIfSystemProperty(named = "build-projects", matches = "true")
    void buildAllProjectsForLocalUse() throws Throwable {
        codestartTest.buildAllProjects();
    }

}

Specific topics

org.acme placeholder for package name

您必须使用 org.acme`作为扩展 codestart 源中的包名称。在生成的项目中,将使用用户指定的包(或组)(并自动替换 `org.acme)。

包将在所有源文件(.java、.kt、.scala)中自动替换。包目录也将自动调整。如果出于某种原因,另一种类型的文件需要用户包名,则您应该为其使用Templates (Qute)和`{project.package-name}`数据占位符(https://github.com/quarkusio/quarkus/blob/main/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/grpc-codestart/base/src/main/proto/hello.tpl.qute.proto#L4[find an example in the grpc proto file, window=_blank])。

codestart.yml

# codestart unique name
name: resteasy-example
# codestart reference, use the extension id
ref: resteasy
# use 'code' (other types are for base codestarts)
type: code
# use 'extension-codestart'
tags: extension-codestart
# public metadata for this example (accessible as data in the templates e.g. {title})
metadata:
  title: RESTEasy Jakarta REST example
  description: Rest is easy peasy with this Hello World RESTEasy resource.
  related-guide-section: https://quarkus.io/guides/getting-started#the-jax-rs-resources
  # (optional) use this in web extensions with a specific path (and also add the index page)
  path: /some-path

Directory Structure

`codestart.yml`是唯一必需的文件。

  • `codestart.yml`必须位于 codestart 的根目录。

  • `./base`包含将独立于指定语言处理的所有文件。

  • `./[java/kotlin/scala]`包含在仅选择了指定语言(覆盖基础)时处理的所有文件。

Dynamic Config Keys in Codestart

gen-info.time = generation time (in milliseconds)
input.selected-extensions[].name|description|guide = list of selected extensions with info
input.selected-extensions-ga = Set of Strings containing the list of extensions groupId:artifactId, useful for dynamic codestarts depending on selected extensions
input.provided-code[].name|tags|title|description|related-guide: list of selected codestarts with info

Static Config Keys in Codestart

quarkus.platform.group-id = BOM groupId
quarkus.platform.artifact-id = BOM artifactId
quarkus.platform.version = BOM version
project.group-id = Project groupId
project.artifact-id = Project artifactId
project.version = Project version
project.name = Project name (if specified)
project.description = Project description (if specified)
project.package-name = Project package name
quarkus.maven-plugin.group-id = Quarkus Maven plugin groupId
quarkus.maven-plugin.artifact-id = Quarkus Maven plugin artifactId
quarkus.maven-plugin.version = Quarkus Maven plugin version
quarkus.gradle-plugin.id = Quarkus Gradle pluginId
quarkus.gradle-plugin.version = Quarkus Gradle plugin version
quarkus.version = Quarkus version
java.version = Java version
kotlin.version = Kotlin version
scala.version = Scala version
scala-maven-plugin.version = Scala Maven plugin version
maven-compiler-plugin.version = Maven compiler plugin version
maven-surefire-plugin.version = Maven Surefire plugin version

Naming Convention for files

  • .tpl.qute`将使用 Qute 处理,并且可以使用数据(.tpl.qute`将从输出文件名中删除)。

  • 某些常见文件,例如 readme.mdsrc/main/resources/application.ymlsrc/main/resources/META-INF/resources/index.html,是从为项目选择 codestarts 中找到的收集片段生成的。

  • other files are copied.

Templates (Qute)

Codestarts 可以使用 Qute 模板`MyClass.tpl.qute.java`进行动态渲染。

这些模板可以使用包含以下内容的数据:

  • 要生成的 codestart 的 data(和公共 metadata)(在 `codestart.yml`中指定)

  • 从用于生成项目的 codestarts 合并 shared-data

  • The user input

  • 某些动态生成的数据(例如 dependencies`和 `test-dependencies

README.md

您可以在`base`目录中添加`README.md`或`README.tpl.qute.md`,它将附加到其他目录。所以只需添加您扩展 codestart 的相关信息。

base/README.tpl.qute.md

{#include readme-header /}

[Optionally, Here you may add information about how to use the example, settings, ...]

`{#include readme-header /}`将使用位于 Quarkus 项目 codestart 中的模板,该模板会显示来自 `codestart.yml`元数据的标准信息。

application config application.yml

根据惯例,您应始终将 Quarkus 配置提供为 yaml 文件(base/src/main/resources/application.yml)。

以下将发生:

  • 与其他扩展 codestarts 配置合并

  • 根据所选扩展,在生成时自动转换为所选配置类型(yaml 或 properties)

index.html and web extension codestarts

扩展 codestarts 可以通过添加以下文件来为生成的 index.html 提供代码段:

base/src/main/resources/META-INF/resources/index.entry.qute.html

{#include index-entry /}

{#include index-entry /} 将使用位于 Quarkus 项目 codestart 中的模板,该模板显示来自 codestart.yml 元数据的标准信息。

Integration test

有一个扩展可以帮助测试扩展 codestarts QuarkusCodestartTest

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-devtools-testing</artifactId>
  <scope>test</scope>
</dependency>

它提供了一种测试方法:

  • 使用快照测试来测试生成项目内容(具有不可变模拟数据)

  • 使用构建/运行帮助程序来测试生成项目构建/运行(具有实际数据)

在所有测试之前,该扩展将使用模拟数据和实际数据用指定语言在 Quarkus 项目中生成项目。你可以在 target/quarkus-codestart-test 目录中找到这些生成项目。你可以在 IDE 中打开 real-data 或使用终端对其进行操作。The real data is the easiest way to iterate on your extension codestart development.

该扩展提供了测试项目构建 buildAllProjects 或仅测试特定语言项目 buildProject(Language language) 的帮助程序。它还提供了使用 Snapshot testing 测试内容的帮助程序。

ConfigYamlCodestartTest 是 Quarkus 核心中的一个好示例。

Snapshot testing

Snapshot 测试是一种确保测试生成的文本不会因版本而异的方法,即版本之间。这意味着每次提交的生成内容应不可变且确定(这是使用模拟数据的原因)。为了执行此类检查,我们自动生成生成内容的快照并将其作为后续测试运行的预期输出的引用进行提交。当模板发生更改时,我们还会提交感应的快照更改。这样,在审查期间,我们可以确保应用的代码更改对生成输出产生预期效果。

该扩展提供了检查内容的帮助程序:

  • checkGeneratedSource() 验证针对所有语言(或特定语言)快照的类。

  • checkGeneratedTestSource() 验证针对所有语言(或特定语言)快照的测试类。

  • assertThatGeneratedFileMatchSnapshot() 检查针对快照的项目文件。

  • 你可以在上述方法返回的内容上使用 AbstractPathAssert.satisfies(checkContains("some content")) 或任何 Path 断言来检查文件是否也包含特定内容。

  • `assertThatGeneratedTreeMatchSnapshots()`可让您将特定语言的项目文件结构(树)与其快照进行对比。

要在本地文件系统上首次生成或更新现有的快照文件,您需要在本地运行测试并开发 codestart 时添加 -Dsnap。它们需要添加为提交的一部分,否则测试将不会通过 CI。

Writing tips

  • 您的扩展 codestart 必须独立于构建工具和 Dockerfile。

  • 扩展 codestart 应该能够相互配合,而不会产生干扰(组合使用)。

  • 请确保您的类名在所有扩展 codestart 中都是唯一的。

  • 仅将 `org.acme`用作包名称。

  • 为您的 REST 路径使用唯一的路径 /[unique]

  • 在 yml `src/main/resources/application.yml`中编写配置。它将与其他 codestart 的配置合并,并自动转换为选定的配置类型(yaml 或 properties)。

  • 您可以从 java 开始,并在另一个 PR 中稍后添加 kotlin(创建问题,以免忘记)。

  • 如果您有任何疑问,请在 [role="bare"][role="bare"]https://quarkusio.zulipchat.com/联系我 @ia3andy。

Issues and Feature requests