it-swarm.com.de

Build- und Versionsnummerierung für Java Projekte (ant, cvs, hudson)

Was sind die aktuellen Best Practices für systematische Build-Nummerierung und Versionsnummernverwaltung in Java) -Projekten?

  • Systematisches Verwalten von Build-Nummern in einer verteilten Entwicklungsumgebung

  • So pflegen Sie die Versionsnummern in source /, die der Laufzeitanwendung zur Verfügung stehen

  • So integrieren Sie sich richtig in das Quell-Repository

  • So verwalten Sie Versionsnummern im Vergleich zu Repository-Tags automatisierter

  • Integration in eine Continuous Build-Infrastruktur

Es gibt eine ganze Reihe von Tools, und ant (das von uns verwendete Build-System) hat eine Aufgabe, bei der die Build-Nummer beibehalten wird. Es ist jedoch nicht klar, wie dies mit mehreren Entwicklern gleichzeitig mithilfe von CVS, svn oder ähnlichem verwaltet werden soll .

[BEARBEITEN]

Im Folgenden sind einige gute und hilfreiche Teil- oder spezifische Antworten aufgeführt, von denen ich einige zusammenfassen werde. Es klingt für mich so, als gäbe es nicht wirklich eine gute "Best Practice", sondern eine Sammlung sich überschneidender Ideen. Nachfolgend finden Sie meine Zusammenfassungen und einige daraus resultierende Fragen, die die Leute möglicherweise als Follow-up beantworten möchten. [Neu bei Stackoverflow ... Bitte geben Sie Kommentare an, wenn ich das falsch mache.]

  • Wenn Sie SVN verwenden, wird die Versionierung eines bestimmten Checkout für die Fahrt bereitgestellt. Die Build-Nummerierung kann dies ausnutzen, um eine eindeutige Build-Nummer zu erstellen, die den jeweiligen Checkout/die Revision identifiziert. [CVS, das wir aus alten Gründen verwenden, bietet nicht genau diese Einsicht ... Manuelle Eingriffe mit Tags führen Sie auf halbem Weg dorthin.]

  • Wenn Sie maven als Build-System verwenden, gibt es Unterstützung für die Erstellung einer Versionsnummer aus dem SCM sowie ein Freigabemodul für die automatische Erstellung von Freigaben. [Wir können Maven aus verschiedenen Gründen nicht verwenden, aber das hilft denen, die es können. [Danke an marcelo-morales ]]

  • Wenn Sie ant als Build-System verwenden, kann die folgende Taskbeschreibung dazu beitragen, die Build-Informationen einer Java .properties-Datei zu erfassen, die dann in Ihren Build eingefügt werden kann In vielerlei Hinsicht. [Wir haben diese Idee erweitert, um von Hudson abgeleitete Informationen einzuschließen, danke marty-lamb ].

  • Ant und Maven (sowie Hudson und Tempomat) bieten eine einfache Möglichkeit, Build-Nummern in eine .properties-Datei oder in eine .txt/.html-Datei zu übertragen. Ist dies "sicher" genug, um zu verhindern, dass es absichtlich oder versehentlich manipuliert wird? Ist es besser, es beim Erstellen in eine "Versionierungs" -Klasse zu kompilieren?

  • Behauptung: Die Build-Nummerierung sollte in einem kontinuierlichen Integrationssystem wie hudson definiert/festgelegt werden. [Danke an marcelo-morales ] Wir haben diesen Vorschlag aufgegriffen, aber er öffnet die Frage der Release-Entwicklung: Wie kommt es zu einem Release? Enthält ein Release mehrere Buildnummern? Gibt es eine sinnvolle Beziehung zwischen Buildnummern aus unterschiedlichen Releases?

  • Frage: Was ist das Ziel einer Build-Nummer? Wird es für die Qualitätssicherung verwendet? Wie? Wird es in erster Linie von Entwicklern verwendet, um zwischen mehreren Builds während der Entwicklung zu unterscheiden, oder mehr, damit die Qualitätssicherung bestimmt, welchen Build ein Endbenutzer hat? Wenn das Ziel die Reproduzierbarkeit ist, sollte dies theoretisch eine Versionsnummer sein - warum nicht? (Bitte beantworten Sie dies als Teil Ihrer Antworten unten. Es wird Ihnen dabei helfen, die von Ihnen getroffenen/vorgeschlagenen Entscheidungen zu beleuchten.)

  • Frage: Gibt es einen Platz für Build-Nummern in manuellen Builds? Ist das so problematisch, dass JEDER eine CI-Lösung verwenden sollte?

  • Frage: Sollten Build-Nummern im SCM eingecheckt werden? Wenn das Ziel darin besteht, einen bestimmten Build zuverlässig und eindeutig zu identifizieren, wie Sie mit einer Vielzahl von kontinuierlichen oder manuellen Build-Systemen umgehen, die abstürzen/neu starten/usw. können.

  • Frage: Sollte eine Build-Nummer kurz und süß sein (dh eine monoton ansteigende Ganzzahl), so dass es einfach ist, Dateinamen für die Archivierung einzugeben, in der Kommunikation leicht zu referenzieren usw., oder sollte sie lang und voller Benutzernamen sein, Datumsstempel, Maschinennamen usw.?

  • Frage: Geben Sie bitte Einzelheiten darüber an, wie die Zuweisung von Build-Nummern in Ihren größeren automatisierten Freigabeprozess passt. Ja, Mavenliebhaber, wir wissen, dass dies getan und getan ist, aber noch haben nicht alle von uns die Kool-Hilfe getrunken ...

