web-dev-qa-db-fra.com

Construction et numérotation de version pour Java projets (ant, cvs, hudson)

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.

131
andersoj

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.

63
Marty Lamb

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();
46
user146146
  • Les numéros de build doivent être associés à un serveur d'intégration continue tel que hudson . Utilisez différents travaux pour différentes branches/équipes/distributions.
  • Pour conserver le numéro de version dans la version finale, je vous recommande simplement d'utiliser maven pour le système de construction. Il créera un fichier .properties archivé dans le fichier final .jar/.war/.w 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 prie de consulter le plugin release pour préparer la publication sur le référentiel source et conserver les versions synchronisées.
6
H Marcelo Morales

Logiciel:

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

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:

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

J'espère que cela t'aides.

6
Raleigh

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.

4
matt b

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.

3
Sasha O

Voici comment j'ai résolu ceci:

  • les sources sont copiées dans le répertoire de construction
  • alors l'anttask "versioninfo" est appliqué
  • 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>
3
flederohr

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.

2
Aaron Digulla

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.

1
Brian Agnew