‘build.plugins.plugin.version’ for … is missing.

Kürzlich habe ich versucht, ein Projekt mit Maven zu bauen und bekam während des Bauens diese Warnung:

[WARNING]
[WARNING] Some problems were encountered while building the effective model for ::jar:
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-jar-plugin is missing. @ line 17, column 15
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]

Wie es sich herausgestellt hat, hat bei einem verwendeten Maven-Plugin die Versionsangabe gefehlt. In der verwendeten pom.xml wurde das Maven-Plugin maven-jar-plugin verwendet, aber die Version hat gefehlt:

<plugin>
  <artifactId>maven-jar-plugin</artifactId>
  <configuration>
    <archive>
      <manifest>
        <mainClass>com.somepackage.SomeMainClass</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin>

Die Version für das Plugin konnte ich recht leicht herausfinden und anschließend die pom.xml anpassen:

 <plugin>
  <artifactId>maven-jar-plugin</artifactId>
  <version>3.1.2</version>
  <configuration>
    <archive>
      <manifest>
        <mainClass>com.somepackage.SomeMainClass</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin> 

Danach ist die Warnung verschwunden.

Dependency Graph Viewer Plugin einrichten

Wenn es mal unübersichtlich mit den Jobs in Jenkins wird, kann man mit dem Dependency Graph Viewer Plugin ein wenig Licht in die Sache bringen. Vor allem dann, wenn es darum geht zu sehen, welcher Job welchen anderen Job startet. Wenn z.B. der Job Bauen den Job Unittests aufruft, lässt sich das dem Dependency Graph Viewer Plugin gut visualisieren.

Ein Beispielgraph produziert durch den Dependency Graph Viewer Plugin

Zuerst muss man natürlich das Plugin in Jenkins installieren. Ich gehe jetzt davon aus, dass klar ist wie man in Jenkins ein Plugin installiert.

Als ich nach der Installation versucht hatte, einen Graph zu erstellen, bekam ich stattdessen in der Browser-Konsole eine Fehlermeldung dieser Art. Es gab wohl auch andere, die davon betroffen waren.

TypeError: div.firstChild is null hudson-behavior.js:862:21
    onSuccess http://192.168.0.26:8080/static/b568631b/scripts/hudson-behavior.js:862
    respondToReadyState http://192.168.0.26:8080/static/b568631b/scripts/prototype.js:1657
    onStateChange http://192.168.0.26:8080/static/b568631b/scripts/prototype.js:1600
    bind http://192.168.0.26:8080/static/b568631b/scripts/prototype.js:414

Die Lösung für das Problem besteht aus zwei Teilen:

1. Sicherstellen, dass graphviz aus dem Betriebssystem installiert ist. Mein Jenkins lief auf einem CentOS-System, da reichte es aus graphviz mit dem Befehl

sudo yum install graphviz

zu installieren. Wichtig ist hierbei, dass man nach der Installation das Programm dot finden kann:

sh-4.2# which dot
/usr/bin/dot

2. In den Systemeinstellungen muss der Pfad zu dem Programm dot gesetzt sein.

Hier kann es aber auch sein, dass man auch keinen Pfad setzen muss. Im Zweifel einfach mal testen. Wichtig ist jedoch, dass der Pfad zum dot Programm vollständig ist und hier nicht nur der Pfad gesetzt wird, in dem sich das Programm dot befindet.

Weitere mögliche Quellen:

ERROR: [WS-CLEANUP] Cannot delete workspace: Remote call on … failed

Vor kurzem hatte ich einen Job in Jenkins, der die Einstellung hatte den Arbeitsbereich auf einem Jenkins-Slave zu löschen bevor der Job fortführen sollte:

Auf einmal bekam ich jedoch regelmäßig Fehlermeldungen dieser Art:

[WS-CLEANUP] Deleting project workspace…
[WS-CLEANUP] Deferred wipeout is used…
ERROR: [WS-CLEANUP] Cannot delete workspace: Remote call on … failed
ERROR: Cannot delete workspace: Remote call on … failed

Die Lösung in diesem Fall war die: Ich musste den Jenkins-Slave erst einmal richtig trennen

um ihn anschließend wieder zu verbinden.

Im Grunde habe ich den Slave-Agenten neu gestartet. Seitdem trat der Fehler nicht mehr auf.

Mit docker-compose ein docker Netzwerk erstellen

Mit docker-compose lassen sich mehrere Container auf einmal starten. Man kann damit auch ein docker Netzwerk erstellen. Mit der docker-compose Version 3.5 oder höher kann man auch einen eigenen Namen für das Netzwerk angeben. Hier sieht man eine docker-compose.yml Datei, die zwei Container startet und ein docker Netzwerk erstellt. Den beiden Containern habe ich jetzt die Namen custom1 und custom2 gegeben. Das Netzwerk heißt hier my-own-network-name. Damit beide Container nach dem Start nicht gleich wieder geschlossen werden, läuft ein unendlicher sleep Prozess in ihnen. Für einfache Beispiele sollte diese docker-compose.yml gut genug sein.

version: '3.5'

services:
    custom1:
        container_name: custom1
        image: centos:latest
        entrypoint:
            - sleep
            - inf
        networks:
            - my-own-network-name
    custom2:
        container_name: custom2
        image: centos:latest
        entrypoint:
            - sleep
            - inf
        networks:
            - my-own-network-name
networks:
    my-own-network-name:
         name: my-own-network-name

Wenn man nun also die Kommandos:

docker-compose up -d
docker network ls

ausführt, sieht man das neu erstellte docker Netzwerk:

Um die erstellten Container und das docker Netzwerk zu löschen, reicht das Kommando

docker-compose down

Caused by: java.lang.NoClassDefFoundError: com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider

Ich hatte kürzlich Jenkins frisch aufgesetzt und die empfohlenen Plugins installiert. Als ich ein Multibranch Pipeline Projekt erstellt hatte und konfigurieren wollte, ging nichts mehr. Ich konnte nichts konfigurieren und bekam zudem diese Fehlermeldung zu Gesicht:

org.apache.commons.jelly.JellyTagException: jar:file:/root/.jenkins/war/WEB-INF/lib/jenkins-core-2.176-SNAPSHOT.jar!/lib/hudson/actions.jelly:39:70: <st:include> com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider at org.apache.commons.jelly.impl.TagScript.handleException(TagScript.java:745) at org.apache.commons.jelly.impl.TagScript.run(TagScript.java:289) at org.apache.commons.jelly.TagSupport.invokeBody(TagSupport.java:161) at org.apache.commons.jelly.tags.core.ForEachTag.doTag(ForEachTag.java:150) at org.apache.commons.jelly.impl.TagScript.run(TagScript.java:269)
...
Caused by: java.lang.NoClassDefFoundError: com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider at java.lang.Class.getDeclaringClass0(Native Method) at java.lang.Class.getDeclaringClass(Class.java:1235) at java.lang.Class.getEnclosingClass(Class.java:1277) at com.cloudbees.plugins.credentials.CredentialsStore.<init>(CredentialsStore.java:102)
...
Caused: javax.servlet.ServletException at org.kohsuke.stapler.jelly.JellyClassTearOff.serveIndexJelly(JellyClassTearOff.java:119) at org.kohsuke.stapler.jelly.JellyFacet.handleIndexRequest(JellyFacet.java:146) at org.kohsuke.stapler.IndexViewDispatcher.dispatch(IndexViewDispatcher.java:30) at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:747)

Die wichtige Fehlermeldung ist hier fett hervorgehoben. Ursache für dieses Problem ist, dass ein Plugin nicht installiert gewesen ist: Das Cloudbees Credentials Plugin.

Ein Visual Studio 2019 Projekt mit CMake erstellen

Seit der Version 3.14 kann man mit CMake Visual Studio 2019 Projekte erstellen. In diesem Post möchte ich kurz zeigen, wie das möglich ist.

Um dieses Beispiel erfolgreich zum Laufen zu bringen, müssen einige Bedingungen erfüllt sein:

  • Visual Studio 2019 muss auf dem Rechner installiert sein.
  • CMake 3.14 (oder höher) muss installiert sein
  • Der Pfad zur cmake.exe muss in der PATH Variable enthalten sein

Mehr braucht es auch nicht. Nun will ich aber mit meinem Beispiel fortfahren. Mein Beispielprojekt für diesen Post besteht nur aus einer einzigen C++-Datei. Das Programm am Ende soll einfach ein Hallo, Welt! in der Kommandozeile ausgeben:

include <iostream>
int main(int argc, char **argv)
{
std::cout << "Hallo, Welt!" << std::endl;
return 0;
}

Wenn man Projekte mit CMake erstellt, gehört dazu eine Datei mit dem Namen CMakeLists.txt. Für dieses Beispiel sieht die so aus:

project(example1)

set(SOURCE_FIlES main1.cpp)

add_executable(example1 ${SOURCE_FIlES})

In der CMakeLists.txt definiere ich in der ersten Zeile den Projektnamen. Diese Zeile sorgt später dafür, dass am Ende eine Solutionsdatei namens example1.sln existiert. Diese Solutionsdatei werden wir später nutzen, um mit Hilfe von Visual Studio 2019 aus dem Code eine ausführbare Datei zu produzieren.

Als nächstes werden alle nötigen Codedateien in der Variablen SOURCE_FILES aufgelistet. Für komplexere Projekte gibt es andere Methoden, aber für unser Beispiel hier ist das ausreichend.

In der letzten Zeile sagen wir dann, dass wir aus den Codedateien eine ausführbare Datei namens example1 erstellt haben wollen (add_executable). Da wir hier unter Windows arbeiten und Visual Studio 2019 nutzen, brauchen wir die Endung hier nicht zu schreiben.

Sowohl die CMakeLists.txt wie auch die C++-Datei sollten in diesem Beispiel in einem Ordner nebeneinander liegen:

CMakeLists.txt und main1.cpp liegen in einem Ordner

Da wir das Projekt nachher noch mit Visual Studio 2019 bauen wollen, rufen wir die x64 Native Tools Command Prompt for VS 2019 aus dem Startmenü auf:

Nun müsste ein Fenster wie das Folgende auftauchen:

In der gestarteten Kommandozile müssen wir nun in den Ordner wechseln, in dem sich die CMakeLists.txt befindet. Sind wir dort, rufen wir CMake mit folgendem Kommando auf:

cmake -G "Visual Studio 16 2019" .

Der Punkt am Ende steht gewollt da. Ist alles gut gegangen, sehen wir am Ende diese Meldung (der Pfad kann sich hierbei anders sein):

-- Build files have been written to: C:/Users/jenkins/Documents/cmake1

Ist alles erfolgreich verlaufen, müsste man in dem Ordner eine Datei namens example1.sln finden. Daneben auch eine example1.vcxproj. Diese Solutionsdatei kann man nun in Visual Studio öffnen oder anderweitig bearbeiten. Jetzt können wir anfangen, das Projekt in der Kommandozeile zu bauen, z.B. durch einen Befehl wie diesen:

msbuild example1.sln

Erste Schritte mit BitBucket und Webhooks

In BitBucket lassen sich sogenannte Webhooks für ein Repository erstellen. Diese Webhooks melden dann dem CI Server, wenn sich was im Repository geändert hat. Ich habe meine ersten Schritte mit BitBucket und den Webhooks gemacht. Diese möchte ich hier aufschreiben.

Ein Webhook in BitBucket zu erstellen, sollte recht einfach sein. Als erstes muss man in die Einstellungen von dem gewünschten Repository gehen, in dem man die Webhooks einrichten möchte:

Dort müsste ein Button “Add Webhook” auftauchen:

Wenn man auf “Add Webhook” klickt, dann sollte ein neues Formular auftauchen, in dem man die gewünschten Einstellungen setzen kann:

Der “Title” ist hierbei frei wählbar. Unter “URL” sollte man in etwa folgendes eingeben (natürlich angepasst):

http://<url-of-jenkins>/git/notifyCommit?url=<url-of-repository>

Man sollte hier darauf achten, dass der Teil <url-of-repository> sowohl im Webhook als auch in dem entsprechendem Job in Jenkins gleich sind.

Nun muss man noch sicher stellen, dass auf Jenkinsseite alle Einstellungen korrekt sind. Wie schon erwähnt, sollte die Repository-URL gleich sein. Im Job von Jenkins sollte zu dem noch ein Haken bei Source Code Management abfragen gesetzt sein. Einen Zeitplan muss man hier jedoch nicht unbedingt setzen:


Setzt man den Haken nicht, wird kein Job gestartet und in BitBucket sieht man dann Meldungen wie diese:

No git jobs using repository: https://bitbucket.org/some-repo... and branches:  No Git consumers using SCM API plugin for: https://bitbucket.org/some-repo...

Ich hoffe, mein Post hat euch gefallen / geholfen.

