Packaging And Releasing With JReleaser

本指南介绍了如何使用 JReleaser工具打包和发布 CLI 应用程序。

This guide covers packaging and releasing CLI applications using the JReleaser tool.

Prerequisites

include::{includes}/prerequisites.adoc[]* GitHub 帐户和 GitHub 个人访问令牌

Unresolved directive in jreleaser.adoc - include::{includes}/prerequisites.adoc[] * a GitHub account and a GitHub Personal Access token

Bootstrapping the project

首先,我们需要一个定义 CLI 应用程序的项目。我们建议使用 PicoCLI扩展。这可以通过使用以下命令来完成:

First, we need a project that defines a CLI application. We recommend using the PicoCLI extension. This can be done using the following command:

Unresolved directive in jreleaser.adoc - include::{includes}/devtools/create-cli.adoc[]

此命令将初始化项目中的文件结构和最低必需的文件集:

This command initializes the file structure and the minimum set of required files in the project:

.
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    └── main
        ├── docker
        │   ├── Dockerfile.jvm
        │   ├── Dockerfile.legacy-jar
        │   └── Dockerfile.native
        ├── java
        │   └── org
        │       └── acme
        │           └── GreetingCommand.java
        └── resources
            └── application.properties

它还将在 pom.xml 中配置 picocli 扩展:

It will also configure the picocli extension in the pom.xml:

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-picocli</artifactId>
    </dependency>

Preparing the project for GitHub releases

在继续之前该项目必须在一个 GitHub 仓库中托管。可以通过登录你的 GitHub 账户,创建一个新仓库,并将新创建的源添加到仓库来完成此任务。选择 main 分支作为默认分支以利用惯例,从而在 pom.xml 中做更少的配置。

The project must be hosted at a GitHub repository before we continue. This task can be completed by logging into your GitHub account, creating a new repository, and adding the newly created sources to said repository. Choose the main branch as default to take advantage of conventions and thus configure less in your pom.xml.

您还需要一个 GitHub 个人访问令牌才能将版本发布到您刚刚创建的存储库。请遵循 creating a personal access token 的官方说明。将新创建的令牌存储在安全的地方,以便将来参考。接下来,您可以选择将令牌配置为名为 JRELEASER_GITHUB_TOKEN 的环境变量,以便工具可以读取它。或者,您可以使用 .yml.toml.json.properties 文件将令牌存储在您选择的安全位置。默认位置是 ~/.jreleaser/config[format]。例如,使用 .yml 格式,此文件可能如下所示:

You also need a GitHub Personal Access token to be able to post a release to the repository you just created. Follow the official documentation for creating a personal access token. Store the newly created token at a safe place for future reference. Next, you have the choice of configuring the token as an environment variable named JRELEASER_GITHUB_TOKEN so that the tool can read it. Alternatively you may store the token at a secure location of your choosing, using a .yml, .toml, .json, or .properties file. The default location is ~/.jreleaser/config[format]. For example, using the .yml format this file could look like:

~/.jreleaser/config.yml
JRELEASER_GITHUB_TOKEN: <github-token-value>

好的。添加所有源并创建一个首次提交。你可以选择自己的提交消息惯例,但是如果你按照 Conventional Commits 规范进行操作,那么在使用 JReleaser 时会获得更好的效果。使用以下消息“build:添加初始源”进行首次提交。

Alright. Add all sources and create a first commit. You can choose your own conventions for commit messages however you can get more bang for your buck when using JReleaser if you follow the Conventional Commits specification. Make your first commit with the following message "build: Add initial sources".

Packaging as a Native Image distribution

Quarkus 已经知道如何使用 GraalVM Native Image 创建一个本机可执行文件。默认设置将遵循命名约定来创建一个可执行文件。但是,JReleaser 工具期望一个发行版,即一个打包为 Zip 或 Tar 文件的传统文件结构。文件结构必须遵循此布局:

Quarkus already knows how to create a native executable using GraalVM Native Image. The default setup will create a single executable file following a naming convention. However, the JReleaser tool expects a distribution that is, a conventional file structure packaged as a Zip or Tar file. The file structure must follow this layout:

.
├── LICENSE
├── README
└── bin
    └── executable

此结构允许你添加可执行文件所需的所有支持文件,例如配置文件、shell 补全脚本、手册页、许可证、自述文件等。

