import io.gitlab.arturbosch.detekt.Detekt import java.time.Instant import java.time.ZoneId import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit plugins { id 'java' id 'groovy' id 'idea' id 'application' id 'jacoco' alias(libs.plugins.kotlin) alias(libs.plugins.jib) alias(libs.plugins.spring) alias(libs.plugins.git) alias(libs.plugins.protobuf) alias(libs.plugins.ktlint) alias(libs.plugins.detekt) } group = 'io.emeraldpay.dshackle' // Version schema: // x.x.x for production, following SemVer model // x.x.x-SNAPSHOT for development version = '0.13.0-SNAPSHOT' java { sourceCompatibility = JavaVersion.VERSION_13 targetCompatibility = JavaVersion.VERSION_13 } repositories { mavenLocal() mavenCentral() maven { url "https://repo.spring.io/snapshot" } maven { url "https://repo.spring.io/milestone" } maven { url "https://maven.emrld.io" } } configurations { compile.exclude group: "commons-logging" compile.exclude group: "ch.qos.logback" compile.exclude group: "org.slf4j", module: "slf4j-jdk14" compile.exclude group: "org.slf4j", module: "log4j-over-slf4j" // should be used only for generation of the stubs, the lib contains grpc classes compile.exclude group: "com.salesforce.servicelibs", module: "reactor-grpc" } dependencies { implementation libs.bundles.kotlin implementation libs.bundles.grpc implementation libs.bundles.netty implementation libs.zeromq implementation(libs.bundles.spring.framework) { exclude module: 'spring-boot-starter-logging' } implementation libs.bundles.reactor implementation(libs.reactor.grpc.stub) implementation libs.micrometer.registry.prometheus implementation libs.lettuce.core implementation libs.bundles.etherjar implementation(libs.emerald.api) { exclude group: 'com.salesforce.servicelibs', module: 'reactor-grpc' } implementation libs.bitcoinj implementation libs.snake.yaml implementation libs.bundles.httpcomponents implementation libs.bundles.jackson implementation libs.bundles.apache.commons implementation libs.bouncycastle implementation libs.caffeine implementation libs.javax.annotations implementation libs.bundles.slf4j testImplementation libs.cglib.nodep testImplementation libs.spockframework.core testImplementation libs.grpc.testing testImplementation libs.reactor.test testImplementation libs.objgenesis testImplementation libs.mockserver.netty testImplementation libs.java.websocket testImplementation libs.equals.verifier testImplementation libs.groovy detektPlugins libs.detekt.formatting } compileKotlin { kotlinOptions { jvmTarget = "13" } } compileTestKotlin { kotlinOptions { jvmTarget = "13" } } test { jvmArgs '-ea' testLogging.showStandardStreams = false testLogging.exceptionFormat = 'full' finalizedBy jacocoTestReport useJUnitPlatform() // getting on CI: // java.security.KeyStoreException: Key protection algorithm not found: java.security.UnrecoverableKeyException: Encrypt Private Key failed: unrecognized algorithm name: PBEWithSHA1AndDESede // at java.base/sun.security.pkcs12.PKCS12KeyStore.setKeyEntry(PKCS12KeyStore.java:700) // at java.base/sun.security.pkcs12.PKCS12KeyStore.engineSetKeyEntry(PKCS12KeyStore.java:597) // at java.base/sun.security.util.KeyStoreDelegator.engineSetKeyEntry(KeyStoreDelegator.java:111) // at java.base/java.security.KeyStore.setKeyEntry(KeyStore.java:1167) // at io.netty.handler.ssl.SslContext.buildKeyStore(SslContext.java:1102) // at io.netty.handler.ssl.ReferenceCountedOpenSslServerContext.newSessionContext(ReferenceCountedOpenSslServerContext.java:123) // ---- // see // https://github.com/bcgit/bc-java/issues/941 // https://bugs.openjdk.java.net/browse/JDK-8266279 // systemProperty "keystore.pkcs12.keyProtectionAlgorithm", "PBEWithHmacSHA256AndAES_256" } application { getMainClass().set('io.emeraldpay.dshackle.StarterKt') } jib { from { image = 'openjdk:13' } to { // by default publish as: // dshackle:shapshot, // dshackle:t, // dshackle: and // dshackle: // dshackle:latest only when publishing first version with zero patch (ex. 1.2.0) image = [ project.hasProperty('docker') ? project.property('docker') : 'emeraldpay', '/dshackle:', 'snapshot' ].join('') tags = [project.version].with(true) { add "t" + DateTimeFormatter.ofPattern("yyyyMMddHHmm").withZone(ZoneId.of('UTC')).format(Instant.now()) add project.version.toString().replaceAll('(\\d+\\.\\d+).+', '$1') } auth { username = System.getenv('DOCKERHUB_USERNAME') password = System.getenv('DOCKERHUB_TOKEN') } } container { jvmFlags = ['-Xms1024m', '-XX:MaxRAMPercentage=60'] mainClass = 'io.emeraldpay.dshackle.StarterKt' args = [] ports = ['2448', '2449', '8545'] } } jar { enabled = true } afterEvaluate { distZip.dependsOn(jar) compileKotlin.dependsOn(generateVersion) jar.dependsOn(generateVersion) } protobuf { protoc { // if $PROTOC_PATH is set then locally installed protoc is used otherwise it is downloaded remotely path = System.getenv("PROTOC_PATH") ?: null artifact = System.getenv("PROTOC_PATH") == null ? "com.google.protobuf:protoc:${libs.versions.protoc.get()}" : null } plugins { } generateProtoTasks { } } sourceSets { main { resources.srcDirs += project.buildDir.absolutePath + "/generated/version" } } task generateVersion() { group = 'Build' description = 'Generate project version' doLast { def version = versionDetails() def resourcesDir = new File(project.buildDir.absolutePath + "/generated/version") resourcesDir.mkdirs() new File(resourcesDir, "version.properties").text = [ "# AUTOMATICALLY GENERATED", "version.app=$project.version", "version.commit=${version.gitHash}", "version.tag=${version.lastTag}", "version.date=${DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneId.of('UTC')).format(Instant.now().truncatedTo(ChronoUnit.SECONDS))} UTC" ].join("\n") } } // Show the list of failed tests and output only for them, helpful for CI ext.failedTests = [] tasks.withType(Test) { def stdout = new LinkedList() beforeTest { TestDescriptor td -> stdout.clear() } onOutput { TestDescriptor td, TestOutputEvent toe -> stdout.addAll(toe.getMessage().split('(?m)$')) while (stdout.size() > 100) { stdout.remove() } } afterTest { TestDescriptor descriptor, TestResult result -> if (result.resultType == org.gradle.api.tasks.testing.TestResult.ResultType.FAILURE) { failedTests << "${descriptor.className} > ${descriptor.name}" if (!stdout.isEmpty()) { println("-------- ${descriptor.className} > ${descriptor.name} OUTPUT ".padRight(120, "-")) stdout.each { print(it) } println("================".padRight(120, "=")) } } } } gradle.buildFinished { if (!failedTests.empty) { println "Failed tests for ${project.name}:" failedTests.each { failedTest -> println failedTest } println "" } } jacocoTestReport { dependsOn test reports { xml.required.set true html.required.set true } afterEvaluate { classDirectories.setFrom(files(classDirectories.files.collect { fileTree(dir: it, exclude: 'io/emeraldpay/dshackle/proto/**') })) } } jacoco { toolVersion = "0.8.7" } detekt { toolVersion = "1.18.1" parallel = false basePath = projectDir debug = false ignoreFailures = true reports { xml { enabled = true destination = file("build/reports/detekt/detekt.xml") } html { enabled = true destination = file("build/reports/detekt/detekt.html") } } } tasks.withType(Detekt).configureEach { jvmTarget = "13" } // formats code for each build tasks.findByName("ktlintCheck").dependsOn("ktlintFormat")