Ich möchte dies wirklich zu einer vollständigen Antwort zusammenfassen, zumindest für das konkrete Beispiel unserer CVS-/Ameisen-/Hudson-Konfiguration, damit jemand eine vollständige Strategie auf der Grundlage dieser Frage entwickeln kann. Ich werde jeden als "Die Antwort" markieren, der eine Beschreibung für diesen speziellen Fall geben kann (einschließlich des CVS-Tag-Schemas, der relevanten CI-Konfigurationselemente und des Freigabeverfahrens, das die Build-Nummer programmgesteuert in das Release einfügt zugänglich.) Wenn Sie nach einer anderen bestimmten Konfiguration fragen/antworten möchten (z. B. svn/maven/cruise control), verweise ich von hier auf die Frage. - Ja

[BEARBEITEN 23. Oktober 09] Ich habe die Antwort mit den meisten Stimmen akzeptiert, weil ich denke, dass es eine vernünftige Lösung ist, während einige der anderen Antworten auch gute Ideen enthalten. Wenn jemand eine Pause einlegen möchte, um einige davon mit marty-lamb zu synthetisieren, werde ich in Betracht ziehen, eine andere zu akzeptieren. Das einzige Problem, das ich mit Marty-Lambs habe, ist, dass es keine zuverlässig serialisierte Build-Nummer erzeugt - es hängt von einer lokalen Uhr des Build-Systems ab, um eindeutige Build-Nummern zu liefern, was nicht großartig ist.

[Edit 10. Juli]

Wir schließen jetzt eine Klasse wie die folgende ein. Dadurch können die Versionsnummern in die endgültige ausführbare Datei kompiliert werden. Verschiedene Formen der Versionsinformationen werden in Protokolldaten und langfristig archivierten Ausgabeprodukten ausgegeben und verwendet, um die Analyse der Ausgabeprodukte (manchmal Jahre später) auf einen bestimmten Build zurückzuführen.

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://[email protected]/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();
  }
}

Hinterlasse Kommentare, wenn dies eine Wiki-Diskussion verdient.

131
andersoj

Für einige meiner Projekte erfasse ich die Subversion-Versionsnummer, die Zeit, den Benutzer, der den Build ausgeführt hat, und einige Systeminformationen, füge sie in eine .properties-Datei ein, die in das Anwendungs-JAR aufgenommen wird, und lese dieses JAR zur Laufzeit.

Der Ant-Code sieht folgendermaßen aus:

<!-- 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 ist einfach, dies zu erweitern, um alle Informationen einzuschließen, die Sie hinzufügen möchten.

63
Marty Lamb

Ihr 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>
...

Ihr Java Code

String ver = MyClass.class.getPackage().getImplementationVersion();
46
user146146
  • Build-Nummern sollten einem Continuous Integration Server wie hudson zugeordnet werden. Verwenden Sie unterschiedliche Jobs für unterschiedliche Zweige/Teams/Verteilungen.
  • Um die Versionsnummer im endgültigen Build beizubehalten, würde ich nur die Verwendung von maven für das Build-System empfehlen. Es wird eine .properties-Datei erstellt, die im endgültigen .jar/.war/.whatever-ar auf META-INF/maven/<project group>/<project id>/pom.properties Archiviert wird. Die .properties-Datei enthält die Versionseigenschaft.
  • Da ich maven empfehle, möchte ich Sie dringend bitten, das Versions-Plugin zu lesen, um die Veröffentlichung im Quell-Repository vorzubereiten und die Versionen synchron zu halten.
6

Software:

  • SVN
  • Ameise
  • Hudson, für die kontinuierliche Integration
  • svntask, eine Ant-Aufgabe zum Auffinden der SVN-Version: http://code.google.com/p/svntask/

Hudson hat drei Builds/Jobs: Continuous, Nightly und Release.

Für einen kontinuierlichen/nächtlichen Build: Die Build-Nummer ist die SVN-Revision, die mit svntask gefunden wurde.

Für einen Release-Build/Job: Build-Nummer ist die von Ant gelesene Release-Nummer aus einer Eigenschaftendatei. Die Eigenschaftendatei kann auch mit dem Release zur Anzeige der Build-Nummer zur Laufzeit verteilt werden.

