如何配置 github action 自动构建发布到 maven central
- 完整生产案例参见:
1. parent pom.xml 配置,如
<profile>
<id>release</id>
<build>
<plugins>
<!-- Source -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Javadoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<additionalOptions>-Xdoclint:none</additionalOptions> <!-- 3.0.0+ -->
<!-- <additionalparam>-Xdoclint:none</additionalparam> -->
<!-- 2.0.0 -->
</configuration>
</execution>
</executions>
</plugin>
<!-- Usage: mvn versions:set -DnewVersion=latest -->
<!-- See: http://www.mojohaus.org/versions-maven-plugin/set-mojo.html -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<configuration>
<!-- <newVersion>latest</newVersion> -->
<allowSnapshots>true</allowSnapshots>
<generateBackupPoms>false</generateBackupPoms>
<processAllModules>true</processAllModules>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<autoVersionSubmodules>true</autoVersionSubmodules>
<!-- <preparationGoals>clean install</preparationGoals> -->
<!-- <developmentVersion>latest</developmentVersion> -->
</configuration>
</plugin>
<!-- GPG -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- https://help.sonatype.com/repomanager2/staging-releases/configuring-your-project-for-deployment -->
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<serverId>sonatype-nexus-staging</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
<stagingProgressTimeoutMinutes>15</stagingProgressTimeoutMinutes>
</configuration>
</plugin>
</plugins>
</build>
<distributionManagement>
<snapshotRepository>
<id>sonatype-nexus-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<!-- see:https://blogs.wl4g.com/archives/56 -->
<!-- Using github workflow auto publishing to Maven central. -->
<!-- see:https://docs.github.com/en/actions/publishing-packages/publishing-java-packages-with-maven#publishing-packages-to-the-maven-central-repository -->
<repository>
<id>sonatype-nexus-staging</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
</profile>
- 红色为核心部分
2. 准备 GPG key
# 生成密钥对
gpg2 --gen-key
# 查看密钥列表
gpg2 --list-keys
gpg2 --list-secret-keys
# 发送到中央密钥服务
gpg2 --keyserver keyserver.ubuntu.com --recv-keys CA925CD6C9E8D064FF05B4728190C4130ABA0F98
# 导出密钥到文件
gpg2 --export-secret-keys --armor CA925CD6C9E8D064FF05B4728190C4130ABA0F98 > you_name@gmail.com.gpg.key
3. 编写 github action(workflow)脚本 (自动导入 GPG key)
- 完整生产案例参见:
- $PROJECT/.github/workflow/run.yaml
name: Build and deploy
on:
workflow_call:
#inputs: {}
secrets: ## see:https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-of-onworkflow_callsecrets
PERSONAL_ACCESS_TOKEN:
description: 'A developer personal token from the caller workflow'
required: false
OSSRH_USERNAME:
description: 'A deploy to Maven central (OSSRH) username from the caller workflow'
required: false
OSSRH_TOKEN:
description: 'A deploy to Maven central (OSSRH) token(password) from the caller workflow'
required: false
MAVEN_GPG_PRIVATE_KEY:
description: 'A deploy to Maven central (OSSRH) GPG private key from the caller workflow'
required: false
MAVEN_GPG_PASSPHRASE:
description: 'A deploy to Maven central (OSSRH) GPG private key password from the caller workflow'
required: false
jobs:
build-deploy:
steps:
- name: Configure GPG Key
if: ${{ inputs.enable-build == true && inputs.enable-deploy == true }}
env:
MAVEN_GPG_PRIVATE_KEY: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
run: |
## Check for gpg keys.
if [[ -z "$MAVEN_GPG_PRIVATE_KEY" || -z "$MAVEN_GPG_PASSPHRASE" ]]; then
echo "ERROR: No MAVEN_GPG_PRIVATE_KEY or MAVEN_GPG_PASSPHRASE defined"; exit 1
fi
## Check for supported gpg version.
gpg_version=$(gpg --version | head -1 | grep -iEo '(([0-9]+)\.([0-9]+)\.([0-9]+))') # eg: 2.2.19
gpg_version_major=$(echo $gpg_version | awk -F '.' '{print $1}')
gpg_version_minor=$(echo $gpg_version | awk -F '.' '{print $2}')
gpg_version_revision=$(echo $gpg_version | awk -F '.' '{print $3}')
if [[ ! ("$gpg_version_major" -ge 2 && "$gpg_version_minor" -ge 1) ]]; then
echo "ERROR: The GPG version must >= $gpg_version_major.$gpg_version_minor.x"; exit 1
fi
rm -rf ~/.gnupg/; mkdir -p ~/.gnupg/private-keys-v1.d/; chmod -R 700 ~/.gnupg/
echo -n "$MAVEN_GPG_PRIVATE_KEY" > /tmp/private.key
#cat /tmp/private.key # for debugging
## FIXED:https://github.com/keybase/keybase-issues/issues/2798#issue-205008630
#export GPG_TTY=$(tty) # Notice: github action the VM instance no tty.
## FIXED:https://bbs.archlinux.org/viewtopic.php?pid=1691978#p1691978
## FIXED:https://github.com/nodejs/docker-node/issues/922
## Note that since Version 2.0 this passphrase is only used if the option --batch has also
## been given. Since Version 2.1 the --pinentry-mode also needs to be set to loopback.
## see:https://www.gnupg.org/documentation/manuals/gnupg/GPG-Esoteric-Options.html#index-allow_002dsecret_002dkey_002dimport
gpg2 -v --pinentry-mode loopback --batch --secret-keyring ~/.gnupg/secring.gpg --import /tmp/private.key
rm -rf /tmp/private.key
ls -al ~/.gnupg/
gpg2 --list-keys
gpg2 --list-secret-keys
## Notice: Test signing should be performed first to ensure that the gpg-agent service has been
## pre-started (gpg-agent --homedir /root/.gnupg --use-standard-socket --daemon), otherwise
## an error may be reported : 'gpg: signing failed: Inappropriate ioctl for device'
echo "Preparing testing the GPG signing ..."
echo "test" | gpg2 -v --pinentry-mode loopback --passphrase $MAVEN_GPG_PASSPHRASE --clear-sign
4. 配置密钥 (github secret variables)
- Project → Settings → Security → Secrets and Variables → Action → New Repository secret 注:不是右上角账号中心的 Settings
- 参见:https://github.com/{owner}/{repo}/settings/secrets/actions
- PERSONAL_ACCESS_TOKEN
- OSSRH_USERNAME
- OSSRH_TOKEN
- 明文密码 (原始静态密码)
- 或使用 mvn -encrypt-password 生成的密钥,参见:
- https://github.com/wl4g/rengine/tree/master/.github/settings-security.xml
- https://github.com/wl4g/rengine/tree/master/.github/mvn-settings.xml
-
- 在多个用户共享同一台构建机器的场景, 应该使用 master password 来加密 repository server password (非明文, 此处用),
官方文档: https://maven.apache.org/guides/mini/guide-encryption.html
- 在多个用户共享同一台构建机器的场景, 应该使用 master password 来加密 repository server password (非明文, 此处用),
-
- 其 master password 应加密存储在 ~/.m2/settings-security.xml, 由命令 mvn -encrypt-master-password 生成.
-
- 其 repository server password 应加密存储在 ~/.m2/settings.xml, 由命令 mvn -encrypt-password 生成.
- MAVEN_GPG_PRIVATE_KEY
- 来自 #2 导出的 GPG 私钥
- MAVEN_GPG_PASSPHRASE
- 来自 #2 生成 GPG 密钥时的明文密码
5. FAQ
-
5.1 在 github action workflow 中执行导入 GPG 错误1:
gpg: key D4862BB6D276F6BC: public key "you_name@gmail.com
" imported gpg: key D4862BB6D276F6BC/D4862BB6D276F6BC: error sending to agent: No such file or directory gpg: error building skey array: No such file or directory gpg: error reading '/home/runner/private.key': No such file or directory gpg: import from '/home/runner/private.key' failed: No such file or directory gpg: Total number processed: 0 gpg: imported: 1 gpg: secret keys read: 1 Error: Process completed with exit code 2. - 解决,参见:
gpg2 -v **--pinentry-mode loopback** --secret-keyring ~/.gnupg/secring.gpg --import /tmp/private.key
-
5.2 在 github action workflow 执行 mvn deploy -Prelease 错误2:gpg: cannot open '/dev/tty': No such device or address
**export GPG_TTY=$(tty)** gpg2 -v --pinentry-mode loopback --secret-keyring ~/.gnupg/secring.gpg --import /tmp/private.key
-
5.3 在 github action workflow 执行 mvn deploy -Prelease 错误2:gpg signing Inappropriate ioctl for device
- 解决,参见:
## 注意:需要先进行测试签名,确保gpg-agent服务已经开通预启动(gpg-agent --homedir /root/.gnupg --use-standard-socket --daemon) ## ,否则可能会报告错误:'gpg: signing failed: Inappropriate ioctl for device' echo "test" | gpg2 -v --pinentry-mode loopback --batch --passphrase $MAVEN_GPG_PASSPHRASE --clear-sign
6. 参考资料
- https://github.com/wl4g/rengine/tree/master/.github/workflows/run.yaml
- https://github.com/wl4g/infra/tree/master/.github/workflows/run.yaml
- https://github.com/settings/tokens
- https://github.com/microsoft/WSL/issues/4029
- https://github.com/systemd/mkosi/issues/351
- https://github.com/keybase/keybase-issues/issues/2798
- https://github.com/nodejs/docker-node/issues/922
- https://central.sonatype.org/publish/requirements/gpg/#distributing-your-public-key
- https://www.gnupg.org/documentation/manuals/gnupg/GPG-Esoteric-Options.html#index-allow_002dsecret_002dkey_002dimport
- https://bbs.archlinux.org/viewtopic.php?pid=1691978#p1691978
- https://blogs.wl4g.com/archives/56