Java Getriebe

Java und NetBeans

Automatische „Implementation Version“ für Module mit Git

Vor knapp einem Jahr hat Jens hier nochmal über die Automatische „Implementation Version“ für Netbeans Module mit Subversion berichtet. Dieser Beitrag behandelt die Möglichkeit eine ähnliche automatische Inkrementierung zu erreichen für den Fall, dass die Quellen über Git verwaltet werden.

Einer der Unterschiede zwischen Git und Subversion besteht darin, dass Subversion die Commits durchnummeriert. Solch eine Nummerierung gibt es bei Git allerdings nicht, denn dort sind die „Commitnummern“ SHA1-Hash-Werte. Diese eignen sich sehr schlecht bis gar nicht für die Versionierung von Modulen, da diese Hash-Werte sehr lang und kompliziert sind und die Hash-Werte von aufeinander folgenden Commits keine aufsteigende Reihenfolge erkennen lassen. Somit muss eine andere Möglichkeit gefunden werden die Implementation-Version eines Moduls zu bestimmen.

Im folgenden ist nun das Ant-Skript zu sehen, mit dem die automatische Versionierung in die Manifestdatei eingetragen wird. Anschließend lässt sich noch eine Beschreibung des Skriptes und der verwendeten Methode zur Bestimmung der Implementation-Version finden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?xml version="1.0" encoding="UTF-8"?>
<!--Usage:

- Place this file in "nbproject" folder of the suite.
- Insert following lines in every module's build script (build.xml)
 <import file="${suite.dir}/nbproject/auto_impl_version.xml"/>
 <target name="build-init" depends="auto-impl-version.update-implementation-version, harness.build-init"/>
(If you have a standalone module, just remove ${suite.dir}/ -->
<project name="auto-impl-version" default="update-implementation-version" basedir=".." xmlns:unless="ant:unless">
  <description>This is al little helper script to set the "Implementation Version" of
    NBModules to the current git revision count.</description>

  <target name="update-implementation-version" depends="init-git-rev-no">
    <echo level="info" message="Writing implementation version: ${implementation.version.git}"/>

    <!--Define the properties for the old manifest file and overwrite the property for
   manifest file to use the newly created file-->
    <property name="module.manifest.mf" location="manifest.mf"/>
    <property name="manifest.mf" location="build/manifest-git.mf"/>

    <!-- check the project settings i.e. that
   'Append Implementation Version automatically' is set -->
    <property file="${module.manifest.mf}" prefix="gmf"/>
    <fail if="gmf.OpenIDE-Module-Specification-Version"
         message="Please enable 'Append Implementation Versions automaticaly'"/>

    <!-- Create the new manifest file -->
    <mkdir dir="build"/>
    <copy file="${module.manifest.mf}" tofile="${manifest.mf}"/>
    <!--Write new Implementation Version to the manifest-->
    <manifest file="${manifest.mf}" mode="update">
      <attribute name="OpenIDE-Module-Implementation-Version" value="${implementation.version.git}"/>
    </manifest>
    <property file="${manifest.mf}" prefix="nmf"/>
    <!--That's it!
   We can finish now-->
  </target>

  <macrodef name="line-count">
    <attribute name="lines-property"/>
    <attribute name="count-var"/>
    <sequential>
      <resourcecount property="@{count-var}" count="0" when="eq">
        <tokens>
          <concat>
            <filterchain>
              <tokenfilter>
                <stringtokenizer delims="${line.separator}" />
              </tokenfilter>
            </filterchain>
            <propertyresource name="@{lines-property}" />
          </concat>
        </tokens>
      </resourcecount>
    </sequential>
  </macrodef>

  <target name="init-git-rev-no"
         description="set the property implementation.version.git to the number of commits that happend in the module">
    <!-- Get the commit messages for the module folder with each message in one line-->
    <exec executable="git" outputproperty="git.log.lines" failonerror="true" failifexecutionfails="true">
      <arg value="log"/>
      <arg value="--oneline"/>
      <arg value="${basedir}"/>
    </exec>
    <!--Count the lines recieved form git log command-->
    <line-count lines-property="git.log.lines" count-var="git.revision.count"/>
    <!--Check if the line count worked, e.g. if the property git.revision.count contains nothing more than a single number-->
    <condition property="legal-rev-number">
      <matches pattern="^[0-9]+$" string="${git.revision.count}"/>
    </condition>
    <fail unless="legal-rev-number"
         message="The 'revision number' from git is not a legal number.${line.separator}Probably there's something wrong.${line.separator}Please contact the author and report the problem."/>

    <property name="implementation.version.git" value="${git.revision.count}"/>
  </target>
</project>

Download ZIP

Das Target update-implementation-version entspricht größtenteils dem von Jens gezeigten. Hierbei wird letzlich zunächst geprüft, ob die Option „Append Implementation Version autmatically“ aktiviert ist. Anschließend wird eine neue manifest.mf Datei erzeugt, in der dann die Implementation-Version aktualisiert wird. Da zuvor die Property ${manifest.mf} auf die neu erzeugte Datei gesetzt wird, kann die neu erzeugte Manifest-Datei beim späteren erzeugen des Moduls berücksichtigt werden. Das eigentlich interessante ant-Target ist init-git-rev-no. Dabei wird der Befehl  git log genutzt. Dieser liefert alle Commitnachrichten inklusive ihrer SHA1-Hash-Werte. Über die Option --oneline wird die Ausgabe jedes Commits in eine Zeile geschrieben und durch die Angabe eines Ordnerpfades am Ende werden nur Commits berücksichtigt, die den entsprechenden Ordner und seine Inhalte betreffen. In der Konsole kann die Zählung dieser Zeilen über den Befehl git log --oneline <dir> | wc -l(Linux)
(bzw. git log --oneline <dir> | find /v "" /c in Windows) erreicht werden. Im oben stehenden ant-Skript werden die Zeilen allerdings mit einem eigenen Makro gezählt. Somit ist eine UNterscheidung der Betriebssysteme nicht notwendig, da der Git-Befehl auf allen Systemen gleich aussieht und auch die Zeilenzählung, über das Skript, auf allen Betriebssystemen gleich funktioniert. Somit ist dieses Skript auch für Continuous Integration, beispielsweise mit einem Jenkins Server, geeignet. Allerdings ist eine Voraussetzung für diese Lösung, dass sich ein Git Kommandozeilen Client im Standardsuchpfad des Systems (%PATH% unter Windows) befindet.

Um das Skript benutzen zu können müssen zwei Schritte erledigt werden:

  1. Einfügen der Datei auto-impl-Version.xml in das Verzeichnis nbproject des Suiteprojektes.
  2. Die build.xml jedes Moduls, welches automatisch eine Versionierungsnummer erhalten soll, muss um die beiden folgenden Zeilen ergänzt werden
    1
    2
    3
    <import file="${suite.dir}/nbproject/auto_impl_version.xml"/>
    <target name="build-init"
    depends="auto-impl-version.update-implementation-version, harness.build-init"/>

    Bei Standalone-Modulen muss allerdings beachtet werden, dass sich die Datei selbst im nbproject Ordner des entsprechenden Moduls befindet, sowie dass die erste Zeile dahingehend abgeändert wird, dass dort das ${suite.dir}/ entfernt wird.

One Response to Automatische „Implementation Version“ für Module mit Git

  1. Eine Alternative habe ich auf Stackoverflow gefunden: https://stackoverflow.com/a/18834279

    1
    git rev-list --count --first-parent HEAD