Jenkins Pipeline: Werte von einem Job zum anderen übergeben

Manchmal möchte man Werte zwischen zwei Jobs weiter geben. Dies kann man mit Jenkins Pipeline recht einfach bewerkstelligen. Dazu müssen beide Jobs richtig konfiguriert sein.

Der erste Job muss die Möglichkeit haben einen zweiten Job mit Parametern zu starten. Wie hier in diesem Jenkinsfile:

pipeline {
agent any
stages {
stage("Build") {
steps {
sh "echo First job"
}
}

stage("Trigger") {
steps {
build job: 'Pipe 2', parameters: [string(name: 'VAR2', value: "somevalue2")], wait: false
}
}
}
}

Der zweite Job (hier “Pipe 2” genannt) muss die Möglichkeit haben, Parameters zu empfangen:

pipeline {
agent any
parameters {
string(name: 'VAR2', defaultValue: 'TESTING', description: '')
}
stages {
stage('Build') {
steps {
echo env.VAR2
sh "printenv"
}
}
}
}

git: error: GH007: Your push would publish a private email address.

Vor kurzem bekam ich beim Arbeiten mit git und GitHub diese Fehlermeldung:

remote: error: GH007: Your push would publish a private email address.
remote: You can make your email public or disable this protection by visiting:
remote: http://github.com/settings/emails
To https://github.com/username/some-repo-name
! [remote rejected] master -> master (push declined due to email privacy restrictions)
error: failed to push some refs to 'https://github.com/username/some-repo-name'

Ursache für diese Fehlermeldung waren zwei Punkte. Der erste Punkt ist eine bestimmte E-Mail-Einstellung auf GitHub selbst: Dort kann man einstellen, dass ein Push auf GitHub nicht erfolgt, wenn in den Metadaten die eigene E-Mail-Adresse mitgeliefert wird.

Der zweite Punkt ist die E-Mail-Konfiguration von git auf der Maschine, auf der man arbeitet. Um diese Fehlermeldung zu umgehen und trotzdem seine E-Mail-Adresse nicht zu veröffentlichen, kann man folgende Schritte tun. Als erstes konfiguriert man seine E-Mail-Adresse um. Hierbei kann man eine standardmäßige noreply-E-Mail von GitHub verwenden:

git config --global user.email "username@users.noreply.github.com"

Wichtig: Statt username in diesem Kommando sollte man seinen eigenen Benutzernamen verwenden. Nun muss man in dem lokalen git-Repository die Metadaten ändern. Das kann man durch den folgenden Befehl erreichen:

git commit --amend --reset-author

Jedenfalls musste ich danach nichts mehr machen, außer einen neuen Push zu probieren.

git push origin master

Will man aber nochmal prüfen, ob in den Metadaten jetzt die richtige, “anonyme” GitHub-E-Mail-Adresse steht, kann man folgendes Kommando ausführen:

git log --raw

Wenn es sich um ein größeres Repo handelt, kann das Log länger werden. Ich hatte jetzt nur ein kleineres Repo zu Hand, da war der Befehl kein Problem.

Azure SLQ Server mit Azure CLI erstellen

Mit Azure CLI kann man mit wenigen Kommandos einen Azure SQL Server aufsetzen.

Zuerst wird eine eine Ressourcegruppe benötigt. Diese wird mit Hilfe folgenden Kommandos erstellt:

az group create --name "rg_sqlserver_example1" --location westeurope

Den Standort und den Namen muss man gegebenenfalls anpassen. Anschließend erstellt man den SQL Server in Azure mit folgendem Kommando:

az sql server create --name "somesqlservername123" \
--resource-group "rg_sqlserver_example1" \
--location westeurope \
--admin-user "someadminname" \
--admin-password "<yourpassword>"

Auch hier sollte man die Einstellungen für die eigenen Zwecke anpassen. Wenn alles gut gegangen ist, müsste man den SQL Server auch im Portal sehen:

Anschließend kann man jetzt noch eine Datenbank erstellen:

az sql db create --resource-group "rg_sqlserver_example1" \
--server "somesqlservername123" \
--name "database_name_1"

Auch hier müsste man nach einer Weile die neu erstellte Datenbank im Portal sehen:

Es gibt auch noch andere Wege, wie man ein SQL Server in Azure aufsetzt. Mehr dazu in diesem kurzen Video (ist aber auch englisch):