加载中...

Github Actions 自动发布 JAR 到 Maven 中央仓库全流程指引


0x00 前言

手动发布的方法详见《发布 JAR 到 Maven 中央仓库全流程指引》。

但是手动发布效率太低,而且每次都要交互输入密码,因此很自然就想到利用 Github Actions 自动发布。

Github Actions 是对标 Gitlab Ci/CD 的流水线工具

0x10 改造流程

在手动发布基础上,需要改造的要点为:

  1. GPG 签名改为非交互模式输入密码
  2. 添加 Github Actions 脚本
  3. 设计合理的发版规约以触发流水线脚本

配置自动发布之前,首先确保手动发布流程能走通

0x20 GPG 签名改为非交互模式

因为 Github Actions 是完全用流水线脚本自动执行的,而手动发布方式中,唯一的交互点就是 GPG 签名时需要输入其密码,故需要改造为无交互方式才能满足自动化执行。

只需要修改 pom.xml 中 maven-gpg-plugin 插件的配置,在手动发布的配置基础上,添加 <configuration> 即可变为非交互模式:

<!-- GPG 签名 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-gpg-plugin</artifactId>
    <version>1.6</version>
    <executions>
        <execution>
            <id>sign-artifacts</id>
            <phase>verify</phase>
            <goals>
                <goal>sign</goal>
            </goals>
            <configuration>
                <!-- 必须配置,用于 gpg 非交互式密码输入 -->
                <gpgArguments>
                    <arg>--pinentry-mode</arg>
                    <arg>loopback</arg>
                </gpgArguments>
            </configuration>
        </execution>
    </executions>
</plugin>

但是建议新建一个 <profile> 专用于流水线非交互式发布,这样就不会影响原有的配置:

<project>
    ... ...
    <profiles>
        ... ...

        <!-- 专用于 Github Actions 命令行发布的 profile (主要是 GPG 签名插件需要配置非交互模式输入密码) -->
        <profile>
            <id>autoDeploy</id>

            <properties>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            </properties>

            <build>
                <plugins>
                    <!-- 编译 maven 工程 -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <version>3.10.0</version>
                        <configuration>
                            <source>${maven.compiler.source}</source>
                            <target>${maven.compiler.target}</target>
                            <encoding>${project.build.sourceEncoding}</encoding>
                        </configuration>
                    </plugin>

                    <!-- 生成源码 jar 包 -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-source-plugin</artifactId>
                        <version>3.2.1</version>
                        <executions>
                            <execution>
                                <id>attach-sources</id>
                                <goals>
                                    <goal>jar</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>

                    <!-- 生成 javadoc 包 -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-javadoc-plugin</artifactId>
                        <version>3.3.2</version>
                        <configuration>
                            <charset>${project.build.sourceEncoding}</charset>
                            <encoding>${project.build.sourceEncoding}</encoding>
                            <docencoding>${project.build.sourceEncoding}</docencoding>

                            <!-- 从 JDK8 开始, Javadoc中 添加了 doclint, 目的是旨在获得符合 W3C HTML 4.01 标准规范的 HTML 文档 -->
                            <!-- 简而言之 Javadoc 不允许出现 html 相关的元素, 若旧注释含有这些元素又不想修改, 只能关闭 doclint -->
                            <additionalOptions>
                                <additionalOption>-Xdoclint:none</additionalOption>
                            </additionalOptions>
                        </configuration>
                        <executions>
                            <execution>
                                <id>attach-javadocs</id>
                                <goals>
                                    <goal>jar</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>

                    <!-- GPG 签名 -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-gpg-plugin</artifactId>
                        <version>1.6</version>
                        <executions>
                            <execution>
                                <id>sign-artifacts</id>
                                <phase>verify</phase>
                                <goals>
                                    <goal>sign</goal>
                                </goals>
                                <configuration>
                                    <!-- 必须配置,用于 gpg 非交互式密码输入 -->
                                    <gpgArguments>
                                        <arg>--pinentry-mode</arg>
                                        <arg>loopback</arg>
                                    </gpgArguments>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>

                    <!-- 分发管理和认证:用于部署和发布到中央仓库 https://mvnrepository.com/ -->
                    <!-- 此插件对于发布 SNAPSHOT 版本时不会触发 -->
                    <!-- 其作用是自动在 https://s01.oss.sonatype.org/ 的 Staging Repositories 中 close 并 release,无需手动干预 -->
                    <plugin>
                        <groupId>org.sonatype.plugins</groupId>
                        <artifactId>nexus-staging-maven-plugin</artifactId>
                        <version>1.6.13</version>
                        <extensions>true</extensions>
                        <configuration>
                            <serverId>sonatype</serverId>   <!-- 要与 distributionManagement 定义的 id 一致 -->
                            <nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
                            <autoReleaseAfterClose>true</autoReleaseAfterClose>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

        ... ...
    </profiles>
    ... ...
