Numerazione di build e versioni per progetti Java (ant, cvs, hudson)


134

Quali sono le migliori pratiche attuali per la numerazione sistematica delle build e la gestione dei numeri di versione nei progetti Java? In particolare:

  • Come gestire i numeri di build in modo sistematico in un ambiente di sviluppo distribuito

  • Come mantenere i numeri di versione in origine / disponibili per l'applicazione runtime

  • Come integrarsi correttamente con il repository di origine

  • Come gestire più automaticamente i numeri di versione rispetto ai tag del repository

  • Come integrarsi con l'infrastruttura a build continua

Ci sono molti strumenti disponibili e ant (il sistema di build che stiamo usando) ha un compito che manterrà un numero di build, ma non è chiaro come gestirlo con più sviluppatori simultanei che usano CVS, svn o simili .

[MODIFICARE]

Di seguito sono apparse diverse risposte parziali o specifiche valide e utili, quindi ne riassumerò alcune. Mi sembra che non ci sia davvero una "buona pratica" forte su questo, piuttosto una raccolta di idee sovrapposte. Di seguito, trova i miei riassunti e alcune domande che ne conseguono a cui la gente potrebbe provare a rispondere come follow-up. [Nuovo su StackOverflow ... si prega di fornire commenti se sto sbagliando.]

  • Se stai usando SVN, arriva il controllo delle versioni di un checkout specifico. La numerazione delle build può sfruttare questa possibilità per creare un numero di build univoco che identifichi il checkout / la revisione specifici. [CVS, che stiamo usando per motivi legacy, non fornisce questo livello di intuizione ... l'intervento manuale con i tag ti porta da lì.]

  • Se si utilizza Maven come sistema di compilazione, è disponibile il supporto per la produzione di un numero di versione da SCM, nonché un modulo di rilascio per la produzione automatica di rilasci. [Non possiamo usare Maven, per una serie di ragioni, ma questo aiuta chi può farlo. [Grazie a marcelo-morales ]]

  • Se si utilizza la formica come sistema di compilazione, la seguente descrizione dell'attività può aiutare a produrre un file Java .properties che acquisisce le informazioni di compilazione, che possono quindi essere piegate nella compilazione in diversi modi. [Abbiamo ampliato questa idea per includere informazioni derivate da hudson, grazie marty-lamb ].

  • Ant e maven (e hudson e cruise control) forniscono mezzi facili per ottenere i numeri di build in un file .properties o in un file .txt / .html. Questo è "sicuro" abbastanza per evitare che venga manomesso intenzionalmente o accidentalmente? È meglio compilarlo in una classe di "versioning" al momento della compilazione?

  • Asserzione: la numerazione delle build deve essere definita / attuata in un sistema di integrazione continua come hudson . [Grazie a marcelo-morales ] Abbiamo preso questo suggerimento, ma apre la domanda di ingegneria del rilascio: come avviene un rilascio? Ci sono più buildnumber in una versione? Esiste una relazione significativa tra i numeri di build di versioni diverse?

  • Domanda: Qual è l'obiettivo alla base di un numero di build? È usato per il controllo qualità? Come? Viene utilizzato principalmente dagli sviluppatori per chiarire le ambiguità tra più build durante lo sviluppo o più per il QA per determinare quale build ha ottenuto un utente finale? Se l'obiettivo è la riproducibilità, in teoria questo è ciò che dovrebbe fornire un numero di versione di rilascio - perché non lo fa? (per favore rispondi a questo come parte delle tue risposte qui sotto, ti aiuterà a illuminare le scelte che hai fatto / suggerito ...)

  • Domanda: c'è un posto per i numeri di build nelle build manuali? È così problematico che TUTTI dovrebbero usare una soluzione CI?

  • Domanda: i numeri di build devono essere archiviati in SCM? Se l'obiettivo è identificare in modo affidabile e inequivocabile una particolare build, come far fronte a una varietà di sistemi di build continui o manuali che potrebbero bloccarsi / riavviare / ecc ...

  • Domanda: se un numero di build deve essere breve e dolce (ad es. Intero monotonicamente crescente) in modo che sia facile attenersi ai nomi dei file per l'archiviazione, facile da consultare nella comunicazione, ecc ... o dovrebbe essere lungo e pieno di nomi utente, datestamp, nomi di macchine, ecc.?

  • Domanda: Fornisci dettagli su come l'assegnazione dei numeri di build si adatta al tuo più ampio processo di rilascio automatico. Sì, amanti della cucina, sappiamo che questo è fatto e fatto, ma non tutti abbiamo ancora bevuto il kool-aid ...

Mi piacerebbe davvero approfondire questo in una risposta completa, almeno per l'esempio concreto della nostra configurazione cvs / ant / hudson, così qualcuno potrebbe costruire una strategia completa basata su questa domanda. Contrassegnerò come "la risposta" chiunque sia in grado di fornire una descrizione dettagliata per questo caso particolare (incluso schema di codifica cvs, elementi di configurazione CI rilevanti e procedura di rilascio che piega il numero di build nel rilascio in modo che sia programmaticamente accessibile.) Se vuoi chiedere / rispondere per un'altra configurazione particolare (ad esempio, svn / maven / cruise control), collegherò alla domanda da qui. --JA

[EDIT 23 ott 09] Ho accettato la risposta più votata perché penso che sia una soluzione ragionevole, mentre molte delle altre risposte includono anche buone idee. Se qualcuno vuole fare una pausa nel sintetizzare alcuni di questi con quelli di Marty-Lamb , prenderò in considerazione l'idea di accettarne uno diverso. L'unica preoccupazione che ho con marty-lamb's è che non produce un numero di build serializzato in modo affidabile - dipende da un orologio locale nel sistema del costruttore per fornire numeri di build inequivocabili, il che non è eccezionale.

[Modifica 10 lug]

Ora includiamo una classe come la seguente. Ciò consente di compilare i numeri di versione nell'eseguibile finale. Forme diverse di informazioni sulla versione vengono emesse nei dati di registrazione, nei prodotti di output archiviati a lungo termine e utilizzate per tracciare la nostra analisi (a volte anni dopo) dei prodotti di output su una build specifica.

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();
  }
}

