Сборка и нумерация версий для проектов Java (ant, cvs, hudson)
каковы текущие рекомендации по систематической нумерации сборок и управлению номерами версий в проектах Java? В частности:
как систематически управлять номерами сборки в распределенной среде разработки
Как поддерживать номера версий в исходном / доступном для приложения среды выполнения
Как правильно интегрировать с исходным репозиторием
как более автоматическое управление номерами версий и тегами репозитория
как интегрировать с инфраструктурой непрерывной сборки
существует довольно много доступных инструментов, и ant (система сборки, которую мы используем) имеет задачу, которая будет поддерживать номер сборки, но неясно, как управлять этим с несколькими параллельными разработчиками, использующими CVS, svn или аналогичные.
[EDIT]
несколько хороших и полезных частичных или конкретных ответы появились ниже, поэтому я подытожу некоторые из них. Мне кажется, что на самом деле нет сильной "лучшей практики" по этому вопросу, а скорее набор перекрывающихся идей. Ниже найдите мои резюме и некоторые возникающие вопросы, на которые люди могут попытаться ответить в качестве последующих действий. [Новое в stackoverflow... пожалуйста, дайте комментарии, если я делаю это неправильно.]
Если вы используете SVN, управление версиями определенной проверки приходит для поездки. Построить нумерацию можно используйте это, чтобы создать уникальный номер сборки, который идентифицирует конкретную проверку / ревизию. [CVS, которые мы используем по устаревшим причинам, не обеспечивает такого уровня понимания... ручное вмешательство с тегами отчасти помогает.]
Если вы используете maven в качестве системы сборки, существует поддержка создания номера версии из SCM, а также модуля выпуска для автоматического создания выпусков. [Мы не можем использовать maven по разным причинам, но это помогает тем, кто может. [Спасибо Марсело Моралес]]
Если вы используете АНТ в качестве вашей системы сборки следующее описание задачи может помочь создать Java .файл свойств захватывает информацию о сборке, которую затем можно сложить в сборку несколькими способами. [Мы расширили эту идею, чтобы включить информацию, полученную от Хадсона, спасибо Марти-агнец].
Ant и maven (и Хадсон и круиз-контроль) обеспечивают легкие средства для получения номеров сборки в a .свойства файла, или .формат txt./HTML-файл. Достаточно ли это "безопасно", чтобы предотвратить преднамеренное или случайное вмешательство? Лучше ли компилировать его в класс "управление версиями" во время сборки?
утверждение: нумерация сборки должна быть определена/введена в систему непрерывной интеграции, например Гудзон. [Спасибо Марсело Моралес] мы сделали это предложение, но оно действительно открывает инженерный вопрос выпуска: Как происходит выпуск? Есть ли несколько buildnumbers в выпуске? Есть ли серьезные отношения между buildnumbers с разных релизов?
вопрос: Какова цель сборки? Он используется для QA? Как? Используется ли он в основном разработчиками для устранения неоднозначности между несколькими сборками во время разработки или больше для QA, чтобы определить, какую сборку получил конечный пользователь? Если цель-воспроизводимость, теоретически это то, что должен обеспечить номер версии выпуска-почему это не так? (ответьте, пожалуйста, это как часть ваших ответов ниже, это поможет осветить выбор, который вы сделали/предложили...)
вопрос: есть ли место для номеров сборки в ручных сборках? Это настолько проблематично, что все должны использовать решение CI?
вопрос: Должны ли номера сборки регистрироваться в SCM? Если цель надежно и однозначно идентифицируя конкретную сборку, как справиться с различными системами непрерывной или ручной сборки, которые могут аварийно завершить работу/перезапустить/и т. д...
вопрос: Должен ли номер сборки быть коротким и сладким (т. е. монотонно увеличивающимся целым числом), чтобы его было легко вставить в имена файлов для архивирования, легко ссылаться в общении и т. д... или он должен быть длинным и полным имен пользователей, меток данных, имен машин и т. д.?
вопрос: Пожалуйста, предоставьте подробную информацию о том, как назначение номеров сборки вписывается в ваш более крупный автоматизированный процесс выпуска. Да, возлюбленные мавены, мы знаем, что это сделано и сделано, но не все из нас уже выпили kool-aid...
Я бы очень хотел конкретизировать это в полный ответ, по крайней мере, для конкретного примера нашей установки cvs/ant/hudson, чтобы кто-то мог построить полную стратегию, основанную на этом вопросе. Я отмечу как "ответ" любого, кто может дать описание супа к орехам для этого конкретного случая (включая схему тегов cvs, соответствующие элементы конфигурации CI и процедуру выпуска, которая складывает номер сборки в выпуск так, чтобы он был программно доступен.) Если вы хотите задать / ответить на другую конкретную конфигурацию (скажем, svn/maven/cruise control), я свяжусь с вопросом отсюда. -- JA
[править 23 окт 09] Я принял ответ с высшим голосованием, потому что я думаю, что это разумное решение, в то время как несколько других ответы также включают хорошие идеи. Если кто-то хочет попробовать синтезировать некоторые из них с помощью Марти-агнецs, Я подумаю о принятии другого. Единственное, что меня беспокоит в marty-lamb, это то, что он не производит надежно сериализованный номер сборки-это зависит от локальных часов в системе строителя, чтобы обеспечить однозначные номера сборки, что не здорово.
[Править 10 Июля]
теперь мы включаем класс ниже. Это позволяет номера версий, которые будут скомпилированы в окончательный исполняемый файл. Различные формы информации о версии испускаются в данных журнала, долгосрочных архивированных выходных продуктах и используются для отслеживания нашего (иногда спустя годы) анализа выходных продуктов до определенной сборки.
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("n----------------------------------------------------------------");
sb.append("nApplication -- ");
sb.append(longStringVersion());
sb.append("n----------------------------------------------------------------n");
return sb.toString();
}
}
оставьте комментарии, если это заслуживает того, чтобы стать обсуждением wiki.
9 ответов
для нескольких моих проектов я фиксирую номер версии subversion, время, пользователя, который запустил сборку, и некоторую системную информацию, вставляю их в a .файл свойств, который включается в jar приложения и читается во время выполнения.
код муравья выглядит так:
<!-- 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>
это просто расширить это, чтобы включить любую информацию, которую вы могли бы добавить.
код построить.в XML
...
<property name="version" value="1.0"/>
...
<target name="jar" depends="compile">
<buildnumber file="build.num"/>
<manifest file="MANIFEST.MF">
...
<attribute name="Main-Class" value="MyClass"/>
<attribute name="Implementation-Version" value="${version}.${build.number}"/>
...
</manifest>
</target>
...
ваш код java
String ver = MyClass.class.getPackage().getImplementationVersion();
- номера сборки должны быть связаны с сервером непрерывной интеграции, таким как Гудзон. Используйте разные задания для разных филиалов / команд / распределений.
- чтобы сохранить номер версии в окончательной сборке, я бы рекомендовал просто использовать maven для системы сборки. Это создаст а .файл свойств архивируется в финал .сосуд./война./какая разница-ar на
META-INF/maven/<project group>/<project id>/pom.properties
. Этот.свойства файла будет содержать свойство version. - Так как я рекомендуя maven, я бы настоятельно рекомендовал вам проверить плагин освободить чтобы подготовить выпуск в исходном репозитории и синхронизировать версии.
программное обеспечение:
- SVN
- АНТ
- Хадсон для непрерывной интеграции
- svntask, задача Ant для поиска версии SVN: http://code.google.com/p/svntask/
Hudson имеет три сборки / задания: непрерывный, ночной и выпуск.
для непрерывной / ночной сборки: Номер сборки-это версия SVN, найденная с помощью svntask.
для сборки/задания выпуска: Номер сборки-номер выпуска, считываемый Ant, из файла свойств. Файл свойств также может быть распространен вместе с выпуском для отображения номера сборки во время выполнения.
скрипт сборки Ant помещает номер сборки в файл манифеста файлов jar/war, которые создаются во время сборки. Применяется ко всем сборкам.
действие после сборки для сборки выпуска, легко выполняемое с помощью плагина Hudson: tag SVN с номером сборки.
преимущества:
- для версии dev jar / war, разработчик может найти редакцию SVN из jar / war и посмотреть соответствующий код в SVN
- для выпуска версия SVN соответствует тегу SVN, в котором есть номер выпуска.
надеюсь, что это помогает.
Я также использую Hudson, хотя гораздо более простой сценарий:
в моем скрипте Ant есть цель, которая выглядит так:
<target name="build-number">
<property environment="env" />
<echo append="false" file="${build.dir}/build-number.txt">Build: ${env.BUILD_TAG}, Id: ${env.BUILD_ID}, URL: ${env.HUDSON_URL}</echo>
</target>
Hudson устанавливает эти переменные среды для меня, когда моя работа выполняется.
в моем случае этот проект является webapp, и я включаю это build-number.txt
файл в корневой папке webapp - мне все равно, кто его видит.
мы не помечаем систему управления версиями, когда это делается, потому что у нас уже есть наша работа Hudson настройте, чтобы пометить его с номером сборки / меткой времени, когда сборка будет успешной.
мое решение охватывает только инкрементные номера сборки для разработки, мы еще недостаточно продвинулись в проекте, где мы покрываем номера выпуска.
вы также можете взглянуть на плагин BuildNumber Maven и задачу Ant в одной банке, найденной в http://code.google.com/p/codebistro/wiki/BuildNumber. Я старался, чтобы все было просто и понятно. Это очень маленький файл jar, который зависит только от установленной Subversion командной строки.
вот как я решил это:
- источники копируются в каталог сборки
- затем применяется анттаска "versioninfo"
- скомпилируйте измененный источник
вот файл java, хранящий информацию о версии:
public class Settings {
public static final String VERSION = "$VERSION$";
public static final String DATE = "$DATE$";
}
и вот анттаск "versioninfo":
<!-- =================================
target: versioninfo
================================= -->
<target name="versioninfo"
depends="init"
description="gets version info from svn"
>
<!--
get svn info from the src folder
-->
<typedef resource="org/tigris/subversion/svnant/svnantlib.xml"
classpathref="ant.classpath"
/>
<svnSetting id="svn.setting"
javahl="false"
svnkit="true"
dateformatter="dd.MM.yyyy"
/>
<svn refid="svn.setting">
<info target="src" />
</svn>
<!--
if repository is a taged version use "v <tagname>"
else "rev <revisionnumber> (SVN)" as versionnumber
-->
<taskdef resource="net/sf/antcontrib/antcontrib.properties"
classpathref="ant.classpath"
/>
<propertyregex property="version"
input="${svn.info.url}"
regexp=".*/tags/(.*)/${ant.project.name}/src"
select="v "
defaultvalue="rev ${svn.info.lastRev} (SVN)"
override="true"
/>
<!--
replace date and version in the versionfile ()
-->
<replace file="build/${versionfile}">
<replacefilter token="$DATE$" value="${svn.info.lastDate}" />
<replacefilter token="$VERSION$" value="${version}" />
</replace>
</target>
вот мои 2 цента:
мой скрипт сборки создает номер сборки (с меткой времени!) каждый раз, когда я создаю приложение. Это создает слишком много чисел, но никогда не слишком мало. Если у меня есть изменение в коде, номер сборки изменится хотя бы один раз.
I версия номера сборки с каждым выпуском (хотя и не между ними). Когда я обновляю проект и получаю новый номер сборки (потому что кто-то другой сделал выпуск), я перезаписываю свою локальную версию и начать сначала. Это может привести к более низкому номеру сборки, поэтому я включил метку времени.
когда происходит выпуск, номер сборки фиксируется как последний элемент в одной фиксации с сообщением "build 1547". После этого, когда это официальный релиз, все дерево помечается. Таким образом, файл сборки всегда имеет все теги, и существует простое сопоставление 1:1 между тегами и номерами сборки.
[EDIT]я развертываю версию.html с мои проекты, а затем я могу использовать скребок, чтобы просто собрать точную карту, что установлено где. Если вы используете Tomcat или аналогичный, поместите номер сборки и метку времени в description
элемент web.в XML. Помните: никогда ничего не запоминайте, когда компьютер может сделать это за вас.
мы запускаем нашу сборку через CruiseControl (вставьте здесь ваш любимый менеджер сборки) и выполняем основную сборку и тесты.
затем мы увеличиваем номер версии, используя Ant и BuildNumber и создайте файл свойств с этой информацией, а также дату сборки и другие метаданные.
У нас есть класс, посвященный чтению этого и предоставлению его GUIs / logs и т. д.
затем мы упаковываем все это и создаем развертываемую привязку вместе номер сборки и соответствующая сборка. Все наши серверы сбрасывают эту мета-информацию при запуске. Мы можем вернуться через журналы CruiseControl и привязать номер сборки к дате и проверкам.