Numérotation de build et de version pour les projets Java (ant, cvs, hudson)

134

Quelles sont les meilleures pratiques actuelles pour la numérotation systématique des builds et la gestion des numéros de version dans les projets Java? Plus précisément:

  • 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 la source / disponibles pour l'application d'exécution

  • Comment s'intégrer correctement au 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 à une infrastructure de construction continue

Il existe un certain nombre d'outils disponibles, et ant (le système de construction que nous utilisons) a une tâche qui maintiendra un numéro de version, mais on ne sait pas comment gérer cela avec plusieurs développeurs simultanés utilisant CVS, svn ou similaire. .

[ÉDITER]

Plusieurs bonnes et utiles réponses partielles ou spécifiques sont apparues ci-dessous, je vais donc en résumer quelques-unes. Il me semble qu'il n'y a pas vraiment de «bonnes pratiques» solides à ce sujet, mais plutôt une collection d'idées qui se chevauchent. Ci-dessous, retrouvez mes résumés et quelques questions qui en découlent auxquelles les gens pourraient essayer de répondre en guise de suivi. [Nouveau dans stackoverflow ... veuillez fournir des commentaires si je fais cela mal.]

  • Si vous utilisez SVN, la gestion des versions d'une extraction spécifique est fournie pour le trajet. La numérotation de build peut exploiter cela pour créer un numéro de build unique qui identifie l'extraction / révision spécifique. [CVS, que nous utilisons pour des raisons héritées, ne fournit pas tout à fait ce niveau de perspicacité ... une intervention manuelle avec des balises vous permet d'y arriver.]

  • Si vous utilisez maven comme système de construction, il existe un support pour la production d'un numéro de version à partir du SCM, ainsi qu'un module de publication pour produire automatiquement des 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 intégrées à votre construction de différentes manières. [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 régulateur de vitesse) fournissent des moyens simples pour obtenir 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 falsifié intentionnellement ou accidentellement? Est-il préférable de le compiler dans une classe de «gestion des versions» au moment de la construction?

  • Assertion: la numérotation des builds doit être définie / mise en œuvre dans un système d'intégration continue comme hudson . [Merci à marcelo-morales ] Nous avons pris cette suggestion, mais cela permet d'ouvrir la question de l'ingénierie de publication: comment une publication se produit-elle? Existe-t-il plusieurs numéros de build dans une version? Existe-t-il une relation significative entre les numéros de build de différentes versions?

  • 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 lever l'ambiguïté entre plusieurs builds pendant le développement, ou plus pour le contrôle qualité afin de déterminer quelle build un utilisateur final a? Si l'objectif est la reproductibilité, c'est en théorie ce que devrait fournir un numéro de version - pourquoi pas? (veuillez répondre à cela dans le cadre de vos réponses ci-dessous, cela aidera à éclairer les choix que vous avez faits / suggérés ...)

  • Question: Y a-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 build 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 qui peuvent planter / redémarrer / etc ...

  • Question: Si un numéro de build doit être court et doux (c'est-à-dire, un nombre entier croissant de manière monotone) afin qu'il soit facile de s'en tenir aux noms de fichiers pour l'archivage, de s'y référer facilement dans la communication, etc ... ou devrait-il être long et plein de noms d'utilisateur, horodatages, noms de machines, 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 publication automatisé plus large. Oui, amateurs de maven, nous savons que c'est fait et fait, mais nous n'avons pas tous encore bu le kool-aid ...

Je voudrais vraiment donner une réponse complète, au moins pour l'exemple concret de notre configuration cvs / ant / hudson, afin que quelqu'un puisse construire une stratégie complète basée sur cette question. Je marquerai comme "La réponse" toute personne qui peut donner une description de la soupe aux noix pour ce cas particulier (y compris le schéma de balisage cvs, les éléments de configuration CI pertinents et la procédure de publication qui replie le numéro de build dans la version de sorte qu'il soit programmé accessible.) Si vous voulez demander / répondre pour une autre configuration particulière (par exemple, svn / maven / régulateur de vitesse), je vais créer un lien vers la question à partir d'ici. --JA

[EDIT 23 octobre 09] J'ai accepté la réponse la plus votée parce que je pense que c'est une solution raisonnable, alors que plusieurs des autres réponses incluent également de bonnes idées. Si quelqu'un veut essayer de synthétiser certains d'entre eux avec des marty-lambs, j'envisagerai d'en accepter un autre. Le seul souci que j'ai avec marty-lamb's est qu'il ne produit pas de 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 génial.

[Modifier le 10 juillet]

Nous incluons maintenant une classe comme 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 retracer notre analyse (parfois des années plus tard) des produits de sortie jusqu'à 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.

Andersoj
la source
2
Pour les futurs lecteurs, veuillez noter que le numéro de révision dans le code que vous avez suggéré est celui du fichier et non la révision globale du référentiel. Pour plus d'informations, voir: subversion.apache.org/faq.html#version-value-in-source
maayank
2
Je me demande si quelqu'un a des approches simples similaires lors de l'utilisation gradleet / ou git?
bnjmn

Réponses:

63

Pour plusieurs de mes projets, je capture le numéro de révision de subversion, l'heure, l'utilisateur qui a exécuté la construction et certaines informations système, je les remplis dans un fichier .properties qui est inclus dans le fichier jar de l'application, et je lis ce fichier 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'étendre cela pour inclure toutes les informations que vous souhaitez ajouter.

Marty Lamb
la source
12
Pour une version multiplateforme, utilisez ceci au lieu de la propriété whoami ci-dessus: <entry key = "builder" value = "$ {user.name}" />
Ed Brannin
-1 pour avoir une solution dépendante de la plate-forme. une bonne façon d'avoir toutes les propriétés disponibles dans un fichier est: `<property name =" antprops.file "location =" $ {build.temp.project.dir} /used_ant.properties "/> <echoproperties destfile =" $ {antprops.file} "/> <! - trier le fichier, UNIQUEMENT pour ant 1.7.0 et plus récent !!! -> <concat> <union> <sort> <tokens> <file file =" $ {antprops .file} "/> <linetokenizer includedelims =" true "/> </tokens> </sort> </union> </concat>`
raudi
Vous engagez-vous à nouveau après avoir fait cela? Ou est-ce fait avant de valider vos révisions réelles?
Charles Wood
6
Comment faire cela pour le dépôt git?
TechCrunch
46

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();
user146146
la source
6
+1 pour utiliser une propriété que Java prend déjà en charge avec une méthode comme celle-là.
Ed Brannin
2
-1 pour avoir le numéro de build dans le build.xml et non dans un fichier de propriétés séparé
raudi
6
  • Les numéros de build doivent être associés à un serveur d'intégration continue comme hudson . Utilisez différents emplois pour différentes branches / équipes / distributions.
  • Pour conserver le numéro de version dans la version finale, je recommanderais simplement d'utiliser maven pour le système de construction. Il créera un fichier .properties archivé dans le fichier .jar / .war / .whatever-ar sur META-INF/maven/<project group>/<project id>/pom.properties. Le fichier .properties contiendra la propriété version.
  • Puisque je recommande maven, je vous exhorte à consulter le plugin de publication pour préparer la version sur le référentiel source et garder les versions synchronisées.
H Marcelo Morales
la source
6

Logiciel:

  • SVN
  • Fourmi
  • Hudson, pour une intégration continue
  • svntask, une tâche Ant pour trouver la révision SVN: http://code.google.com/p/svntask/

Hudson a trois builds / jobs: Continuous, Nightly et Release.

Pour une build Continuous / Nightly: le numéro de build est la révision SVN, trouvée en utilisant svntask.

Pour une version / un travail de version: le 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 la version pour afficher le numéro de build au moment de l'exécution.

Le script de build Ant place le numéro de build dans le fichier manifeste des fichiers jar / war créés lors de la build. S'applique à toutes les versions.

Action post-build pour les builds Release, réalisée facilement à l'aide d'un plug-in Hudson: balisez SVN avec le numéro de build.

Avantages:

  • Pour une version de développement d'un jar / war, le développeur peut trouver la révision SVN depuis le fichier jar / war et rechercher le code correspondant dans SVN
  • Pour une version, la révision SVN est celle qui correspond à la balise SVN qui contient le numéro de version.

J'espère que cela t'aides.

Raleigh
la source
Avez-vous la version / le travail de version vérifiant le fichier de propriétés de numéro de construction / version dans SVN?
matt b
Pour les builds continus, le numéro de build est le numéro de révision SVN. Donc, rien à archiver. Pour les versions de version, c'est le fichier archivé qui est utilisé pour obtenir le numéro de version. En règle générale, l'ingénieur de publication ou toute autre personne souhaite mettre à jour ce fichier bien avant la publication.
Raleigh
4

J'utilise également Hudson, bien que dans un scénario beaucoup plus simple:

Mon script Ant contient une cible qui ressemble à:

<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 s'exécute.

Dans mon cas, ce projet est une application Web et j'inclus ce build-number.txtfichier dans le dossier racine de l'application Web - je me fiche de savoir qui le voit.

Nous ne marquons pas le contrôle de source lorsque cela est fait, car nous avons déjà configuré notre travail Hudson pour le marquer avec le numéro de build / l'horodatage lorsque la construction est réussie.

Ma solution ne couvre que les numéros de build incrémentiels pour le développement, nous ne sommes pas allés assez loin dans le projet où nous couvrons encore les numéros de version.

mat b
la source
de plus (au moins dans les versions actuelles de jenkins), vous pouvez vérifier les propriétés: `env.SVN_URL_1 env.SVN_REVISION_1 env.SVN_URL_2 env.SVN_REVISION_2 etc.
raudi
3

Vous pouvez également jeter un œil au plugin BuildNumber Maven et à la tâche Ant dans un fichier jar disponible à l' adresse 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 installée par Subversion.

Sasha O
la source
3

Voici comment j'ai résolu ceci:

  • les sources sont copiées dans le répertoire de construction
  • puis l'anttask "versioninfo" est appliquée
  • compiler les sources modifiées

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>
Flederohr
la source
2

Voici mes 2 cents:

  • Mon script de build crée un numéro de build (avec horodatage!) À chaque fois que je crée 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 j'obtiens 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 numéro de build inférieur, c'est pourquoi j'ai inclus l'horodatage.

  • Lorsqu'une version se produit, le numéro de build est validé comme dernier élément d'une seule validation avec le message "build 1547". Après cela, lorsqu'il s'agit d'une version officielle, l'arbre entier est étiqueté. De cette façon, le fichier de construction a toujours toutes les balises et il existe un simple mappage 1: 1 entre les balises et les numéros de build.

[EDIT] Je déploie une version.html avec mes projets et ensuite, je peux utiliser un grattoir pour simplement collecter une carte précise ce qui est installé où. Si vous utilisez Tomcat ou similaire, placez le numéro de build et l'horodatage dans l' descriptionélément de web.xml . N'oubliez pas: ne mémorisez jamais rien lorsque vous pouvez demander à un ordinateur de le faire pour vous.

Aaron Digulla
la source
Oui ... c'est ce que nous avons aussi. Et c'est pénible à utiliser, quand vous devez vous rappeler quelle version incroyablement longue a été déployée ici ou là. Trop de chiffres, c'est aussi un problème ...
Varkhan
@Varkhan: Pourquoi? Il suffit de mettre la version quelque part où un grattoir HTML peut la récupérer pour vous ...
Aaron Digulla
1

Nous exécutons notre build via CruiseControl (insérez votre gestionnaire de build préféré ici), et effectuons le build principal 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 ces informations plus la date de construction et d'autres métadonnées.

Nous avons une classe dédiée à la lecture et à la fourniture aux interfaces graphiques / journaux, etc.

Nous emballons ensuite tout cela et construisons un déployable liant le numéro de build et la build correspondante. Tous nos serveurs vident ces méta-informations au démarrage. Nous pouvons parcourir les journaux CruiseControl et lier le numéro de build à la date et aux enregistrements.

Brian Agnew
la source
Conseil: Si vous utilisez CC et le BuildNumber d'Ant, vous pouvez utiliser PropertyFileLabelIncrementer pour synchroniser les étiquettes CC avec le numéro de build.
Jeffrey Fredrick