Нумерация сборок и версий для проектов Java (ant, cvs, hudson)
каковы текущие рекомендации по систематической нумерации сборок и управлению номерами версий в проектах Java? В частности:
как систематически управлять номерами сборок в распределенной среде разработки
Как сохранить номера версий в исходном / доступном для выполнения приложении
Как правильно интегрироваться с исходным репозиторием
как более автоматическое управление номерами версий и тегами репозитория
как интегрироваться с инфраструктурой непрерывной сборки
существует довольно много доступных инструментов, и ant (система сборки, которую мы используем) имеет задачу, которая будет поддерживать номер сборки, но неясно, как управлять этим с несколькими параллельными разработчиками, использующими CVS, svn или аналогичные.
[EDIT]
несколько хороших и полезных частичных или конкретных ответы появились ниже, поэтому я подытожу некоторые из них. Мне кажется, что на самом деле нет сильной "лучшей практики" по этому вопросу, а скорее набор перекрывающихся идей. Ниже вы найдете мое резюме и некоторые вопросы, которые люди могут попытаться ответить прослеживания. [Новый для stackoverflow... пожалуйста, предоставьте комментарии, если я делаю это неправильно.]
Если вы используете SVN, управление версиями конкретной проверки приходит вместе с поездкой. Построить нумерацию можно используйте это для создания уникального номера сборки, который идентифицирует конкретную проверку / ревизию. [CVS, который мы используем по наследственным причинам, не обеспечивает такого уровня понимания... ручное вмешательство с тегами дает вам часть пути туда.]
Если вы используете maven в качестве системы сборки, есть поддержка для создания номера версии из SCM, а также модуль выпуска для автоматического создания релизов. [Мы не можем использовать maven по разным причинам, но это помогает тем, кто может. [Спасибо Марсело Моралес]]
Если вы используете АНТ как ваша система сборки, следующее описание задачи может помочь создать Java .файл свойств захватывает информацию о сборке, которая затем может быть свернута в вашу сборку несколькими способами. [Мы расширили эту идею, чтобы включить информацию, полученную от Хадсона, спасибо Марти-агнец].
Муравей и maven (и Хадсон и круиз-контроль) обеспечивают легкие средства для получения номера сборки в a .свойства файла, или .формат txt./HTML-файл. Достаточно ли это "безопасно", чтобы предотвратить его преднамеренное или случайное вмешательство? Лучше ли скомпилировать его в класс "versioning" во время сборки?
утверждение: нумерация сборки должна быть определена / введена в систему непрерывной интеграции, например Гудзон. [Спасибо Марсело Моралес] мы сделали это предложение, но оно взломает инженерный вопрос о выпуске: Как происходит выпуск? Есть ли несколько buildnumbers в релизе? Существует ли значимая связь между номерами зданий из разных выпусков?
вопрос: Какова цель сборки? Он используется для контроля качества? Как? Используется ли он в основном разработчиками для устранения неоднозначности между несколькими сборками во время разработки или больше для QA, чтобы определить, какую сборку получил конечный пользователь? Если цель-воспроизводимость, теоретически это то, что должен обеспечить номер версии релиза-почему бы и нет? (ответьте, пожалуйста, это как часть ваших ответов ниже, это поможет осветить выбор, который вы сделали/предложили...)
вопрос: есть ли место для номеров сборки в ручных сборках? Это настолько проблематично, что все должны использовать решение CI?
вопрос: Должны ли номера сборки быть возвращены в SCM? Если цель надежно и однозначно определяя конкретную сборку, как справиться с различными непрерывными или ручными системами сборки, которые могут аварийно завершить работу/перезапустить/и т. д...
вопрос: Должен ли номер сборки быть коротким и сладким (т. е. монотонно увеличивающимся целым числом), чтобы его легко вставлять в имена файлов для архивирования, легко ссылаться на связь и т. д... или он должен быть длинным и полным имен пользователей, меток даты, имен машин и т. д.?
вопрос: Пожалуйста, предоставьте подробную информацию о том, как назначение номеров сборки вписывается в ваш более крупный автоматизированный процесс выпуска. Да, любители мэйвена, мы знаем, что это сделано и сделано, но не все из нас уже выпили kool-aid...
Я действительно хотел бы конкретизировать это в полный ответ, по крайней мере, для конкретного примера нашей установки cvs/ant/hudson, чтобы кто-то мог построить полную стратегию, основанную на этом вопросе. Я отмечу как "ответ" любого, кто может дать описание супа к орехам для этого конкретного случая (включая схему тегов cvs, соответствующие элементы конфигурации CI и процедуру выпуска, которая складывает номер сборки в выпуск, чтобы он был программно доступен.) Если вы хотите задать/ответить на другую конкретную конфигурацию (скажем, svn/maven/cruise control), я свяжусь с этим вопросом отсюда. -- JA
[EDIT 23 Oct 09] Я принял топ-проголосовали ответ, потому что я думаю, что это разумное решение, в то время как несколько других ответы также включают в себя хорошие идеи. Если кто-то хочет взять трещину в синтезе некоторых из них с Марти-агнецs, Я подумаю о принятии другого. Единственная проблема, которую я имею с marty-lamb, заключается в том, что он не производит надежно сериализованный номер сборки-это зависит от локальных часов в системе строителя, чтобы обеспечить однозначные номера сборки, что не очень хорошо.
[Edit Jul 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();
}
}
оставляйте комментарии, если это заслуживает того, чтобы стать Вики-дискуссии.
9 ответов:
для нескольких моих проектов я записываю номер версии subversion, время, пользователя, который запускал сборку, и некоторую системную информацию, набиваю их в a .файл свойств, который включается в jar приложения, и считывает этот 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 для системы сборки. Он будет создавать .файл свойств заархивирован в финал .сосуд./война./как бы то ни было-АР на
META-INF/maven/<project group>/<project id>/pom.properties
. Этот.свойства файла будет содержать свойство version.- Так как я рекомендуя maven, я бы настоятельно рекомендовал вам проверить плагин освободить чтобы подготовить релиз в исходном репозитории и синхронизировать версии.
программное обеспечение:
- SVN
- АНТ
- Хадсон для непрерывной интеграции
- svntask, Муравьиная задача найти SVN ревизию: http://code.google.com/p/svntask/
Хадсон имеет три сборки / задания: непрерывные, ночные и релиз.
для непрерывной / ночной сборки: Номер сборки-это версия SVN, найденная с помощью svntask.
для сборки/задания выпуска: Номер сборки - это номер выпуска, прочитанный Муравей, из файла свойств. Файл свойств также может быть распространен вместе с выпуском для отображения номера сборки во время выполнения.
сценарий сборки Ant помещает номер сборки в файл манифеста файлов jar/war, которые создаются во время сборки. Применяется ко всем сборкам.
действие после сборки для сборки выпуска, выполненное легко с помощью плагина Hudson: tag SVN с номером сборки.
преимущества:
- для dev версии jar / war, разработчик может найти ревизию SVN из jar / war и посмотреть соответствующий код в SVN
- для выпуска версия SVN-это та, которая соответствует тегу SVN, в котором есть номер выпуска.
надеюсь, что это помогает.
Я использую Хадсон также, хотя и гораздо более простой сценарий:
У моего муравьиного скрипта есть цель, которая выглядит так:
<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>
Хадсон устанавливает эти переменные среды для меня, когда моя работа выполняется.
в моем случае этот проект является веб-приложением, и я включаю это
build-number.txt
файл в корневой папке веб-приложения-мне все равно, кто его видит.мы не помечаем систему управления версиями, когда это делается, потому что у нас уже есть наша работа 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 цента:
мой скрипт сборки создает номер сборки (с меткой времени!) каждый раз, когда я создаю приложение. Это создает слишком много чисел, но никогда не слишком мало. Если у меня есть изменения в коде, номер сборки изменится по крайней мере один раз.
Я версия номер сборки с каждым выпуском (хотя и не между ними). Когда я обновляю проект и получаю новый номер сборки (потому что кто-то другой сделал релиз), я перезаписываю свою локальную версию и начать все сначала. Это может привести к более низкому номеру сборки, поэтому я включил метку времени.
когда происходит релиз, номер сборки фиксируется как последний элемент в одной фиксации с сообщением "build 1547". После этого, когда это официальный релиз,все дерево помечается. Таким образом, файл сборки всегда имеет все теги и существует простое сопоставление 1:1 между тегами и номерами сборки.
[EDIT] я развертываю версию.html с мои проекты, а затем, я могу использовать скребок, чтобы просто собрать точную карту, что установлено где. Если вы используете Tomcat или аналогичный, поместите номер сборки и метку времени в
description
элемент web.xml. Помните: никогда не запоминайте ничего, когда вы можете иметь компьютер сделать это за вас.
мы запускаем нашу сборку через CruiseControl (вставьте здесь свой любимый менеджер сборки) и выполняем основную сборку и тесты.
затем мы увеличиваем номер версии с помощью Ant и Номер_сборки и создайте файл свойств с этой информацией плюс дата сборки и другие метаданные.
У нас есть класс, предназначенный для чтения этого и предоставления его в GUIs/logs и т. д.
затем мы упаковываем все это и создаем развертываемое связывание номер сборки и соответствующая сборка. Все наши серверы сбрасывают эту мета-информацию при запуске. Мы можем вернуться к журналам CruiseControl и привязать номер сборки к дате и чекинам.