Lascia commenti se questo merita di diventare una discussione wiki.


2
Per i lettori futuri, si noti che il numero di revisione nel codice che hai suggerito è del file, non la revisione globale del repository. Per maggiori informazioni, consultare: subversion.apache.org/faq.html#version-value-in-source
maayank

2
Mi chiedo se qualcuno ha approcci semplici simili quando si usa gradlee / o git?
bnjmn,

Risposte:


63

Per molti dei miei progetti acquisisco il numero di revisione della sovversione, il tempo, l'utente che ha eseguito la build e alcune informazioni di sistema, li inserisco in un file .properties che viene incluso nel vaso dell'applicazione e leggo quel vaso in fase di esecuzione.

Il codice della formica è simile al seguente:

<!-- 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>

È semplice estenderlo per includere qualsiasi informazione che potresti voler aggiungere.


12
Per una versione multipiattaforma, utilizzare questo invece della proprietà whoami sopra: <entry key = "builder" value = "$ {user.name}" />
Ed Brannin,

-1 per avere una soluzione dipendente dalla piattaforma. un buon modo per avere tutte le proprietà di formiche disponibili in un file è: `<property name =" antprops.file "location =" $ {build.temp.project.dir} /used_ant.properties "/> <echoproperties destfile =" $ {antprops.file} "/> <! - ordina il file, SOLO per formica 1.7.0 e successive !!! -> <concat> <union> <sort> <tokens> <file file =" $ {antprops .file} "/> <linetokenizer includedelims =" true "/> </tokens> </sort> </union> </concat>`
raudi

Ti impegni nuovamente dopo averlo fatto? O è fatto prima di eseguire le tue effettive revisioni?
Charles Wood,

6
Come fare questo per il repository git?
TechCrunch,

46

Il tuo 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>
...

Il tuo codice Java

String ver = MyClass.class.getPackage().getImplementationVersion();

6
+1 per l'utilizzo di una proprietà che Java già supporta con un metodo simile.
Ed Brannin,

2
-1 per avere il numero di build in build.xml e non in un file di proprietà separato
raudi

6
  • I numeri di build devono essere associati a un server di integrazione continua come hudson . Utilizzare lavori diversi per diverse filiali / squadre / distribuzioni.
  • Per mantenere il numero di versione nella build finale, consiglierei di usare solo Maven per il sistema di build. Creerà un file .properties archiviato nell'ultimo .jar / .war / .whatever-ar su META-INF/maven/<project group>/<project id>/pom.properties. Il file .properties conterrà la proprietà version.
  • Dal momento che sto raccomandando Maven, ti esorto a dare un'occhiata al plugin di rilascio per preparare la versione sul repository di origine e mantenere sincronizzate le versioni.

6

Software:

  • SVN
  • Formica
  • Hudson, per l'integrazione continua
  • svntask, un'attività Ant per trovare la revisione SVN: http://code.google.com/p/svntask/

Hudson ha tre build / lavori: Continuo, Notturno e Rilascio.

Per una build continua / notturna: il numero di build è la revisione SVN, trovata usando svntask.

Per un build / processo di rilascio: il numero di build è il numero di rilascio, letto da Ant, da un file Proprietà. Il file delle proprietà può anche essere distribuito con il rilascio per visualizzare il numero di build in fase di esecuzione.

Lo script di compilazione Ant inserisce il numero di build nel file manifest dei file jar / war che vengono creati durante la compilazione. Si applica a tutte le build.

