containerized Jenkins (#179)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/179 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
		
							
								
								
									
										3
									
								
								.aliases
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								.aliases
									
									
									
									
									
								
							| @@ -129,8 +129,7 @@ function _gwTest() { | |||||||
|     # delierately in separate gradlew-calls to avoid Testcontains-PostgreSQL problem spillover |     # delierately in separate gradlew-calls to avoid Testcontains-PostgreSQL problem spillover | ||||||
|     time (_gwTest1 unitTest "$@" && |     time (_gwTest1 unitTest "$@" && | ||||||
|         _gwTest1 officeIntegrationTest bookingIntegrationTest hostingIntegrationTest "$@" && |         _gwTest1 officeIntegrationTest bookingIntegrationTest hostingIntegrationTest "$@" && | ||||||
|         _gwTest1 scenarioTest "$@" && |         _gwTest1 scenarioTest "$@" && _gwTest1 migrationTest "$@"); | ||||||
|         _gwTest1 importHostingAssets "$@"); |  | ||||||
|   elif [ $# -eq 0 ] || [[ $1 == -* ]]; then |   elif [ $# -eq 0 ] || [[ $1 == -* ]]; then | ||||||
|     time _gwTest1 test "$@"; |     time _gwTest1 test "$@"; | ||||||
|   else |   else | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								Jenkins/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Jenkins/Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | FROM jenkins/jenkins:lts-jdk21 | ||||||
|  |  | ||||||
|  | USER root | ||||||
|  |  | ||||||
|  | # Docker CLI installieren | ||||||
|  | RUN apt-get update && apt-get install -y docker.io && usermod -aG docker jenkins | ||||||
|  |  | ||||||
|  | # grant user jenkins access to /var/run/docker.sock | ||||||
|  | RUN usermod -aG messagebus jenkins | ||||||
|  |  | ||||||
|  | # install plugins | ||||||
|  | COPY Jenkins.plugins /usr/share/jenkins/ref/plugins.txt | ||||||
|  | RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt | ||||||
|  |  | ||||||
|  | USER jenkins | ||||||
							
								
								
									
										8
									
								
								Jenkins/Jenkins.plugins
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Jenkins/Jenkins.plugins
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | git | ||||||
|  | workflow-aggregator | ||||||
|  | pipeline-github-lib | ||||||
|  | docker-workflow | ||||||
|  | credentials | ||||||
|  | git-client | ||||||
|  | blueocean | ||||||
|  | coverage | ||||||
							
								
								
									
										138
									
								
								Jenkins/Jenkinsfile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								Jenkins/Jenkinsfile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | |||||||
|  | pipeline { | ||||||
|  |     parameters { | ||||||
|  |         string(name: 'AGENT_CPUS', defaultValue: '2.5', description: 'CPU limit for the build agent') | ||||||
|  |         string(name: 'AGENT_NETWORK', defaultValue: 'host', description: 'Network to be used for build agent') | ||||||
|  |         booleanParam(name: 'QUICK_RUN', defaultValue: false, description: 'false: all stages but slow, true: just some stages and fast') | ||||||
|  |     } | ||||||
|  |     agent { | ||||||
|  |         dockerfile { | ||||||
|  |             filename 'Jenkins/agent/Dockerfile' | ||||||
|  |             args """--user root --network ${params.AGENT_NETWORK} | ||||||
|  |                     --volume /var/run/docker.sock:/var/run/docker.sock | ||||||
|  |                     --memory=8g --cpus=${params.AGENT_CPUS}""" | ||||||
|  |        } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     environment { | ||||||
|  |         GRADLE_USER_HOME = "${env.WORKSPACE}/.gradle-cache" | ||||||
|  |         DOCKER_HOST = 'unix:///var/run/docker.sock' | ||||||
|  |         HSADMINNG_POSTGRES_ADMIN_USERNAME = 'admin' | ||||||
|  |         HSADMINNG_POSTGRES_RESTRICTED_USERNAME = 'restricted' | ||||||
|  |         HSADMINNG_MIGRATION_DATA_PATH = 'migration' | ||||||
|  |         TESTCONTAINERS_RYUK_DISABLED = true | ||||||
|  |         TESTCONTAINERS_LOG_LEVEL = "DEBUG" | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     triggers { | ||||||
|  |         pollSCM('H/1 * * * *') | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     stages { | ||||||
|  |         stage('Detect Docker Environment') { | ||||||
|  |             steps { | ||||||
|  |                 sh '''#!/bin/bash +x | ||||||
|  |                     if command -v docker >/dev/null 2>&1; then | ||||||
|  |                       if docker info --format '{{.SecurityOptions}}' 2>/dev/null | grep -q 'rootless'; then | ||||||
|  |                         echo "🟡 Docker daemon is running in ROOTLESS mode" | ||||||
|  |                       else | ||||||
|  |                         echo "🟢 Docker daemon is running in ROOTFUL mode" | ||||||
|  |                       fi | ||||||
|  |                     else | ||||||
|  |                       echo "❌ Docker CLI not found" | ||||||
|  |                     fi''' | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         stage('Checkout') { | ||||||
|  |             steps { | ||||||
|  |                 checkout scm | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         stage('Compile') { | ||||||
|  |             steps { | ||||||
|  |                 sh './gradlew clean processSpring compileJava compileTestJava --no-daemon --console=plain' | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         stage('Tests') { | ||||||
|  |             parallel { | ||||||
|  |                 stage('Other Tests') { | ||||||
|  |                     stages { | ||||||
|  |                         stage('Unit-Tests') { | ||||||
|  |                             steps { | ||||||
|  |                                 sh './gradlew unitTest --no-daemon --console=plain' | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         stage('Migration-Tests') { | ||||||
|  |                             steps { | ||||||
|  |                                 sh './gradlew migrationTest --no-daemon --console=plain' | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         stage('Scenario-Tests') { | ||||||
|  |                             steps { | ||||||
|  |                                 sh './gradlew scenarioTest --no-daemon --console=plain' | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         stage('General-Tests') { | ||||||
|  |                             when { | ||||||
|  |                                 expression { !params.QUICK_RUN } | ||||||
|  |                             } | ||||||
|  |                             steps { | ||||||
|  |                                 sh './gradlew generalIntegrationTest --no-daemon --console=plain' | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         stage('Booking+Hosting-Tests') { | ||||||
|  |                             when { | ||||||
|  |                                 expression { !params.QUICK_RUN} | ||||||
|  |                             } | ||||||
|  |                             steps { | ||||||
|  |                                 sh './gradlew bookingIntegrationTest hostingIntegrationTest --no-daemon --console=plain' | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // in parallel because these tests take about as much time as all others combined | ||||||
|  |                 stage('Office-Tests') { | ||||||
|  |                     when { | ||||||
|  |                         expression { !params.QUICK_RUN } | ||||||
|  |                     } | ||||||
|  |                     steps { | ||||||
|  |                         sh './gradlew officeIntegrationTest --no-daemon --console=plain --fail-fast' | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         stage ('Checks') { | ||||||
|  |             steps { | ||||||
|  |                 sh './gradlew check -x pitest -x test -x dependencyCheckAnalyze --no-daemon' | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     post { | ||||||
|  |         always { | ||||||
|  |             // archive test results | ||||||
|  |             junit testResults: 'build/test-results/*/*.xml', allowEmptyResults: true, checksName: '', skipPublishingChecks: true | ||||||
|  |  | ||||||
|  |             // archive the JaCoCo coverage report | ||||||
|  |             // recordCoverage tools: [jacoco(pattern: 'build/reports/jacoco/test/jacocoTestReport.xml')] | ||||||
|  |             sh 'find build -name jacocoTestReport.xml' | ||||||
|  |             archiveArtifacts artifacts: 'build/reports/jacoco/**/jacocoTestReport.xml', allowEmptyArchive: true | ||||||
|  |  | ||||||
|  |             // archive scenario-test reports in HTML format | ||||||
|  |             sh './gradlew convertMarkdownToHtml' | ||||||
|  |             archiveArtifacts artifacts: | ||||||
|  |                     'build/doc/scenarios/*.html, ' + | ||||||
|  |                     'build/reports/dependency-license/dependencies-without-allowed-license.json', | ||||||
|  |                 allowEmptyArchive: true | ||||||
|  |  | ||||||
|  |             // cleanup workspace | ||||||
|  |             cleanWs() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								Jenkins/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								Jenkins/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | DOCKER := docker | ||||||
|  | SOCKET := /var/run/docker.sock | ||||||
|  | VOLUME := jenkins_home | ||||||
|  |  | ||||||
|  | .PHONY: build run bash init-pw unprotected protected start stop rm purge | ||||||
|  |  | ||||||
|  | # building the Jenkins image | ||||||
|  | build: | ||||||
|  | 	$(DOCKER) build -t jenkins-docker . | ||||||
|  |  | ||||||
|  | # initially running the Jenkins container | ||||||
|  | run: | ||||||
|  | 	$(DOCKER) run --detach \ | ||||||
|  | 		--dns 8.8.8.8 \ | ||||||
|  | 		--network bridge \ | ||||||
|  | 		--publish 8080:8080 --publish 50000:50000 \ | ||||||
|  | 		--volume $(SOCKET):/var/run/docker.sock \ | ||||||
|  | 		--volume $(VOLUME):/var/jenkins_home \ | ||||||
|  | 		--restart unless-stopped \ | ||||||
|  | 		--name jenkins jenkins-docker | ||||||
|  |  | ||||||
|  | # (re-) starts the Jenkins container | ||||||
|  | start: | ||||||
|  | 	$(DOCKER) start jenkins | ||||||
|  |  | ||||||
|  | # opens a bash within the Jenkins container | ||||||
|  | bash: | ||||||
|  | 	$(DOCKER) exec -it jenkins bash | ||||||
|  |  | ||||||
|  | # prints the inital password of a newly setup Jenkins | ||||||
|  | init-pw: | ||||||
|  | 	$(DOCKER) exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword | ||||||
|  |  | ||||||
|  | # disables security for the Jenkins, allows login without credentials | ||||||
|  | unprotected: | ||||||
|  | 	docker exec -it jenkins sed -i 's|<useSecurity>true</useSecurity>|<useSecurity>false</useSecurity>|' /var/jenkins_home/config.xml | ||||||
|  | 	docker exec -it jenkins grep useSecurity /var/jenkins_home/config.xml | ||||||
|  |  | ||||||
|  | # enables security for the Jenkins, requires login with credentials | ||||||
|  | protected: | ||||||
|  | 	docker exec -it jenkins sed -i 's|<useSecurity>true</useSecurity>|<useSecurity>true</useSecurity>|' /var/jenkins_home/config.xml | ||||||
|  | 	docker exec -it jenkins grep useSecurity /var/jenkins_home/config.xml | ||||||
|  |  | ||||||
|  | # stops the Jenkins container | ||||||
|  | stop: | ||||||
|  | 	$(DOCKER) stop jenkins | ||||||
|  |  | ||||||
|  | # removes the Jenkins container | ||||||
|  | rm: stop | ||||||
|  | 	$(DOCKER) rm jenkins | ||||||
|  |  | ||||||
|  | # purges the Jenkins volume (finally deletes the configuration) | ||||||
|  | purge: rm | ||||||
|  | 	$(DOCKER) volume rm $(VOLUME) | ||||||
							
								
								
									
										9
									
								
								Jenkins/agent/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Jenkins/agent/Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | FROM eclipse-temurin:21-jdk | ||||||
|  | RUN apt-get update && \ | ||||||
|  |     apt-get install -y \ | ||||||
|  |         postgresql-client \ | ||||||
|  |         bind9-utils \ | ||||||
|  |         docker.io \ | ||||||
|  |         pandoc && \ | ||||||
|  |     apt-get clean && \ | ||||||
|  |     rm -rf /var/lib/apt/lists/* | ||||||
							
								
								
									
										103
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										103
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							| @@ -1,103 +0,0 @@ | |||||||
| pipeline { |  | ||||||
|     agent { |  | ||||||
|         dockerfile { |  | ||||||
|             filename 'etc/jenkinsAgent.Dockerfile' |  | ||||||
|             // additionalBuildArgs  ... |  | ||||||
|             args '--network=bridge --user root -v $PWD:$PWD \ |  | ||||||
|                     -v /var/run/docker.sock:/var/run/docker.sock --group-add 984 \ |  | ||||||
|                     --memory=6g --cpus=3' |  | ||||||
|        } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     environment { |  | ||||||
|         DOCKER_HOST = 'unix:///var/run/docker.sock' |  | ||||||
|         HSADMINNG_POSTGRES_ADMIN_USERNAME = 'admin' |  | ||||||
|         HSADMINNG_POSTGRES_RESTRICTED_USERNAME = 'restricted' |  | ||||||
|         HSADMINNG_MIGRATION_DATA_PATH = 'migration' |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     triggers { |  | ||||||
|         pollSCM('H/1 * * * *') |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     stages { |  | ||||||
|         stage('Checkout') { |  | ||||||
|             steps { |  | ||||||
|                 checkout scm |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         stage ('Compile') { |  | ||||||
|             steps { |  | ||||||
|                 sh './gradlew clean processSpring compileJava compileTestJava --no-daemon' |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         stage ('Tests') { |  | ||||||
|             parallel { |  | ||||||
|                 stage('Unit-Tests') { |  | ||||||
|                     steps { |  | ||||||
|                         sh './gradlew unitTest  --no-daemon' |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 stage('General-Tests') { |  | ||||||
|                     steps { |  | ||||||
|                         sh './gradlew generalTest --no-daemon' |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 stage('Office-Tests') { |  | ||||||
|                     steps { |  | ||||||
|                         sh './gradlew officeIntegrationTest --no-daemon' |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 stage('Booking+Hosting-Tests') { |  | ||||||
|                     steps { |  | ||||||
|                         sh './gradlew bookingIntegrationTest hostingIntegrationTest --no-daemon' |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 stage('Test-Imports') { |  | ||||||
|                     steps { |  | ||||||
|                         sh './gradlew importHostingAssets --no-daemon' |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 stage ('Scenario-Tests') { |  | ||||||
|                     steps { |  | ||||||
|                         sh './gradlew scenarioTest --no-daemon' |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         stage ('Check') { |  | ||||||
|             steps { |  | ||||||
|                 sh './gradlew check -x pitest -x dependencyCheckAnalyze --no-daemon' |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     post { |  | ||||||
|         always { |  | ||||||
|             // archive test results |  | ||||||
|             junit 'build/test-results/test/*.xml' |  | ||||||
|  |  | ||||||
|             // archive the JaCoCo coverage report in XML and HTML format |  | ||||||
|             jacoco( |  | ||||||
|                 execPattern: 'build/jacoco/*.exec', |  | ||||||
|                 classPattern: 'build/classes/java/main', |  | ||||||
|                 sourcePattern: 'src/main/java' |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|             // archive scenario-test reports in HTML format |  | ||||||
|             sh ''' |  | ||||||
|                 ./gradlew convertMarkdownToHtml |  | ||||||
|             ''' |  | ||||||
|             archiveArtifacts artifacts: |  | ||||||
|                     'build/doc/scenarios/*.html, ' + |  | ||||||
|                     'build/reports/dependency-license/dependencies-without-allowed-license.json', |  | ||||||
|                 allowEmptyArchive: true |  | ||||||
|  |  | ||||||
|             // cleanup workspace |  | ||||||
|             cleanWs() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -151,7 +151,7 @@ tasks.named<Test>("test") { | |||||||
|         excludeTestsMatching("net.hostsharing.hsadminng.**.generated.**") |         excludeTestsMatching("net.hostsharing.hsadminng.**.generated.**") | ||||||
|         // Add more exclude patterns if needed |         // Add more exclude patterns if needed | ||||||
|     } |     } | ||||||
|     finalizedBy(tasks.named("jacocoTestReport")) // generate report after tests |     finalizedBy(tasks.named("jacocoTestReport")) // generate a report after tests | ||||||
| } | } | ||||||
|  |  | ||||||
| // OpenAPI Source Code Generation | // OpenAPI Source Code Generation | ||||||
| @@ -425,18 +425,23 @@ tasks.named<JacocoCoverageVerification>("jacocoTestCoverageVerification") { | |||||||
| tasks.register<Test>("unitTest") { | tasks.register<Test>("unitTest") { | ||||||
|     useJUnitPlatform { |     useJUnitPlatform { | ||||||
|         excludeTags( |         excludeTags( | ||||||
|             "importHostingAssets", "scenarioTest", "generalIntegrationTest", |             "importHostingAssets", "scenarioTest", "migrationTest", "generalIntegrationTest", | ||||||
|             "officeIntegrationTest", "bookingIntegrationTest", "hostingIntegrationTest" |             "officeIntegrationTest", "bookingIntegrationTest", "hostingIntegrationTest" | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     testLogging { | ||||||
|  |         events("passed", "skipped", "failed", "standardOut", "standardError") | ||||||
|  |         showStandardStreams = true | ||||||
|  |     } | ||||||
|  |  | ||||||
|     group = "verification" |     group = "verification" | ||||||
|     description = "runs all unit-tests which do not need a database" |     description = "runs all unit-tests which do not need a database" | ||||||
|  |  | ||||||
|     mustRunAfter(tasks.named("spotlessJava")) |     mustRunAfter(tasks.named("spotlessJava")) | ||||||
| } | } | ||||||
|  |  | ||||||
| // HOWTO: run all integration tests which are not specific to a module, like base, rbac, config etc. | // HOWTO: run all integration tests that are not specific to a module, like base, rbac, config etc. | ||||||
| tasks.register<Test>("generalIntegrationTest") { | tasks.register<Test>("generalIntegrationTest") { | ||||||
|     useJUnitPlatform { |     useJUnitPlatform { | ||||||
|         includeTags("generalIntegrationTest") |         includeTags("generalIntegrationTest") | ||||||
| @@ -484,6 +489,17 @@ tasks.register<Test>("hostingIntegrationTest") { | |||||||
|     mustRunAfter(tasks.named("spotlessJava")) |     mustRunAfter(tasks.named("spotlessJava")) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | tasks.register<Test>("migrationTest") { | ||||||
|  |     useJUnitPlatform { | ||||||
|  |         includeTags("migrationTest") | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     group = "verification" | ||||||
|  |     description = "run database migration tests" | ||||||
|  |  | ||||||
|  |     mustRunAfter(tasks.named("spotlessJava")) | ||||||
|  | } | ||||||
|  |  | ||||||
| tasks.register<Test>("importHostingAssets") { | tasks.register<Test>("importHostingAssets") { | ||||||
|     useJUnitPlatform { |     useJUnitPlatform { | ||||||
|         includeTags("importHostingAssets") |         includeTags("importHostingAssets") | ||||||
| @@ -574,14 +590,22 @@ tasks.register("convertMarkdownToHtml") { | |||||||
|  |  | ||||||
|     // Define the template file using project.file |     // Define the template file using project.file | ||||||
|     val templateFile = project.file("doc/scenarios/.template.html") |     val templateFile = project.file("doc/scenarios/.template.html") | ||||||
|     // Define input directory using layout property |  | ||||||
|     val inputDir = layout.buildDirectory.dir("doc/scenarios") |  | ||||||
|  |  | ||||||
|     // Use inputs and outputs for better up-to-date checks |  | ||||||
|     inputs.file(templateFile).withPathSensitivity(PathSensitivity.NONE) |     inputs.file(templateFile).withPathSensitivity(PathSensitivity.NONE) | ||||||
|     inputs.dir(inputDir).withPathSensitivity(PathSensitivity.RELATIVE) |  | ||||||
|  |     // Define input+output directory using layout property | ||||||
|  |     val inputDir = layout.buildDirectory.dir("doc/scenarios") | ||||||
|     outputs.dir(inputDir) // Output HTMLs will be in the same directory |     outputs.dir(inputDir) // Output HTMLs will be in the same directory | ||||||
|  |  | ||||||
|  |     onlyIf { | ||||||
|  |         val dir = inputDir.get().asFile | ||||||
|  |         if (!dir.exists()) { | ||||||
|  |             logger.lifecycle("Skipping convertMarkdownToHtml because ${dir} does not exist (scenarioTest skipped).") | ||||||
|  |             false | ||||||
|  |         } else { | ||||||
|  |             true | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     doFirst { |     doFirst { | ||||||
|         // Check if pandoc is installed using exec and capturing output/errors |         // Check if pandoc is installed using exec and capturing output/errors | ||||||
|         val result = project.exec { |         val result = project.exec { | ||||||
| @@ -598,21 +622,9 @@ tasks.register("convertMarkdownToHtml") { | |||||||
|         if (!templateFile.exists()) { |         if (!templateFile.exists()) { | ||||||
|             throw GradleException("Template file '$templateFile' not found.") |             throw GradleException("Template file '$templateFile' not found.") | ||||||
|         } |         } | ||||||
|         // Ensure input directory exists (Gradle handles this implicitly usually, but explicit check is fine) |  | ||||||
|         if (!inputDir.get().asFile.exists()) { |  | ||||||
|             logger.warn("Input directory ${inputDir.get().asFile} does not exist, skipping Pandoc conversion.") |  | ||||||
|             // Potentially disable the task or skip doLast if input dir missing |  | ||||||
|             enabled = false // Example: disable task if input dir doesn't exist yet |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     doLast { |     doLast { | ||||||
|         // Check if input dir exists again, in case it was created between doFirst and doLast |  | ||||||
|         if (!inputDir.get().asFile.exists()) { |  | ||||||
|             logger.warn("Input directory ${inputDir.get().asFile} still does not exist, skipping Pandoc conversion.") |  | ||||||
|             return@doLast // Skip execution |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Gather all Markdown files in the input directory |         // Gather all Markdown files in the input directory | ||||||
|         project.fileTree(inputDir) { |         project.fileTree(inputDir) { | ||||||
|             include("*.md") |             include("*.md") | ||||||
|   | |||||||
| @@ -21,10 +21,18 @@ | |||||||
|             "moduleVersion": "1.0.0", |             "moduleVersion": "1.0.0", | ||||||
|             "moduleName": "org.jspecify:jspecify" |             "moduleName": "org.jspecify:jspecify" | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             "moduleLicense": null, | ||||||
|  |             "#moduleLicense": "BSD 3-clause", | ||||||
|  |             "moduleVersion": "4.13.0", | ||||||
|  |             "moduleName": "org.antlr:antlr4-runtime" | ||||||
|  |         }, | ||||||
|  |  | ||||||
|         { "moduleLicense": "BSD License" }, |         { "moduleLicense": "BSD License" }, | ||||||
|         { "moduleLicense": "BSD-2-Clause" }, |         { "moduleLicense": "BSD-2-Clause" }, | ||||||
|  |         { "moduleLicense": "The 2-Clause BSD License" }, | ||||||
|         { "moduleLicense": "BSD-3-Clause" }, |         { "moduleLicense": "BSD-3-Clause" }, | ||||||
|  |         { "moduleLicense": "The 3-Clause BSD License" }, | ||||||
|         { "moduleLicense": "The BSD License" }, |         { "moduleLicense": "The BSD License" }, | ||||||
|  |  | ||||||
|         { "moduleLicense": "The New BSD License" }, |         { "moduleLicense": "The New BSD License" }, | ||||||
| @@ -45,6 +53,8 @@ | |||||||
|  |  | ||||||
|         { "moduleLicense": "GNU Library General Public License v2.1 or later" }, |         { "moduleLicense": "GNU Library General Public License v2.1 or later" }, | ||||||
|         { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception" }, |         { "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception" }, | ||||||
|  |         { "moduleLicense": "GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1" }, | ||||||
|  |  | ||||||
|         { "moduleLicense": "GPL2 w/ CPE" }, |         { "moduleLicense": "GPL2 w/ CPE" }, | ||||||
|  |  | ||||||
|         { "moduleLicense": "LGPL, version 2.1"}, |         { "moduleLicense": "LGPL, version 2.1"}, | ||||||
| @@ -57,11 +67,8 @@ | |||||||
|  |  | ||||||
|         { "moduleLicense": "WTFPL" }, |         { "moduleLicense": "WTFPL" }, | ||||||
|  |  | ||||||
|         { |         { "moduleLicense":  "Creative Commons Legal Code" }, | ||||||
|             "moduleLicense": "Public Domain, per Creative Commons CC0", |         { "moduleLicense":  "PUBLIC DOMAIN" }, | ||||||
|             "moduleVersion": "2.0.3" |         { "moduleLicense": "Public Domain, per Creative Commons CC0" } | ||||||
|         } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +0,0 @@ | |||||||
| FROM eclipse-temurin:21-jdk |  | ||||||
| RUN apt-get update && \ |  | ||||||
|     apt-get install -y bind9-utils pandoc && \ |  | ||||||
|     apt-get clean && \ |  | ||||||
|     rm -rf /var/lib/apt/lists/* |  | ||||||
|  |  | ||||||
| @@ -89,6 +89,7 @@ import static org.assertj.core.api.Assertions.assertThat; | |||||||
| import static org.assertj.core.api.Assumptions.assumeThat; | import static org.assertj.core.api.Assumptions.assumeThat; | ||||||
| import static org.springframework.util.FileCopyUtils.copyToByteArray; | import static org.springframework.util.FileCopyUtils.copyToByteArray; | ||||||
|  |  | ||||||
|  | @Tag("migrationTest") | ||||||
| @Tag("importHostingAssets") | @Tag("importHostingAssets") | ||||||
| @DataJpaTest(properties = { | @DataJpaTest(properties = { | ||||||
|         "spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///importHostingAssetsTC}", |         "spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///importHostingAssetsTC}", | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.BEFORE_TE | |||||||
|  * <p>During a release, the generated dump has to be committed to git and will be used in future test-runs |  * <p>During a release, the generated dump has to be committed to git and will be used in future test-runs | ||||||
|  * until it gets replaced with a new dump at the next release.</p> |  * until it gets replaced with a new dump at the next release.</p> | ||||||
|  */ |  */ | ||||||
| @Tag("officeIntegrationTest") | @Tag("migrationTest") | ||||||
| @DataJpaTest(properties = { | @DataJpaTest(properties = { | ||||||
|         "spring.datasource.url=jdbc:tc:postgresql:15.5-bookworm:///liquibaseMigrationTestTC", |         "spring.datasource.url=jdbc:tc:postgresql:15.5-bookworm:///liquibaseMigrationTestTC", | ||||||
|         "hsadminng.superuser=${HSADMINNG_SUPERUSER:import-superuser@hostsharing.net}", |         "hsadminng.superuser=${HSADMINNG_SUPERUSER:import-superuser@hostsharing.net}", | ||||||
|   | |||||||
| @@ -46,6 +46,7 @@ import static org.junit.platform.commons.util.StringUtils.isNotBlank; | |||||||
| public abstract class UseCase<T extends UseCase<?>> { | public abstract class UseCase<T extends UseCase<?>> { | ||||||
|  |  | ||||||
|     private static final HttpClient client = HttpClient.newHttpClient(); |     private static final HttpClient client = HttpClient.newHttpClient(); | ||||||
|  |     private static final int HTTP_TIMEOUT_SECONDS = 20; // FIXME: configurable in environment | ||||||
|     private final ObjectMapper objectMapper = new ObjectMapper(); |     private final ObjectMapper objectMapper = new ObjectMapper(); | ||||||
|  |  | ||||||
|     protected final ScenarioTest testSuite; |     protected final ScenarioTest testSuite; | ||||||
| @@ -160,7 +161,7 @@ public abstract class UseCase<T extends UseCase<?>> { | |||||||
|                 .GET() |                 .GET() | ||||||
|                 .uri(new URI("http://localhost:" + testSuite.port + uriPath)) |                 .uri(new URI("http://localhost:" + testSuite.port + uriPath)) | ||||||
|                 .header("Authorization", "Bearer " + ScenarioTest.RUN_AS_USER) |                 .header("Authorization", "Bearer " + ScenarioTest.RUN_AS_USER) | ||||||
|                 .timeout(seconds(10)) |                 .timeout(seconds(HTTP_TIMEOUT_SECONDS)) | ||||||
|                 .build(); |                 .build(); | ||||||
|         final var response = client.send(request, BodyHandlers.ofString()); |         final var response = client.send(request, BodyHandlers.ofString()); | ||||||
|         return new HttpResponse(HttpMethod.GET, uriPath, null, response); |         return new HttpResponse(HttpMethod.GET, uriPath, null, response); | ||||||
| @@ -175,7 +176,7 @@ public abstract class UseCase<T extends UseCase<?>> { | |||||||
|                 .uri(new URI("http://localhost:" + testSuite.port + uriPath)) |                 .uri(new URI("http://localhost:" + testSuite.port + uriPath)) | ||||||
|                 .header("Content-Type", "application/json") |                 .header("Content-Type", "application/json") | ||||||
|                 .header("Authorization", "Bearer " + ScenarioTest.RUN_AS_USER) |                 .header("Authorization", "Bearer " + ScenarioTest.RUN_AS_USER) | ||||||
|                 .timeout(seconds(10)) |                 .timeout(seconds(HTTP_TIMEOUT_SECONDS)) | ||||||
|                 .build(); |                 .build(); | ||||||
|         final var response = client.send(request, BodyHandlers.ofString()); |         final var response = client.send(request, BodyHandlers.ofString()); | ||||||
|         return new HttpResponse(HttpMethod.POST, uriPath, requestBody, response); |         return new HttpResponse(HttpMethod.POST, uriPath, requestBody, response); | ||||||
| @@ -190,7 +191,7 @@ public abstract class UseCase<T extends UseCase<?>> { | |||||||
|                 .uri(new URI("http://localhost:" + testSuite.port + uriPath)) |                 .uri(new URI("http://localhost:" + testSuite.port + uriPath)) | ||||||
|                 .header("Content-Type", "application/json") |                 .header("Content-Type", "application/json") | ||||||
|                 .header("Authorization", "Bearer " + ScenarioTest.RUN_AS_USER) |                 .header("Authorization", "Bearer " + ScenarioTest.RUN_AS_USER) | ||||||
|                 .timeout(seconds(10)) |                 .timeout(seconds(HTTP_TIMEOUT_SECONDS)) | ||||||
|                 .build(); |                 .build(); | ||||||
|         final var response = client.send(request, BodyHandlers.ofString()); |         final var response = client.send(request, BodyHandlers.ofString()); | ||||||
|         return new HttpResponse(HttpMethod.PATCH, uriPath, requestBody, response); |         return new HttpResponse(HttpMethod.PATCH, uriPath, requestBody, response); | ||||||
| @@ -204,7 +205,7 @@ public abstract class UseCase<T extends UseCase<?>> { | |||||||
|                 .uri(new URI("http://localhost:" + testSuite.port + uriPath)) |                 .uri(new URI("http://localhost:" + testSuite.port + uriPath)) | ||||||
|                 .header("Content-Type", "application/json") |                 .header("Content-Type", "application/json") | ||||||
|                 .header("Authorization", "Bearer " + ScenarioTest.RUN_AS_USER) |                 .header("Authorization", "Bearer " + ScenarioTest.RUN_AS_USER) | ||||||
|                 .timeout(seconds(10)) |                 .timeout(seconds(HTTP_TIMEOUT_SECONDS)) | ||||||
|                 .build(); |                 .build(); | ||||||
|         final var response = client.send(request, BodyHandlers.ofString()); |         final var response = client.send(request, BodyHandlers.ofString()); | ||||||
|         return new HttpResponse(HttpMethod.DELETE, uriPath, null, response); |         return new HttpResponse(HttpMethod.DELETE, uriPath, null, response); | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.mapper.Array; | |||||||
| import net.hostsharing.hsadminng.mapper.StrictMapper; | import net.hostsharing.hsadminng.mapper.StrictMapper; | ||||||
| import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; | import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; | ||||||
| import net.hostsharing.hsadminng.rbac.test.JpaAttempt; | import net.hostsharing.hsadminng.rbac.test.JpaAttempt; | ||||||
|  | import org.junit.jupiter.api.BeforeAll; | ||||||
| import org.junit.jupiter.api.Tag; | import org.junit.jupiter.api.Tag; | ||||||
| import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||||
| @@ -41,6 +42,11 @@ class ContextIntegrationTests { | |||||||
|     @PersistenceContext |     @PersistenceContext | ||||||
|     private EntityManager em; |     private EntityManager em; | ||||||
|  |  | ||||||
|  |     @BeforeAll | ||||||
|  |     static void disableRyuk() { | ||||||
|  |         System.setProperty("testcontainers.ryuk.disabled", "true"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void defineWithoutHttpServletRequestUsesCallStack() { |     void defineWithoutHttpServletRequestUsesCallStack() { | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user