Automatiskt byggnummer

Varje gång vi bygger en releasekandidat av systemet, deployar till en testmiljö eller kanske bara behöver en version vi vill kunna referera till i exempelvis felrapporter, ska bygget ges ett unikt versionsnummer.

Ett versionsnummer kan vara ett enkelt löpnummer eller kanske bygga på semantic versioning (1.2.42).

Det är praktiskt att tillgängligöra versionsnumret på ett antal olika sätt: Som inspekterbar i artefakten själv i MANIFEST.MF, i Mavens pom-versioner, maskinläsbar i runtime, till exempel i en endpoint /appversion i ett REST-gränssnitt eller synligt i användargränssnittet.

Screen Shot 2014-09-22 at 22.33.23

Versionen utgörs av två komponenter: pipeline-id:built-number. Pipeline-id identifierar en build pipeline (vilket utgörs av en viss branch av en viss applikation) och är ett löpnummer som skapas av byggservern Jenkins. Att Jenkins byggnummer utgör en versionskomponent är en fördel, eftersom det innebär att det inte är så lätt att släppa ut en version som inte är byggd och taggad på rätt sätt, kanske som innehåller lokala modifikationer i källkoden som utgör normalfallet på en utvecklarmaskin. En lokalt byggd version kommer med denna lösning heta något i stil med (det mest Maven-minimalistiska) 1-SNAPSHOT medan ett officiellt releasebygge heter i stil med 1.0.42.

Version i MANIFEST.MF

För att få in versionen i artefakten, lägg följande i pom.xml:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <AppVersion>${project.version}.${BUILD_NUMBER}</AppVersion>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
    </build>
 

Om man inspekterar MANIFEST.MF efter att ha byggt med detta innehåller den något i stil med:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: daniel
Start-Class: hello.Application
AppVersion: 1-SNAPSHOT.${BUILD_NUMBER}
Spring-Boot-Version: 1.2.0.M1
Created-By: Apache Maven 3.0.5
Build-Jdk: 1.8.0_11
Main-Class: org.springframework.boot.loader.JarLauncher

Då denna version är byggd lokalt och inte av byggservern blir versionen 0-SNAPSHOT.${BUILD_NUMBER} och den gör inte många glada, men den talar i alla fall ganska tydligt om att detta inte är ett officiellt release-bygge.

Vi kommer att konfigurera Jenkins-jobbet att sätta pom-versionen till något vettigt, till exempel 1.2, och BUILD_NUMBER kommer att initieras med aktuellt byggnummer, så den resulterande versionen om den byggs på Jenkins kommer då att bli till exempel 1.2.42.

Version i runtime

För att kunna stödja access till versionen i runtime behöver vi en utility-klass:

public final class BuildInfo {
    private BuildInfo() { }

    public static String getAppVersion() {
        InputStream in = BuildInfo.class.getResourceAsStream("/BuildInfo.properties");
        if (in != null) {
            try {
                Properties props = new Properties();
                props.load(in);
                return props.getProperty("AppVersion");
            } catch (IOException e) {
                return "Cannot read BuildInfo.properties";
            } finally {
                try {
                    in.close();
                } catch (IOException ignore) {
                }
            }
        }
        throw new IllegalArgumentException("Missing BuildInfo.properties");
    }
}

Eftersom det inte är rättframt att läsa in MANIFEST.MF i runtime så går vi den breda enkla vägen och duplicerar versionen i en propertyfil src/main/resources/BuildInfo.properties. Lägg in en rad i den:

AppVersion=${project.version}

Därefter slår vi på Mavens variabelsubstituering för resursfilen på följande (något pratiga) sätt:

        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>BuildInfo.properties</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>BuildInfo.properties</exclude>
                </excludes>
            </resource>
        </resources>

Så här ser det ut om man bara vill slå på filtreringen för just filen BuildInfo.properties och inte alla andra eventuella resursfiler.

När man nu bygger med Maven kommer texten ${project.version} i propertyfilen att bytas ut mot Maven-modulens pom-version (jag har valt att sätta den till 1-SNAPSHOT initialt så behöver jag aldrig mer ändra den i mainline/trunk).

REST endpoint /appversion

Nu kan vi enkelt exponera versionen i till exempel en REST endpoint:

@Controller
public class AppVersionController {
    @RequestMapping(value = "/appversion", method = RequestMethod.GET)
    @ResponseBody
    public String getAppVersion() {
        return BuildInfo.getAppVersion();
    }
}

Jenkins-jobb: yourapp-build-release

Ange URL till ditt Git repository, gör en clone.

Sätt Maven-version:

mvn versions:set -DnewVersion=1.0.${BUILD_NUMBER}

Bygg allt och deploya till artefakt repository och tagga:

mvn deploy scm:tag

Klart!

Så här enkelt kan man bygga en release. maven-release-plugin behövs inte längre tycker jag och Axel: Maven Release Plugin: The Final Nail in the Coffin

IT Consultant at CAG Edge. Cloud and Continuous Delivery specialist, software developer and architect, Node.js, Java.

Publicerad i Continuous Delivery, Java, Uncategorized

Kategorier

WP to LinkedIn Auto Publish Powered By : XYZScripts.com