Michael Hoennig
2022-11-25 db17a2e9903e4bb3aad7c8851af4811951c0bc42
commit | author | age
6c33bb 1 plugins {
a2f2fd 2     id 'java'
db17a2 3     id 'org.springframework.boot' version '3.0.0'
810c39 4     id 'io.spring.dependency-management' version '1.1.0'
cdc21f 5     id 'io.openapiprocessor.openapi-processor' version '2022.2'
c4531c 6     id 'com.github.jk1.dependency-license-report' version '2.1'
810c39 7     id "org.owasp.dependencycheck" version "7.3.0"
398f15 8     id "com.diffplug.spotless" version "6.11.0"
a66ed8 9     id 'jacoco'
7d4815 10     id 'info.solidsoft.pitest' version '1.9.0'
8b4e78 11     id 'se.patrikerdes.use-latest-versions' version '0.2.18'
810c39 12     id 'com.github.ben-manes.versions' version '0.43.0'
6c33bb 13 }
MH 14
15 group = 'net.hostsharing'
16 version = '0.0.1-SNAPSHOT'
17
d9ee00 18 wrapper {
MH 19     distributionType = Wrapper.DistributionType.BIN
20     gradleVersion = '7.5'
21 }
22
6c33bb 23 configurations {
MH 24     compileOnly {
25         extendsFrom annotationProcessor
26     }
3de8b0 27     testCompile {
3eec8a 28         extendsFrom testAnnotationProcessor
MH 29
3de8b0 30         // Only JUNit 5 (Jupiter) should be used at compile time.
MH 31         // For runtime it's still needed by testcontainers, though.
32         exclude group: 'junit', module: 'junit'
33         exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
34     }
6c33bb 35 }
MH 36
37 repositories {
38     mavenCentral()
cdc21f 39     maven { url 'https://repo.spring.io/milestone' }
MH 40     maven { url 'https://repo.spring.io/snapshot' }
433d0e 41 }
MH 42
43 java {
44     toolchain {
45         languageVersion = JavaLanguageVersion.of(17)
46     }
6c33bb 47 }
MH 48
49 ext {
50     set('testcontainersVersion', "1.17.3")
51 }
a2f2fd 52
MH 53 // wrapper
6c33bb 54
MH 55 dependencies {
56     implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
57     implementation 'org.springframework.boot:spring-boot-starter-data-rest'
58     implementation 'org.springframework.boot:spring-boot-starter-jdbc'
59     implementation 'org.springframework.boot:spring-boot-starter-web'
67e850 60     implementation 'org.springframework.boot:spring-boot-starter-validation'
c6d92e 61     implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.8.1'
cdc21f 62     implementation 'org.springdoc:springdoc-openapi:2.0.0-M7'
6c33bb 63     implementation 'org.liquibase:liquibase-core'
cdc21f 64     implementation 'com.vladmihalcea:hibernate-types-60:2.20.0'
4f22df 65     implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4'
cdc21f 66     implementation 'org.openapitools:jackson-databind-nullable:0.2.4'
MH 67     implementation 'org.apache.commons:commons-text:1.10.0'
eeab68 68     implementation 'org.modelmapper:modelmapper:3.1.0'
d98b8f 69     implementation 'org.iban4j:iban4j:3.2.3-RELEASE'
6c33bb 70
MH 71     compileOnly 'org.projectlombok:lombok'
3eec8a 72     testCompileOnly 'org.projectlombok:lombok'
6c33bb 73
MH 74     developmentOnly 'org.springframework.boot:spring-boot-devtools'
75
76     runtimeOnly 'org.postgresql:postgresql'
77
78     annotationProcessor 'org.projectlombok:lombok'
3eec8a 79     testAnnotationProcessor 'org.projectlombok:lombok'
6c33bb 80
MH 81     testImplementation 'org.springframework.boot:spring-boot-starter-test'
82     testImplementation 'org.testcontainers:testcontainers'
83     testImplementation 'org.testcontainers:junit-jupiter'
84     testImplementation 'org.testcontainers:postgresql'
398f15 85     testImplementation 'com.tngtech.archunit:archunit-junit5:1.0.0'
0e4602 86     testImplementation 'io.rest-assured:spring-mock-mvc'
9fb661 87     testImplementation 'org.hamcrest:hamcrest-core:2.2'
398f15 88     testImplementation 'org.pitest:pitest-junit5-plugin:1.1.0'
6c33bb 89 }
MH 90
91 dependencyManagement {
92     imports {
93         mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}"
94     }
95 }
96
a66ed8 97 // Java Compiler Options
433d0e 98 tasks.withType(JavaCompile) {
a66ed8 99     options.compilerArgs += [
MH 100             "-parameters" // keep parameter names => no need for @Param for SpringData
101     ]
433d0e 102 }
MH 103
d7eed0 104 // Configure tests
6c33bb 105 tasks.named('test') {
MH 106     useJUnitPlatform()
d7eed0 107     jvmArgs '-Duser.language=en'
MH 108     jvmArgs '-Duser.country=US'
6c33bb 109 }
a2f2fd 110
a66ed8 111 // OpenAPI Source Code Generation
eeab68 112 openapiProcessor {
cd9be1 113     springRoot {
MH 114         processorName 'spring'
67e850 115         processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
eeab68 116         apiPath "$projectDir/src/main/resources/api-definition.yaml"
MH 117         mapping "$projectDir/src/main/resources/api-mappings.yaml"
cdc21f 118         targetDir "$projectDir/build/generated/sources/openapi-javax"
cd9be1 119         showWarnings true
MH 120         openApiNullable true
121     }
122     springRbac {
123         processorName 'spring'
67e850 124         processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
cd9be1 125         apiPath "$projectDir/src/main/resources/api-definition/rbac/rbac.yaml"
MH 126         mapping "$projectDir/src/main/resources/api-definition/rbac/api-mappings.yaml"
cdc21f 127         targetDir "$projectDir/build/generated/sources/openapi-javax"
cd9be1 128         showWarnings true
MH 129         openApiNullable true
130     }
131     springTest {
132         processorName 'spring'
67e850 133         processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
cd9be1 134         apiPath "$projectDir/src/main/resources/api-definition/test/test.yaml"
MH 135         mapping "$projectDir/src/main/resources/api-definition/test/api-mappings.yaml"
cdc21f 136         targetDir "$projectDir/build/generated/sources/openapi-javax"
eeab68 137         showWarnings true
0e4602 138         openApiNullable true
eeab68 139     }
af90fe 140     springHs {
MH 141         processorName 'spring'
67e850 142         processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
4e90f5 143         apiPath "$projectDir/src/main/resources/api-definition/hs-office/hs-office.yaml"
MH 144         mapping "$projectDir/src/main/resources/api-definition/hs-office/api-mappings.yaml"
cdc21f 145         targetDir "$projectDir/build/generated/sources/openapi-javax"
af90fe 146         showWarnings true
MH 147         openApiNullable true
148     }
eeab68 149 }
MH 150 sourceSets.main.java.srcDir 'build/generated/sources/openapi'
af90fe 151 abstract class ProcessSpring extends DefaultTask {}
MH 152 tasks.register('processSpring', ProcessSpring)
153 ['processSpringRoot', 'processSpringRbac', 'processSpringTest', 'processSpringHs'].each {
154     project.tasks.processSpring.dependsOn it
cd9be1 155 }
af90fe 156 project.tasks.processResources.dependsOn processSpring
MH 157 project.tasks.compileJava.dependsOn processSpring
cdc21f 158
MH 159 // Rename javax to jakarta in OpenApi generated java files because
160 // io.openapiprocessor.openapi-processor 2022.2 does not yet support the openapiprocessor useSpringBoot3 config option.
161 task openApiGenerate(type: Copy) {
162     from "$buildDir/generated/sources/openapi-javax"
163     into "$buildDir/generated/sources/openapi"
164     filter { line -> line.replaceAll('javax', 'jakarta') }
165 }
166 compileJava.source "$buildDir/generated/sources/openapi"
167 compileJava.dependsOn openApiGenerate
168 openApiGenerate.dependsOn processSpring
eeab68 169
a66ed8 170 // Spotless Code Formatting
a2f2fd 171 spotless {
MH 172     java {
0e4602 173         // removeUnusedImports() TODO: reactivate once it can deal with multi-line-strings
MH 174         indentWithSpaces(4)
a2f2fd 175         endWithNewline()
MH 176         toggleOffOn()
eeab68 177
0e4602 178         target fileTree(rootDir) {
eeab68 179             include '**/*.java'
MH 180             exclude '**/generated/**/*.java'
181         }
a2f2fd 182     }
MH 183 }
80b1e0 184 project.tasks.check.dependsOn(spotlessCheck)
MH 185
a66ed8 186 // OWASP Dependency Security Test
80b1e0 187 dependencyCheck {
MH 188     cveValidForHours=4
189     format = 'ALL'
190     suppressionFile = 'etc/owasp-dependency-check-suppression.xml'
191     failOnError = true
192     failBuildOnCVSS = 7
193 }
194 project.tasks.check.dependsOn(dependencyCheckAnalyze)
4ec261 195 project.tasks.dependencyCheckAnalyze.doFirst { // Why not doLast? See README.md!
fd96bf 196     println "OWASP Dependency Security Report: file:///${project.rootDir}/build/reports/dependency-check-report.html"
MH 197 }
198
c4531c 199
a66ed8 200 // License Check
c4531c 201 licenseReport {
MH 202     excludeBoms = true
203     allowedLicensesFile = new File("$projectDir/etc/allowed-licenses.json")
204 }
205 project.tasks.check.dependsOn(checkLicense)
a66ed8 206
MH 207 // JaCoCo Test Code Coverage
208 jacoco {
209     toolVersion = "0.8.8"
210 }
211 test {
212     finalizedBy jacocoTestReport // generate report after tests
213     excludes = [
cd9be1 214             'net.hostsharing.hsadminng.**.generated.**',
a66ed8 215     ]
MH 216 }
217 jacocoTestReport {
218     dependsOn test
219     afterEvaluate {
220         classDirectories.setFrom(files(classDirectories.files.collect {
221             fileTree(dir: it, exclude: [
cd9be1 222                     "net/hostsharing/hsadminng/**/generated/**/*.class",
4e90f5 223                     "net/hostsharing/hsadminng/hs/HsadminNgApplication.class"
a66ed8 224             ])
MH 225         }))
226     }
4ec261 227     doFirst { // Why not doLast? See README.md!
a66ed8 228         println "HTML Jacoco Test Code Coverage Report: file://${reports.html.outputLocation.get()}/index.html"
MH 229     }
230 }
231 project.tasks.check.dependsOn(jacocoTestCoverageVerification)
232 jacocoTestCoverageVerification {
233     violationRules {
234         rule {
235             limit {
894150 236                 minimum = 0.92
a66ed8 237             }
MH 238         }
239
240         // element: PACKAGE, BUNDLE, CLASS, SOURCEFILE or METHOD
241         // counter:  INSTRUCTION, BRANCH, LINE, COMPLEXITY, METHOD, or CLASS
242         // value: TOTALCOUNT, COVEREDCOUNT, MISSEDCOUNT, COVEREDRATIO or MISSEDRATIO
243
244         rule {
245             element = 'CLASS'
246             excludes = [
cd9be1 247                     'net.hostsharing.hsadminng.**.generated.**',
a66ed8 248                     'net.hostsharing.hsadminng.HsadminNgApplication',
5ada0d 249                     'net.hostsharing.hsadminng.ping.PingController',
MH 250                     'net.hostsharing.hsadminng.mapper.Mapper'
a66ed8 251             ]
MH 252
253             limit {
254                 counter = 'LINE'
255                 value = 'COVEREDRATIO'
6ec4c8 256                 minimum = 0.98
a66ed8 257             }
MH 258         }
259         rule {
260             element = 'METHOD'
261             excludes = [
cd9be1 262                     'net.hostsharing.hsadminng.**.generated.**',
2124d4 263                     'net.hostsharing.hsadminng.HsadminNgApplication.main',
5ada0d 264                     'net.hostsharing.hsadminng.ping.PingController.*'
4aa8b8 265             ]
a66ed8 266
MH 267             limit {
268                 counter = 'BRANCH'
269                 value = 'COVEREDRATIO'
6ec4c8 270                 minimum = 1.00
a66ed8 271             }
MH 272         }
273     }
274 }
275
9fb661 276 // pitest mutation testing
MH 277 pitest {
278     targetClasses = ['net.hostsharing.hsadminng.**']
279     excludedClasses = [
280             'net.hostsharing.hsadminng.config.**',
af90fe 281             'net.hostsharing.hsadminng.**.*Controller',
cd9be1 282             'net.hostsharing.hsadminng.**.generated.**'
9fb661 283     ]
MH 284
285     targetTests = ['net.hostsharing.hsadminng.**.*UnitTest', 'net.hostsharing.hsadminng.**.*RestTest']
286     excludedTestClasses = ['**AcceptanceTest*', '**IntegrationTest*']
287
cdc21f 288     pitestVersion = '1.9.9'
MH 289     junit5PluginVersion = '1.1.0'
9fb661 290
MH 291     threads = 4
292
293     // As Java unit tests are pretty pointless in our case, this maybe makes not much sense.
af90fe 294     mutationThreshold = 71
MH 295     coverageThreshold = 57
d3312c 296     testStrengthThreshold = 87
9fb661 297
MH 298     outputFormats = ['XML', 'HTML']
299     timestampedReports = false
300 }
301 project.tasks.check.dependsOn(project.tasks.pitest)
4ec261 302 project.tasks.pitest.doFirst { // Why not doLast? See README.md!
9fb661 303     println "PiTest Mutation Report: file:///${project.rootDir}/build/reports/pitest/index.html"
MH 304 }
305
306
8b4e78 307 // Dependency Versions Upgrade
MH 308 useLatestVersions {
309     finalizedBy check
310 }
810c39 311
MH 312 def isNonStable = { String version ->
313     def stableKeyword = ['RELEASE', 'FINAL', 'GA'].any { it -> version.toUpperCase().contains(it) }
314     def regex = /^[0-9,.v-]+(-r)?$/
315     return !stableKeyword && !(version ==~ regex)
316 }
317
318 tasks.named("dependencyUpdates").configure {
319     rejectVersionIf {
320         isNonStable(it.candidate.version)
321     }
322 }