This structure lets you add all kinds of supporting files required by the executable, such as configuration files, shell completion scripts, man pages, license, readme, and more.

Creating the distribution

我们可以利用 maven-assembly-plugin 来创建此类发行版。我们还将使用 os-maven-plugin 来正确识别此可执行文件可以运行的平台,并将该平台添加到发行版的文件名中。

We can leverage the maven-assembly-plugin to create such a distribution. We’ll also make use of the os-maven-plugin to properly identify the platform on which this executable can run, adding said platform to the distribution’s filename.

首先,让我们将 os-maven-plugin 添加到 pom.xml 中。此插件用作 Maven 扩展,因此必须将其添加到文件的 <build> 部分:

First, let’s add the os-maven-plugin to the pom.xml. This plugin works as a Maven extension and as such must be added to the <build> section of the file:

  <build>
    <extensions>
      <extension>
        <groupId>kr.motd.maven</groupId>
        <artifactId>os-maven-plugin</artifactId>
        <version>1.7.1</version>
      </extension>
    </extensions>
    <!-- ... -->

接下来,Linux 和 macOS 平台上的本机可执行文件通常没有文件扩展名,但 Windows 可执行文件有,因此在重命名生成的可执行文件时我们需要处理此问题。我们还可以将生成的发布版本放置在其自己的目录中,以避免弄乱 target 目录。因此,让我们向 pom.xml 中现有的 <properties> 部分添加一些属性:

Next, native executables on Linux and macOS platforms typically do not have a file extension but Windows executables do, we need to take care of this when renaming the generated executable. We can also place the generated distributions into their own directory to avoid cluttering the target directory. Thus, let’s add a couple of properties to the existing <properties> section in the pom.xml:

<executable-suffix/>
<distribution.directory>${project.build.directory}/distributions</distribution.directory>

现在我们配置 maven-assembly-plugin 以创建一个包含可执行文件和执行其工作可能需要的任何支持文件的 Zip 和 Tar 文件。特别注意发行版的名称,这是我们利用 os-maven-plugin 检测到的平台属性的位置。此插件在自己的配置文件中配置,其中 single 目标绑定到 package 阶段。这样做的目的是为了避免在每次调用构建时都重建发行版,因为我们只需要在准备发布时才进行此操作。

Now we configure the maven-assembly-plugin to create a Zip and a Tar file containing the executable and any supporting files it may need to perform its job. Take special note on the name of the distribution, this is where we make use of the platform properties detected by the os-maven-plugin. This plugin is configured in its own profile with the single goal bound to the package phase. It’s done this way to avoid rebuilding the distribution every single time the build is invoked, as we only needed when we’re ready for a release.

    <profile>
      <id>dist</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
              <attach>false</attach>
              <appendAssemblyId>false</appendAssemblyId>
              <finalName>${project.artifactId}-${project.version}-${os.detected.classifier}</finalName>
              <outputDirectory>${distribution.directory}</outputDirectory>
              <workDirectory>${project.build.directory}/assembly/work</workDirectory>
              <descriptors>
                <descriptor>src/main/assembly/assembly.xml</descriptor>
              </descriptors>
            </configuration>
            <executions>
              <execution>
                <id>make-distribution</id>
                <phase>package</phase>
                <goals>
                  <goal>single</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
    <profile>
      <id>dist-windows</id>
      <activation>
        <os>
          <family>windows</family>
        </os>
      </activation>
      <properties>
        <executable-suffix>.exe</executable-suffix>
      </properties>
    </profile>

请注意,配置了两个配置文件。dist 配置文件配置了 assembly 插件,并且以这种方式配置,即必须通过将 -Pdist 作为命令标志传递来显式激活该配置。另一方面,当在 Windows 平台上运行构建时,dist-windows 配置文件会自动激活。第二个配置文件负责设置 executable-suffix 属性的值,这是装配描述符所必需的,如下所示:

Note that two profiles are configured. The dist profile configures the assembly plugin, and it’s configured in such a way that it must be activated explicitly by passing -Pdist as a command flag. On the other hand the dist-windows profile becomes active automatically when the build is run on a Windows platform. This second profile takes care of setting the value for the executable-suffix property which is required by the assembly descriptor, as shown next:

src/main/assembly/assembly.xml
<assembly
        xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
    <id>dist</id>
    <formats>
        <format>tar.gz</format>
        <format>zip</format>
        <format>dir</format>
    </formats>
    <files>
        <file>
            <source>${project.build.directory}/${project.artifactId}-${project.version}-runner${executable-suffix}</source>
            <outputDirectory>./bin</outputDirectory>
            <destName>${project.artifactId}${executable-suffix}</destName>
        </file>
    </files>
</assembly>

以下是在 macOS 上调用 ./mvnw -Pdist package 时由装配插件创建的文件:

These are the files created by the assembly plugin when invoking ./mvnw -Pdist package on macOS:

$ tree target/distributions/
target/distributions/
├── app-1.0.0-SNAPSHOT-osx-x86_64
│   └── app-1.0.0-SNAPSHOT-osx-x86_64
│       └── bin
│           └── app
├── app-1.0.0-SNAPSHOT-osx-x86_64.tar.gz
└── app-1.0.0-SNAPSHOT-osx-x86_64.zip

请随意更新装配描述符以包含其他文件,例如 LICENSE、自述文件或可执行文件使用者需要的任何其他文件。在此处进行另一次提交,提交消息为“build:配置发行版装配”。

Feel free to update the assembly descriptor to include additional files such as LICENSE, readme, or anything else needed by the consumers of the executable. Make another commit right here with "build: Configure distribution assembly".

我们已准备好进入下一阶段:配置版本。

We’re ready to go to the next phase: configuring the release.

Adding JReleaser

JReleaser 工具可以通过多种方式调用:作为 CLI 工具、作为 Docker 镜像或作为 Maven 插件。鉴于我们已经在使用 Maven,最后一个选项非常方便。让我们添加另一个包含发布配置的配置文件,因为我们再次不需要此行为一直处于活动状态,而只在准备发布版本时才需要:

The JReleaser tool can be invoked in many ways: as a CLI tool, as a Docker image, or as a Maven plugin. The last option is very convenient given that we are already working with Maven. Let’s add yet another profile that contains the release configuration as once again we don’t require this behavior to be active all the time only when we’re ready to post a release:

    <profile>
      <id>release</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.jreleaser</groupId>
            <artifactId>jreleaser-maven-plugin</artifactId>
            <version>1.6.0</version>
          </plugin>
        </plugins>
      </build>
    </profile>

此时我们可以调用一些目标,例如我们可以通过调用 ./mvnw -Prelease jreleaser:config 命令要求 JReleaser 打印其当前配置。该工具将输出它关于该项目知道的所有信息。我们还可以通过调用 ./mvnw -Prelease jreleaser:changelog 来生成变更日志。将把包含变更日志的文件放置在 target/jreleaser/release/CHANGELOG.md 中,此时应如下所示:

There are a few goals we can invoke at this point, we can for example ask JReleaser to print out its current configuration by invoking the ./mvnw -Prelease jreleaser:config command. The tool will output everything that it knows about the project. We can also generate the changelog by invoking ./mvnw -Prelease jreleaser:changelog. A file containing the changelog will be placed at target/jreleaser/release/CHANGELOG.md which at this point should look like this:

target/jreleaser/release/CHANGELOG.md
## Changelog

8ef3307 build: Configure distribution assembly
5215200 build: Add initial sources

不太令人兴奋,但是我们可以通过指示 JReleaser 按照我们自己的约定格式化变更日志来更改此内容。你可以手动指定模式来对提交进行分类,但是如果你选择遵循约定提交规范,我们可以指示 JReleaser 执行相同的操作。将以下内容添加到 JRelaser 插件配置部分:

Not very exciting, but we can change this by instructing JReleaser to format the changelog according to our own conventions. You can manually specify patterns to categorize commits however if you chose to follow Conventional Commits we can instruct JReleaser to do the same. Add the following to the JReleaser plugin configuration section:

            <configuration>
              <jreleaser>
                <release>
                  <github>
                    <changelog>
                      <formatted>ALWAYS</formatted>
                      <preset>conventional-commits</preset>
                    </changelog>
                  </github>
                </release>
              </jreleaser>
            </configuration>

再次运行以前的 Maven 命令并检查生成的变更日志,现在它应该如下所示:

Run the previous Maven command once again and inspect the generated changelog, it should now look like this:

target/jreleaser/release/CHANGELOG.md
## Changelog