Azione post-build per build di Release, eseguita facilmente utilizzando un plug-in Hudson: tag SVN con il numero di build.

Benefici:

  • Per una versione di sviluppo di un jar / war, lo sviluppatore può trovare la revisione SVN dal jar / war e cercare il codice corrispondente in SVN
  • Per un rilascio, la revisione SVN è quella corrispondente al tag SVN che contiene il numero di rilascio.

Spero che questo ti aiuti.


Hai il build / job di rilascio che controlla il file delle proprietà del numero di build / release in SVN?
matt b

Per build continue, il numero di build è il numero di revisione SVN. Quindi, nulla da archiviare. Per i build di rilascio, è il file di check-in utilizzato per ottenere il numero di build. In genere, l'ingegnere del rilascio o chiunque altro vorrebbe aggiornare questo file con largo anticipo rispetto al rilascio.
Raleigh,

4

Sto usando anche Hudson, anche se uno scenario molto più semplice:

Il mio script Ant ha un target al suo interno che assomiglia a:

<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 imposta queste variabili di ambiente per me ogni volta che il mio lavoro viene eseguito.

Nel mio caso, questo progetto è una webapp e sto includendo questo build-number.txtfile nella cartella principale della webapp - non mi interessa davvero chi lo vede.

Non eseguiamo il tag del controllo del codice sorgente quando questo è fatto perché abbiamo già impostato il nostro lavoro Hudson per taggarlo con il numero di build / timestamp quando la build ha esito positivo.

La mia soluzione copre solo i numeri di build incrementali per lo sviluppo, non siamo arrivati ​​abbastanza avanti nel progetto in cui stiamo ancora coprendo i numeri di rilascio.


inoltre (almeno nelle versioni jenkins attuali) è possibile verificare le proprietà: `env.SVN_URL_1 env.SVN_REVISION_1 env.SVN_URL_2 env.SVN_REVISION_2 ecc.
raudi


3

Ecco come ho risolto questo:

  • i sorgenti vengono copiati nella directory di compilazione
  • quindi viene applicato l'antask "versioninfo"
  • compilare le fonti modificate

Ecco il file java che memorizza le informazioni sulla versione:

public class Settings {

    public static final String VERSION = "$VERSION$";
    public static final String DATE = "$DATE$";

}

Ed ecco l'antask "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>

2

Ecco i miei 2 centesimi:

  • Il mio script di creazione crea un numero di build (con timestamp!) Ogni volta che costruisco l'app. Questo crea troppi numeri ma mai troppo pochi. Se ho una modifica nel codice, il numero di build cambierà almeno una volta.

  • I versione il numero di build con ogni versione (anche se non in mezzo). Quando aggiorno il progetto e ottengo un nuovo numero di build (perché qualcun altro ha rilasciato una versione), sovrascrivo la mia versione locale e ricomincio. Questo può portare a un numero di build inferiore, motivo per cui ho incluso il timestamp.

  • Quando si verifica una versione, il numero di build viene eseguito il commit come ultimo elemento in un singolo commit con il messaggio "build 1547". Dopodiché, quando si tratta di una versione ufficiale, l'intero albero viene taggato. In questo modo, il file di build ha sempre tutti i tag e esiste una semplice mappatura 1: 1 tra tag e numeri di build.

[EDIT] Distribuisco un version.html con i miei progetti e quindi posso usare uno scraper per raccogliere semplicemente una mappa accurata di ciò che è installato dove. Se stai usando Tomcat o simili, inserisci il numero di build e il timestamp descriptionnell'elemento di web.xml . Ricorda: non memorizzare mai nulla quando puoi avere un computer per te.


Sì ... è quello che abbiamo anche noi. Ed è un dolore da usare, quando devi ricordare quale build incredibilmente lunga è stata distribuita qui o là. Anche troppi numeri sono un problema ...
Varkhan,

@Varkhan: Perché?
Inserisci

1

Eseguiamo la nostra build tramite CruiseControl (inserisci qui il tuo build manager preferito) ed eseguiamo la build e i test principali.

Quindi incrementiamo il numero di versione usando Ant e BuildNumber e creiamo un file di proprietà con queste informazioni più la data di compilazione e altri metadati.

Abbiamo una classe dedicata a leggere questo e fornirlo a GUI / registri ecc.

Quindi impacchettiamo tutto questo e costruiamo un deployable legando insieme il numero di build e la build corrispondente. Tutti i nostri server scaricano queste meta informazioni all'avvio. Possiamo tornare indietro attraverso i registri di CruiseControl e legare il numero di build alla data e ai check-in.


Suggerimento: se si utilizza CC e BuildNumber di Ant, è possibile utilizzare PropertyFileLabelIncrementer per mantenere sincronizzate le etichette CC con il numero di build.
Jeffrey Fredrick,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.