【Maven教程】(十二):版本管理 ——版本号定义约定及相关概念,自动化版本发布与创建分支,GPG签名 ~

news2024/12/25 1:15:38

Maven · 版本管理

  • 1️⃣ 版本管理的概念
  • 2️⃣ Maven 的版本号定义约定
  • 3️⃣ 主干、标签与分支
  • 4️⃣ 自动化版本发布
  • 5️⃣ 自动化创建分支
  • 6️⃣ GPG签名
      • 6.1 GPG 及其基本使用
      • 6.2 Maven GPG Plugin
  • 🌾 总结

一个健康的项目通常有一个长期、合理的版本演变过程。例如JUnit有3.7、3.8、
3.8.1、3.8.2、4.0、4.1等版本。Maven本身的版本也比较多,如最早的Maven1; Maven2有2.0.9、2.0.10、2.1.0、2.2.0、2.2.1等各种版本;而最新的Maven3则拥有3.0-alpha-1、3.0-alpha-2、3.0-alpha-7、3.0-beta-1等版本。除了这些对外发布的版本之外,前面还介绍了Maven特有的快照版本的概念。这些版本中的每个数字代表了什么? alpha、beta是什么意思? 快照版和发布版的区别是什么? 我们应该如何科学地管理自己的项目版本? 本文将会详细解答这些问题。

阅读本文的时候还需要分清版本管理 (Version Management) 和版本控制 (Version Control) 的区别。版本管理是指项目整体版本的演变过程管理,如从 1.0-SNAPSHOT到1.0, 再到1.1-SNAPSHOT。版本控制是指借助版本控制工具(如 Subversion)追踪代码的每一个变更。本文重点讲述的是版本管理,但是读者将会看到,版本管理通常也会涉及一些版本控制系统的操作及概念。在阅读的时候应特别留意这两者的关系和区别。

在这里插入图片描述


1️⃣ 版本管理的概念

前面文章中谈到,为了方便团队的合作,在项目开发的过程中,大家都应该使用快照版本,Maven能够很智能地处理这种特殊的版本,解析项目各个模块最新的“快照”。快照版本机制促进团队内部的交流,但是当项目需要对外发布时,我们显然需要提供非常稳定的版本,使用该版本应当永远只能够定位到唯一的构件,而不是像快照版本那样,定位的构件随时可能发生变化。对应地,我们称这类稳定的版本为发布版。项目发布了一个版本之后,就进入下一个开发阶段,项目也就自然转换到新的快照版本中。

版本管理关心的问题之一就是这种快照版和发布版之间的转换。项目经过了一段时间的1.0-SNAPSHOT的开发之后,在某个时刻发布了1.0正式版,然后项目又进入了1.1-SNAPSHOT的开发,这个版本可能添加了一些有趣的特性,然后在某个时刻发布1.1正式版。项目接着进入1.2-SNAPSHOT的开发。由于快照对应了项目的开发过程,因此往往对应了很长的时间,而正式版本对应了项目的发布,因此仅仅代表某个时刻项目的状态,如图所示。

在这里插入图片描述
理想的发布版本应当对应了项目某个时刻比较稳定的状态,这包括源代码的状态以及
构建的状态,因此这个时候项目的构建应当满足以下的条件:

  • 所有自动化测试应当全部通过。毫无疑问,失败的测试代表了需要修复的问题,因此发布版本之前应该确保所有测试都能得以正确执行。
  • 项目没有配置任何快照版本的依赖。快照版本的依赖意味着不同时间的构建可能会
    引入不同内容的依赖,这显然不能保证多次构建能够生成同样的结果。
  • 项目没有配置任何快照版本的插件。 快照版本的插件配置可能会在不同时间引入不
    容内容的Maven 插件,从而影响 Maven 的行为,破坏构建的稳定性。
  • 项目所包含的代码已经全部提交到版本控制系统中。项目已经发布了,可源代码却 不在版本控制系统中,甚至丢失了。这意味着项目丢失了某个时刻的状态,因此这 种情况必须避免,版本发布的时候必须确保所有的源代码都已经提交了。

只有上述条件都满足之后,才可以将快照版本更新为发布版本,例如将1.0-SNAPSHOT 更新为1.0, 然后生成版本为1.0的项目构件。

不过这里还缺少一步关键的版本控制操作。如果你了解任何一种版本控制工具,如 Subversion, 那就应该能想到项目发布与标签 (Tag) 的关系。版本控制系统记录代码的每一个变化,通常这些变化都被维护在主干 (Trunk)中,但是当项目发布的时候,开发人员就应该使用标签记录这一特殊时刻项目的状态。以Subversion为例,日常的变更维护在主干中,包含各种源码版本r1 、r2 、… 、t284 、… 。 要找到某个时刻的项目状态会比较麻烦,而使用标签就可以明确地将某个源码版本(也就是项目状态)从主干中标记出来,放到单独的位置,这样在之后的任何时刻,我们都能够快速地得到发布版本的源代码,从而能够比较各个版本的差异,甚至重新构建一个同样版本的构件。

因此,将项目的快照版本更新至发布版本之后,应当再执行一次Maven 构建,以确保项目状态是健康的。然后将这一变更提交到版本控制系统的主干中。接着再为当前主干的 状态打上标签。以 Subversion 为例,这几个步骤对应的命令如下:

