2011. szeptember 14., szerda

Verziószám megjelenítése az alkalmazásban

Technológiák: Maven 3.0.3, Build Number Maven Plugin 1.0

Kocka írt nem olyan régen egy blog post-jában arról, hogy hogyan lehet az alkalmazás verziószámát kiírni a felületre. Mivel én is nemrég csináltam meg több projektünkben is, álljon itt az én megoldásom.

Új projektjeink már Maven-t használnak build-eléshez, ahol a pom.xml tartalmazza a verziószámot. Én azt javaslom, hogy ez legyen is elég. Ez egyértelműen azonosít egy artifact-ot, azaz egy alkalmazást (vagy annak moduljait). Maven használata esetén a SNAPSHOT verziószámmal rendelkező artifact-ból lehet több is, de a release-elt artifact-ból csak egy lehet, ha egyszer kiadtuk, az már többet nem módosulhat. Javasolt tesztelésre, élesre csakis release-elt artifact-ot kitenni, így egyértelműen azonosítható, így elég lesz az azonosításra a verziószám is. Abban az esetben, ha mi SNAPSHOT verziókat is ki akarunk adni tesztelésre (amit nem javaslok), érdemes még valamilyen plusz azonosítót társítani a verziószám mellé. Ez lehet egy egyedileg kézzel megadott érték (ekkor elég nagy a hibalehetőség), egy automatikusan növelt egész szám (ennek a tárolásával lehetnek gondok), timestamp, vagy a legegyszerűbb esetben az SCM revision number. De ismétlem, erre csak akkor van szükség, ha snapshot-okat is kiadunk tesztelésre, és azokat akarjuk egyedileg azonosítani, ellenkező esetben elég a verziószám. A verziószámhoz tartozó információkat pedig lehetőleg az issue tracker tartalmazza. Ebből is látszik, hogy az SCM-et én egyszerű tárolónak tekintem, ott nem szeretek plusz meta információkat tárolni, vagy pl. a revision number-t venni bármi alapjául.

Amennyiben csak a release-elt alkalmazás verziószámát akarjuk kiírni, elegendő egy properties állományt létrehozni, és a Maven-t rávenni, hogy ebbe az állományba írja bele a verziószámot. Utána az alkalmazásba ezt a properties állományt kell becsomagolni, majd ezt beolvasni (pl. classpath-ról, getResourceAsStream-mel). Egy példa alkalmazás letölthető, mellyel ezt demonstrálom. Nem javaslom a MANIFEST.MF állomány piszkálását, mert ez csak akkor működik, ha a JAR be van csomagolva, ha pl. az alkalmazásszerver deploy-kor kicsomagolja az alkalmazást, akkor nem fog működni. A verziószám beírását a properties állományba a Maven a resoure-ok filter-elésével képes megoldani. Ekkor definiáljuk a következőt a pom.xml-ben:

<build>
 <resources>
  <resource>
   <directory>src/main/resources</directory>
   <filtering>true</filtering>
  </resource>
 </resources>
</build>

Valamint a properties állományban:

version=${project.version}

Ekkor látni fogjuk, hogy amikor a Maven átmásolja a properties állományt, kicseréli benne a ${project.version} szöveget a projekt verziószámára.

Amennyiben a MANIFEST.MF állományban is látni szeretnénk a verziószámot, a következővel egészítsük ki a pom.xml-t.

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-jar-plugin</artifactId>
 <version>2.1</version>
 <configuration>
  <archive>
   <manifest>
    <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
   </manifest>
  </archive>
 </configuration>
</plugin>

Ha mégis úgy döntünk, hogy szükségünk van build number-re is, mert SNAPSHOT verziót is azonosítani akarunk, akkor használhatjuk a Build Number Maven Plugin-t. Ennek két üzemmódja van. A build number-nek vagy az SCM revizióját használja, vagy megadhatunk neki egy formátumot. A formátumot a MessageFormat osztályban leírtaknak megfelelően lehet megadni. Itt behelyettesíthető egy vagy több egész szám, melye(ke)t automatikusan növel, a timestamp, valamint konstans értékek (szöveg és szám is).

Érdekes, hogy mindkét esetben definiálni kell az scm tag-et a pom.xml-ben, mert mindenképp lefuttat egy svn status parancsot, ahonnan ki tudja olvasni a branch nevét, amin éppen vagyunk. Ha nem adjuk meg az scm tag-et, a következő hibajelzést kapjuk:

Failed to execute goal org.codehaus.mojo:buildnumber-maven-plugin:1.0:create (default) on project versioninfo: Execution default of goal org.codehaus.mojo:buildnumber-maven-plugin:1.0:create failed: The scm url cannot be null. -> [Help 1]

Először nézzük, hogy mi történik, ha a build number-t a revision-ből akarjuk venni. Ehhez a következőt illesszük a pom.xml-be:

<plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>buildnumber-maven-plugin</artifactId>
 <version>1.0</version>
 <executions>
  <execution>
   <id>buildNumber</id>
   <phase>validate</phase>
   <goals>
    <goal>create</goal>
   </goals>
   <configuration>
    <doCheck>true</doCheck>
    <doUpdate>true</doUpdate>
   </configuration>
  </execution>
 </executions>
</plugin>

Ekkor egyrészt lefuttat egy "svn status" parancsot, és ellenőrzi, hogy van-e módosított állomány. Majd egy "svn update" parancsot, és egy "svn info" parancsot. Ez alapján eltárolja a revision number-t a ${buildNumber} property-be, az aktuális timestamp-et a ${timestamp} property-be és a branch-et, amin vagyunk a ${scmBranch} property-be (ez lehet a trunk is). Ezeket a property-ket aztán a properties fájlba írva a Maven filter-eli.

Furcsa mód amennyiben a formátumos megadást is használni akarjuk, akkor azt nem tudjuk itt konfigurálni, hanem fel kell venni egy újabb execution tag-et.

<execution>
 <id>buildInfo</id>
 <phase>validate</phase>
 <goals>
  <goal>create</goal>
 </goals>
 <configuration>
  <buildNumberPropertyName>buildInfo</buildNumberPropertyName>
  <format>Incremental build number {0}.{1} 
at {2,time} on {2,date}, build on build server {3}{4}, env var: {5}.</format>
  <items>
   <item>buildNumber0</item>
   <item>buildNumber1</item>
   <item>timestamp</item>
   <item>jupiter</item>
   <item implementation="java.lang.Integer">8</item>
   <item>${envVar}</item>
  </items>
 </configuration>
</execution>

Látható, hogy hogyan lehet megadni a formátumot a format tag-ben, és ekkor meg kell adni az items tag-et is, felsorolva a behelyettesítendő értékeket. A buildNumber/d* (ami azt jelenti, hogy a buildNumber szó, megtoldva egy egész számmal) azt fogja eredményezni, hogy létrejön egy buildNumber.properties állomány (helye, neve konfigurálható), és ebben fogja tárolni az aktuális verziót, és build esetén megnöveli egyel. Az előző példában két ilyen verziószámot növelget, és jegyez be ebbe az állományba. A timestamp-et is ki lehet írni, méghozzá formázva (még érdekesebb formátum pl. {0,date,yyyy-MM-dd HH:mm:ss}), valamint konstansokat is meg lehet adni. Ebbe az a jó, hogy akár egy Maven property-t is, ahogy az ${envVar} property mutatja. Ekkor a build-et a "mvn -D envVar=CI install" paranccsal indítva a CI szót fogja a formátumba behelyettesíteni. Ez azért jó, mert akár a build szerveren, a CI (pl. Hudson/Jenkins) is be tudja ezt külön-külön állítani.

A fenti konfigurációval futtatva a build-et a következőt kapjuk:

Incremental build number 1.1 at 23:50:06 on 2011.09.13., build on build server jupiter8, env var: CI.

Ezt az értéket, ahogy a buildNumberPropertyName tag-ben láthatjuk, a ${buildInfo} property-be fogja eltenni (hogy ne üsse az előző ${buildNumber} proerty-t). Ezt a properties állományunkban a Maven megintcsak tudja filter-elni.

Amennyiben ezeket az értékeket a MANIFEST.MF-ben is szerepeltetni akarjuk, a következőt kell a pom-xml-be írni a maven-jar-plugin konfigurációjánál:

<archive>
...
 <manifestEntries>
  <Implementation-Build>${buildNumber}</Implementation-Build>
 </manifestEntries>
...
</archive>

5 megjegyzés:

  1. Szimpi megoldas. Regebben mondjuk jobban szerettem kozvetlenul eclipse-vel elstartolni az appot mondjuk wtp-ben, ez nem futtatta le a buildet. Utobb aztan atszoktam a maven jetty pluginra, abban nem volt annyi bug, mint a wtp-ben :D szoval azzal valoszinuleg ez is korrekten mukodik.

    Legkozelebb azt hiszem mar ez kerul bevetesre. Most hogy az svn kb eltunt.

    VálaszTörlés
  2. Örülök! :)

    Későn írtam a cikket, ezért pár dolog kimaradt. Egyrészt az, hogy a build number plugin nem csak Subversion-t támogat. Valamint az, hogy a buildNumber.properties állományokat nem commit-olja, egyszerűen módosítja, amit nekünk kell commit-olni. Ez szerintem elég nagy hibalehetőség. E miatt is biztosabb a csak verziószám használata.

    VálaszTörlés
  3. ..és ha webappunk van, tegyük a felületen elérhetővé a verziószámot, build id-t és version control id-t (pl egy /version url-en), jól tud jönni

    VálaszTörlés
  4. Nagyon hasznos dolgokról írsz pont megfelelő mélységben! Csak így tovább!

    VálaszTörlés