## 🛠  Build
- 8ef3307 Configure distribution assembly (Andres Almiray)
- 5215200 Add initial sources (Andres Almiray)


## Contributors
We'd like to thank the following people for their contributions:
Andres Almiray

可能还有其他可以应用的格式选项,但目前这些选项就足够了。让我们立即进行另一项提交,将“build: Configure JReleaser plugin”作为提交消息。如果您愿意,您可以再次生成变更日志,并看到将此最新提交添加到文件中。

There are more formatting options you may apply but for now these will suffice. Let’s make yet another commit right now, with "build: Configure JReleaser plugin" as a commit message. If you want you can generate the changelog once again and see this latest commit added to the file.

Adding distributions to the release

我们已经达到了可以配置二进制分发的阶段。如果您运行 ./mvnw -Prelease jreleaser:config 命令,您会注意到没有提及我们在之前的步骤中配置的任何分发文件。这是因为该工具没有它们的隐式知识,我们必须告诉 JReleaser 我们想要发布哪些文件。这样,可以将创建分发与发布资产分离,因为您可能希望随意添加或删除文件。对于这个特定案例,我们将为 macOS 和 Windows 配置 Zip 文件,为 Linux 配置 Tar 文件。这些文件必须添加到 JReleaser 插件配置部分,如下所示:

We’ve reached the point where we can configure the binary distributions. If you run the ./mvnw -Prelease jreleaser:config command you’ll notice there’s no mention of any distribution files that we configured in previous steps. This is because the tool has no implicit knowledge of them, we must tell JReleaser which files we’d like to release. This decouples creation of distributions from release assets as you might like to add or remove files at your leisure. For this particular case we’ll configure Zip files for both macOS and Windows, and a Tar file for Linux. These files must be added to the JReleaser plugin configuration section, like so:

            <configuration>
              <jreleaser>
                <release>
                  <github>
                    <changelog>
                      <formatted>ALWAYS</formatted>
                      <preset>conventional-commits</preset>
                    </changelog>
                  </github>
                </release>
                <distributions>
                  <app>
                    <type>BINARY</type>
                    <artifacts>
                      <artifact>
                        <path>${distribution.directory}/{{distributionName}}-{{projectVersion}}-linux-x86_64.tar.gz</path>
                        <platform>linux-x86_64</platform>
                      </artifact>
                      <artifact>
                        <path>${distribution.directory}/{{distributionName}}-{{projectVersion}}-windows-x86_64.zip</path>
                        <platform>windows-x86_64</platform>
                      </artifact>
                      <artifact>
                        <path>${distribution.directory}/{{distributionName}}-{{projectVersion}}-osx-x86_64.zip</path>
                        <platform>osx-x86_64</platform>
                      </artifact>
                    </artifacts>
                  </app>
                </distributions>
              </jreleaser>
            </configuration>

我们可以看到一个名为 app 的分发(为了方便起见,与项目的 artifactId 相同),其中配置了 3 个工件。请注意,使用 Maven 属性和 Mustache 模板来定义路径。如果您愿意,可以使用显式值,或依靠属性对配置进行参数化。在构建验证期间,Maven 属性会急切解析,而在执行 JReleaser 插件目标期间,Mustache 模板会延时解析。每个工件必须定义一个 platform 属性,以便唯一标识它们。如果我们运行 ./mvnw -Prelease jreleaser:config ,我们将很快收到一个错误,因为现在存在已配置的分发,该插件期望项目提供更多元数据:

We can appreciate a distribution named app (same as the project’s artifactId for convenience) with 3 configured artifacts. Note the use of Maven properties and Mustache templates to define the paths. You may use explicit values if you want or rely on properties to parameterize the configuration. Maven properties resolve eagerly during build validation while Mustache templates resolve lazily during the execution of the JReleaser plugin goals. Each artifact must define a platform property that uniquely identifies them. If we run the ./mvnw -Prelease jreleaser:config we’ll quickly get an error as now that there’s a configured distribution the plugin expects more metadata to be provided by the project:

[ERROR] == JReleaser ==
[ERROR] project.copyright must not be blank
[ERROR] project.description must not be blank
[ERROR] project.website must not be blank
[ERROR] project.docsUrl must not be blank
[ERROR] project.license must not be blank
[ERROR] project.authors must not be blank