$mvn clean install
$svn commit pom.xml -m "prepare to release 1.0"
$svn copy -m "tag release 1.0"\
https://svn.xiaoshan.com/project/trunk\
https://svn.xiaoshan.com/project/tags/1.0

至此, 一个版本发布的过程完成了。接下来要做的就是更新发布版本至新的快照版本, 如从1.0到1.1-SNAPSHOT。


2️⃣ Maven 的版本号定义约定

到目前为止,读者应该已经清楚了解了快照版和发布版的区别。现在再深入看一下 1.0、1.1、1.2.1、3.0-beta 这样的版本号后面又遵循了怎样的约定。了解了这样的约定之后,就可以正确地为自己的产品或者项目定义版本号,而你的用户也能了解到隐藏在版本号中的信息。

看一个实际的例子,这里有一个版本:

1.3.4-beta-2

这往往表示了该项目或产品的第一个重大版本的第三个次要版本的第四次增量版本的beta-2里程碑。很拗口? 那一个个分开解释:“1”表示了该版本是第一个重大版本;“3” 表示这是基于重大版本的第三个次要版本;“4”表示该次要版本的第四个增量;最后的 “beta-2” 表示该增量的某一个里程碑。

也就是说,Maven 的版本号定义约定是这样的:

<主版本> . <次版本> . <增量版本> - <里程碑版本>

主版本和次版本之间,以及次版本和增量版本之间用点号分隔,里程碑版本之前用连字号分隔。下面解释其中每一个部分的意义:

  • 主版本: 表示了项目的重大架构变更。例如, Maven2 和 Maven1 相去甚远;Struts 1和Struts2 采用了不同的架构;JUnit 4 较 JUnit 3 增加了标注支持。
  • 次版本: 表示较大范围的功能增加和变化,及Bug 修复。例如 Nexus 1.5较 1 .4添加了 LDAP 的支持,并修复了很多Bug, 但从总体架构来说,没有什么变化。
  • 增量版本: 一般表示重大 Bug 的修复,例如项目发布了1.4.0版本之后,发现了一
    个影响功能的重大Bug, 则应该快速发布一个修复了Bug 的1.4.1版本。
  • 里程碑版本: 顾名思义,这往往指某一个版本的里程碑。例如,Maven3 已经发布了 很多里程碑版本,如3.0-alpha-1 、3.0-alpha-2 、3.0-beta-1 等。这样的版本与正式的 3.0相比,往往表示不是非常稳定,还需要很多测试。

需要注意的是,不是每个版本号都必须拥有这四个部分。 一般来说,主版本和次版本都会声明,但增量版本和里程碑就不一定了。例如,像3.8这样的版本没有增量和里程碑, 2.0-beta-1 没有增量。但我们不会看到有人省略次版本,简单地给出主版本显然是不够的。
当用户在声明依赖或插件未声明版本时, Maven 就会根据上述的版本号约定自动解析 最新版本。这个时候就需要对版本号进行排序。对于主版本、次版本和增量版本来说,比较是基于数字的,因此 1.5>1.4>1.3.11>1.3.9。而对于里程碑版本,Maven 则只进行 简单的字符串比较,因此会得到1.2-beta-3>1.2-beta-11 的结果。这一点需要留意。


3️⃣ 主干、标签与分支

使用版本控制工具时我们都会遇到主干 (trunk) 、 标签 (tag) 和 branch ( 分支) 的概念。前面文章已经涉及了主干与标签。这里再详细将这几个概念阐述一下,因为理解它们是 理解 Maven 版本管理的基础。

  • 主干:项目开发代码的主体,是从项目开始直到当前都处于活动的状态。从这里可以获得项目最新的源代码以及几乎所有的变更历史。
  • 分支:从主干的某个点分离出来的代码拷贝,通常可以在不影响主干的前提下在这里进行重大Bug 的修复,或者做一些实验性质的开发。如果分支达到了预期的目的, 通常发生在这里的变更会被合并 (merge) 到主干中。
  • 标签:用来标识主干或者分支的某个点的状态,以代表项目的某个稳定状态,这通常就是版本发布时的状态。

