instalar - Numeración de compilación y versión para proyectos de Java(ant, cvs, hudson)
instalar apache ant (9)
¿Cuáles son las mejores prácticas actuales para la numeración de compilación sistemática y la gestión del número de versión en los proyectos de Java? Específicamente:
Cómo administrar los números de compilación sistemáticamente en un entorno de desarrollo distribuido
Cómo mantener los números de versión en fuente / disponible para la aplicación en tiempo de ejecución
Cómo integrarse correctamente con el repositorio fuente
Cómo gestionar más automáticamente los números de versión frente a las etiquetas de repositorio
Cómo integrarse con la infraestructura de construcción continua
Hay bastantes herramientas disponibles, y hormiga (el sistema de compilación que estamos usando) tiene una tarea que mantendrá un número de compilación, pero no está claro cómo administrar esto con múltiples desarrolladores simultáneos que usan CVS, svn o similar. .
[EDITAR]
Varias respuestas parciales o específicas buenas y útiles aparecieron a continuación, por lo que resumiré algunas de ellas. Me parece que no hay una "buena práctica" fuerte en esto, sino una colección de ideas superpuestas. A continuación, encuentre mis resúmenes y algunas preguntas resultantes que la gente podría tratar de responder como seguimiento. [Nuevo en stackoverflow ... por favor proporcione comentarios si estoy haciendo esto mal.]
Si está utilizando SVN, el versionado de un pago específico viene para el viaje. La numeración de compilación puede explotar esto para crear un número de compilación único que identifique la revisión / revisión específica. [CVS, que estamos usando por razones heredadas, no proporciona este nivel de conocimiento ... la intervención manual con etiquetas lo lleva a un lugar intermedio allí].
Si está utilizando maven como su sistema de compilación, existe soporte para producir un número de versión del SCM, así como un módulo de lanzamiento para producir automáticamente las versiones. [No podemos usar maven, por una variedad de razones, pero esto ayuda a quienes sí pueden. [Gracias a marcelo-morales ]]
Si está utilizando ant como su sistema de compilación, la siguiente descripción de tareas puede ayudar a producir un archivo Java .properties que capture la información de compilación, que luego se puede incorporar a su compilación de varias maneras. [Ampliamos esta idea para incluir información derivada de hudson, gracias marty-lamb ].
Ant y maven (y Hudson y control de crucero) proporcionan medios fáciles para obtener números de compilación en un archivo .properties, o en un archivo .txt / .html. ¿Es esto "seguro" suficiente para evitar que sea manipulado de forma intencional o accidental? ¿Es mejor compilarlo en una clase de "control de versiones" en tiempo de compilación?
Afirmación: la numeración de compilación debe definirse / promulgarse en un sistema de integración continua como hudson . [Gracias a marcelo-morales ] Hemos tomado esta sugerencia, pero abre la cuestión de la ingeniería de lanzamiento: ¿cómo ocurre un lanzamiento? ¿Hay varios buildnumbers en una versión? ¿Existe una relación significativa entre buildnumbers de diferentes lanzamientos?
Pregunta: ¿Cuál es el objetivo detrás de un número de compilación? ¿Se usa para QA? ¿Cómo? ¿Lo utilizan principalmente los desarrolladores para eliminar la ambigüedad entre varias compilaciones durante el desarrollo, o más para QA determinar qué compilación obtuvo un usuario final? Si el objetivo es la reproducibilidad, en teoría esto es lo que debe proporcionar un número de versión de lanzamiento, ¿por qué no? (Por favor, responda esto como parte de sus respuestas a continuación, esto ayudará a iluminar las elecciones que ha hecho / sugerido ...)
Pregunta: ¿Hay un lugar para construir números en compilaciones manuales? ¿Es esto tan problemático que TODOS deberían usar una solución de CI?
Pregunta: ¿Deben registrarse los números de construcción en el SCM? Si el objetivo es identificar de manera confiable e inequívoca una compilación en particular, cómo hacer frente a una variedad de sistemas de compilación continuos o manuales que pueden bloquearse / reiniciarse / etc ...
Pregunta: ¿Debería un número de compilación ser breve y atractivo (es decir, un número entero que aumenta monótonamente) para que sea fácil incluir los nombres de los archivos para el archivo, sea fácil referirlo a la comunicación, etc. o debería ser largo y estar lleno de nombres de usuario, datestamps, nombres de máquinas, etc.
Pregunta: proporcione detalles sobre cómo la asignación de los números de compilación se ajusta a su proceso de lanzamiento automatizado más grande. Sí, amantes de los maven, sabemos que esto está hecho y hecho, pero no todos hemos bebido el kool-aid todavía ...
Realmente me gustaría desarrollar esto en una respuesta completa, al menos para el ejemplo concreto de nuestra configuración cvs / ant / hudson, para que alguien pueda construir una estrategia completa basada en esta pregunta. Marcaré como "La respuesta" a cualquiera que pueda dar una descripción de sopa a nueces para este caso particular (incluido el esquema de etiquetado de cvs, elementos de configuración de CI relevantes y procedimiento de liberación que pliega el número de compilación en el lanzamiento de manera programática accesible.) Si desea preguntar / responder por otra configuración en particular (por ejemplo, svn / maven / control de crucero) voy a vincular a la pregunta desde aquí. --JA
[EDIT 23 oct 09] Acepté la respuesta más votadas porque creo que es una solución razonable, mientras que varias de las otras respuestas también incluyen buenas ideas. Si alguien quiere intentar descifrar algunos de estos con marty-lamb ''s, consideraré aceptar uno diferente. La única preocupación que tengo con Marty-lamb es que no produce un número de compilación confiablemente serializado; depende de un reloj local en el sistema del constructor para proporcionar números de construcción no ambiguos, lo cual no es muy bueno.
[Editar Jul 10]
Ahora incluimos una clase como la siguiente. Esto permite que los números de versión se compilen en el ejecutable final. Se emiten diferentes formas de información de versión en datos de registro, productos de salida archivados a largo plazo, y se usan para rastrear nuestro análisis (a veces años después) de productos de salida para una compilación específica.
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();
}
}
Deje comentarios si esto merece convertirse en una discusión wiki.
Aquí está mi 2 centavos:
Mi script de compilación crea un número de compilación (con timestamp!) Cada vez que construyo la aplicación. Esto crea demasiados números, pero nunca demasiado pocos. Si tengo un cambio en el código, el número de compilación cambiará al menos una vez.
Versión del número de compilación con cada versión (aunque no entre medio). Cuando actualizo el proyecto y obtengo un nuevo número de compilación (porque alguien más hizo un lanzamiento), sobrescribo mi versión local y comienzo de nuevo. Esto puede llevar a un número de compilación menor, razón por la cual he incluido la marca de tiempo.
Cuando ocurre una publicación, el número de compilación se confirma como el último elemento en una única confirmación con el mensaje "compilar 1547". Después de eso, cuando se trata de un lanzamiento oficial, todo el árbol está etiquetado. De esta forma, el archivo de compilación siempre tiene todas las etiquetas y existe una asignación simple 1: 1 entre etiquetas y números de compilación.
[EDITAR] Desplegué un version.html con mis proyectos y luego, puedo usar un raspador para simplemente recolectar un mapa preciso en el que está instalado. Si usa Tomcat o similar, coloque el número de compilación y la marca de tiempo en el elemento de description
de web.xml . Recuerde: Nunca memorice nada cuando pueda hacer que una computadora lo haga por usted.
Así es como resolví esto:
- las fuentes se copian en el directorio de compilación
- entonces se aplica la anttask "versioninfo"
- compilar las fuentes modificadas
Aquí está el archivo java que almacena la información de la versión:
public class Settings {
public static final String VERSION = "$VERSION$";
public static final String DATE = "$DATE$";
}
Y aquí está la anttask "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 /1"
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>
Ejecutamos nuestra compilación a través de CruiseControl (inserte aquí su administrador de compilación favorito) y realizamos la compilación principal y las pruebas.
Luego incrementamos el número de versión usando Ant y BuildNumber y creamos un archivo de propiedad con esta información más la fecha de compilación y otros metadatos.
Tenemos una clase dedicada a leer esto y proporcionarlo a las GUI / registros, etc.
A continuación, empaquetamos todo esto y creamos un despliegue que vincula el número de compilación y la compilación correspondiente. Todos nuestros servidores vuelcan esta meta información en el arranque. Podemos retroceder a través de los registros de CruiseControl y vincular el número de compilación a la fecha y los registros.
Para varios de mis proyectos, capturo el número de revisión de subversión, el tiempo, el usuario que ejecutó la compilación y cierta información del sistema, los rellene en un archivo .properties que se incluye en el archivo jar de la aplicación, y lo leo en tiempo de ejecución.
El código de la hormiga se ve así:
<!-- 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>
Es simple extender esto para incluir cualquier información que desee agregar.
Software:
- SVN
- Hormiga
- Hudson, para una integración continua
- svntask, una tarea Ant para encontrar la revisión de SVN: http://code.google.com/p/svntask/
Hudson tiene tres builds / jobs: Continuous, Nightly y Release.
Para una construcción Continua / Nocturna: el número de compilación es la revisión SVN, que se encuentra usando svntask.
Para una compilación / tarea de lanzamiento: el número de compilación es el número de la versión, leída por Ant, desde un archivo de propiedades. El archivo de propiedades también se puede distribuir con el lanzamiento para mostrar el número de compilación en tiempo de ejecución.
El script de compilación Ant coloca el número de compilación en el archivo de manifiesto de los archivos jar / war que se crean durante la compilación. Se aplica a todas las construcciones.
Acción posterior a la construcción para compilaciones de Release, realizada fácilmente con un complemento de Hudson: etiquetar SVN con el número de compilación.
Beneficios:
- Para una versión dev de un jar / war, el desarrollador puede encontrar la revisión SVN del jar / war y buscar el código correspondiente en SVN
- Para un lanzamiento, la revisión SVN es la que corresponde a la etiqueta SVN que tiene el número de versión en ella.
Espero que esto ayude.
También estoy usando Hudson, aunque es un escenario mucho más simple:
Mi script Ant tiene un objetivo que se ve así:
<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 establece estas variables de entorno cada vez que se ejecuta mi trabajo.
En mi caso, este proyecto es una aplicación web y estoy incluyendo este archivo build-number.txt
en la carpeta raíz de la aplicación web; realmente no me importa quién lo vea.
No etiquetamos el control de origen cuando esto se hace porque ya tenemos nuestro trabajo de Hudson configurado para etiquetarlo con el número de compilación / marca de tiempo cuando la construcción es exitosa.
Mi solución solo cubre los números incrementales de desarrollo, no hemos llegado lo suficientemente lejos en el proyecto donde cubrimos los números de lanzamiento todavía.
También puede consultar el plugin BuildNumber Maven y la tarea Ant en un contenedor que se encuentra en http://code.google.com/p/codebistro/wiki/BuildNumber . Traté de hacerlo simple y directo. Es un archivo jar muy pequeño que solo depende de la línea de comando Subversion instalada.
Tu build.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>
...
Tu código java
String ver = MyClass.class.getPackage().getImplementationVersion();
- Los números de compilación deben asociarse con un servidor de integración continua como hudson . Use diferentes trabajos para diferentes ramas / equipos / distribuciones.
- Para mantener el número de versión en la compilación final, recomendaría usar maven para el sistema de compilación. Creará un archivo .properties archivado en el .jar / .war / .whatever-ar final en
META-INF/maven/<project group>/<project id>/pom.properties
. El archivo .properties contendrá la propiedad de la versión. - Como estoy recomendando a Maven, le recomiendo que revise el complemento de publicación para preparar el lanzamiento en el repositorio de origen y mantener sincronizadas las versiones.