问题描述
当前在 Java 项目中系统构建编号和版本号管理的最佳实践是什么?具体:
What are current best-practices for systematic build numbering and version number management in Java projects? Specifically:
如何在分布式开发环境中系统地管理内部版本号
How to manage build numbers systematically in a distributed development environment
如何在源代码中维护版本号/对运行时应用程序可用
How to maintain version numbers in source / available to the runtime application
如何与源代码库正确集成
How to properly integrate with source repository
如何更自动地管理版本号与存储库标签
How to more automatically manage version numbers vs. repository tags
如何与持续构建基础架构集成
How to integrate with continuous build infrastructure
有相当多的工具可用,ant(我们正在使用的构建系统)有一个维护构建号的任务,但不清楚如何使用 CVS、svn 与多个并发开发人员进行管理,或类似的.
There are quite a number of tools available, and ant (the build system we're using) has a task that will maintain a build number, but it's not clear how to manage this with multiple, concurrent developers using CVS, svn, or similar.
下面出现了几个很好且有用的部分或具体答案,因此我将总结其中的一些.在我看来,这方面并没有真正强有力的最佳实践",而是一系列重叠的想法.在下面,找到我的总结和一些由此产生的问题,人们可能会在后续行动中尝试回答这些问题.[stackoverflow 的新手...如果我做错了,请提供评论.]
Several good and helpful partial or specific answers have appeared below, so I'll summarize a few of them. It sounds to me like there is not really a strong "best practice" on this, rather a collection of overlapping ideas. Below, find my summaries and some resulting questions that folks might try to answer as follow-ups. [New to stackoverflow... please provide comments if I'm doing this wrong.]
如果您使用的是 SVN,则需要对特定结帐进行版本控制.内部版本编号可以利用这一点来创建一个唯一的内部版本编号,用于标识特定的检出/修订版本.[出于遗留原因,我们使用 CVS 并不能提供这种级别的洞察力……使用标签进行手动干预可以帮助您实现目标.]
If you are using SVN, versioning of a specific checkout comes along for the ride. Build numbering can exploit this to create a unique build number that identifies the specific checkout/revision. [CVS, which we are using for legacy reasons, doesn't provide quite this level of insight... manual intervention with tags gets you part way there.]
如果您使用 maven 作为构建系统,则支持从 SCM 生成版本号,以及用于自动生成版本的发布模块.[由于各种原因,我们不能使用 maven,但这有助于那些可以使用的人.[感谢 marcelo-morales]]
If you are using maven as your build system, there is support for producing a version number from the SCM, as well as a release module for automatically producing releases. [We can't use maven, for a variety of reasons, but this helps those who can. [Thanks to marcelo-morales]]
如果您使用 ant 作为构建系统,以下任务描述有助于生成 Java.properties 文件捕获构建信息,然后可以通过多种方式将其折叠到您的构建中.[我们扩展了这个想法以包含 hudson 派生的信息,感谢 marty-lamb].
If you are using ant as your build system, the following task description can help produce a Java .properties file capturing build information, which can then be folded into your build in a number of ways. [We expanded on this idea to include hudson-derived information, thanks marty-lamb].
Ant 和 maven(以及 hudson 和 Cruise Control)提供了将内部版本号放入 .properties 文件或 .txt/.html 文件的简单方法.这是否安全"足以防止它被有意或无意地篡改?在构建时将其编译为版本控制"类是否更好?
Ant and maven (and hudson and cruise control) provide easy means for getting build numbers into a .properties file, or into a .txt/.html file. Is this "safe" enough to keep it from being tampered with intentionally or accidentally? Is it better to compile it into a "versioning" class at build time?
断言:构建编号应该在像 hudson 这样的持续集成系统中定义/制定.[感谢 marcelo-morales] 我们采纳了这个建议,但它确实解决了发布工程问题:如何是否发生释放?一个版本中是否有多个内部版本号?不同版本的内部版本号之间是否存在有意义的关系?
Assertion: Build numbering should be defined/enacted in a continuous integration system like hudson. [Thanks to marcelo-morales] We have taken this suggestion, but it does crack open the release engineering question: How does a release happen? Are there multiple buildnumbers in a release? Is there a meaningful relationship between buildnumbers from differing releases?
问题:内部版本号背后的目标是什么?是否用于 QA?如何?它主要由开发人员用于在开发过程中消除多个构建之间的歧义,还是更多地用于 QA 以确定最终用户获得的构建?如果目标是可重复性,理论上这就是发布版本号应该提供的——为什么不呢?(请将此作为您下面答案的一部分来回答,这将有助于阐明您做出/建议的选择...)
Question: What is the objective behind a build number? Is it used for QA? How? Is it used primarily by developers to disambiguate between multiple builds during development, or more for QA to determine what build an end-user got? If the goal is reproducibility, in theory this is what a release version number should provide -- why doesn't it? (please answer this as a part of your answers below, it will help illuminate the choices you have made/suggested...)
问题:在手动构建中是否有构建编号的位置?这是不是每个人都应该使用 CI 解决方案的问题?
Question: Is there a place for build numbers in manual builds? Is this so problematic that EVERYONE should be using a CI solution?
问题:是否应该将内部版本号签入 SCM?如果目标是可靠且明确地识别特定构建,如何应对可能崩溃/重启/等的各种连续或手动构建系统...
Question: Should build numbers be checked in to the SCM? If the goal is reliably and unambiguously identifying a particular build, how to cope with a variety of continuous or manual build systems that may crash/restart/etc...
问题:内部版本号是否应该短小精悍(即单调递增的整数),以便于归档的文件名、易于在通信中引用等...或应该是长而完整的用户名、日期戳、机器名称等?
Question: Should a build number be short and sweet (i.e., monotonically increasing integer) so that it's easy to stick into file names for archival, easy to refer to in communication, etc... or should it be long and full of usernames, datestamps, machine names, etc?
问题:请详细说明内部版本号的分配如何适应更大的自动化发布流程.是的,maven 爱好者,我们知道这已经完成了,但并不是我们所有人都喝过 kool-aid...
Question: Please provide details about how the assignment of build numbers fits into your larger automated release process. Yes, maven lovers, we know this is done and done, but not all of us have drunk the kool-aid quite yet...
我真的很想将其充实为一个完整的答案,至少对于我们的 cvs/ant/hudson 设置的具体示例,以便有人可以基于此问题构建完整的策略.我将标记为答案"的任何人都可以对这种特殊情况进行全面的描述(包括 cvs 标记方案、相关的 CI 配置项,以及将内部版本号折叠到版本中的发布程序,以便以编程方式可访问.)如果您想询问/回答其他特定配置(例如 svn/maven/cruise control),我将从这里链接到该问题.--JA
I'd really like to flesh this out into a complete answer, at least for the concrete example of our cvs/ant/hudson setup, so someone could build a complete strategy based on this question. I'll mark as "The Answer" anyone who can give a soup-to-nuts description for this particular case (including cvs tagging scheme, relevant CI config items, and release procedure that folds the build number into the release such that it's programmatically accessible.) If you want to ask/answer for another particular configuration (say, svn/maven/cruise control) I'll link to the question from here. --JA
我接受了最高投票的答案,因为我认为这是一个合理的解决方案,而其他几个答案也包括好主意.如果有人想尝试将其中一些与 marty-lamb 合成,我会考虑接受一个不同的.我对 marty-lamb's 唯一担心的是它不会产生可靠的序列化版本号——它依赖于构建器系统的本地时钟来提供明确的版本号,这不是很好.
I accepted the top-voted answer because I think it's a reasonable solution, while several of the other answers also include good ideas. If someone wants to take a crack at synthesizing some of these with marty-lamb's, I'll consider accepting a different one. The only concern I have with marty-lamb's is that it doesn't produce a reliably serialized build number -- it depends on a local clock at the builder's system to provide unambiguous build numbers, which isn't great.
我们现在包含一个如下所示的类.这允许将版本号编译成最终的可执行文件.不同形式的版本信息会在日志数据、长期存档的输出产品中发出,并用于跟踪我们(有时是几年后)对特定构建的输出产品分析.
We now include a class like the below. This allows the version numbers to be compiled into the final executable. Different forms of the version info are emitted in logging data, long-term archived output products, and used to trace our (sometimes years-later) analysis of output products to a specific build.
public final class AppVersion
{
// SVN should fill this out with the latest tag when it's checked out.
private static final String APP_SVNURL_RAW =
"$HeadURL: svn+ssh://user@host/svnroot/app/trunk/src/AppVersion.java $";
private static final String APP_SVN_REVISION_RAW = "$Revision: 325 $";
private static final Pattern SVNBRANCH_PAT =
Pattern.compile("(branches|trunk|releases)\/([\w\.\-]+)\/.*");
private static final String APP_SVNTAIL =
APP_SVNURL_RAW.replaceFirst(".*\/svnroot\/app\/", "");
private static final String APP_BRANCHTAG;
private static final String APP_BRANCHTAG_NAME;
private static final String APP_SVNREVISION =
APP_SVN_REVISION_RAW.replaceAll("\$Revision:\s*","").replaceAll("\s*\$", "");
static {
Matcher m = SVNBRANCH_PAT.matcher(APP_SVNTAIL);
if (!m.matches()) {
APP_BRANCHTAG = "[Broken SVN Info]";
APP_BRANCHTAG_NAME = "[Broken SVN Info]";
} else {
APP_BRANCHTAG = m.group(1);
if (APP_BRANCHTAG.equals("trunk")) {
// this isn't necessary in this SO example, but it
// is since we don't call it trunk in the real case
APP_BRANCHTAG_NAME = "trunk";
} else {
APP_BRANCHTAG_NAME = m.group(2);
}
}
}
public static String tagOrBranchName()
{ return APP_BRANCHTAG_NAME; }
/** Answers a formatter String descriptor for the app version.
* @return version string */
public static String longStringVersion()
{ return "app "+tagOrBranchName()+" ("+
tagOrBranchName()+", svn revision="+svnRevision()+")"; }
public static String shortStringVersion()
{ return tagOrBranchName(); }
public static String svnVersion()
{ return APP_SVNURL_RAW; }
public static String svnRevision()
{ return APP_SVNREVISION; }
public static String svnBranchId()
{ return APP_BRANCHTAG + "/" + APP_BRANCHTAG_NAME; }
public static final String banner()
{
StringBuilder sb = new StringBuilder();
sb.append("
----------------------------------------------------------------");
sb.append("
Application -- ");
sb.append(longStringVersion());
sb.append("
----------------------------------------------------------------
");
return sb.toString();
}
}
如果这值得成为 wiki 讨论,请留下评论.
Leave comments if this deserves to become a wiki discussion.
推荐答案
对于我的几个项目,我捕获了 subversion 版本号、时间、运行构建的用户和一些系统信息,将它们塞进一个 .properties 文件中包含在应用程序 jar 中,并在运行时读取该 jar.
For several of my projects I capture the subversion revision number, time, user who ran the build, and some system information, stuff them into a .properties file that gets included in the application jar, and read that jar at runtime.
蚂蚁代码如下所示:
<!-- software revision number -->
<property name="version" value="1.23"/>
<target name="buildinfo">
<tstamp>
<format property="builtat" pattern="MM/dd/yyyy hh:mm aa" timezone="America/New_York"/>
</tstamp>
<exec executable="svnversion" outputproperty="svnversion"/>
<exec executable="whoami" outputproperty="whoami"/>
<exec executable="uname" outputproperty="buildsystem"><arg value="-a"/></exec>
<propertyfile file="path/to/project.properties"
comment="This file is automatically generated - DO NOT EDIT">
<entry key="buildtime" value="${builtat}"/>
<entry key="build" value="${svnversion}"/>
<entry key="builder" value="${whoami}"/>
<entry key="version" value="${version}"/>
<entry key="system" value="${buildsystem}"/>
</propertyfile>
</target>
扩展它以包含您可能想要添加的任何信息很简单.
It's simple to extend this to include whatever information you might want to add.
这篇关于Java 项目的构建和版本编号(ant、cvs、hudson)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!