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

Lesezeit: 12 Minuten

Benutzer-Avatar
andersoj

Was sind aktuelle Best Practices für eine systematische Build-Nummerierung und Versionsnummernverwaltung in Java-Projekten? Speziell:

  • Systematische Verwaltung von Build-Nummern in einer verteilten Entwicklungsumgebung

  • So pflegen Sie Versionsnummern in der Quelle / verfügbar für die Laufzeitanwendung

  • So integrieren Sie richtig in das Quell-Repository

  • Wie man Versionsnummern besser automatisch verwaltet als Repository-Tags

  • Integration in eine kontinuierliche Build-Infrastruktur

Es ist eine ganze Reihe von Tools verfügbar, und ant (das von uns verwendete Build-System) hat eine Aufgabe, die eine Build-Nummer verwaltet, aber es ist nicht klar, wie dies mit mehreren gleichzeitigen Entwicklern, die CVS, svn oder ähnliches verwenden, verwaltet werden soll .

[EDIT]

Unten sind einige gute und hilfreiche Teil- oder spezifische Antworten erschienen, daher fasse ich einige davon zusammen. Es klingt für mich so, als gäbe es dafür nicht wirklich eine starke “Best Practice”, sondern eine Sammlung sich überschneidender Ideen. Unten finden Sie meine Zusammenfassungen und einige daraus resultierende Fragen, die die Leute versuchen könnten, als Folgemaßnahmen zu beantworten. [New to stackoverflow… please provide comments if I’m doing this wrong.]

  • Wenn Sie SVN verwenden, kommt die Versionierung eines bestimmten Checkouts mit. Die Build-Nummerierung kann dies ausnutzen, um eine eindeutige Build-Nummer zu erstellen, die den spezifischen Checkout/die spezifische Revision identifiziert. [CVS, which we are using for legacy reasons, doesn’t provide quite this level of insight… manual intervention with tags gets you part way there.]

  • Wenn Sie Maven als Build-System verwenden, gibt es Unterstützung für die Erstellung einer Versionsnummer aus dem SCM sowie ein Release-Modul zur automatischen Erstellung von Releases. [We can’t use maven, for a variety of reasons, but this helps those who can. [Thanks to marcelo-morales]]

  • Wenn Sie verwenden Ameise Als Ihr Build-System kann die folgende Aufgabenbeschreibung dabei helfen, eine Java-.properties-Datei zu erstellen, die Build-Informationen erfasst, die dann auf verschiedene Weise in Ihren Build eingebunden werden können. [We expanded on this idea to include hudson-derived information, thanks marty-lamb].

  • Ant und Maven (und Hudson und Cruise Control) bieten einfache Möglichkeiten, um Build-Nummern in eine .properties-Datei oder in eine .txt/.html-Datei zu bekommen. Ist dies “sicher” genug, um zu verhindern, dass es absichtlich oder versehentlich manipuliert wird? Ist es besser, es zur Build-Zeit in eine “Versionierungs”-Klasse zu kompilieren?

  • Behauptung: Die Build-Nummerierung sollte in einem Continuous-Integration-System wie z Hudson. [Thanks to marcelo-morales] Wir haben diesen Vorschlag aufgegriffen, aber er reißt die Frage des Release-Engineerings auf: Wie läuft ein Release ab? Gibt es mehrere Build-Nummern in einer Version? Gibt es eine sinnvolle Beziehung zwischen Buildnummern aus unterschiedlichen Releases?

  • Frage: Was ist das Ziel hinter einer Build-Nummer? Wird es für QA verwendet? Wie? Wird es hauptsächlich von Entwicklern verwendet, um während der Entwicklung zwischen mehreren Builds eindeutig zu unterscheiden, oder eher für die QA, um festzustellen, welchen Build ein Endbenutzer erhalten hat? Wenn das Ziel die Reproduzierbarkeit ist, sollte dies theoretisch eine Release-Versionsnummer liefern – warum nicht? (Bitte beantworten Sie dies als Teil Ihrer Antworten unten, es wird 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 ALLE eine CI-Lösung nutzen sollten?

  • Frage: Sollen Build-Nummern in den SCM eingecheckt werden? Wenn das Ziel darin besteht, einen bestimmten Build zuverlässig und eindeutig zu identifizieren, wie geht man mit einer Vielzahl von kontinuierlichen oder manuellen Build-Systemen um, die abstürzen / neu starten / etc …

  • Frage: Sollte eine Build-Nummer kurz und bündig sein (d. h. eine monoton ansteigende Ganzzahl), damit sie einfach in Dateinamen für die Archivierung eingefügt werden kann, in der Kommunikation leicht darauf verwiesen werden kann usw., oder sollte sie lang und voller Benutzernamen sein? Datumsstempel, Maschinennamen usw.?

  • Frage: Bitte machen Sie Angaben dazu, wie die Zuweisung von Build-Nummern in Ihren größeren automatisierten Freigabeprozess passt. Ja, Maven-Liebhaber, wir wissen, dass dies getan und getan ist, aber noch nicht alle von uns haben das Kool-Aid getrunken …

Ich würde dies wirklich gerne zu einer vollständigen Antwort ausarbeiten, zumindest für das konkrete Beispiel unseres cvs/ant/hudson-Setups, damit jemand auf der Grundlage dieser Frage eine vollständige Strategie erstellen kann. Ich werde jeden als “Die Antwort” markieren, der eine Suppen-zu-Nuss-Beschreibung für diesen speziellen Fall geben kann (einschließlich CVS-Tagging-Schema, relevanten CI-Konfigurationselementen und Release-Prozedur, die die Build-Nummer so in die Version einfügt, dass sie programmgesteuert ist zugänglich.) Wenn Sie nach einer anderen bestimmten Konfiguration fragen/antworten möchten (z. B. svn/maven/cruise control), verlinke ich von hier aus auf die Frage. –JA

[EDIT 23 Oct 09]

Ich habe die am besten bewertete Antwort akzeptiert, weil ich denke, dass es eine vernünftige Lösung ist, während einige der anderen Antworten auch gute Ideen enthalten. Wenn jemand versuchen möchte, einige davon mit Marty-Lamm zu synthetisieren, werde ich in Betracht ziehen, ein anderes zu akzeptieren. Die einzige Sorge, die ich bei Marty-Lamb habe, ist, dass es keine zuverlässig serialisierte Build-Nummer erzeugt – es hängt von einer lokalen Uhr im System des Builders ab, um eindeutige Build-Nummern bereitzustellen, was nicht großartig ist.

[Edit Jul 10]

Wir fügen 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 unsere (manchmal Jahre später) Analyse von Ausgabeprodukten zu einem bestimmten Build zu verfolgen.

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 + "https://stackoverflow.com/" + 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 werden sollte.

  • Für zukünftige Leser beachten Sie bitte, dass die Revisionsnummer in dem von Ihnen vorgeschlagenen Code von der Datei und nicht von der globalen Revision des Projektarchivs stammt. Weitere Informationen finden Sie unter: subversion.apache.org/faq.html#version-value-in-source

    – Majank

    16. Januar 2011 um 16:12 Uhr

  • Ich frage mich, ob jemand ähnliche einfache Ansätze bei der Verwendung hat gradle und/oder git?

    – bnjmn

    9. Januar 2014 um 19:48 Uhr

Benutzer-Avatar
Martin Lamm

Bei mehreren meiner Projekte erfasse ich die Subversion-Revisionsnummer, die Zeit, den Benutzer, der den Build ausgeführt hat, und einige Systeminformationen, stopfe sie in eine .properties-Datei, die in das Anwendungs-JAR aufgenommen wird, und lese dieses JAR zur Laufzeit.

Der Ant-Code sieht so 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 aufzunehmen, die Sie hinzufügen möchten.

  • Verwenden Sie für eine plattformübergreifende Version dies anstelle der Whoami-Eigenschaft oben:

    – Ed Brannin

    21. Mai 2010 um 21:18 Uhr

  • -1 für eine plattformabhängige Lösung. Eine gute Möglichkeit, alle verfügbaren Ant-Eigenschaften in einer Datei zu haben, ist: ` `

    – raudi

    5. März 2013 um 12:00 Uhr


  • Verpflichten Sie sich erneut, nachdem Sie dies getan haben? Oder wird dies getan, bevor Sie Ihre eigentlichen Revisionen festschreiben?

    – Karl Holz

    22. Januar 2014 um 16:34 Uhr

  • Wie macht man das für das Git-Repository?

    – TechCrunch

    3. Dezember 2014 um 18:29 Uhr

Benutzer-Avatar
Benutzer146146

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

  • +1 für die Verwendung einer Eigenschaft, die Java bereits mit einer solchen Methode unterstützt.

    – Ed Brannin

    21. Mai 2010 um 21:33 Uhr

  • -1, um die Build-Nummer in der build.xml und nicht in einer separaten Eigenschaftendatei zu haben

    – raudi

    5. März 2013 um 12:01 Uhr

  • Build-Nummern sollten mit einem Continuous-Integration-Server wie z Hudson. Verwenden Sie unterschiedliche Jobs für unterschiedliche Branchen/Teams/Distributionen.
  • Um die Versionsnummer im endgültigen Build beizubehalten, würde ich empfehlen, einfach zu verwenden Maven für Bausystem. Es wird eine .properties-Datei erstellt, die in der endgültigen .jar/.war/.whatever-ar on archiviert wird META-INF/maven/<project group>/<project id>/pom.properties. Die .properties-Datei enthält die Versionseigenschaft.
  • Da ich maven empfehle, würde ich Sie dringend bitten, sich das anzusehen Plugin freigeben um die Veröffentlichung im Quell-Repository vorzubereiten und die Versionen synchron zu halten.

Benutzer-Avatar
Raleigh

Software:

  • SVN
  • Ameise
  • Hudson, für kontinuierliche Integration
  • svntask, eine Ant-Aufgabe, um die SVN-Revision zu finden: http://code.google.com/p/svntask/

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

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

Für einen Release-Build/Job: Build-Nummer ist die Release-Nummer, die von Ant aus einer Eigenschaftendatei gelesen wird. Die Properties-Datei kann auch mit dem Release verteilt werden, um die Build-Nummer zur Laufzeit anzuzeigen.

Das Ant-Build-Skript fügt die Build-Nummer in die Manifest-Datei 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 einfach mit einem Hudson-Plug-In durchgeführt werden kann: Taggen Sie SVN mit der Build-Nummer.

Vorteile:

  • Für eine Dev-Version einer JAR/War-Datei kann der Entwickler die SVN-Revision aus der JAR/War-Datei finden und den entsprechenden Code in SVN nachschlagen
  • Bei einer Veröffentlichung ist die SVN-Revision diejenige, die dem SVN-Tag entspricht, das die Veröffentlichungsnummer enthält.

Hoffe das hilft.

Ich verwende auch Hudson, obwohl ein weitaus einfacheres Szenario:

Mein Ant-Skript enthält ein Ziel, das so 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 schließe diese ein build-number.txt Datei im Root-Ordner der Webapp – es ist mir eigentlich egal, wer sie sieht.

Wir markieren die Quellcodeverwaltung nicht, wenn dies erledigt ist, da wir unseren Hudson-Job bereits eingerichtet haben, um ihn mit der Build-Nummer/dem Zeitstempel zu markieren, wenn der Build erfolgreich ist.

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

  • Zusätzlich (zumindest in aktuellen Jenkins-Versionen) können Sie die Eigenschaften überprüfen:`env.SVN_URL_1 env.SVN_REVISION_1 env.SVN_URL_2 env.SVN_REVISION_2 usw.

    – raudi

    5. März 2013 um 13:22 Uhr


Benutzer-Avatar
Sascha O

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

  • Zusätzlich (zumindest in aktuellen Jenkins-Versionen) können Sie die Eigenschaften überprüfen:`env.SVN_URL_1 env.SVN_REVISION_1 env.SVN_URL_2 env.SVN_REVISION_2 usw.

    – raudi

    5. März 2013 um 13:22 Uhr


Benutzer-Avatar
Flederohr

So habe ich das gelöst:

  • Die Quellen werden in das Build-Verzeichnis kopiert
  • dann wird die anttask “versioninfo” angewendet
  • kompilieren Sie die modifizierten 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 der 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="https://stackoverflow.com/questions/690419/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="https://stackoverflow.com/questions/690419/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>

1334480cookie-checkBuild- und Versionsnummerierung für Java-Projekte (ant, cvs, hudson)

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy