开发效率 - Quarkus CLI

人们很少意识到,Quarkus CLI 自 2019 年 Quarkus 首次公开版本发布以来就已可用。它最初只允许创建项目和操作扩展。以下命令显示了支持的命令列表。

quarkus --help
Usage: quarkus <command> [<args>]

These are the common quarkus commands used in various situations

Options:
  -h, --help

quarkus commands:
    list-extensions  List extensions for a project
    add-extension  Adds extensions to a project
    create-project  Creates a base Quarkus maven project

如今,在 3.1.2.Final 版本中,它包含近 30 个命令,分布在 6 个主要类别中。其中 3 个类别是 3.0 路线图的一部分,将是本文的重点。特别是,本文将介绍使用 Quarkus CLI 构建容器镜像、部署和扩展 Quarkus CLI。

使用 Quarkus CLI 构建容器镜像

提供一种简单的 Quarkus 容器镜像创建方式并非新鲜事。自早期以来,Quarkus 就提供了处理容器镜像构建的扩展,使用了诸如以下技术:

使用这些扩展需要将它们添加到项目中,例如:

quarkus ext add quarkus-container-image-docker

此外,还需要额外的配置选项来触发容器镜像构建。

./mvnw package -Dquarkus.container-image.build=true

虽然这可以很好地工作,但用户仍然需要了解这些扩展以及启用它们所需的特殊配置选项。换句话说,用户需要方便地链接到 Quarkus 容器镜像文档,以查看可用选项及其使用方法。

此外,用户每次需要切换扩展时都需要修改项目配置。这很简单,但应该是可选的,因为实际应用程序不依赖于容器镜像的构建方式。此外,这可能会增加版本控制日志中的噪音。

使用 Quarkus CLI 构建容器镜像

Quarkus 3.0 引入了一种使用 Quarkus CLI 构建容器镜像的替代方法。在 CLI 的最新版本中,提供了新的子命令来构建和推送容器镜像。这些命令列在 quarkus --help 的输出中。

quarkus --help | grep image
 image                   Build or push project container image.
   build                 Build a container image.
     docker              Build a container image using Docker.
     buildpack           Build a container image using Buildpack.
     jib                 Build a container image using Jib.
     openshift           Build a container image using Openshift.
   push                  Push a container image.

例如,为了执行 docker 构建:

quarkus image build docker

请注意,该命令不要求用户以任何方式编辑其构建文件(例如 pom.xml 或 build.gradle),并且可以在任何项目中运行,而无需任何特定的扩展。甚至可以在空的 Quarkus 项目中运行。

quarkus create app hello
cd hello
quarkus image build docker

无需额外配置,即使用户决定切换到不同的容器镜像技术,如 jib。

quarkus image build jib

最后但同样重要的是,CLI 确实提供了额外的帮助,例如代码补全和帮助消息。

quarkus image build jib --help

部署应用程序

以与构建容器镜像类似的方式,Quarkus 允许将应用程序部署到 KubernetesOpenShift 等平台。同样,这需要使用扩展和额外的构建选项来启用部署。例如,要在 Kubernetes 上部署应用程序,需要显式地将扩展添加到项目中,并使用 quakrus.kubernetes.deploy 属性启用部署。

quarkus ext add quarkus-kubernetes
./mvnw package -Dquarkus.kubernetes.deploy=true

使用 Quarkus CLI 进行部署

在 Quarkus 3.0 中,CLI 包含 deploy 子命令,这是与部署相关的命令的入口点。使用 quarkus --help 可以列出所有相关命令。

quarkus --help | grep deploy
deploy                  Deploy application.
  kubernetes            Perform the deploy action on kubernetes.
  openshift             Perform the deploy action on openshift.
  knative               Perform the deploy action on knative.
  kind                  Perform the deploy action on kind.
  minikube              Perform the deploy action on minikube.

这些命令允许开发人员轻松地将 Quarkus 应用程序从一个平台部署到另一个平台,而无需弄乱其项目文件。