</project>

完整的配置可参考开源工程 exp-libs-refactorpom.xml

0x30 添加 Github Actions 脚本

在 Maven 工程的根目录创建 Github Actions 脚本 .github/workflows/auto_deploy.yml ,其内容如下:

name: Automatically deploy to sonatype
on:
  # 手动触发构建
  workflow_dispatch:

  # (从版本分支)创建 release 的时候触发 (release 版)
  release:
    types: [published]

  # 把特性分支合并到 v* 版本分支时触发 (snapshot 版)
  pull_request:
    branches: [v*]

  # 推送特性分支时触发 (snapshot 版)
#  push:
#    branches: [feature-*]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      # 拉取仓库
      - name: Check out repo
        uses: actions/checkout@v2

      # 设置 Java 环境
      - name: Set up Java env
        uses: actions/setup-java@v1
        with:
          java-version: 1.8
          server-id: sonatype                 # 要与 distributionManagement 定义的 id 一致
          server-username: MAVEN_USERNAME
          server-password: MAVEN_CENTRAL_TOKEN
          gpg-passphrase: MAVEN_GPG_PASSPHRASE
          gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}

      # 发布 Jar 到 Maven 中央仓库
      - name: Publish to Sonatype Maven Central
        run: mvn clean deploy -P autoDeploy   # 指定使用 pom.xml 的 <profile>.<id>autoDeploy</id> 配置进行发布
        env:
          MAVEN_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
          MAVEN_CENTRAL_TOKEN: ${{ secrets.SONATYPE_PASSWORD }}
          MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE_KEY }}

此脚本需要修改的内容为:

  • 触发条件: 按需修改,主要依赖自行设计的发版规则
  • java-version: JDK 版本,根据实际工程修改
  • server-id: 要与 pom.xml 中 <distributionManagement> 定义的 <id> 一致
  • mvn clean deploy -P autoDeploy: -P 后面的参数与 pom.xml 中定义的非交互模式的 <profile>.<id> 一致

此脚本需要到仓库的 settings -> secrets -> actions -> New repository secret 中设置的变量有 4 个:

  • SONATYPE_USERNAME: 在 Sonatype JIRA 申请的帐号
  • SONATYPE_PASSWORD: 在 Sonatype JIRA 申请的密码
  • GPG_PASSPHRASE_KEY: 在生成 GPG 密钥对时所设置的保护密码
  • GPG_PRIVATE_KEY: 通过 GPG 命令生成密钥对时的私钥

查询 GPG 私钥的方法见:《GnuPG 环境安装与配置

完整的流水线脚本可参考开源工程 exp-libs-refactorauto_depoly.yml

0x40 设计合理的发版规约

但有了流水线自动化,还需要配合一套发版规约才能使其有效运转起来。

各个组织、各个人、各个项目的发版规约不尽相同,故此处不深入讨论这个问题,但是笔者的开源工程 exp-libs-refactor 设计了一套发版规约以配合上述的流水线脚本,或许能给大家参考一下。

0xFF 参考文档


文章作者: EXP
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 EXP !
  目录