Das Ant-Build-Skript fügt die Build-Nummer in die Manifestdatei der JAR/WAR-Dateien ein, die während des Builds erstellt werden. Gilt für alle Builds.

Post-Build-Aktion für Release-Builds, die mithilfe eines Hudson-Plug-in-Tags (SVN) mit der Build-Nummer einfach durchgeführt werden kann.

Leistungen:

  • Für eine Entwicklerversion eines JAR/WAR kann der Entwickler die SVN-Revision des JAR/WAR finden und den entsprechenden Code in SVN nachschlagen
  • Bei einem Release entspricht die SVN-Revision dem SVN-Tag mit der Release-Nummer.

Hoffe das hilft.

6
Raleigh

Ich benutze auch Hudson, obwohl ein viel einfacheres Szenario:

Mein Ant-Skript enthält ein Ziel, das wie folgt aussieht:

<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 legt diese Umgebungsvariablen für mich fest, wenn mein Job ausgeführt wird.

In meinem Fall ist dieses Projekt eine Webapp und ich füge diese build-number.txt - Datei in den Stammordner der Webapp ein - es ist mir egal, wer sie sieht.

In diesem Fall wird die Quellcodeverwaltung nicht mit Tags versehen, da unser Hudson-Job bereits so eingerichtet ist, dass er bei erfolgreicher Erstellung mit der Build-Nummer/dem Zeitstempel versehen wird.

Meine Lösung deckt nur die inkrementellen Build-Nummern für die Entwicklung ab. Wir sind in dem Projekt, in dem wir die Release-Nummern noch behandeln, nicht weit genug gekommen.

4
matt b

Möglicherweise möchten Sie auch das BuildNumber Maven-Plugin und die Ant-Aufgabe in einem Jar unter http://code.google.com/p/codebistro/wiki/BuildNumber ansehen. Ich habe versucht, es einfach und unkompliziert zu machen. Es ist eine sehr kleine JAR-Datei, die nur von der installierten Kommandozeilen-Subversion abhängt.

3
Sasha O

So habe ich das gelöst:

  • die Quellen werden in das Erstellungsverzeichnis kopiert
  • dann wird die anttask "versioninfo" angewendet
  • kompilieren Sie die geänderten Quellen

Hier ist die Java Datei, in der die Versionsinformationen gespeichert sind:

public class Settings {

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

}

Und hier ist die 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

Hier sind meine 2 Cent:

  • Mein Build-Skript erstellt bei jedem Erstellen der App eine Build-Nummer (mit Zeitstempel!). Dies schafft zu viele Zahlen, aber niemals zu wenige. Wenn ich den Code ändere, ändert sich die Build-Nummer mindestens einmal.

  • Ich versioniere die Build-Nummer mit jeder Veröffentlichung (wenn auch nicht dazwischen). Wenn ich das Projekt aktualisiere und eine neue Build-Nummer erhalte (weil jemand anderes eine Veröffentlichung durchgeführt hat), überschreibe ich meine lokale Version und beginne von vorne. Dies kann zu einer niedrigeren Build-Nummer führen, weshalb ich den Zeitstempel eingefügt habe.

  • Wenn eine Freigabe erfolgt, wird die Build-Nummer als letztes Element eines einzelnen Commits mit der Meldung "build 1547" festgeschrieben. Danach, wenn es eine offizielle Veröffentlichung ist, wird der gesamte Baum markiert. Auf diese Weise enthält die Build-Datei immer alle Tags und es gibt eine einfache 1: 1-Zuordnung zwischen Tags und Build-Nummern.

[EDIT] Ich stelle eine version.html mit meinen Projekten bereit und kann dann mit einem Scraper einfach eine genaue Karte erfassen, was wo installiert ist. Wenn Sie Tomcat oder ähnliches verwenden, geben Sie die Build-Nummer und den Zeitstempel in das description -Element von web.xml ein. Denken Sie daran: Merken Sie sich niemals etwas, wenn ein Computer dies für Sie erledigen kann.

2
Aaron Digulla

Wir führen unseren Build über CruiseControl aus (fügen Sie hier Ihren bevorzugten Build-Manager ein) und führen den Hauptbuild und die Tests durch.

Anschließend erhöhen wir die Versionsnummer mit Ant und BuildNumber und erstellen eine Eigenschaftendatei mit diesen Informationen sowie dem Erstellungsdatum und anderen Metadaten.

Wir haben eine Klasse, die sich dem Lesen und Bereitstellen für GUIs/Logs usw. widmet.

Dann packen wir das alles zusammen und bauen ein implementierbares Paket, das die Build-Nummer und den entsprechenden Build zusammenhält. Alle unsere Server speichern diese Metainformationen beim Start. Wir können die CruiseControl-Protokolle noch einmal durchgehen und die Build-Nummer mit dem Datum und den Eincheckdaten verknüpfen.

1
Brian Agnew