可以通过两种方式提供此元数据:作为 JReleaser 插件配置的一部分或使用标准 POM 元素。如果您选择前一个选项,则插件的配置可能如下所示:

This metadata can be provided in two ways: either as part of the JReleaser plugin’s configuration or using standard POM elements. If you choose the former option then the plugin’s configuration may look like this:

            <configuration>
              <jreleaser>
                <project>
                 <description>app - Sample Quarkus CLI application</description>
                 <links>
                   <homepage>[role="bare"]https://github.com/aalmiray/app</homepage>
                   <documentation>[role="bare"]https://github.com/aalmiray/app</documentation>
                 </links>
                 <license>APACHE-2.0</license>
                 <authors>Andres Almiray</authors>
                 <copyright>2021 Kordamp</copyright>
                </project>
                <!-- ... -->

如果您选择使用标准 POM 元素,则您的 pom.xml 至少必须包含这些条目,当然要将值调整为您自己的值:

If you choose to use standard POM elements then your pom.xml must contain these entries at the very least, of course adapting values to your own:

  <name>app</name>
  <description>app -- Sample Quarkus CLI application</description>
  <inceptionYear>2021</inceptionYear>
  <url>https://github.com/aalmiray/app</url>
  <developers>
    <developer>
      <id>aalmiray</id>
      <name>Andres Almiray</name>
    </developer>
  </developers>
  <licenses>
    <license>
      <name>Apache-2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>

然而,我们还没有脱离困境,再次调用 ./mvnw -Prelease jreleaser:config 仍然会导致另一个错误,这次失败与丢失的工件有关。这是因为我们还没有组装所有必需的工件,但是该插件期望它们很容易获得。在这里,您可以选择在其他节点上构建必需的工件,然后将它们复制到各自的预期位置——这是一项可以通过在多个节点上运行 GitHub Actions 工作流来执行的任务。或者,您可以指示 JReleaser 忽略某些工件,并只选择与您当前平台匹配的工件。之前我们展示了在 macOS 上创建分发时的分发样子,假设我们仍然在该平台上,我们拥有正确的工件。

Yet, we’re not still out of the woods as invoking the ./mvnw -Prelease jreleaser:config once more will still result in another error, this time the failure relates to missing artifacts. This is because we did not assemble all required artifacts, yet the plugin expects them to be readily available. Here you have the choice to build the required artifacts on other nodes then copy them to their expected locations — a task that can be performed running a GitHub Actions workflow on multiple nodes. Or you can instruct JReleaser to ignore some artifacts and select only those that match your current platform. Previously we showed how the distribution would look like when created on macOS, assuming we’re still on that platform we have the correct artifact.

此时,我们可以指示 JReleaser 仅选择与 macOS 匹配的工件,方法是使用附加标志调用 jreleaser:config 目标: ./mvnw -Prelease jreleaser:config -Djreleaser.select.current.platform 。这次该命令将成功并打印出模型。请注意,只有 macOS 工件的路径已被完全解析,而其他 2 个路径保持不变。

We can instruct JReleaser to select only artifacts that match macOS at this point by invoking the jreleaser:config goal with an additional flag: ./mvnw -Prelease jreleaser:config -Djreleaser.select.current.platform. This time the command will succeed and print out the model. Note that only the path for the macOS artifact has been fully resolved, leaving the other 2 paths untouched.

让我们在此处进行一次提交,以“build: Configure distribution artifacts”作为消息。我们可以通过调用不同的目标立即创建发布: ./mvnw -Prelease jreleaser:release -Djreleaser.select.current.platform 。这将在所选存储库中创建一个 Git 发布,其中包括为存储库添加标签、上传变更日志、所有分发工件及其作为发布资产的校验和。

Let’s make one more commit here with "build: Configure distribution artifacts" as message. We can create a release right now, by invoking a different goal: ./mvnw -Prelease jreleaser:release -Djreleaser.select.current.platform. This will create a Git release at the chosen repository, which includes tagging the repository, uploading the changelog, all distribution artifacts and their checksum as release assets.

但在我们这样做之前,让我们添加一个附加功能,让我们创建一个 Homebrew 配方,这将使用户更轻松地在 macOS 上使用二进制分发,好吗?

But before we do that let’s add one additional feature, let’s create a Homebrew formula that will make it easy for macOS users to consume the binary distribution, shall we?