设想一个团队,一些开发人员使用 kind,另一些开发人员使用 minikube。在 3.0 之前,他们每次需要从版本控制中拉取更改时,都必须暂存并应用他们选择的扩展。或者,他们可以配置构建配置文件。通过使用 CLI,用户可以部署到他们选择的平台,即使在与项目配置中的平台不一致的情况下也是如此。例如,如果项目包含 Quarkus Kubernetes 扩展,但用户更喜欢使用 kind 扩展并利用针对 kind 的优化清单。

quarkus deploy kind

需要注意的是,通过为每个平台提供一个命令,用户可以在执行这些命令时轻松地设置特定于平台的配置(请参阅 --help 输出)。

总结镜像构建和部署命令

Quarkus 3.0 引入了新的 CLI 命令来构建容器镜像和进行部署。这些命令通过消除与项目设置和配置相关的步骤来改善开发人员体验。它们允许开发人员轻松地试验不同的技术,并通过提供帮助消息、提示和补全来指导他们。

Quarkus 的未来版本将把这个概念扩展到 Quarkus Azure FunctionsQuarkus Amazon Lambda 等领域。

CLI 插件

CLI 为开发人员带来了一些非常有趣的功能,但可惜的是,它不能无限增长,因为它需要保持合理的大小。这种需求促使为 CLI 实现了插件系统,该系统允许以插件的形式动态添加命令。

什么是插件?

插件通过以下方式之一实现命令:

  • 作为任意可执行文件

  • 作为 Java 源代码文件

  • 作为 jar(带 main 方法)

  • 作为 Maven 构件

  • 作为 JBang 别名

插件通过显式使用 Quarkus CLI 或隐式添加扩展到项目来添加到 CLI。

让我们看看 CLI 中有哪些与插件相关的可用命令。

quarkus --help | grep plug
plugin, plug            Configure plugins of the Quarkus CLI.
  list, ls              List CLI plugins.
  add                   Add plugin(s) to the Quarkus CLI.
  remove                Remove plugin(s) to the Quarkus CLI.

最初,没有安装任何插件,因此 quarkus plug list 返回一个空列表。

quarkus plug list
No plugins installed!
To include the installable plugins in the list, append --installable to the command.

它还返回一个提示,建议使用 --installable,但什么是 installable 插件?

Installable 指的是在 PATH 中找到的可执行文件,或者以 quarkus 前缀开头的 JBang 别名。注意:该命令需要 JBang(如果尚未安装,则会提示用户安装)。

quarkus plug list --installable
  Name    	 Type  	 Scope 	 Location               	 Description
  fmt     	 jbang 	 user  	 quarkus-fmt@quarkusio
  kill    	 jbang 	 user  	 quarkus-kill@quarkusio
  quarkus 	 jbang 	 user  	 quarkus@quarkusio

列出的插件是 JBang 别名,这些别名在 quarkus.io JBang 目录(默认启用)中可用。可以使用 JBang 二进制文件添加更多目录。

编写插件

让我们看看如何为 Quarkus CLI 创建插件。开箱即用地,Quarkus CLI 提供了 3 种创建项目的方式:

  • Web 应用

  • 命令行应用程序

  • Quarkus 扩展

quarkus --help | grep -A3 create
create                  Create a new project.
  app                   Create a Quarkus application project.
  cli                   Create a Quarkus command-line project.
  extension             Create a Quarkus extension project

我们将创建一个 create 插件,使用 Quarkus Quickstarts 创建新应用程序。这就像编写一个从 GitHub 克隆存储库并复制所选快速入门的脚本一样简单。为了增加一些额外价值,我们还将使用 稀疏检出,并将深度限制为 1。这最大限度地减少了传输的数据量并加快了速度。此外,回忆 稀疏检出 的实际步骤并不容易,因此将其作为脚本非常方便。

#!/bin/bash

DIRECTORY=$1
REPO_URL="https://github.com/quarkusio/quarkus-quickstarts.git"

# Create a new directory for your Git repo and navigate into it
mkdir $DIRECTORY
cd $DIRECTORY

# Initialize a new Git repository here
git init

# Add the repository from GitHub as a place your local Git repo can fetch from
git remote add origin $REPO_URL
git config core.sparseCheckout true
echo "$DIRECTORY" >> .git/info/sparse-checkout

