Каковы современные рекомендации по систематической нумерации сборок и управлению номерами версий в проектах Java? В частности:
Как систематически управлять номерами сборки в распределенной среде разработки
Как сохранить номера версий в исходном / доступном для приложения времени выполнения
Как правильно интегрироваться с исходным хранилищем
Как более автоматически управлять номерами версий по сравнению с тегами репозитория
Как интегрироваться с инфраструктурой непрерывной сборки
Существует довольно много доступных инструментов, и у ant (используемой нами системы сборки) есть задача, которая будет поддерживать номер сборки, но не ясно, как управлять этим с несколькими параллельными разработчиками, использующими CVS, svn. или аналогичный.
[EDIT]
Ниже приведены несколько хороших и полезных частичных или конкретных ответов, поэтому я кратко изложу некоторые из них. Для меня это звучит так, будто на самом деле нет сильной «лучшей практики», скорее, это набор совпадающих идей. Ниже, найдите мои резюме и некоторые итоговые вопросы, на которые люди могут попытаться ответить как продолжение. [Новое в stackoverflow ... пожалуйста, предоставьте комментарии, если я делаю это неправильно.]
Если вы используете SVN, для поездки предоставляется версия определенной проверки. Нумерация сборок может использовать это для создания уникального номера сборки, который идентифицирует конкретную проверку / ревизию. [CVS, который мы используем по унаследованным причинам, не обеспечивает такого уровня понимания ... ручное вмешательство с тегами поможет вам в этом.]
Если вы используете maven в качестве своей системы сборки, есть поддержка для создания номера версии из SCM, а также модуль выпуска для автоматического создания выпусков. [Мы не можем использовать Maven по разным причинам, но это помогает тем, кто может. [Благодаря Марсело-Моралесу ]]
Если вы используете ant в качестве своей системы сборки, следующее описание задачи может помочь создать файл .properties Java, содержащий информацию о сборке, которая затем может быть встроена в вашу сборку в несколько пути. [Мы расширили эту идею, чтобы включить информацию, полученную из Хадсона, спасибо marty-lamb ].
Ant и maven (и hudson и круиз-контроль) предоставляют простые средства для получения номеров сборок в файл .properties или в файл .txt / .html. Достаточно ли это «безопасно», чтобы предотвратить его умышленное или случайное вмешательство? Во время сборки лучше скомпилировать его в класс управления версиями?
Утверждение: нумерация сборки должна быть определена / введена в систему непрерывной интеграции, например, hudson . [Благодаря marcelo-morales ] Мы воспользовались этим предложением, но оно раскрыло вопрос разработки релиза: как происходит релиз? Есть ли в релизе несколько сборочных номеров? Есть ли значимая связь между номерами сборки из разных версий?
Вопрос: Какая цель стоит за номером сборки? Это используется для QA? Как? Используется ли он главным образом разработчиками для устранения неоднозначности между несколькими сборками во время разработки или для QA, чтобы определить, какую сборку получил конечный пользователь? Если целью является воспроизводимость, теоретически это то, что должен предоставить номер версии выпуска - почему бы и нет? (ответьте на это как часть ваших ответов ниже, это поможет осветить выбор, который вы сделали / предложили ...)
Вопрос: Есть ли место для номеров сборок в сборках вручную? Это так проблематично, что КАЖДЫЙ должен использовать решение CI?
Вопрос: Должны ли номера сборок регистрироваться в SCM? Если целью является надежное и однозначное определение конкретной сборки, как справиться с множеством систем непрерывной или ручной сборки, которые могут привести к сбою / перезапуску и т. Д.
Вопрос: Должен ли номер сборки быть коротким и приятным (т. Е. Монотонно увеличивающимся целым числом), чтобы его можно было легко вводить в имена файлов для архивирования, легко ссылаться в общении и т. Д. ... или это должно быть длинные и полные имена пользователей, даты, имена машин и т.д.?
Вопрос: Пожалуйста, предоставьте подробную информацию о том, как назначение номеров сборки вписывается в ваш более крупный процесс автоматического выпуска. Да, любители мавена, мы знаем, что это сделано и сделано, но не все из нас уже выпили kool-aid ...
Я бы очень хотел уточнить это в полном ответе, по крайней мере, для конкретного примера нашей установки cvs / ant / hudson, чтобы кто-то мог построить полную стратегию на основе этого вопроса. Я отмечу как «Ответ» любого, кто может дать подробное описание для этого конкретного случая (включая схему тегирования cvs, соответствующие элементы конфигурации CI и процедуру выпуска, которая сворачивает номер сборки в выпуск таким образом, чтобы он был программным образом доступно.) Если вы хотите спросить / ответить для другой конкретной конфигурации (скажем, svn / maven / круиз-контроль), я сошлюсь на вопрос здесь. --JA
[РЕДАКТИРОВАТЬ 23 октября 09]
Я принял ответ с наибольшим количеством голосов, потому что я думаю, что это разумное решение, в то время как некоторые другие ответы также включают хорошие идеи. Если кто-то захочет попробовать синтезировать некоторые из них с marty-lamb , я подумаю о принятии другого. Единственная проблема, которую я испытываю с 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();
}
}
Оставляйте комментарии, если это заслуживает того, чтобы стать вики-обсуждением.