Configuring Homebrew as a packager

Homebrew 是 macOS 用户用来安装和管理二进制文件的流行选择。本质上,Homebrew 包是一个 Ruby 文件(称为配方),在目标环境中执行以安装或升级特定二进制文件。JReleaser 可以从二进制分发(例如我们已经配置的那个)创建配方。

Homebrew is a popular choice among macOS users to install and manage binaries. Homebrew packages are at their core a Ruby file (known as a formula) that’s executed on the target environment to install or upgrade a particular binary. JReleaser can create formulae from binary distributions such as the one we already have configured.

为此,我们只需要在 JReleaser 插件配置中启用 Homebrew,如下所示:

For this to work we simply have to enable Homebrew in the JReleaser plugin configuration like so:

                <distributions>
                  <app>
                    <type>BINARY</type>
                    <brew>
                      <active>ALWAYS</active>
                    </brew>
                    <artifacts>
                      <artifact>
                        <path>${distribution.directory}/{{distributionName}}-{{projectVersion}}-linux-x86_64.tar.gz</path>
                        <platform>linux-x86_64</platform>
                      </artifact>
                      <artifact>
                        <path>${distribution.directory}/{{distributionName}}-{{projectVersion}}-windows-x86_64.zip</path>
                        <platform>windows-x86_64</platform>
                      </artifact>
                      <artifact>
                        <path>${distribution.directory}/{{distributionName}}-{{projectVersion}}-osx-x86_64.zip</path>
                        <platform>osx-x86_64</platform>
                      </artifact>
                    </artifacts>
                  </app>
                </distributions>

最后一点:发布非快照版本时,建议发布 Homebrew 配方,因此将项目的版本从 1.0.0-SNAPSHOT 变成 1.0.0.Alpha1 或直接变为 1.0.0 ,由您决定。进行最后一次提交,大功告成:以 feat: Add Homebrew packager configuration 作为提交消息。好了,我们终于准备好了,让我们发布吧!

One last thing: it’s a good practice to publish Homebrew formulae for non-snapshot releases thus change the project’s version from 1.0.0-SNAPSHOT to say 1.0.0.Alpha1 or go directly with 1.0.0 as you feel like doing. One last commit, and we’re done: say feat: Add Homebrew packager configuration as commit message. Alright, we’re finally ready, let’s post a release!

Creating a release

pom.xml 中添加配置的 whirlwind 之旅已经尘埃落定,但这只是为了让项目准备好进行首次发布;后续发布对配置的修改较少。我们可以使用 jreleaser:full-release 目标创建 Git 发布和 Homebrew 配方,但如果您仍然对事情的发展方式心存疑虑,则可以以干运行模式调用该目标,即让 JReleaser 执行所需的所有本地操作,而不会影响远程资源,如 Git 存储库。它看起来如下所示:

It’s been quite the whirlwind tour of adding configuration to the pom.xml but that’s just for getting the project ready for its first release; subsequent release require less tampering with configuration. We can create a git release and the Homebrew formula with the jreleaser:full-release goal but if you still have some doubts on how things may play out then you can invoke the goal in dry-run mode that is, let JReleaser perform all local operations as needed without affecting remote resources such as Git repositories. This is how it would look like:

# because we changed the project's version
./mvnw -Dnative,dist package
./mvnw -Prelease jreleaser:full-release -Djreleaser.select.current.platform -Djreleaser.dry.run=true