# Fetch just the history of the specific directory
git fetch --depth 1 origin main:$DIRECTORY

# Checkout the specific directory
git checkout $DIRECTORY
mv $DIRECTORY/* .
rm -rf $DIRECTORY
rm -rf .git

让我们将上面的脚本保存在名为 quarkus-create-from-quickstart 的文件中,并将其添加到 PATH。quarkus- 是必需的前缀,create 是插件将被安装的命令名称。下次运行 quarkus plug list --installable 时,它会拾取该脚本。

quarkus plug list --installable
  Name                   	 Type       	 Scope 	 Location                                         	 Description
  create-from-quickstart 	 executable 	 user  	 /home/iocanel/bin/quarkus-create-from-quickstart
  fmt                    	 jbang      	 user  	 quarkus-fmt@quarkusio
  kill                   	 jbang      	 user  	 quarkus-kill@quarkusio
  quarkus                	 jbang      	 user  	 quarkus@quarkusio

Use the 'plugin add' subcommand and pass the location of any plugin listed above, or any remote location in the form of URL / GACTV pointing to a remote plugin.

现在可以使用以下命令安装插件:

quarkus plug add create-from-quickstart
Added plugin:
    Name                   	 Type       	 Scope 	 Location                                         	 Description
 *  create-from-quickstart 	 executable 	 user  	 /home/iocanel/bin/quarkus-create-from-quickstart

插件现在出现在 quarkus --helpcreate 命令下。

quarkus --help | grep -A4 create
create                  Create a new project.
  app                   Create a Quarkus application project.
  cli                   Create a Quarkus command-line project.
  extension             Create a Quarkus extension project
  from-quickstart

它可以像常规命令一样使用。让我们用它来从 Hibernate ORM Panache Quickstart 创建应用程序。

quarkus create from-quickstart hibernate-orm-panache-quickstart
利用您的 Java 技能编写插件

使用 Shell 脚本或任意二进制文件(用任何语言编写)是编写插件的一种方式。Java 开发人员也可以利用他们的 Java 技能。包含 main 方法的任何源文件或定义了 main 类的任何 jar 都可以通过传递其位置(路径或 URL)直接使用。对于 jar,也支持 Maven 坐标形式的 GACTV(<组 ID>:<Artifact ID>:<Classifier>:<Type>:<Version>)。

让我们用 Java 重写 create-from-github,看看如何将 Java 源文件插入 Quarkus CLI。实现将使用 jgitcommons.io。为了简化依赖管理,源文件包含 JBang 元注释,它定义了前面提到的依赖项。

///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r
//DEPS commons-io:commons-io:2.11.0
//JAVA_OPTIONS -Djava.io.tmpdir=/tmp

import org.eclipse.jgit.api.*;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.transport.*;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Set;
import org.apache.commons.io.FileUtils;

public class CreateFromQuickstart {

    private static final String REPO_URL = "https://github.com/quarkusio/quarkus-quickstarts.git";
    private static final String FETCH = "+refs/heads/*:refs/remotes/origin/*";

    public static void main(String[] args) {
        String directory = args[0];
        Set<String> paths = Set.of(directory);
        try {
            Path cloneDir = Files.createTempDirectory("create-from-quickstart-");
            Git git = Git.init().setDirectory(cloneDir.toFile()).call();

            StoredConfig config = git.getRepository().getConfig();
            config.setString("remote", "origin", "url", REPO_URL);
            config.setString("remote", "origin", "fetch", FETCH);
            config.setBoolean("core", null, "sparseCheckout", true);
            config.setBoolean("core", null, "sparseCheckout", true);
            config.save();

            Path file = cloneDir.resolve(".git").resolve("info").resolve("sparse-checkout");
            file.getParent().toFile().mkdirs();
            Files.write(file, directory.getBytes());
            FetchResult result = git.fetch().setRemote("origin").setRefSpecs(new RefSpec(FETCH)).setThin(false).call();
            git.checkout().setName("origin/main").call();
            File source = cloneDir.resolve(directory).toFile();
            File destination = new File(directory);
            FileUtils.copyDirectory(source, destination);
            FileUtils.deleteDirectory(cloneDir.toFile());
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

要将此源文件添加为 Quarkus CLI 插件:

quarkus plug add /path/to/CreateFromQuickstart.java
Added plugin:
    Name                 	 Type 	 Scope 	 Location                                     	 Description
 *  CreateFromQuickstart 	 java 	 user  	 /path/to/CreateFromQuickstart.java

请注意,名称是从实际文件/类名派生的,它使用 驼峰式命名法,因此不匹配 create 子命令。相反,它被添加为 create 的同级。

quarkus --help
Commands:
  create                  Create a new project.
    app                   Create a Quarkus application project.
    cli                   Create a Quarkus command-line project.
    extension             Create a Quarkus extension project
  # more commands here
  CreateFromQuickstart

截至 3.1.2.Final,没有直接的方法可以为插件设置别名。但是,JBang 支持别名。以下是如何使用别名:

jbang alias add --name quarkus-create-from-quickstart ~/path/to/CreateFromQuickstart.java
[jbang] Alias 'quarkus-create-from-quickstart' added to '/home/user/.jbang/jbang-catalog.json'
quarkus plug add create-from-quickstart
Added plugin:
    Name                   	 Type  	 Scope 	 Location                       	 Description
 *  create-from-quickstart 	 jbang 	 user  	 quarkus-create-from-quickstart
项目特定插件

在迄今为止的所有示例中,插件被列为 用户范围。这意味着插件对用户是全局的。但是,也可以有 项目范围 的插件。这很重要,因为它允许:

  • 拥有项目特定插件

  • 按项目覆盖版本

  • 共享插件目录(通过版本控制)

  • 支持扩展提供的插件

quarkus plug add 命令在项目内调用时,插件会被添加到项目目录中,除非使用了 --user 选项。插件目录保存在项目根目录的 .quarkus 中。通过将此文件夹添加到版本控制,项目插件目录会在项目用户之间共享。在这种情况下,最好还将实际的插件源文件包含在版本控制中,或者使用共享的 JBang 目录。

让我们创建一个脚本,允许用户在其 ArgoCD 开发实例中设置其项目。ArgoCDKubernetes 的 GitOps 持续交付工具。以下示例演示了其设置过程可以作为 Quarkus CLI 插件自动化。

更具体地说,该插件执行以下操作:

  • 安装 ArgoCD 二进制文件

  • ArgoCD 资源安装到目标集群

  • 生成项目的 Kubernetes 清单

  • 将生成的资源添加到版本控制

  • 将项目设置为 ArgoCD

即使上述某些步骤只需要执行一次(例如,将清单添加到版本控制),其余步骤也必须为每个开发人员环境执行。因此,与其将脚本永远添加到某个共享文件夹或存储库中而被遗忘,不如将其作为 CLI 插件放在项目内部是有意义的。脚本的来源可以是这样的:

#!/bin/bash
set -e
ARGOCD_VERSION="v2.7.4"

check_requirements() {
    if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
        echo "Error: The current folder is not under version control."
        exit 1
    fi

    if [ ! -f "target/kubernetes/kubernetes.yml" ]; then
        mvn quarkus:deploy -Dquarkus.kubernetes.deploy=false
        if [ ! -f "target/kubernetes/kubernetes.yml" ]; then
         echo "Error: The target/kubernetes/kubernetes.yml file does not exist."
         exit 1
        fi
    fi
}

install_argocd_binary() {
    OS="`uname`"
    case $OS in
        'Linux')
        OS='linux'
        ;;
        'Darwin')
        OS='darwin'
        ;;
        *) ;;
    esac

    if ! command -v argocd &> /dev/null
    then
        curl -sSL -o $HOME/bin/argocd https://github.com/argoproj/argo-cd/releases/download/${ARGOCD_VERSION}/argocd-${OS}-amd64
        chmod +x $HOME/bin/argocd
    fi
}

install_argocd_resources() {
    if ! kubectl get namespace | grep -q 'argocd'; then
        kubectl create namespace argocd
    fi
    if ! kubectl get pods -n argocd | grep -q 'argocd-server'; then
        kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/${ARGOCD_VERSION}/manifests/install.yaml

        kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=argocd-server -n argocd --timeout=120s
    fi
}

wait_for_port() {
    local PORT=$1
    local TIMEOUT=5
    local START_TIME=$SECONDS
    while :
    do
        if nc -v $1 &> /dev/null; then
            nc -z localhost $PORT  && return
        fi
        if (( SECONDS - START_TIME >= TIMEOUT )); then
            return
        fi
        sleep 1
    done
}

cleanup() {
    kill $PORT_FORWARD_PID
}

create_app() {
    NAMESPACE=`kubectl config view --minify --output 'jsonpath={..namespace}'`
    GIT_URL=`git remote get-url origin | sed -s "s/git@github.com:/https:\/\/github.com\//"`
    GIT_BRANCH=`git branch -l | grep "*" | awk '{print $2}'`
    APP_DIR=`git rev-parse --show-toplevel`
    APP_NAME=`git rev-parse --show-toplevel | xargs basename`
    ARGOCD_PASSWORD=`argocd admin initial-password argo -n argocd | head -n1`


    if [ -f "$APP_DIR/.argocd" ]; then
        mkdir $APP_DIR/.argocd
    fi
    cp target/kubernetes/kubernetes.yml $APP_DIR/.argocd/
    if [ -n "$(git status --porcelain | grep -v '?')" ]; then
        git add $APP_DIR/.argocd
        git commit -m "Add generated manifests to argocd" && git push origin $BRANCH
    fi
    kubectl port-forward svc/argocd-server -n argocd 9443:443 > /dev/null 2>&1 &
    PORT_FORWARD_PID=$!
    trap  "cleanup" EXIT SIGINT SIGTERM
    wait_for_port 9443
    argocd login localhost:9443 --username admin --password $ARGOCD_PASSWORD --insecure

    argocd app create $APP_NAME --repo $GIT_URL --path .argocd --dest-server https://kubernetes.default.svc --dest-namespace default
    argocd app sync $APP_NAME
}

check_requirements
install_argocd_binary
install_argocd_resources
create_app

让我们将文件保存在 bin/quarkus-argocd-setup 下,并将其添加为插件。

quarkus plug add bin/quarkus-argocds-setup

现在,通过调用 quarkus argocd-setup,应用程序已设置为与 ArgoCD 一起使用。

扩展提供的插件

Quarkus 扩展可以为项目提供的 CLI 插件做出贡献。目前,以下 Quarkiverse 扩展提供插件:

让我们看看当此类扩展添加到项目时,事情是如何工作的。以下命令添加了 Quarkus Helm 扩展,以及经常一起使用的 Kubernetes 和 docker 扩展。

quarkus ext add quarkus-helm quarkus-kubernetes quarkus-container-image-docker
[SUCCESS] ✅  Extension io.quarkiverse.helm:quarkus-helm:1.0.7 has been installed
[SUCCESS] ✅  Extension io.quarkus:quarkus-kubernetes has been installed
[SUCCESS] ✅  Extension io.quarkus:quarkus-container-image-docker has been installed

下次使用 CLI 时,`helm 插件应该会自动添加。

quarkus --help
Plugin catalog last updated on: 07/06/2023 10:29:05. Syncing!
Looking for the newly published extensions in registry.quarkus.io
Options:
# option details
Commads:
# commands
helm

现在可以使用该插件使用 Helm Charts 安装应用程序。该插件本身是对官方 Helm 二进制文件的简单包装,简化了其使用。例如,可以使用以下命令轻松安装应用程序:

quarkus helm install
总结插件

Quarkus CLI 插件系统不仅是 Quarkus 团队调整 Quarkus CLI 大小和模块化的方式,它还为团队提供了一种创建特定于其项目的脚本和配方,并与代码一起分发它们的方式。

另请参阅

如果您想了解更多关于新的 Quarkus CLI 功能的信息,请务必查看以下 Quarkus Insights 系列。它们演示了新功能,并有望激发您创建自己插件的想法。