From fcd96215450e5a673bc980bd9a8f128f378ea4ee Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Thu, 4 Jul 2024 14:26:34 +0200 Subject: [PATCH 1/2] Refactor docker Jenkins installation. The new installation procedure is based on the Jenkins tutorial in https://github.com/jenkins-docs/quickstart-tutorials --- bin/jenkins.sh | 2 +- doc/Continuous-Integration.md | 60 ++--- doc/images/quality-monitor.png | Bin 0 -> 71403 bytes docker-compose.yaml | 66 +++++- docker/images/java-agent/Dockerfile | 27 +++ docker/images/java11-agent/Dockerfile | 65 ----- .../images/java11-agent/docker-entrypoint.sh | 27 --- docker/images/java11-agent/unsafe | 27 --- docker/images/java11-agent/unsafe.pub | 1 - docker/images/jenkins-controller/Dockerfile | 27 ++- docker/images/jenkins-controller/jenkins.yaml | 223 +++++++----------- docker/images/jenkins-controller/plugins.txt | 11 +- .../freestyle-analysis-model/config.xml | 165 +++++++++++++ .../freestyle-analysis-model/nextBuildNumber | 1 + .../history-coverage-model/config.xml | 168 +++++++++++++ .../history-coverage-model/nextBuildNumber | 1 + .../pipeline-codingstyle/config.xml | 138 +++++++++++ .../pipeline-codingstyle/nextBuildNumber | 1 + docker/images/key-generator/Dockerfile | 25 ++ docker/images/key-generator/keygen.sh | 42 ++++ 20 files changed, 756 insertions(+), 321 deletions(-) create mode 100644 doc/images/quality-monitor.png create mode 100644 docker/images/java-agent/Dockerfile delete mode 100644 docker/images/java11-agent/Dockerfile delete mode 100644 docker/images/java11-agent/docker-entrypoint.sh delete mode 100644 docker/images/java11-agent/unsafe delete mode 100644 docker/images/java11-agent/unsafe.pub create mode 100644 docker/images/jenkins-controller/preconfigured-jobs/freestyle-analysis-model/config.xml create mode 100644 docker/images/jenkins-controller/preconfigured-jobs/freestyle-analysis-model/nextBuildNumber create mode 100644 docker/images/jenkins-controller/preconfigured-jobs/history-coverage-model/config.xml create mode 100644 docker/images/jenkins-controller/preconfigured-jobs/history-coverage-model/nextBuildNumber create mode 100644 docker/images/jenkins-controller/preconfigured-jobs/pipeline-codingstyle/config.xml create mode 100644 docker/images/jenkins-controller/preconfigured-jobs/pipeline-codingstyle/nextBuildNumber create mode 100644 docker/images/key-generator/Dockerfile create mode 100644 docker/images/key-generator/keygen.sh diff --git a/bin/jenkins.sh b/bin/jenkins.sh index fb019d12..335b5867 100755 --- a/bin/jenkins.sh +++ b/bin/jenkins.sh @@ -1,6 +1,6 @@ #!/bin/bash -docker pull jenkins/jenkins:lts-alpine +docker pull jenkins/jenkins:latest-jdk21 docker compose build --pull diff --git a/doc/Continuous-Integration.md b/doc/Continuous-Integration.md index 8fdaaa01..7be136a8 100644 --- a/doc/Continuous-Integration.md +++ b/doc/Continuous-Integration.md @@ -1,24 +1,18 @@ # Continuous Integration des Coding Style -Gemäß dem Grundsatz **eat your own dogfood** ist dieser Coding Style bereits für die Continuous Integration -in [GitHub Actions](https://github.com/features/actions) und [Jenkins](https://jenkins.io) vorbereitet. +Gemäß dem Grundsatz **eat your own dogfood** ist dieser Coding Style bereits für die Continuous Integration in [GitHub Actions](https://github.com/features/actions), [GitLab CI](https://docs.gitlab.com/ee/ci/) und [Jenkins](https://jenkins.io) vorbereitet. ## Maven Konfiguration -Sowohl für GitHub Actions als auch für Jenkins erfolgt die Automatisierung des Builds über Maven. Im zugehörigen -[POM](../pom.xml) sind alle Versionen der benutzten Maven Plugins und der benötigten Abhängigkeiten über Properties -definiert, d.h. eine Aktualisierung lässt sich im entsprechenden Abschnitt leicht selbst durchführen bzw. wird -über den [Dependabot](https://dependabot.com) Roboter von GitHub automatisch über einen Pull Request aktualisiert. +Sowohl für GitHub Actions als auch für Jenkins erfolgt die Automatisierung des Builds über Maven. Im zugehörigen [POM](../pom.xml) sind alle Versionen der benutzten Maven Plugins und der benötigten Abhängigkeiten über Properties definiert, d.h. eine Aktualisierung lässt sich im entsprechenden Abschnitt leicht selbst durchführen bzw. wird über den [Dependabot](https://dependabot.com) Roboter von GitHub automatisch über einen Pull Request aktualisiert. U.a. sind die folgenden Plugins vorkonfiguriert: -- maven-compiler-plugin: konfiguriert die Java Version auf Java 8 und legt alle Error Prone Regeln fest. Die Java - Version kann beliebig aktualisiert werden. +- maven-compiler-plugin: konfiguriert die Java Version auf Java 11 und legt alle Error Prone Regeln fest. Die Java Version kann beliebig aktualisiert werden. - maven-javadoc-plugin: aktiviert die strikte Prüfung von JavaDoc Kommentaren -- maven-jar-plugin: legt einen Modulnamen fest, falls das Projekt in Java 9 oder höher verwendet wird. Außerdem wird -ein test-jar konfiguriert, sodass alle Tests (und abstrakte Testklassen) auch als Dependencies genutzt werden können. -- maven-pmd-plugin: prüft das Projekt mit PMD, die Regeln liegen in den Dateien [pmd-java-configuration.xml](../etc/pmd-java-configuration.xml), [pmd-tests-configuration.xml](../etc/pmd-tests-configuration.xml) und [pmd-javascript-configuration.xml](../etc/pmd-javascript-configuration.xml). -- maven-checkstyle-plugin: prüft das Projekt mit CheckStyle, die Regeln liegen in den Dateien [checkstyle-java-configuration.xml](../etc/checkstyle-java-configuration.xml) und [checkstyle-tests-configuration.xml](../etc/checkstyle-tests-configuration.xml). -- spotbugs-maven-plugin: prüft das Projekt mit SpotBugs, alle Regeln werden verwendet mit den Ausnahmen definiert in der Datei [spotbugs-exclusion-filter.xml](../etc/spotbugs-exclusion-filter.xml). -- org.revapi: prüft, ob die aktuelle Versionsnummer die [semantische Versionierung](https://semver.org) berücksichtigt (source and binary). D.h. es gilt: +- maven-jar-plugin: legt einen Modulnamen fest. Außerdem wird ein test-jar konfiguriert, sodass alle Tests (und abstrakte Testklassen) auch als Dependencies genutzt werden können. +- maven-pmd-plugin: prüft das Projekt mit [PMD](https://pmd.github.io/), die Regeln liegen in den Dateien [pmd-java-configuration.xml](../etc/pmd-java-configuration.xml), [pmd-tests-configuration.xml](../etc/pmd-tests-configuration.xml) und [pmd-javascript-configuration.xml](../etc/pmd-javascript-configuration.xml). +- maven-checkstyle-plugin: prüft das Projekt mit [CheckStyle](https://checkstyle.sourceforge.io/), die Regeln liegen in den Dateien [checkstyle-java-configuration.xml](../etc/checkstyle-java-configuration.xml) und [checkstyle-tests-configuration.xml](../etc/checkstyle-tests-configuration.xml). +- spotbugs-maven-plugin: prüft das Projekt mit [SpotBugs](https://spotbugs.github.io/), alle Regeln werden verwendet mit den Ausnahmen definiert in der Datei [spotbugs-exclusion-filter.xml](../etc/spotbugs-exclusion-filter.xml). +- revapi-maven-plugin: prüft, ob die aktuelle Versionsnummer die [semantische Versionierung](https://semver.org) berücksichtigt (source and binary). D.h. es gilt: 1. Eine neue **Major** Version wurde definiert, wenn das API nicht mehr abwärtskompatibel ist. 2. Eine neue **Minor** Version wurde definiert, wenn eine neue Funktionalität abwärtskompatibel hinzugefügt wurde. 3. Eine neue **Patch** Version wurde definiert, wenn Fehler abwärtskompatibel behoben wurden. @@ -30,41 +24,23 @@ ein test-jar konfiguriert, sodass alle Tests (und abstrakte Testklassen) auch al [![GitHub Actions](https://github.com/uhafner/codingstyle/workflows/GitHub%20CI/badge.svg)](https://github.com/uhafner/codingstyle/actions) -Die Konfiguration der Continuous Integration in GitHub Actions is sehr [einfach](../.github/workflows/ci.yml). -Da der gesamte Build über Maven automatisiert ist, besteht die Konfiguration eigentlich nur aus einem Maven Aufruf, -der das Projekt baut, alle Tests (Unit und Integrationstests) ausgeführt, die statische Code Analyse durchführt -und schließlich werden nochmals alle Test mit dem Code Coverage Tool JaCoCo analysiert. Bei einem erfolgreichen -Build werden die Code Coverage Ergebnisse in die Platform [CodeCov](https://app.codecov.io/gh/uhafner/codingstyle) hochgeladen. -GitHub Actions bietet die Möglichkeit, Matrix Builds durchzuführen: d.h., der Build wird auf den Plattformen Linux, -Windows und macOS parallel durchgeführt. +Die Konfiguration der Continuous Integration in GitHub Actions is sehr [einfach](../.github/workflows/ci.yml) über eine Pipeline möglich. Da der gesamte Build über Maven automatisiert ist, besteht die Pipeline eigentlich nur aus einem Maven Aufruf, der das Projekt baut, alle Tests (Unit und Integrationstests) ausgeführt, die statische Code Analyse durchführt und schließlich die Coverage misst. GitHub Actions bietet auch die Möglichkeit, Matrix Builds durchzuführen: d.h., der Build wird z.B. auf den Plattformen Linux, Windows und macOS oder mit den Java Versionen 17 und 21 parallel durchgeführt. Ein Beispiel für die Konfiguration eines Matrix Builds ist in der Datei [ci.yml](../.github/workflows/ci.yml) zu finden. + +Wenn gewünscht, können die Ergebnisse der statischen Code Analyse und der Code Coverage Tools auch direkt im Commit oder Pull-Request angezeigt werden. Dazu muss in der Pipeline meine [Quality Monitor Action](https://github.com/uhafner/quality-monitor) aktiviert werden. Eine Beispielkonfiguration ist in der Datei [quality-monitor.yml](../.github/workflows/quality-monitor.yml) zu finden, das Ergebnis in der nachfolgenden Abbildung: + +![Quality Monitor](images/quality-monitor.png) ## Jenkins -Eine Beispielintegration in Jenkins ist auch bereits vorhanden. Diese ist im [Jenkinsfile](../Jenkinsfile) hinterlegt -und startet die Integration in mehreren Schritten (Stages). Zunächst werden auch hier alle Schritte wie in GitHub Actions -aufgerufen. Anschließend erfolgt noch ein Start der Mutation Coverage mit [PIT](http://pitest.org). Insgesamt ist -die CI Konfiguration für Jenkins umfangreicher, da nicht nur der eigentliche Build konfiguriert wird, sondern -auch die Darstellung der Ergebnisse im Jenkins UI über die entsprechenden Jenkins Plugins konfiguriert wird. -Eine solche Visualisierung ist unter GitHub Actions nicht verfügbar. +Eine Beispielintegration mit Jenkins ist auch bereits vorhanden. Diese ist im [Jenkinsfile](../Jenkinsfile) hinterlegt und startet die Integration in mehreren Schritten (Stages). Zunächst werden auch hier alle Schritte wie in GitHub Actions aufgerufen. Anschließend erfolgt noch ein Start der Mutation Coverage mit [PIT](http://pitest.org). Insgesamt ist die CI Konfiguration für Jenkins umfangreicher, da nicht nur der eigentliche Build konfiguriert wird, sondern auch die Darstellung der Ergebnisse im Jenkins UI über die entsprechenden Jenkins Plugins konfiguriert wird. ### Lokale CI in Jenkins (über Docker Compose) -Da es für Jenkins keinen öffentlichen Service wie bei GitHub Actions gibt, um eigene Projekte zu bauen, muss die Jenkins -Integration lokal durchgeführt werden. Zur Vereinfachung des Jenkins Setup ist in diesem Coding Style eine -lauffähige Jenkins Installation enthalten (im Sinne von *Infrastructure as Code*). -Diese kann über `bin/jenkins.sh` gestartet werden. Anschließend wird die -aktuelle Jenkins LTS Version mit allen benötigten Plugins in einem Docker Container gebaut und gestartet (das dauert -beim ersten Aufruf etwas). Dazu wird ebenso ein als Docker Container initialisierter Java Agent (**Achtung**: Java 8) -verbunden, der die Builds ausführt. +Da es für Jenkins keinen öffentlichen Service wie bei GitHub Actions gibt, um eigene Projekte zu bauen, muss die Jenkins Integration lokal durchgeführt werden. Zur Vereinfachung des Jenkins Setup ist in diesem Coding Style eine lauffähige Jenkins Installation enthalten (im Sinne von *Infrastructure as Code*). Diese kann über `bin/jenkins.sh` gestartet werden. Anschließend wird die aktuelle Jenkins LTS Version mit allen benötigten Plugins in einem Docker Container gebaut und gestartet (das dauert beim ersten Aufruf etwas). Dazu wird ebenso ein als Docker Container initialisierter Java Agent verbunden, der die Builds ausführt. + -Nach einem erfolgreichen Start von Jenkins ist dann unter [http://localhost:8080/job/Codingstyle/](http://localhost:8080/job/Codingstyle/) der entsprechende Jenkins Job sichtbar. -Der Zugang auf diesen lokalen Rechner erfolgt zur Vereinfachung -mit Benutzer `admin` und Passwort `admin`, anschließend hat man volle Jenkins Administrationsrechte. -Der Job `Codingstyle` muss danach manuell gestartet werden, -die Ergebnisse der Tests, Code und Mutation Coverage sowie statischen Analyse werden dann automatisch -visualisiert. Das Jenkins Home Verzeichnis ist im Docker Container als externes Volume angelegt: d.h. der Zugriff kann -auf dem Host direkt im Verzeichnis `docker/volumes/jenkins-home` erfolgen. +Nach einem erfolgreichen Start von Jenkins sind dann unter [http://localhost:8081](http://localhost:8080/job/Codingstyle/) mehrere Jenkins Jobs sichtbar. Einer dieser Jobs baut das vorliegende Coding Style Projekt. Der Zugang auf diesen lokalen Rechner erfolgt zur Vereinfachung mit Benutzer `admin` und Passwort `admin`, anschließend hat man volle Jenkins Administrationsrechte. Die jeweiligen Jobs müssen danach manuell gestartet werden, die Ergebnisse der Tests, Code und Mutation Coverage sowie statischen Analyse werden dann automatisch visualisiert. Das Jenkins Home Verzeichnis ist im Docker Container als externes Volume angelegt: d.h. der Zugriff kann auf dem Host direkt im Verzeichnis `docker/volumes/jenkins-home` erfolgen. -Nach einem ersten Build in Jenkins sollte sich dann folgendes Bild ergeben: +Nach einem ersten Build in Jenkins sollte sich dann in etwa folgendes Bild ergeben: ![Jenkins Build Summary](images/build-result.png) diff --git a/doc/images/quality-monitor.png b/doc/images/quality-monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..5dd3f157d9f2ab1bccef83fc0208b007f2dc3bff GIT binary patch literal 71403 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 literal 0 HcmV?d00001 diff --git a/docker-compose.yaml b/docker-compose.yaml index b085be7a..471d4ff3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,20 +1,70 @@ -version: "3" +name: codingstyle + services: - jenkins-controller: + jenkins: + container_name: jenkins build: context: docker/images/jenkins-controller volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./docker/volumes/jenkins-home:/var/jenkins_home:cached + - ./docker/volumes/jenkins-home:/var/jenkins_home # Mounts the local jenkins_home volume to the /var/jenkins_home path inside the container + - agent-ssh-dir:/ssh-dir # Mounts the shared volume agent-ssh-dir to a path inside the container ports: - 8081:8080 # Jenkins UI - HOST:CONTAINER environment: - TRY_UPGRADE_IF_NO_MARKER=true - - JAVA_OPTS= -Dstapler.jelly.noCache=true -Dhudson.remoting.ClassFilter=com.google.common.collect.ImmutableListMultimap -DexecutableWar.jetty.disableCustomSessionIdCookieName=true -DexecutableWar.jetty.sessionIdCookieName=warnings-ng-devenv + - JAVA_OPTS= -Dstapler.jelly.noCache=true -Dhudson.remoting.ClassFilter=com.google.common.collect.ImmutableListMultimap -DexecutableWar.jetty.disableCustomSessionIdCookieName=true -DexecutableWar.jetty.sessionIdCookieName=codingstyle user: ${CURRENT_UID} restart: unless-stopped - java11-agent: - build: ./docker/images/java11-agent depends_on: - - jenkins-controller + key-generator: + condition: service_completed_successfully # Depends on the successful completion of the sidekick_service + healthcheck: + test: ["CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1"] + # Checks if the conductor_ok file exists in the /ssh-dir path + interval: 5s + timeout: 10s + retries: 5 + + key-generator: + container_name: key-generator + build: + context: docker/images/key-generator + stdin_open: true + tty: true + # The entrypoint script generates the SSH keys and outputs them to the /ssh-dir directory. + entrypoint: sh -c "/usr/local/bin/keygen.sh /ssh-dir" # Runs the keygen.sh script and specifies the output directory + volumes: + - agent-ssh-dir:/ssh-dir # Mounts the agent-ssh-dir volume to the /ssh-dir path inside the container + # The healthcheck command checks if the conductor_ok file exists in the /ssh-dir directory. + healthcheck: + test: ["CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1"] + # Checks if the conductor_ok file exists in the /ssh-dir path + interval: 5s + timeout: 10s + retries: 5 + + java-agent: + container_name: java-agent + build: docker/images/java-agent + depends_on: + key-generator: + condition: service_completed_successfully # Depends on the successful completion of the sidekick_service + jenkins: + condition: service_started restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "[ -f /ssh-dir/conductor_ok ] || exit 1"] + # Checks if the conductor_ok file exists in the /ssh-dir path + interval: 5s + timeout: 10s + retries: 5 + volumes: + - agent-ssh-dir:/home/jenkins/.ssh:ro # Mounts the agent-ssh-dir volume to the /home/jenkins/.ssh path inside the container as read-only + - ${HOME}/.m2/repository:/home/jenkins/.m2/repository # Mounts the local Maven repository to the /home/jenkins/.m2 path inside the container + +volumes: + agent-ssh-dir: + name: agent-ssh-dir # Creates a named volume called agent-ssh-dir + jenkins_home: + name: jenkins_home # Creates a named volume called jenkins_home + external: true diff --git a/docker/images/java-agent/Dockerfile b/docker/images/java-agent/Dockerfile new file mode 100644 index 00000000..2bb4ad6f --- /dev/null +++ b/docker/images/java-agent/Dockerfile @@ -0,0 +1,27 @@ +FROM jenkins/ssh-agent:latest-jdk21 + +# Install prerequisites for Java and Maven +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Install Maven +ARG MAVEN_VERSION=3.9.8 + +SHELL ["/bin/bash", "-eo", "pipefail", "-c"] +RUN curl -sS -L -O --output-dir /tmp/ --create-dirs https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ + && printf "%s" "$(sha512sum /tmp/apache-maven-${MAVEN_VERSION}-bin.tar.gz)" | sha512sum -c - \ + && curl -sS -L -O --output-dir /tmp/ --create-dirs https://downloads.apache.org/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz.sha512 \ + && printf "%s /tmp/apache-maven-${MAVEN_VERSION}-bin.tar.gz" "$(cat /tmp/apache-maven-${MAVEN_VERSION}-bin.tar.gz.sha512)" | sha512sum --check --status - \ + && tar xzf "/tmp/apache-maven-${MAVEN_VERSION}-bin.tar.gz" -C /opt/ \ + && rm "/tmp/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \ + && ln -s /opt/apache-maven-${MAVEN_VERSION} /opt/maven \ + && ln -s /opt/maven/bin/mvn /usr/bin/mvn \ + && mkdir -p /etc/profile.d \ + && echo "export JAVA_HOME=$JAVA_HOME \n \ + export M2_HOME=/opt/maven \n \ + export PATH=${M2_HOME}/bin:${PATH}" > /etc/profile.d/maven.sh +ENV M2_HOME="/opt/maven" +ENV PATH="${M2_HOME}/bin/:${PATH}" +RUN echo "PATH=${PATH}" >> /etc/environment && chown -R jenkins:jenkins "${JENKINS_AGENT_HOME}" diff --git a/docker/images/java11-agent/Dockerfile b/docker/images/java11-agent/Dockerfile deleted file mode 100644 index 871fcbbb..00000000 --- a/docker/images/java11-agent/Dockerfile +++ /dev/null @@ -1,65 +0,0 @@ -FROM eclipse-temurin:11-alpine - -RUN apk update \ - && apk add bash git openssh dos2unix curl \ - && mkdir /root/.ssh \ - && chmod 0700 /root/.ssh \ - && ssh-keygen -A \ - && sed -i s/^#PasswordAuthentication\ yes/PasswordAuthentication\ no/ /etc/ssh/sshd_config \ - && sed -i -e "s/MACs /MACs hmac-sha1,/g" /etc/ssh/sshd_config \ - && echo "RSAAuthentication yes" > /etc/ssh/sshd_config \ - && echo "UsePAM yes" > /etc/ssh/sshd_config \ - && echo "PubkeyAuthentication yes" > /etc/ssh/sshd_config -USER root - -ARG MAVEN_VERSION=3.8.6 -ARG USER_HOME_DIR="/root" -ARG SHA=f790857f3b1f90ae8d16281f902c689e4f136ebe584aba45e4b1fa66c80cba826d3e0e52fdd04ed44b4c66f6d3fe3584a057c26dfcac544a60b301e6d0f91c26 -ARG BASE_URL=https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries - -RUN mkdir -p /opt/maven /opt/maven/ref \ - && echo "Downloading maven" \ - && curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ - \ - && echo "Checking download hash" \ - && echo "${SHA} /tmp/apache-maven.tar.gz" | sha512sum -c - \ - \ - && echo "Unzipping maven" \ - && tar -xzf /tmp/apache-maven.tar.gz -C /opt/maven --strip-components=1 \ - \ - && echo "Cleaning and setting links" \ - && rm -f /tmp/apache-maven.tar.gz \ - && ln -s /opt/maven/bin/mvn /usr/bin/mvn - -RUN addgroup -S jenkins && adduser -D agent -G jenkins -RUN echo "agent:Docker!" | chpasswd - -RUN mkdir /home/agent/.ssh -RUN chmod 700 /home/agent/.ssh -RUN chown agent:jenkins /home/agent/.ssh - -COPY --chown=agent:jenkins unsafe.pub /home/agent/.ssh/authorized_keys -RUN chmod 600 /home/agent/.ssh/authorized_keys - -RUN mkdir /var/data -VOLUME /var/data - -COPY docker-entrypoint.sh / -RUN chmod u+x docker-entrypoint.sh -RUN dos2unix /docker-entrypoint.sh - -RUN git config --global user.name "Jenkins Java 11 Agent" -RUN git config --global user.email "java11.agent@jenkins.master" - -EXPOSE 22 - -ENV JAVA_HOME /opt/java/openjdk -ENV MAVEN_HOME /opt/maven - -RUN java --version -RUN mvn --version - -ENTRYPOINT ["/docker-entrypoint.sh"] - -# -D in CMD below prevents sshd from becoming a daemon. -e is to log everything to stderr. -CMD ["/usr/sbin/sshd", "-D", "-e"] diff --git a/docker/images/java11-agent/docker-entrypoint.sh b/docker/images/java11-agent/docker-entrypoint.sh deleted file mode 100644 index c9185eee..00000000 --- a/docker/images/java11-agent/docker-entrypoint.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -if [ ! -f "/etc/ssh/ssh_host_rsa_key" ]; then - # generate fresh rsa key - ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -t rsa -fi -if [ ! -f "/etc/ssh/ssh_host_dsa_key" ]; then - # generate fresh dsa key - ssh-keygen -f /etc/ssh/ssh_host_dsa_key -N '' -t dsa -fi - -# prepare run dir -if [ ! -d "/var/run/sshd" ]; then - mkdir -p /var/run/sshd -fi - -TARGET_GID=$(stat -c "%g" /var/data) -addgroup -g $TARGET_GID tempgroup -addgroup agent tempgroup -chmod 770 /var/data -chmod g+s /var/data -chown agent:jenkins /var/data - -echo Added user agent to group GID $TARGET_GID - -# Execute the CMD from the Dockerfile: -exec "$@" diff --git a/docker/images/java11-agent/unsafe b/docker/images/java11-agent/unsafe deleted file mode 100644 index d7d7b319..00000000 --- a/docker/images/java11-agent/unsafe +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA86cZk1vZh/OwzJDEqga0nnKEh1QpCm1uSDjvuLp2pPDPHMp/ -m9Il9O8ZAZ+IVCUXygO3s1BvBF/a7uVYp7XqjDiTj5Jwrs5lEtl4/1iXXKtwbpAc -bsoiEaFDl7Amc4pUfwzsjaqY4yN4jW+JaLOf/+GqCjwpiyJxZ0N2ONlWoph9dvfH -JBK+LUcbKIqbBAADT12d6ePIBsDn9Yaeem0xSJrwEzcMTFYj6asinMlWgO7VwgZx -JPxhMG+jIq6Mh20tFbwc/XGTqSb/vMkJ8i53RnYm5PoT2xqeYlQCLJtvBRBfuBb5 -WJaQcKvTmLoTgkRCbx/QsQzIWzl4MCj56PRsjQIDAQABAoIBAAGTiy7Q4U9n3DT2 -ms8ey/xacVEO0lUm8Be3hpWDX1Eh3bUp+jlf2q8C/P5tscwZkVXVQFMAqjc1B42U -Hka3fpT5qLq9D82RuEWu8oF0aUZINaoBdK2i0SWcDXvlv9nvgyxvQPiJqgOOLzF7 -D0CGKPrW0urOCNbFmkY4wYMMpOrYXnwb6bc1p7snbzeRigaoGvSgvH7fx2Steg1o -j50C4BKVtXPKQdmckG2SFn0T+U1iCsRG+KNcENX2vX8gyrXImAH093WTjKsmM9et -ddWB+molSnXR/MNrf6BB2mpvXLNyR2/RgBd2jwSQnpDkpms4Br5nek3YYN1dBRL4 -6bofHWECgYEA+7n5OIEbvpMtGxJwOovj0KZMzPkHyQH/DZzo48rS+39goNk/0KLF -c3L3sHbT3Lr4qA/6JOCjlzw7o2AbOrRL4ke1uqcCVQMdDqZdvNezMvTzqEbQGdHD -aFnEcUV2tvEwP11q37ianBRPH5stOnEwQNuv6AJo5LKwi4mTS7qEW9cCgYEA98oJ -h+vMKpXGdJzkSDMzYBrC2tYgqjby6+zGKz8BZ58YecsL+oi2GXBaDTfK+16CKeFM -8+qQN9Kl1ZNOlk64XJXjt77h0FcFuGe+6rUpM1aEizrf9sWPVZO+QQfhnjsiAhtQ -YX783ydy9rMn1FDPMtNNq4GMhGsFCaL4RupOjjsCgYAnk9XbTHFQRVOSLhP3IIdx -BrSMhZrzv5yaR1FWf00svZozr/SYmP7yZ+EJnaUxzzPJOLnbknYmERJPXYzqbe6A -ZUXtUtTLCPJIm1+hkUhbeqfUjU2qwZA3l+WK6aEAomszizyCcEPexlKqZXt29NTh -XakKkVZsnqujRL4j6e9lgQKBgQDvhD8EQJyAyXgkvoc3dy6BBj019WdrwWO9Q4km -wmdkN3gcOnYgvUdwfZa+UiEGLAub2eldmW3AWADu2s5LIlq5PDX7Jir3DTc9UiNM -ksL5mfbS8p0M11i+uupbx/eB0N0FtktTgsGCH4rUBsdIRriSA4h/cOFYGm6rKvnc -6p32gwKBgHZYmXzuBWZlWEmPiXbTaI4egJugur5FrT6BJfiLsN2MHBJi9k1IpKEP -SaT+v0IXJ8jP4gSiu4/gyJQpkn7yiMNhwYWlQt+1zyIkHjUsEG82Z8Mqpjx2EJgG -MxDybQux1uk0hyCmMS757WkbTyi0pTWz7PgTIdfmYqhZVV8KRSUi ------END RSA PRIVATE KEY----- diff --git a/docker/images/java11-agent/unsafe.pub b/docker/images/java11-agent/unsafe.pub deleted file mode 100644 index 0305a8c3..00000000 --- a/docker/images/java11-agent/unsafe.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDzpxmTW9mH87DMkMSqBrSecoSHVCkKbW5IOO+4unak8M8cyn+b0iX07xkBn4hUJRfKA7ezUG8EX9ru5VinteqMOJOPknCuzmUS2Xj/WJdcq3BukBxuyiIRoUOXsCZzilR/DOyNqpjjI3iNb4los5//4aoKPCmLInFnQ3Y42VaimH1298ckEr4tRxsoipsEAANPXZ3p48gGwOf1hp56bTFImvATNwxMViPpqyKcyVaA7tXCBnEk/GEwb6MiroyHbS0VvBz9cZOpJv+8yQnyLndGdibk+hPbGp5iVAIsm28FEF+4FvlYlpBwq9OYuhOCREJvH9CxDMhbOXgwKPno9GyN unsafe@nowhere diff --git a/docker/images/jenkins-controller/Dockerfile b/docker/images/jenkins-controller/Dockerfile index 96dcae18..7b8d42ae 100644 --- a/docker/images/jenkins-controller/Dockerfile +++ b/docker/images/jenkins-controller/Dockerfile @@ -1,17 +1,28 @@ -FROM jenkins/jenkins:lts-alpine +# Prepare a Debian-based Docker image with several utilities installed to automatically generate SSH keys +FROM jenkins/jenkins:latest-jdk21 -USER root -RUN addgroup -g 102 docker -RUN adduser jenkins docker -RUN apk add libltdl +# We switch back to the Jenkins user for the remaining operations. USER jenkins -# Install plugins +# We copy the jobs directory from our current directory to the Jenkins home directory in the image. +COPY preconfigured-jobs /usr/share/jenkins/ref/jobs + +# We write the Jenkins version to the UpgradeWizard state file. +# This prevents the Upgrade Wizard from showing up when Jenkins starts. +RUN echo "${JENKINS_VERSION}" > /usr/share/jenkins/ref/jenkins.install.UpgradeWizard.state + +# We copy a list of plugins to install to the Jenkins ref directory in the image. COPY plugins.txt /usr/share/jenkins/ref/plugins.txt + +# We use the Jenkins plugin CLI to install the plugins listed in the plugins.txt file. RUN jenkins-plugin-cli --verbose -f /usr/share/jenkins/ref/plugins.txt -# Create admin user and don't start the wizard -RUN echo 2.0 > /usr/share/jenkins/ref/jenkins.install.UpgradeWizard.state +# We copy a pre-configured Jenkins configuration file to the Jenkins ref directory in the image. +# This allows us to pre-configure Jenkins with our desired settings. +COPY jenkins.yaml /usr/share/jenkins/ref/jenkins.yaml + +# Create an admin user and don't start the wizard +RUN echo 2.x > /usr/share/jenkins/ref/jenkins.install.UpgradeWizard.state ENV JENKINS_OPTS -Djenkins.install.runSetupWizard=false COPY security.groovy /usr/share/jenkins/ref/init.groovy.d/basic-security.groovy diff --git a/docker/images/jenkins-controller/jenkins.yaml b/docker/images/jenkins-controller/jenkins.yaml index 3a48a123..f7673691 100644 --- a/docker/images/jenkins-controller/jenkins.yaml +++ b/docker/images/jenkins-controller/jenkins.yaml @@ -7,6 +7,8 @@ jenkins: standard: excludeClientIPFromCrumb: false disableRememberMe: false + disabledAdministrativeMonitors: + - "hudson.diagnosis.ReverseProxySetupMonitor" markupFormatter: rawHtml: disableSyntaxHighlighting: false @@ -14,48 +16,105 @@ jenkins: myViewsTabBar: "standard" nodes: - permanent: + labelString: "docker linux agent java jdk21 java21" launcher: ssh: - credentialsId: "java-agent-ssh-private-key" - host: "java11-agent" - launchTimeoutSeconds: 210 - maxNumRetries: 10 + credentialsId: "jenkins-ssh-agent-private-key" + host: "java-agent" port: 22 - retryWaitTime: 15 - jvmOptions: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8001" - javaPath: "/opt/java/openjdk/bin/java" sshHostKeyVerificationStrategy: "nonVerifyingKeyVerificationStrategy" - name: "java11-agent" + name: "java-agent" + nodeDescription: "Jenkins Docker Agent with Java 21 and Maven" numExecutors: 1 - remoteFS: "/var/data" - nodeProperties: - - envVars: - env: - - key: "JAVA_HOME" - value: "/opt/java/openjdk" - - key: "MAVEN_HOME" - value: "/opt/maven" + remoteFS: "/home/jenkins/agent" + retentionStrategy: "always" numExecutors: 0 primaryView: - all: - name: "all" + list: + columns: + - "status" + - "weather" + - "jobName" + - "gitBranchSpecifierColumn" + - "lastSuccess" + - "lastFailure" + - "lastDuration" + - coverageTotalsColumn: + columnName: "Line Coverage" + metric: LINE + - coverageTotalsColumn: + columnName: "Branch Coverage" + metric: BRANCH + - "buildButton" + includeRegex: ".*" + name: "List View" projectNamingStrategy: "standard" quietPeriod: 5 - remotingSecurity: - enabled: true scmCheckoutRetryCount: 0 securityRealm: local: allowsSignup: false enableCaptcha: false slaveAgentPort: 50000 - systemMessage: "

