Нумерация сборок и версий для проектов 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 130

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 и привязать номер сборки к дате и чекинам.