[INFO] --- jreleaser-maven-plugin:1.6.0:full-release (default-cli) @ app ---
[INFO] JReleaser 1.6.0
[INFO]   - basedir set to /tmp/app
[INFO]   - outputdir set to /tmp/app/target/jreleaser
[WARNING] Platform selection is in effect
[WARNING] Artifacts will be filtered by platform matching: [osx-x86_64]
[INFO] git-root-search set to false
[INFO] Loading variables from /Users/aalmiray/.jreleaser/config.toml
[INFO] Validating configuration
[INFO] Strict mode set to false
[INFO] Project version set to 1.0.0.Alpha1
[INFO] Release is not snapshot
[INFO] Timestamp is 2023-04-27T15:06:34.289907+02:00
[INFO] HEAD is at 73603ac
[INFO] Platform is osx-x86_64
[INFO] dry-run set to true
[INFO] Generating changelog
[INFO] Storing changelog: target/jreleaser/release/CHANGELOG.md
[INFO] Cataloging artifacts
[INFO]   [sbom] Cataloging is not enabled. Skipping
[INFO] Calculating checksums for distributions and files
[INFO]   [checksum] target/distributions/app-1.0.0.Alpha1-osx-x86_64.zip.sha256
[INFO] Signing distributions and files
[INFO]   [sign] Signing is not enabled. Skipping
[INFO] Deploying Maven artifacts
[INFO]   [maven] Deploying is not enabled. Skipping
[INFO] Uploading distributions and files
[INFO]   [upload] Uploading is not enabled. Skipping
[INFO] Releasing to https://github.com/aalmiray/app@main
[INFO]  - uploading app-1.0.0.Alpha1-osx-x86_64.zip
[INFO]  - uploading checksums_sha256.txt
[INFO] Preparing distributions
[INFO]   - Preparing app distribution
[INFO]     [brew] preparing app distribution
[INFO] Packaging distributions
[INFO]   - Packaging app distribution
[INFO]     [brew] packaging app distribution
[INFO] Publishing distributions
[INFO]   - Publishing app distribution
[INFO]     [brew] publishing app distribution
[INFO]     [brew] setting up repository aalmiray/homebrew-tap
[INFO] Announcing release
[INFO]   [announce] Announcing is not enabled. Skipping
[INFO] Writing output properties to target/jreleaser/output.properties
[INFO] JReleaser succeeded after 0.620 s

JReleaser 将为我们执行以下任务:

JReleaser will perform the following tasks for us:

  • Generate a changelog based on all commits from the last tag (if any) to the latest commit.

  • Calculate SHA256 (default) checksums for all input files.

  • Sign all files with GPG. In our case we did not configure this step thus it’s skipped.

  • Upload assets to JFrog Artifactory or AWS S3. We also skip this step as it’s not configured.

  • Create a Git release at the chosen repository, tagging it.

  • Upload all assets, including checksums.

  • Create a Homebrew formula, publishing to https://github.com/aalmiray/homebrew-tap.

当然没有远程存储库受到影响,因为我们可以赞赏“-Djreleaser.dry.run=true”属性的有效性。如果您有这方面的倾向,请检查“target/jreleaser/package/app/brew/Formula/app.rb”的内容,它定义了要发布的 Homebrew 公式。它看起来应该如下所示:

Of course no remote repository was affected as we can appreciate the -Djreleaser.dry.run=true property was in effect. If you’re so inclined inspect the contents of target/jreleaser/package/app/brew/Formula/app.rb which defines the Homebrew formula to be published. It should look something like this:

app.rb
# Generated with JReleaser 1.6.0 at 2023-04-27T15:06:34.289907+02:00
class App < Formula
  desc "app -- Sample Quarkus CLI application"
  homepage "pass:[https://github.com/aalmiray/app]"
  url "pass:[https://github.com/aalmiray/app/releases/download/v1.0.0.Alpha1/app-1.0.0.Alpha1-osx-x86_64.zip]"
  version "1.0.0.Alpha1"
  sha256 "85c9918b23e3ac4ef64d5dd02714e241231d3f1358afdba09d3fd0b9a889e131"
  license "Apache-2.0"


  def install
    libexec.install Dir["*"]
    bin.install_symlink "#{libexec}/bin/app" => "app"
  end

  test do
    output = shell_output("#{bin}/app --version")
    assert_match "1.0.0.Alpha1", output
  end
end

准备就绪后,通过从命令行中简单地删除“-Djreleaser.dry.run”标志,这次为真创建版本,然后浏览您的存储库,看看新创建的版本。

When ready, create a release for real this time by simply removing the -Djreleaser.dry.run flag from the command line, then browse to your repository and look at the freshly created release.

Further reading

Reference

作为参考,这里有“pom.xml”的完整内容:

As a reference, these are the full contents of the pom.xml:

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.acme</groupId>
  <artifactId>app</artifactId>
  <version>1.0.0.Alpha1</version>
  <name>app</name>
  <description>app -- Sample Quarkus CLI application</description>
  <inceptionYear>2021</inceptionYear>
  <url>https://github.com/aalmiray/app</url>
  <developers>
    <developer>
      <id>aalmiray</id>
      <name>Andres Almiray</name>
    </developer>
  </developers>
  <licenses>
    <license>
      <name>Apache-2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>
  <properties>
    <executable-suffix/>
    <distribution.directory>${project.build.directory}/distributions</distribution.directory>
    <compiler-plugin.version>3.13.0</compiler-plugin.version>
    <maven.compiler.parameters>true</maven.compiler.parameters>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
    <quarkus.platform.group-id>{quarkus-platform-groupid}</quarkus.platform.group-id>
    <quarkus.platform.version>{quarkus-version}</quarkus.platform.version>
    <surefire-plugin.version>3.0.0</surefire-plugin.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>${quarkus.platform.artifact-id}</artifactId>
        <version>${quarkus.platform.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-picocli</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-arc</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-junit5</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <extensions>
      <extension>
        <groupId>kr.motd.maven</groupId>
        <artifactId>os-maven-plugin</artifactId>
        <version>1.7.1</version>
      </extension>
    </extensions>
    <plugins>
      <plugin>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>quarkus-maven-plugin</artifactId>
        <version>${quarkus.platform.version}</version>
        <extensions>true</extensions>
        <executions>
          <execution>
            <goals>
              <goal>build</goal>
              <goal>generate-code</goal>
              <goal>generate-code-tests</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${compiler-plugin.version}</version>
        <configuration>
          <parameters>${maven.compiler.parameters}</parameters>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${surefire-plugin.version}</version>
        <configuration>
          <systemPropertyVariables>
            <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
            <maven.home>${maven.home}</maven.home>
          </systemPropertyVariables>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <profiles>
    <profile>
      <id>native</id>
      <activation>
        <property>
          <name>native</name>
        </property>
      </activation>
      <properties>
        <skipITs>false</skipITs>
        <quarkus.native.enabled>true</quarkus.native.enabled>
      </properties>
    </profile>
    <profile>
      <id>dist</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
              <attach>false</attach>
              <appendAssemblyId>false</appendAssemblyId>
              <finalName>${project.artifactId}-${project.version}-${os.detected.classifier}</finalName>
              <outputDirectory>${distribution.directory}</outputDirectory>
              <workDirectory>${project.build.directory}/assembly/work</workDirectory>
              <descriptors>
                <descriptor>src/main/assembly/assembly.xml</descriptor>
              </descriptors>
            </configuration>
            <executions>
              <execution>
                <id>make-distribution</id>
                <phase>package</phase>
                <goals>
                  <goal>single</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
    <profile>
      <id>dist-windows</id>
      <activation>
        <os>
          <family>windows</family>
        </os>
      </activation>
      <properties>
        <executable-suffix>.exe</executable-suffix>
      </properties>
    </profile>
    <profile>
      <id>release</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.jreleaser</groupId>
            <artifactId>jreleaser-maven-plugin</artifactId>
            <version>1.6.0</version>
            <configuration>
              <jreleaser>
                <!--project>
                 <description>app - Sample Quarkus CLI application</description>
                 <website>https://github.com/aalmiray/app</website>
                 <docsUrl>https://github.com/aalmiray/app</docsUrl>
                 <license>APACHE-2.0</license>
                 <authors>Andres Almiray</authors>
                 <copyright>2021 Kordamp</copyright>
                </project-->
                <release>
                  <github>
                    <changelog>
                      <formatted>ALWAYS</formatted>
                      <preset>conventional-commits</preset>
                    </changelog>
                  </github>
                </release>
                <distributions>
                  <app>
                    <type>BINARY</type>
                    <brew>
                      <active>ALWAYS</active>
                    </brew>
                    <artifacts>
                      <artifact>
                        <path>${distribution.directory}/{{distributionName}}-{{projectVersion}}-linux-x86_64.tar.gz</path>
                        <platform>linux-x86_64</platform>
                      </artifact>
                      <artifact>
                        <path>${distribution.directory}/{{distributionName}}-{{projectVersion}}-windows-x86_64.zip</path>
                        <platform>windows-x86_64</platform>
                      </artifact>
                      <artifact>
                        <path>${distribution.directory}/{{distributionName}}-{{projectVersion}}-osx-x86_64.zip</path>
                        <platform>osx-x86_64</platform>
                      </artifact>
                    </artifacts>
                  </app>
                </distributions>
              </jreleaser>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
</project>