Warnings Next Generation Plugin Showcase

" + systemMessage: "

Java and Maven Showcase

" updateCenter: sites: - id: "default" url: "https://updates.jenkins.io/update-center.json" + views: + - list: + columns: + - "status" + - "weather" + - "jobName" + - "gitBranchSpecifierColumn" + - "lastSuccess" + - "lastFailure" + - "lastDuration" + - coverageTotalsColumn: + columnName: "Line Coverage" + metric: LINE + - coverageTotalsColumn: + columnName: "Branch Coverage" + metric: BRANCH + - "buildButton" + includeRegex: ".*" + name: "List View" + - dashboard: + columns: + - "status" + - "weather" + - "jobName" + - "lastSuccess" + - "lastFailure" + - "lastDuration" + - coverageTotalsColumn: + columnName: "Line Coverage" + metric: LINE + - coverageTotalsColumn: + columnName: "Branch Coverage" + metric: BRANCH + - "buildButton" + includeStdJobList: true + includeRegex: ".*" + leftPortlets: + - issuesChartPortlet: + height: 600 + name: "Static analysis issues chart" + name: "Dashboard View" + rightPortlets: + - issuesTablePortlet: + name: "Static analysis issues per tool and job" + showIcons: true viewsTabBar: "standard" +appearance: + locale: + ignoreAcceptLanguage: true + systemLocale: "USE_BROWSER_LOCALE" + prism: + theme: PRISM + security: apiToken: creationOfLegacyTokenEnabled: false @@ -71,49 +130,18 @@ credentials: domainCredentials: - credentials: - basicSSHUserPrivateKey: - id: "java-agent-ssh-private-key" + description: "Private SSH key for Jenkins agent" + id: "jenkins-ssh-agent-private-key" privateKeySource: directEntry: - privateKey: "-----BEGIN RSA PRIVATE KEY----- \n - MIIEowIBAAKCAQEA86cZk1vZh/OwzJDEqga0nnKEh1QpCm1uSDjvuLp2pPDPHMp/ \n - m9Il9O8ZAZ+IVCUXygO3s1BvBF/a7uVYp7XqjDiTj5Jwrs5lEtl4/1iXXKtwbpAc \n - bsoiEaFDl7Amc4pUfwzsjaqY4yN4jW+JaLOf/+GqCjwpiyJxZ0N2ONlWoph9dvfH \n - JBK+LUcbKIqbBAADT12d6ePIBsDn9Yaeem0xSJrwEzcMTFYj6asinMlWgO7VwgZx \n - JPxhMG+jIq6Mh20tFbwc/XGTqSb/vMkJ8i53RnYm5PoT2xqeYlQCLJtvBRBfuBb5 \n - WJaQcKvTmLoTgkRCbx/QsQzIWzl4MCj56PRsjQIDAQABAoIBAAGTiy7Q4U9n3DT2 \n - ms8ey/xacVEO0lUm8Be3hpWDX1Eh3bUp+jlf2q8C/P5tscwZkVXVQFMAqjc1B42U \n - Hka3fpT5qLq9D82RuEWu8oF0aUZINaoBdK2i0SWcDXvlv9nvgyxvQPiJqgOOLzF7 \n - D0CGKPrW0urOCNbFmkY4wYMMpOrYXnwb6bc1p7snbzeRigaoGvSgvH7fx2Steg1o \n - j50C4BKVtXPKQdmckG2SFn0T+U1iCsRG+KNcENX2vX8gyrXImAH093WTjKsmM9et \n - ddWB+molSnXR/MNrf6BB2mpvXLNyR2/RgBd2jwSQnpDkpms4Br5nek3YYN1dBRL4 \n - 6bofHWECgYEA+7n5OIEbvpMtGxJwOovj0KZMzPkHyQH/DZzo48rS+39goNk/0KLF \n - c3L3sHbT3Lr4qA/6JOCjlzw7o2AbOrRL4ke1uqcCVQMdDqZdvNezMvTzqEbQGdHD \n - aFnEcUV2tvEwP11q37ianBRPH5stOnEwQNuv6AJo5LKwi4mTS7qEW9cCgYEA98oJ \n - h+vMKpXGdJzkSDMzYBrC2tYgqjby6+zGKz8BZ58YecsL+oi2GXBaDTfK+16CKeFM \n - 8+qQN9Kl1ZNOlk64XJXjt77h0FcFuGe+6rUpM1aEizrf9sWPVZO+QQfhnjsiAhtQ \n - YX783ydy9rMn1FDPMtNNq4GMhGsFCaL4RupOjjsCgYAnk9XbTHFQRVOSLhP3IIdx \n - BrSMhZrzv5yaR1FWf00svZozr/SYmP7yZ+EJnaUxzzPJOLnbknYmERJPXYzqbe6A \n - ZUXtUtTLCPJIm1+hkUhbeqfUjU2qwZA3l+WK6aEAomszizyCcEPexlKqZXt29NTh \n - XakKkVZsnqujRL4j6e9lgQKBgQDvhD8EQJyAyXgkvoc3dy6BBj019WdrwWO9Q4km \n - wmdkN3gcOnYgvUdwfZa+UiEGLAub2eldmW3AWADu2s5LIlq5PDX7Jir3DTc9UiNM \n - ksL5mfbS8p0M11i+uupbx/eB0N0FtktTgsGCH4rUBsdIRriSA4h/cOFYGm6rKvnc \n - 6p32gwKBgHZYmXzuBWZlWEmPiXbTaI4egJugur5FrT6BJfiLsN2MHBJi9k1IpKEP \n - SaT+v0IXJ8jP4gSiu4/gyJQpkn7yiMNhwYWlQt+1zyIkHjUsEG82Z8Mqpjx2EJgG \n - MxDybQux1uk0hyCmMS757WkbTyi0pTWz7PgTIdfmYqhZVV8KRSUi \n - -----END RSA PRIVATE KEY----- \n" - scope: GLOBAL - username: "agent" - - gitHubApp: - appID: "74721" - description: "GitHub App" - id: "github-app" - privateKey: ${GITHUB_APP} - scope: GLOBAL + privateKey: ${readFile:/ssh-dir/jenkins_agent_ed} + scope: SYSTEM + username: "jenkins" unclassified: globalDefaultFlowDurabilityLevel: durabilityHint: PERFORMANCE_OPTIMIZED location: - adminAddress: "Adresse nicht konfiguriert " + adminAddress: "Address not configured " url: "http://localhost:8080/" mailer: charset: "UTF-8" @@ -125,21 +153,11 @@ tool: git: installations: - home: "git" - name: "Default" + name: "git-default" maven: installations: - - name: "mvn-default" - properties: - - installSource: - installers: - - maven: - id: "3.6.3" - jdk: - defaultProperties: - - installSource: - installers: - - jdkInstaller: - acceptLicense: false + - home: "/opt/maven" + name: "mvn-default" pipelineMaven: publisherOptions: - jacocoPublisher: @@ -152,66 +170,3 @@ tool: triggerDownstreamUponResultNotBuilt: false triggerDownstreamUponResultSuccess: true triggerDownstreamUponResultUnstable: false - -jobs: - - script: > - pipelineJob('pipeline-codingstyle') { - definition { - cpsScm { - scriptPath 'Jenkinsfile' - scm { - git { - remote { url 'https://github.com/uhafner/codingstyle.git' } - branch '*/main' - extensions {} - } - } - } - } - }; - freeStyleJob('freestyle-codingstyle') { - scm { - git { - remote { url 'https://github.com/uhafner/codingstyle.git' } - branch '*/main' - extensions {} - } - } - steps { - maven { - mavenInstallation('mvn-default') - goals('clean verify') - properties(skipITs: true) - } - } - publishers { - recordIssues { - sourceCodeEncoding('UTF-8') - aggregatingResults(true) - tools { - mavenConsole {} - java {} - javaDoc {} - taskScanner { - highTags('FIXME') - normalTags('TODO') - includePattern('**/*.java') - excludePattern('target/**/*') - } - spotBugs { - pattern('**/target/spotbugsXml.xml') - useRankAsPriority(true) - } - checkStyle { - pattern('**/target/checkstyle-result.xml') - } - pmd { - pattern('**/target/pmd.xml') - } - cpd { - pattern('**/target/cpd.xml') - } - } - } - } - }; diff --git a/docker/images/jenkins-controller/plugins.txt b/docker/images/jenkins-controller/plugins.txt index a89cbd9d..beccaa33 100644 --- a/docker/images/jenkins-controller/plugins.txt +++ b/docker/images/jenkins-controller/plugins.txt @@ -1,5 +1,5 @@ -ace-editor analysis-model-api +ansicolor antisamy-markup-formatter apache-httpcomponents-client-4-api authentication-tokens @@ -7,7 +7,7 @@ autograding bouncycastle-api branch-api cloudbees-folder -code-coverage-api +coverage command-launcher configuration-as-code credentials @@ -18,7 +18,6 @@ docker-commons docker-workflow durable-task email-ext -filesystem_scm file-operations form-element-path git @@ -27,13 +26,12 @@ git-forensics git-server github github-api +github-checks github-branch-source -handlebars jackson2-api javadoc jdk-tool job-dsl -jquery-detached jsch junit locale @@ -42,9 +40,7 @@ mailer matrix-auth matrix-project maven-plugin -metrics-aggregation mock-security-realm -momentjs pam-auth pipeline-build-step pipeline-graph-analysis @@ -57,7 +53,6 @@ pipeline-model-extensions pipeline-rest-api pipeline-stage-step pipeline-stage-tags-metadata -pitmutation plain-credentials resource-disposer scm-api diff --git a/docker/images/jenkins-controller/preconfigured-jobs/freestyle-analysis-model/config.xml b/docker/images/jenkins-controller/preconfigured-jobs/freestyle-analysis-model/config.xml new file mode 100644 index 00000000..0dc75efa --- /dev/null +++ b/docker/images/jenkins-controller/preconfigured-jobs/freestyle-analysis-model/config.xml @@ -0,0 +1,165 @@ + + + + + false + + + 2 + + + https://github.com/jenkinsci/analysis-model.git + + + + + */main + + + false + + + + true + false + false + false + + false + + + clean verify -ntp -Pci + mvn-default + skipITs=true + false + + + true + + + + + + + + 1 + + 2 + + + + JACOCO + + + + + JUNIT + + + + + + + false + Code Coverage + MODIFIED_LINES + false + false + false + false + + + + LAST_BUILD + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + FIXME + TODO + + false + false + **/*.java + + + + + + + + + false + true + + + + + + + + false + + + + + + + + false + 50 + 25 + + + UTF-8 + + EVERY_BUILD + false + false + 0 + 0 + + LOW + + + false + false + false + false + true + NEW + false + + AGGREGATION_TOOLS + + + + + \ No newline at end of file diff --git a/docker/images/jenkins-controller/preconfigured-jobs/freestyle-analysis-model/nextBuildNumber b/docker/images/jenkins-controller/preconfigured-jobs/freestyle-analysis-model/nextBuildNumber new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/docker/images/jenkins-controller/preconfigured-jobs/freestyle-analysis-model/nextBuildNumber @@ -0,0 +1 @@ +1 diff --git a/docker/images/jenkins-controller/preconfigured-jobs/history-coverage-model/config.xml b/docker/images/jenkins-controller/preconfigured-jobs/history-coverage-model/config.xml new file mode 100644 index 00000000..f8d6e8fe --- /dev/null +++ b/docker/images/jenkins-controller/preconfigured-jobs/history-coverage-model/config.xml @@ -0,0 +1,168 @@ + + + + + false + + + + true + + + false + diff --git a/docker/images/jenkins-controller/preconfigured-jobs/history-coverage-model/nextBuildNumber b/docker/images/jenkins-controller/preconfigured-jobs/history-coverage-model/nextBuildNumber new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/docker/images/jenkins-controller/preconfigured-jobs/history-coverage-model/nextBuildNumber @@ -0,0 +1 @@ +1 diff --git a/docker/images/jenkins-controller/preconfigured-jobs/pipeline-codingstyle/config.xml b/docker/images/jenkins-controller/preconfigured-jobs/pipeline-codingstyle/config.xml new file mode 100644 index 00000000..2bb17b43 --- /dev/null +++ b/docker/images/jenkins-controller/preconfigured-jobs/pipeline-codingstyle/config.xml @@ -0,0 +1,138 @@ + + + + + false + + + + true + + + false + \ No newline at end of file diff --git a/docker/images/jenkins-controller/preconfigured-jobs/pipeline-codingstyle/nextBuildNumber b/docker/images/jenkins-controller/preconfigured-jobs/pipeline-codingstyle/nextBuildNumber new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/docker/images/jenkins-controller/preconfigured-jobs/pipeline-codingstyle/nextBuildNumber @@ -0,0 +1 @@ +1 diff --git a/docker/images/key-generator/Dockerfile b/docker/images/key-generator/Dockerfile new file mode 100644 index 00000000..995a5eda --- /dev/null +++ b/docker/images/key-generator/Dockerfile @@ -0,0 +1,25 @@ +# Prepare a Debian-based Docker image with several utilities installed to automatically generate SSH keys +FROM debian:bookworm-20240612 + +# Copy all shell scripts from the current directory to /usr/local/bin/ in the image. +COPY *sh /usr/local/bin/ + +# Make all shell scripts in /usr/local/bin/ executable. +RUN chmod +x /usr/local/bin/*.sh + +# The RUN command executes a series of commands in the new layer of the image and commits the results. +# The following commands are executed: + +# 1. Update the package list. +# 2. Install necessary dependencies including several utilities and remove the package list to reduce the image size. +RUN apt update \ + && apt install -y --no-install-recommends ca-certificates curl git gnupg nano openssh-client procps unzip wget \ + && rm -rf /var/lib/apt/lists/* && rm -fr /tmp/* + +# Run the keygen.sh script with /ssh-dir as an argument. +# This script is expected to generate SSH keys and store them in /ssh-dir. +RUN /usr/local/bin/keygen.sh /ssh-dir + +# The CMD command specifies the default command to execute when the container starts. +# In this case, it prints a message and lists the contents of /ssh-dir. +CMD ["sh", "-c", "echo 'Export stage is ready'; ls -l /ssh-dir/"] diff --git a/docker/images/key-generator/keygen.sh b/docker/images/key-generator/keygen.sh new file mode 100644 index 00000000..f65a9922 --- /dev/null +++ b/docker/images/key-generator/keygen.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# This script generates a new SSH key pair and updates a Docker Compose file with the public key. +# Create secrets directory if it doesn't exist +set -x +LOC=$1 +if [[ -z "$LOC" ]]; then + LOC="./" +fi + +echo "Location is $LOC" +ls -artl "$LOC" + +rm "$LOC/conductor_ok" + +mkdir -p "$LOC/secrets" + +# Remove existing keys +rm -fr "$LOC/jenkins_agent_ed" "$LOC/jenkins_agent_ed.pub" + +# Generate new ed25519 SSH key pair +ssh-keygen -t ed25519 -f "$LOC/jenkins_agent_ed" -N "" + +# Set appropriate permissions for private key +chmod 444 "$LOC/jenkins_agent_ed" + +# Extract public key +pubkey=$(cat "$LOC/jenkins_agent_ed.pub") + +echo "The public key is $pubkey" + +# Update the authorized_keys file with the public key +echo "$pubkey" > "$LOC/authorized_keys" && chown 1000:1000 "$LOC/authorized_keys" + +# Generate a random token for JCasc +openssl rand -hex 24 > "$LOC/secrets/jcasc_token" +cat "$LOC/secrets/jcasc_token" + +# This file will be used by other containers to know we went up to the end of the key/token generation +echo "OK" > "$LOC/conductor_ok" + +# Display success message +echo "SSH key pair generated successfully." From a723a156c7510b9b6f91664b9802e4c9dec19b4e Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Thu, 4 Jul 2024 16:24:26 +0200 Subject: [PATCH 2/2] Use LTS version of Jenkins. --- bin/jenkins.sh | 2 +- doc/Continuous-Integration.md | 2 +- docker/images/jenkins-controller/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/jenkins.sh b/bin/jenkins.sh index 335b5867..d9b691e4 100755 --- a/bin/jenkins.sh +++ b/bin/jenkins.sh @@ -1,6 +1,6 @@ #!/bin/bash -docker pull jenkins/jenkins:latest-jdk21 +docker pull jenkins/jenkins:lts-jdk21 docker compose build --pull diff --git a/doc/Continuous-Integration.md b/doc/Continuous-Integration.md index 7be136a8..1518e54a 100644 --- a/doc/Continuous-Integration.md +++ b/doc/Continuous-Integration.md @@ -36,7 +36,7 @@ Eine Beispielintegration mit Jenkins ist auch bereits vorhanden. Diese ist im [J ### Lokale CI in Jenkins (über Docker Compose) -Da es für Jenkins keinen öffentlichen Service wie bei GitHub Actions gibt, um eigene Projekte zu bauen, muss die Jenkins Integration lokal durchgeführt werden. Zur Vereinfachung des Jenkins Setup ist in diesem Coding Style eine lauffähige Jenkins Installation enthalten (im Sinne von *Infrastructure as Code*). Diese kann über `bin/jenkins.sh` gestartet werden. Anschließend wird die aktuelle Jenkins LTS Version mit allen benötigten Plugins in einem Docker Container gebaut und gestartet (das dauert beim ersten Aufruf etwas). Dazu wird ebenso ein als Docker Container initialisierter Java Agent verbunden, der die Builds ausführt. +Da es für Jenkins keinen öffentlichen Service wie bei GitHub Actions gibt, um eigene Projekte zu bauen, muss die Jenkins Integration lokal auf einem Team-Server durchgeführt werden. Zur Vereinfachung des Jenkins Setup ist in diesem Coding Style eine lauffähige Jenkins Installation enthalten (im Sinne von *Infrastructure as Code*). Diese kann über `bin/jenkins.sh` gestartet werden. Anschließend wird die aktuelle Jenkins LTS Version mit allen benötigten Plugins in einem Docker Container gebaut und gestartet (das dauert beim ersten Aufruf etwas). Dazu wird ebenso ein als Docker Container initialisierter Java Agent verbunden, der die Builds ausführt. Nach einem erfolgreichen Start von Jenkins sind dann unter [http://localhost:8081](http://localhost:8080/job/Codingstyle/) mehrere Jenkins Jobs sichtbar. Einer dieser Jobs baut das vorliegende Coding Style Projekt. Der Zugang auf diesen lokalen Rechner erfolgt zur Vereinfachung mit Benutzer `admin` und Passwort `admin`, anschließend hat man volle Jenkins Administrationsrechte. Die jeweiligen Jobs müssen danach manuell gestartet werden, die Ergebnisse der Tests, Code und Mutation Coverage sowie statischen Analyse werden dann automatisch visualisiert. Das Jenkins Home Verzeichnis ist im Docker Container als externes Volume angelegt: d.h. der Zugriff kann auf dem Host direkt im Verzeichnis `docker/volumes/jenkins-home` erfolgen. diff --git a/docker/images/jenkins-controller/Dockerfile b/docker/images/jenkins-controller/Dockerfile index 7b8d42ae..f2bf19ec 100644 --- a/docker/images/jenkins-controller/Dockerfile +++ b/docker/images/jenkins-controller/Dockerfile @@ -1,5 +1,5 @@ # Prepare a Debian-based Docker image with several utilities installed to automatically generate SSH keys -FROM jenkins/jenkins:latest-jdk21 +FROM jenkins/jenkins:lts-jdk21 # We switch back to the Jenkins user for the remaining operations. USER jenkins