Quelles sont les meilleures pratiques actuelles en matière de numérotation de construction systématique et de gestion du numéro de version dans les Java?)?
Comment gérer systématiquement les numéros de build dans un environnement de développement distribué
Comment gérer les numéros de version dans les sources/disponibles pour l'application d'exécution
Comment intégrer correctement le référentiel source
Comment gérer plus automatiquement les numéros de version par rapport aux balises de référentiel
Comment s'intégrer à l'infrastructure de construction continue
Ant (le système de construction que nous utilisons) a une tâche qui permet de conserver un numéro de construction, mais la manière de le gérer avec plusieurs développeurs simultanés utilisant CVS, svn ou similaire n'est pas claire. .
[MODIFIER]
Plusieurs réponses partielles ou spécifiques utiles et utiles sont apparues ci-dessous, je vais donc en résumer quelques-unes. Il me semble qu’il n’ya pas vraiment de "meilleure pratique" en la matière, mais plutôt un ensemble d’idées chevauchantes. Ci-dessous, retrouvez mes résumés et quelques questions qui en résultent, auxquels les gens pourraient essayer de répondre en tant que suivis. [Nouveau sur stackoverflow ... veuillez commenter si je ne le fais pas correctement.]
Si vous utilisez SVN, le contrôle de version d'une caisse spécifique accompagne le trajet. La numérotation des versions peut exploiter cette possibilité pour créer un numéro de version unique identifiant l'extraction/la révision spécifique. [CVS, que nous utilisons pour des raisons héritées du passé, n'apporte pas ce niveau de perspicacité… une intervention manuelle avec des balises vous met en partie à mi-chemin.]
Si vous utilisez maven en tant que système de génération, il est possible de générer un numéro de version à partir du SCM, ainsi qu'un module de version pour la production automatique de versions. [Nous ne pouvons pas utiliser Maven, pour diverses raisons, mais cela aide ceux qui le peuvent. [Merci à marcelo-morales ]]
Si vous utilisez ant comme système de construction, la description de tâche suivante peut vous aider à produire un fichier Java .properties capturant les informations de construction, qui peuvent ensuite être pliées dans votre construction. de plusieurs façons. [Nous avons développé cette idée pour inclure des informations dérivées de hudson, merci marty-lamb ].
Ant et maven (et hudson et cruise control) permettent d’obtenir facilement des numéros de build dans un fichier .properties ou dans un fichier .txt/.html. Est-ce suffisamment "sûr" pour l'empêcher d'être manipulé intentionnellement ou accidentellement? Est-il préférable de le compiler dans une classe "versioning" au moment de la construction?
Assertion: La numérotation des versions doit être définie/définie dans un système d'intégration continue tel que hudson . [Merci à marcelo-morales ] Nous avons pris cette suggestion, mais elle ouvre la question technique des versions: comment se produit une version? Y a-t-il plusieurs numéros de construction dans une version? Existe-t-il une relation significative entre les numéros de construction de versions différentes?
Question: Quel est l'objectif derrière un numéro de build? Est-il utilisé pour l'assurance qualité? Comment? Est-il principalement utilisé par les développeurs pour distinguer des versions multiples lors du développement, ou davantage pour le contrôle qualité afin de déterminer la version obtenue par l'utilisateur final? Si l'objectif est la reproductibilité, c'est en théorie ce qu'un numéro de version devrait fournir - pourquoi ne le fait-il pas? (répondez s'il vous plaît à cela dans le cadre de vos réponses ci-dessous, cela aidera à éclairer les choix que vous avez faits/suggérés ...)
Question: Existe-t-il une place pour les numéros de build dans les builds manuels? Est-ce si problématique que TOUT LE MONDE devrait utiliser une solution CI?
Question: Les numéros de construction doivent-ils être enregistrés dans le SCM? Si l'objectif est d'identifier de manière fiable et sans ambiguïté une construction particulière, comment faire face à une variété de systèmes de construction continue ou manuelle susceptibles de planter/de redémarrer/etc.
Question: Un numéro de build doit-il être court et simple (c’est-à-dire un nombre entier augmentant de manière monotone) de sorte qu’il soit facile de coller des noms de fichiers pour les archives, de s'y référer facilement dans la communication, etc. horodatage, noms de machine, etc.?
Question: Veuillez fournir des détails sur la manière dont l'attribution des numéros de build s'intègre dans votre processus de validation automatisé plus large. Oui, maven amoureux, nous savons que cela est fait et fait, mais nous n'avons pas tous bu le kool-aid jusqu'à maintenant ...
J'aimerais vraiment en préciser la réponse, du moins pour l'exemple concret de notre configuration cvs/ant/hudson, afin que quelqu'un puisse élaborer une stratégie complète à partir de cette question. Je qualifierai de "La réponse" tous ceux qui peuvent donner une description détaillée de ce cas particulier (y compris le schéma de marquage CVS, les éléments de configuration CI pertinents et la procédure de publication qui insère le numéro de version dans la version de sorte qu'il soit programmé accessible.) Si vous souhaitez demander/répondre pour une autre configuration particulière (par exemple, svn/maven/régulateur de vitesse), je vous renvoie à la question à partir de maintenant. --JA
[EDIT 23 oct. 09] J'ai accepté la réponse qui a obtenu le vote le plus élevé parce que je pense que c'est une solution raisonnable, alors que plusieurs autres réponses incluent également de bonnes idées. Si quelqu'un veut essayer de synthétiser certaines de celles-ci avec marty-lamb , j'envisagerai d'en accepter une autre. La seule préoccupation que j'ai avec Marty-Lamb est qu'elle ne produit pas un numéro de build sérialisé de manière fiable. Cela dépend d'une horloge locale du système du constructeur pour fournir des numéros de build sans ambiguïté, ce qui n'est pas terrible.
[Edit Jul 10]
Nous incluons maintenant une classe comme celle ci-dessous. Cela permet aux numéros de version d'être compilés dans l'exécutable final. Différentes formes d'informations de version sont émises dans les données de journalisation, les produits de sortie archivés à long terme, et utilisées pour suivre notre analyse (parfois des années plus tard) des produits de sortie par rapport à une version spécifique.
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();
}
}
Laissez des commentaires si cela mérite de devenir une discussion wiki.
Pour plusieurs de mes projets, je saisis le numéro de révision Subversion, l’heure, l’utilisateur qui a exécuté la construction, ainsi que des informations système, puis les renseigne dans un fichier .properties inclus dans le fichier jar de l’application, et le lit au moment de l’exécution.
Le code de la fourmi ressemble à ceci:
<!-- 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>
Il est simple d’élargir ceci pour inclure toutes les informations que vous pourriez vouloir ajouter.
Votre 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>
...
Votre code Java
String ver = MyClass.class.getPackage().getImplementationVersion();
META-INF/maven/<project group>/<project id>/pom.properties
. Le fichier .properties contiendra la propriété version.Logiciel:
Hudson a trois versions/emplois: Continu, Nuit et Sortie.
Pour une construction continue/nocturne: numéro de construction est la révision SVN, trouvée à l'aide de svntask.
Pour une version build/job: numéro de version est le numéro de version, lu par Ant, à partir d'un fichier de propriétés. Le fichier de propriétés peut également être distribué avec l'édition pour afficher le numéro de version lors de l'exécution.
Le script de génération Ant place le numéro de construction dans le fichier manifeste des fichiers jar/war créés lors de la construction. S'applique à toutes les générations.
Action post-build pour les builds Release, réalisée facilement à l'aide d'un plug-in Hudson: tag SVN avec le numéro de build.
Avantages:
J'espère que cela t'aides.
J'utilise également Hudson, bien que le scénario soit beaucoup plus simple:
Mon script Ant a une cible qui ressemble à ceci:
<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 définit ces variables d'environnement pour moi chaque fois que mon travail est exécuté.
Dans mon cas, ce projet est une webapp et j'inclus ceci build-number.txt
fichier dans le dossier racine de la webapp - je me fiche de qui le voit.
Nous ne balisons pas le contrôle de source lorsque cela est fait car notre tâche Hudson est déjà configurée pour le baliser avec le numéro de construction/l'horodatage lorsque la génération réussit.
Ma solution ne couvre que les numéros de construction incrémentiels pour le développement, nous n'avons pas encore suffisamment progressé dans le projet où nous couvrons actuellement les numéros de version.
Vous pouvez également consulter le plug-in BuildNumber Maven et la tâche Ant dans l'un des fichiers jar figurant à l'emplacement http://code.google.com/p/codebistro/wiki/BuildNumber . J'ai essayé de le rendre simple et direct. C'est un très petit fichier jar qui ne dépend que de la ligne de commande Subversion installée.
Voici comment j'ai résolu ceci:
Voici le fichier Java) stockant les informations de version:
public class Settings {
public static final String VERSION = "$VERSION$";
public static final String DATE = "$DATE$";
}
Et voici l'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>
Voici mes 2 centimes:
Mon script de construction crée un numéro de construction (avec horodatage!) À chaque fois que je construis l'application. Cela crée trop de nombres mais jamais trop peu. Si j'ai un changement dans le code, le numéro de build changera au moins une fois.
Je version le numéro de version avec chaque version (mais pas entre les deux). Lorsque je mets à jour le projet et que je reçois un nouveau numéro de version (parce que quelqu'un d'autre a publié une version), j'écrase ma version locale et je recommence. Cela peut conduire à un nombre de build inférieur, raison pour laquelle j'ai inclus l'horodatage.
Lorsqu’une version est publiée, le numéro de build est validé comme dernier élément d’un commit avec le message "build 1547". Après cela, quand il s'agit d'une version officielle, tout l'arbre est étiqueté. De cette façon, le fichier de construction contient toujours toutes les balises et il existe un simple mappage 1: 1 entre les balises et les numéros de construction.
[EDIT] Je déploie un fichier version.html avec mes projets, puis je peux utiliser un grattoir pour collecter simplement une carte précise de ce qui est installé où. Si vous utilisez Tomcat ou similaire, mettez le numéro de build et l'horodatage dans l'élément description
de web.xml . Rappelez-vous: ne mémorisez jamais quoi que ce soit lorsque vous pouvez avoir un ordinateur qui le fait pour vous.
Nous exécutons notre construction via CruiseControl (insérez ici votre gestionnaire de construction préféré) et effectuons la construction principale et les tests.
Nous incrémentons ensuite le numéro de version à l'aide de Ant et BuildNumber et créons un fichier de propriétés avec cette information, ainsi que la date de construction et d'autres métadonnées.
Nous avons une classe dédiée à lire ceci et à le fournir aux interfaces graphiques/journaux, etc.
Nous emballons ensuite tout cela et construisons un déploiement déployant un lien entre le numéro de build et le build correspondant. Tous nos serveurs renvoient cette méta-information au démarrage. Nous pouvons parcourir les journaux CruiseControl et associer le numéro de construction à la date et aux enregistrements.