本文采用 Subversion 作为版本控制系统,如果对上述概念不清晰,请参考开放的《Subversion 与版本控制》(http://svnbook.red-bean.com/) 一书。
使用Maven 管理项目版本的时候,也涉及了很多的版本控制系统操作。下面就以一个实际的例子来介绍这些操作是如何执行的。

在这里插入图片描述

图下方最长的箭头表示项目的主干,项目最初的版本是1.0.0-SNAPSHOT, 经过
一段时间的开发后,1.0.0版本发布,这个时候就需要打一个标签,图中用一个长条表示。 然后项目进入1.1.0-SNAPSHOT 状态,大量的开发工作都完成在主干中,添加了 一些新特性并修复了很多Bug 之后,项目1.1.0发布,同样,这时候需要打另一个标签。

发布过后,项目进入1.2.0-SNAPSHOT 阶段,可这个时候用户报告1.1.0 版本有一个重大的Bug, 需要尽快修复,我们不能在主干中修Bug, 因为主干有太多的变化,无法在短时间内测试完毕并发布,我们也不能停止1.2.0-SNAPSHOT的开发, 因此这时候可以基于1.1.0 创建一个 1.1.1-SNAPSHOT的分支,在这里进行Bug修复,然后为用户发布一个1.1.1增量版本,同时打上标签。当然,还不能忘了把Bug修复涉及的变更合并到1.2.0-SNAPSHOT的主干中。主干在开发一段时间之后,发布1.2.0版本,然后进入到新版本1.3.0-SNAPSHOT的开发过程中。

一个典型的项目版本变化过程,涉及了快照版与发布版之间的切换、Maven版本号约定的应用,以及版本控制系统主干、标签和分支的使用。这其实也是一个不成文的行业标准,理解这个过程之后,不仅能够更方便地学习开源项目,也能对项目的版本管理更加标准和清晰。


4️⃣ 自动化版本发布

前几节已经详细介绍了版本发布时所需要完成的工作,读者如果愿意,则完全可以手动地执行这些操作,检查是否有未提交代码、是否有快照依赖、更新快照版至发布版、 执 行Maven 构建以及为源代码打标签等。事实上,如果对这一过程不是很熟悉,那么还是 应该一步步地操作一遍,以得到最直观的感受。

当熟悉了版本发布流程之后,就会希望借助工具将这一流程自动化。Maven Release Pugin 就提供了这样的功能,只要提供一些必要的信息,它就能帮我们完成上述所有版本发布所涉及的操作。下面介绍如何使用 Maven Release Plugin 发布项目版本。
Maven Release Plugin 主要有三个目标,它们分别为:

  • release:prepare 准备版本发布,依次执行下列操作:
    ■ 检查项目是否有未提交的代码。
    ■ 检查项目是否有快照版本依赖。
    ■ 根据用户的输入将快照版本升级为发布版。
    ■ 将POM 中的 SCM 信息更新为标签地址。
    ■ 基于修改后的 POM 执行 Maven 构建。
    ■ 提交 POM 变更。
    ■ 基于用户输入为代码打标签。
    ■ 将代码从发布版升级为新的快照版。
    ■ 提交POM 变更。
  • release:rollback 回退 release:prepare 所执行的操作。将 POM回退至 release:prepare 之前的状态,并提交。需要注意的是,该步骤不会删除 release:prepare 生成的标签, 因此用户需要手动删除。
  • release:perform 执行版本发布。签出 release:prepare 生成的标签中的源代码,并在此基础上执行 mvn deploy 命令打包并部署构件至仓库。
    要为项目发布版本,首先需要为其添加正确的版本控制系统信息,这是因为Maven Release Plugin 需要知道版本控制系统的主干、标签等地址信息后才能执行相关的操作。 一般 配置项目的 SCM 信息如代码所示:
<project>
	<scm>
		<connection>scm:svn:http://192.168.1.103/app/trunk</connection>
		<developerConnection>scm:svn:https://192.168.1.103/app/trunk</developerConnection>
		<url>http:// 192.168.1.103/account/trunk</url>
	</scm>...
</project>

代码中的 connection元素表示一个只读的 scm 地址,而developerConnection 元素表示可写的scm 地址 ,url 则表示可以在浏览器中访问的scm 地址。为了能让 Maven识别, connection和 developerConnection 必须以 scm 开头,冒号之后的部分表示版本控制工具类型(这里是 svn),Maven 还支持cvs、git 等。接下来才是实际的scm 地址,该例中的connection 使用了http 协议,而 developerConnection 则由于涉及写操作,使用https 协议进行了保护。

该配置只告诉 Maven当前代码的位置(主干),而版本发布还要涉及标签操作。因此,还需要配置 Maven Release Plugin 告诉其标签的基础目录,如代码所示:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-release-plugin</artifactId>
	<version>2.0</version>
	<configuration>
		<tagBase>https://192.168.1.103/app/tags/</tagBase>
	</configuration>
</plugin>

在执行 release:prepare 之前还有两个注意点:第一,系统必须要提供 svn 命令行工具, Maven 需要svn 命令行工具执行相关操作,而无法使用图形化的工具,如 TortoiseSVN; 第二 ,POM必须配置了可用的部署仓库,因为 release:perform 会执行 deploy 操作将构件发布到仓库中。关于如何配置部署仓库可参考前面的文章 。

一切就绪之后,在项目根目录下运行如下命令:

$mvn release:prepare

Maven Release Plugin 开始准备发布版本,如果它检测到项目有未提交的代码,或者项 目有快照版的依赖,则会提示出错。如果一切都没问题,则会提示用户输入想要发布的版本号、标签的名称以及新的快照版本号。例如:

What is the release version for "App"?(com.xiaoshan.mvnbook:app)1.0.0::
What is scm release tag or label for "App"?(com.xiaoshan.mvnbook:app)app-1.0.0:: 
What is the new development version for"App"?(com.xiaoshan.mvnbook:app)1.0.1- SNAPSHOT::1.1.0-SNAPSHOT

如果项目的 arifactld 为 app, 发布前的版本为1.0.0-SNAPSHOT, 则 Maven Release Plugìn 会提示使用发布版本号1.0.0, 使用标签名称 app-1.0.0, 新的开发版本为1.0.1-SNAPSHOT。如果这些模式值正是你想要的,直接按 Enter 键即可,否则就输入想要的值再按 Enter 键,如上例中为新的开发版本输入了值1.1.0-SNAPSHOT。

基于这些信息,Maven Release Plugin会将版本从1.0.0-SNAPSHOT更新为1.0.0,并更新 SCM地址 http://192.168.1.103/app/trunk 至 http://192.168.1.103/app/tags/app-1.0.0 。 在此 基础上运行一次 Maven 构建以防止意外的错误出现,然后将这两个变化提交,并为该版本打上标签,标签地址是 http://192.168.1.103/app/tags/app-1.0.0 。即 tagBase 路径加上标签名称。之后,Maven Release Plugin 会将 POM中的版本信息从1.0.0升级到1.1.0-SNAPSHOT 并提交。

至此 ,release:prepare 的工作完成。如果这时你发现了一些问题,例如将标签名称配置错了,则可以使用 release:rollback 命令回退发布, Maven Release Plugin 会将 POM 的配置回 退到 release:prepare 之前的状态。但需要注意的是,版本控制系统中的标签并不会被删除, 也就是说,用户需要手动执行版本控制系统命令删除该标签。

在多模块项目中执行 release:prepare 的时候,。默认maven-release-plugin 会提示用户每个模块发布版本号及新的开发版本号。例如,如果在 account-parent 模块中配置正确的 scm信息之后进行项目发布,就会看到如下的输出:

What is the release version for "Account Parent"?(com.xiaoshan.mvnbook.account: account-parent)1.0.0::
What is the release version for "Account Persist"?(com.xiaoshan.mvnbook.account: account-persist)1.0.0::
What is the release version for "Account Captcha"?(com.xiaoshan.mvnbook.account: account-captcha)1.0.0::
What is the release version for "Account Service"?(com.xiaoshan.mvnbook.account: account-service)1.0.0::
What is the release version for "Account Web"?
(com.xiaoshan.mvnbook.account: account-web)1.0.0::
What is ScM release tag or label for "Account Parent"?(com.xiaoshan.mvnbook.account: account-parent)account-parent-1.0.0::
What is the new development version for "Account Parent"?(cam.xiaoshan.mvnbook.account: account-parent)1.0.1-SNAPSHOT::
What is the new  development version  for  "Account  Email"?(com.xiaoshan.mvnbook.account: account-email)1.0.1-SNAPSHOT::
What is the new development version for "Account Persist"?(com.xiaoshan.mvnbook.account: account-persist)1.0.1-SNAPSHOT::
What is the new development version for "Account Captcha"?(com.xiaoshan.mmbook.account: account-captcha)1.0.1-SNAPSHOT::
What is the new development version for "Account Service"?(com.xiaoshan.mvnbook.account: account-service)1.0.1-SNAPSHOT::
What is the new development version for "Account Web"?
(com.xiaoshan.mvnbook.account account-web)1.0.1-SNAPSHOT::

在很多情况下,我们会希望所有模块的发布版本以及新的 SNAPSHOT 开发版本都保持 一致。为了避免重复确认,maven-release-plugin 提供了autoVersionSubmodules 参数。例如运行下面的命令后,maven-release-plugin 就会自动为所有子模块使用与父模块一致的发布版本和新的SNAPSHOT 版本:

$mvn release:prepare DautoVersionSubmodules=true

如果检查下来release:prepare 的结果没有问题,标签和新的开发版本都是正确的,可以执行如下发布执行命令:

$mvn release:perform

该命令将标签中的代码签出,执行 mvn deploy 命令构建刚才准备的1.0.0版本,并部署到仓库中。至此,版本1.0.0正式发布完成。由于它已经被部署到了 Maven 仓库中,其 他人可以方便地配置对它的依赖。
细心的读者可能会发现,如果你所发布项目的打包类型为 jar, 在执行 release:perform 之后,不仅项目的主构件会被生成并发布到仓库中,基于该主构件的-sources.jar 和-javadoc.jar也会生成并发布。对于你的用户来说,这无疑是非常方便的,他们不仅能够下载你的主构件,还能够得到项目的源码和 Javadoc。那么 ,release:perform 是怎样生成 -sources.jar和-javadoc.jar 的呢?

前面介绍过,所有Maven项目的 POM 都继承自超级POM, 而如果打开超级POM, 就能发现如代码所示内容:

<profiles>
	<profile>
		<id>release-profile</id>
		<activation>
			<property>
				<name>performRelease</name>
				<value>true</value>
			</property>
		</activation>
		<build>
			<plugins>
				<plugin>
					<inherited>true</inherited>
					<artifactId>maven-source-plugin</artifactId>
					<executions>
						<execution>
							<id>attach-sources</id>
							<goals>
								<goal>jar</goal>
							</goals>
						</execution>
					<executions>
				</plugin>
				<plugin>
					<inherited>true</inherited>
					<artifactId>maven-javadoc-plugin</artifactId>
					<executions>
						<execution>
							<id>attach-javadocs</id>
							<goals>
								<goal>jar</goal>
							</goals>
						</execution>
					</executions>
				</plugin>
				<plugin>
					<inherited>true</inherited>
					<artifactId>maven-deploy-plugin</artifactId>
					<configuration>
						<updateReleaseInfo>true</updateReleaseInfo>
					</configuration>
				</plugin>
			</plugins>
		</build>
	</profile>
</profiles>

超级POM中定义了一个名为release-profile 的 Maven Profile,Prolile 是指一段在特定情况 下被激活并更改 Maven行为的配置,本书后续会有专门的章节详细阐述。这里看到 activate 元素下有一个名为 performRelease、值为 true 的属性配置,这表示当 Maven 运行时,如果运行环境中有 performRelease 属性且值为true 的时候,该Profile 就被激活。也就是说,该 Profile 下的配置会得到应用。那么, 什么情况下Maven运行环境中会有名为 performRelease、值为 true 的属性呢? 可以在命令行指定。例如:

$mvn clean install -DperformRelease=true

但是,大家可能已经猜到了,在执行 release:perform 的 时 候 ,Maven Release Plugin 会自动生成值为 true 的 performRelease 属性。这时,超级 POM 中的 release-profile 就会被激活。

这个 Profile配置了3 个Maven 插件 ,maven-sources-plugin 的 jar目标会为项目生成 -source.jar 文件, maven-javadoc-plugin 的 jar 目标会为项目生成 -javadoc.jar 文件,而 maven- deploy-plugin 的 update-release-info 配置则会在部署的时候更新仓库中的元数据,告诉仓库该版本是最新的发布版。每个插件配置中值为 true 的 inherited 元素则表示该插件配置可以被子POM 继承。

在日常的快照开发过程中,往往没有必要每次都生成-source.jar 和-javadoc.jar, 但是当项 发布的时候,这些文件就显得十分重要。超级 POM 中的 release-profile 就是为了这种情形而设计的。需要注意的是,这种隐式的配置对于不熟悉 Maven 的用户来说可能会显得十分令人费解,因此将来的 Maven 版本中可能会从超级POM中移除这段配置,所以如果用户希望在发布版本时自动生成 -sources.jar 和 -javadoc.jar,最好还是在自己的POM 中显式地配置这些插件。

5️⃣ 自动化创建分支

上面介绍了如何使用Maven Release Plugin 自动化版本发布,而分支创建的操作还没有具体涉及。本节就继续基于实际的样例讲解如何自动化创建分支。

在第二节的图中可以看到,在正式发布版本1.1.0的同时,还可以创建一个分支用来修复 将来这个版本可能遇到的重大 Bug。 这个过程可以手工完成,例如使用svn copy 操作将主干代码复制到一个名为1.1.x 的分支中,然后修改分支中的POM文件,升级其版本为1.1.1-SNAPSHOT, 这会涉及很多Subversion 操作。

使用 Maven Release Plugin 的 branch 目标,它能够帮我们自动化这些操作:

  • 检查本地有无未提交的代码。
  • 为分支更改 POM的版本,例如从1.1.0-SNAPSHOT改变成1.1.1-SNAPSHOT。
  • 将POM中的 SCM 信息更新为分支地址。
  • 提交以上更改。
  • 将主干的代码复制到分支中。
  • 修改本地代码使其回退到分之前的版本(用户可以指定新的版本)。
  • 提交本地更改。

当然,为了让Maven Release Plugin 为我们工作,和版本发布一样,必须在 POM 中提 供正确的SCM 信息。此外,由于分支操作会涉及版本控制系统里的分支地址,因此还要为 Maven Release Plugin 配置分支基础目录,如代码:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-release-plugin</artifactId>
	<version>2.0</version>
	<configuration>
		<tagBase>https://192.168.1.103/app/tags/</tagBase>
		<branchBage>https://192.168.1.103/app/branches/</branchBase>
	</configuration>
</plugin>

然而 tagBase 和 branchBase 并非是一定要配置的。如果为版本控制仓库使用了标准的 Subversion 布局,即在平行的 trunk/tags/branches 目录下分别放置项目主干代码、标签代码 和分支代码,那么 Maven Release Plugin 就能够自动根据主干代码位置计算出标签及分支代码位置,因此你就可以省略这两项配置。

理解了创建分支所将执行的实际行为后,就可以在项目目录下运行如下命令以创建分支:

$mvn release:branch -DbranchName=1.1.x -DupdateBranchVersions=true -DupdateworkingCopyVersions=false

上述命令中使用了Maven Release Plugin 的 branch 目标, -DbranchName=1.1.x 用来配置所要创建的分支的名称, -DupdateBranchVersions=true 表示为分支使用新的版本, -DupdateWorkingCopyVersions =false 表示不更新本地代码(即主干)的版本。运行上述命令之后, Maven 会提示输入分支项目的版本。例如:

What is the branch version for "app"?(com.xiaoshan.mvnbook;app)1.1.1-SNAPSHOT::

用户根据自己的需要为分支输人新的版本后按 Enter 键 ,Maven 就会处理其余的操作。
最后,用户就能在源码库中找到 Maven 创建的分支,如 https://192.168.1.103/app/branches/1.1.x/ 。 在这里 ,POM 中的版本已经升级到了1.1.1-SNAPSHOT。

6️⃣ GPG签名

当从中央仓库下载第三方构件的时候,你可能会想要验证这些文件的合法性,例如它们是由开源项目官方发布的,并且没有被篡改过。同样地,当发布自己项目给客户使用的 时候,你的客户也会想要验证这些文件是否是由你的项目组发布的,且没有被恶意纂改过。
PGP(Pretty Good Privacy) 就是这样一个用来帮助提高安全性的技术。PGP 最常用来给电子 邮件进行加密、解密以及提供签名,以提高电子邮件交流的安全性。本节介绍如何使用 PGP技术为发布的 Maven构件签名,为项目增强安全性。

6.1 GPG 及其基本使用

GnuPG (简称 GPG, 来 自http://www.gnupg.org/) 是 PGP 标准的一个免费实现,无论
是类 UNIX平台还是Windows平台,都可以使用它。GPG 能够帮助我们为文件生成签名、管理密钥以及验证签名等。

首先,访问 http://www.gnupg.org/download/ 并下载对应自己平台的 GPG 分发包,按照官方的文档将 GPG 安装完毕,运行如下命令检查安装:

juveng@juven-ubuntu:~ $gpg --version
gpg(GnuPG) 1.4.9Copyright(C)2008 Free Software Foundation,Inc.
License GPLv3+:GNU GPL version 3 or later

在使用GPG 之前,先得为自己准备一个密钥对,即一个私钥和一个公钥。之后才可以使用私钥对文件进行签名,并且将公钥分发到公钥服务器供其他用户下载,用户可以使用 公钥对签名进行验证。
使用如下命令生成密钥对:

juven@juven-ubuntu:~$gpg --gen-key

GPG 会问你密钥的类型、大小和有效时间,通常使用默认的值即可。GPG 还会要求你 输入自己的名称、电子邮件地址和对密钥的注释,这些内容会被包含在公钥中并被你的用户看到,因此务必正确填写。最后,还可以提供一个密码来保护密钥,这不是强制性的, 但通常最好提供以防止别人得到你的密钥后恶意使用。你将来需要使用私钥和密码为文件 提供签名,因此一定要认证保护它们。

现在已经有了密钥对,就可以在命令行中查看它们(其他导入到本地机器的密钥也会被显示), 如下面的命令可用来列出所有公钥:

juven@juven-ubuntu: ~$gpg --list-keys
/home/juven/.gnupg/pubring.gpg
------------------------------
pub 1024D/C6EED57A 2010-01-13
uid Juven  Xu(Juven Xu works at Sonatype)juven@sonatype.com
sub 2048g/D704745C 2010-01-13

这里的/home/juven/.gnupg/pubring.gpg表示公钥存储的位置。以 pub 开头的一行显示 公钥的长度(1024D) 、ID(C6EED57A) 以及创建日期(2010-01-13)。下一行显示了公钥 的UID, 也就是一个由名称、注释和邮件地址组成的字符串。最后一行显示的子钥不用关心。

类似地,下面的命令用来列出本机私钥:

juven@juven-ubuntu: ~$gpg --list -secret-keys
/home/juven/.gnupg/secring.gpg
sec 1024D/C6EED57A 2010-01-13
uid       Juven Xu(Juven Xu works at Sonatype)
ssb 2048g/D704745C 2010-01-13

对 GPG 的公私钥有了基本的了解之后,就可以使用如下命令为任意文件创建一个 ASCII格式的签名:

juven@juven-ubuntu: ~$gpg -ab temp.java

这里的 -a 选项告诉GPG 创建ASCIⅡ 格式的输出,而-b 选项则告诉GPG 创建一个独立的签名文件。如果你的私钥拥有密码,这个时候就需要输入密码。如果私钥没有密码, 那么只要他人获得了你的私钥,就能够以你的名义对任何内容进行签名,这是非常危险的。

在该例中, GPG 会创建一个名为 temp.java.asc 的签名文件,这时就可以将这个后缀名 为 .ase 的签名文件连同原始文件一起分发给你的用户。如果你的用户已经导入了你的公 钥,就可以运行如下命令验证原始文件:

$gpg --verify temp.java.asc

为了能让你的用户获取公钥并验证你分发的文件,需要将公钥分发到公钥服务器中。 例 如 ,hkp://pgp.mit.edu 是美国麻省理工学院提供的公钥服务器,运行如下命令可将公钥 分发到该服务器中:

$gpg --keyserver hkp://pgp.mit.edu --send-keys C6EED57A

这里的–keyserver 选项用来指定分发服务器的地址, --send-keys 用来指定想要分发公钥的 ID 。你可以罗列本地公钥来查看它们的ID 。需要注意的是,公钥会在各个公钥服务器中被同步,因此你不需要重复地往各个服务器分发同一公钥。
现在,你的用户可以将服务器上的公钥导入到本地机器:

$gpg --keyserver hkp://pgp.mit.edu --recv-keys C6EED57A

上述就是一个基本的签名、分发并验证的流程,在使用Maven 发布项目的时候,可以 使 用GPG 为发布文件提供签名。现在读者应该已经知道如何手工完成这一步骤了,下面介 绍如何使用 Maven GPG Plugin 自动化签名这一步骤。


6.2 Maven GPG Plugin

手动地对 Maven构件进行签名并将这些签名部署到 Maven 仓库中是一件耗时的体力活。 而使用Maven GPG Plugin 只需要提供几行简单的配置,它就能够帮我们自动完成签名这一工作。
在使用 Maven GPG Plugin 之前,首先需要确认命令行下的 gpg 是可用的,然后如代码 所示配置 POM。

<project>
	<build>
		<plugins>
			<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-gpg-plugin</artifactId>
			<version>1.0</version>
				<executions>
					<execution>
						<id>sign-artifacts</id>
						<phase>verify</phase>
						<goals>
							<goal>sign</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

然后就可以使用一般的 mvn命令签名并发布项目构件:

$mvn  clean  deploy  -Dgpg.passphrase=yourpasephrase

如果不提供-Dgpg.passphrase 参数,运行时就会要求输入密码。
如果有一些已经发布了但没有被签名的文件,你仍然想对其签名并发布到 Maven 仓库 中,上述方式显然是行不通的,因为POM已经不允许被修改。好在 Maven GPG Plugin 为此 提供了另外一个目标。例如:

$mvn gpg:sign-and-deploy-file
> -DpomFile=target/myapp-1.0.pom
> -Dfile=target/myapp-1.0.jar
> -Durl=http://oss.sonatype.org/service/local/staging/deploy/maven2/
> -DrepositoryId=sonatype_oss

在这里可以指定要签名的POM 及相关文件、Maven 仓库的地址和 ID, Maven GPG Plugin 就会帮你签名文件并部署到仓库中。

读者可以想到, GPG 签名这一步骤只有在项目发布时才显得必要,对日常的 SNAPSHOT构件进行签名不仅没有多大的意义,反而会比较耗时。因此,只需要配置 Maven PGP Plugin 在项目发布的时候运行,那么如何判断项目发布呢? 回顾代码, 在超级 POM中有一个 release-profile, 该 Profile 只有在 Maven 属性 performRelease 为 true 的时候才被激活,而 release:perform执行的时候,就会将该属性置为 true, 这正是项目进行版本发布的时刻。因此,类似地,可以在 settings.xml 或者 POM 中创建如代码所示 Profile:

<profiles>
	<profile>
		<id>release-sign-artifacts</id>
		<activation>
			<property>
				<name>performRelease</name>
				<value>true</value>
			</property>
		</activation>
		<build>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-gpg-plugin</artifactId>
					<version>1.0</version>
					<executions>
						<execution>
							<id>sign-artifacts</id>
							<phase>verify</phase>
							<goals>
								<goal></sign</goal>
							</goals>
						</execution>
					</executions>
				</plugin>
			</plugins>
		</build>
	</profile>
</profiles>

最后需要一提的是,由于一个已知的Maven Release Plugin 的Bug, release:perform 执行过程中签名可能会导致进程永久挂起。为了避免该情况,用户需要为 Maven Release Plugin 提供mavenExecutorld 配置,如代码所示:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-release-plugin</artifactId>
	<version>2.0</version>
	<configuration>
		<tagBase>https://192.168.1.103/app/tags/</tagBase>
		<branchBase>https://192.168.1.103/app/branches/</branchBase>
		<mavenExecutorId>forked-path</mavenExecutorId>
	</configuration>
</plugin>

至此, 一个较为规范的自动化签名配置就完成了。当执行 release:perform 发布项目版 本的时候,maven-gpg-plugin 会被自动调用对构件进行签名。当然,这个时候你需要根据命令行提示输入私钥密码。

🌾 总结

项目开发到一定阶段后,就必然要面对版本发布的问题,本文介绍了Maven 的版本管理方式,包括快照版和发布版之间的转换、各种版本号的意义以及项目版本与版本控制系统 ( 如 Subversion) 之间的关系。理解了版本转换与SCM 操作的关系后,就可以使用Maven Release Plugin 自动化版本发布和创建分支等操作。最后介绍了如何在版本发布的时候使用GPG 为构件提供签名,以提供更强的安全性。


温习回顾上一篇(点击跳转)
《【Maven教程】(十一):使用 Maven 构建 Web应用 —— 使用 jetty-maven-plugin 进行测试、使用 Cargo 实现自动化部署~》

继续阅读下一篇(点击跳转)
《》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1302209.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue H5项目,怎么引入uni.webview sdk,调用uni postMessage实现手机蓝牙连接打印功能(uniapp)

前言 目前公司Vue H5项目&#xff0c;用webview打包成APP&#xff0c;现产品提出这样打包出来的app运行较慢&#xff0c;需要用uniapp方式&#xff08;即使用HBuilder编辑器来打包H5&#xff09;来打包&#xff0c;那需要的基座就不是安卓的基座而是uniapp的基座&#xff0c;而…

如何通过内网穿透工具实现任意浏览器远程访问Linux本地zabbix web管理界面

前言 Zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff1b;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 本地zabbix web管理界面限制在只能局域…

GD32F30X-RT-Thread学习-线程管理

1. 软硬件平台 GD32F307E-START Board开发板MDK-ARM Keil 2.RT-Thread Nano 3.RT-Thread 内核学习-线程管理 ​ 在多线程操作系统中&#xff0c;可以把一个复杂的应用分解成多个小的、可调度的、序列化的程序单元&#xff0c;当合理地划分任务并正确地执行时&#xff0c;这…

我的acer电脑U盘装系统前BIOS设置及装系统过程中的操作

1、开机长按F2进入BIOS设置 2、使能F12 3、调整boot顺序&#xff0c;使USB启动的优先级最高 4、按F10保存退出 5、插入U盘开机&#xff0c;boot选择界面无需操作&#xff0c;等待几秒&#xff0c;默认进入U盘系统 由于既使能了F12&#xff0c;又将U盘的优先级进调整到了最高&…

三层交换原理

三层交换机出现的背景 早期的网络中一般使用二层交换机来搭建局域网&#xff0c;而不同局域网之间的网络互通由路由器来完成。那时的网络流量&#xff0c;局域网内部的流量占了绝大部分&#xff0c;而网络间的通信访问量比较少&#xff0c;使用少量路由器已经足够应付了。 但…

鸿蒙生态千帆起:从者众,行则远

“轻舟已过万重山”&#xff0c;鸿蒙的成长速度惊人&#xff0c;一定程度上打破了iOS和安卓二分天下的格局。短短四年时间&#xff0c;搭载华为鸿蒙系统的生态设备数已经突破7亿&#xff0c;开发者突破220万。据Counterpoint数据显示&#xff0c;华为HarmonyOS系统在中国的市场…

钓鱼网站域名识别工具dnstwist算法研究

先上一个AI的回答&#xff1a; dnstwist是一种钓鱼网站域名识别工具&#xff0c;可帮助用户识别和检测可能被恶意使用的域名。它通过生成类似的域名变体来模拟攻击者可能使用的钓鱼域名&#xff0c;并提供了一系列有用的功能和信息。 dnstwist能够生成一组类似的域名变体&…

Linux基础指令(2)

今天我们继续来学我们有关于Linux的指令&#xff0c;今天的指令要比上次多多了。开始我们的学习吧。 man手册 先来看标题&#xff0c;手册我们第一时间想到的就是手册的查阅功能&#xff0c;我们都知道在我们上小学的时候&#xff0c;如果遇到不会的字&#xff0c;我们会通过…

淘宝1688京东解析商品详情方法丨API接口指南及相关文档说明

要解析淘宝、1688和京东的商品详情&#xff0c;可以按照以下步骤进行&#xff1a; 获取API接口权限&#xff1a;首先&#xff0c;需要在对应的平台上申请API接口权限。这通常涉及到注册开发者账号&#xff0c;创建应用&#xff0c;并获取App Key和App Secret。编写API请求代码…

Docker build 无法解析域名

### 报错 Docker build 无法解析域名 报错&#xff1a;ERROR [ 2/12] RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo 解决Docker build无法解析域名 # 追加到 etc/docker/daemon.json&#xff0c;注意JSON的格式 {"dn…

机器学习硬件十年:性能变迁与趋势

本文分析了机器学习硬件性能的最新趋势&#xff0c;重点关注不同GPU和加速器的计算性能、内存、互连带宽、性价比和能效等指标。这篇分析旨在提供关于ML硬件能力及其瓶颈的全面视图。本文作者来自调研机构Epoch&#xff0c;致力于研究AI发展轨迹与治理的关键问题和趋势。 &…

设计模式-门面模式(Facade)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、定义二、结构 前言 在组件构建过程中&#xff0c;某些接口之间直接依赖会带来很多问题&#xff0c;甚至无法直接实现。采用一层间接接口&#xff0c;来隔离…

Appium 并行测试多个设备

一、前置说明 在自动化测试中&#xff0c;经常需要验证多台设备的兼容性&#xff0c;Appium可以用同一套测试运例并行测试多个设备&#xff0c;以达到验证兼容性的目的。 解决思路&#xff1a; 查找已连接的所有设备&#xff1b;为每台设备启动相应的Appium Server&#xff1b…

URL提示不安全

当用户访问一个没有经过SSL证书加密的网站&#xff08;即使用HTTP而不是HTTPS协议&#xff09;&#xff0c;或者SSL证书存在问题时&#xff0c;浏览器URL会显示不安全提示。这些提示旨在保护用户免受潜在的恶意活动&#xff0c;并提醒他们谨慎对待这些不安全的网站。那么该如何…

28. Python Web 编程:Django 基础教程

目录 安装使用创建项目启动服务器创建数据库创建应用创建模型设计路由设计视图设计模版 安装使用 Django 项目主页&#xff1a;https://www.djangoproject.com 访问官网 https://www.djangoproject.com/download/ 或者 https://github.com/django/django Windows 按住winR 输…

Python中的并发编程(3)线程池、锁

concurrent.futures 提供的线程池 concurrent.futures模块提供了线程池和进程池简化了多线程/进程操作。 线程池原理是用一个任务队列让多个线程从中获取任务执行&#xff0c;然后返回结果。 常见的用法是创建线程池&#xff0c;提交任务&#xff0c;等待完成并获取结果&…

mysql 字符串合并方法以及合并为null问题

concat()不推荐 mysql一般提供了两种一种是concat()函数一种是concat_ws()函数&#xff0c;前者合并字符串有个弊端&#xff0c;合并字段不能有null值&#xff0c; 否则如下图合并后会是null concat_ws()推荐 concat_ws()函数可以解决合并字符串为null问题&#xff0c;conca…

人工智能期末复习重点【只针对(适合)个人】

第二章 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12.框架题 12.1地震框架 12.2洪水框架 13.第二章总结 第三章 14. 15. 3.1.1 推理的定义 16. 3.1.2 推理方式及其分类 &#xff08;1&#xff09;确定性推理&#xff1a; u 推理时所用的 知识与证据 都是 确定的 &…

在 Qt Creator 中编写 Doxygen 风格的注释

2023年12月10日&#xff0c;周日上午 如何生成Doxygen 风格的注释 在需要Doxygen 风格注释的函数上方输入 /**&#xff0c;然后按下 Enter 键。Qt Creator 将自动为你生成一个注释模板。 输入&#xff0c;Qt Creator会自动帮你补全Doxygen标签 不得不说&#xff0c;写了Doxyge…

Linux库之动态库静态库

一、什么是库&#xff08;Library&#xff09; 二、库的分类 三、静态库、动态库优缺点 四、静态库的制作和使用 五、动态库的制作和使用 SO-NAME–解决主版本号之间的兼容问题 基于符号的版本机制 共享库系统路径 共享库的查找过程 有用的环境变量 gcc 编译器常用选项 Linux共…