diff --git a/.github/workflows/example-projects.yml b/.github/workflows/example-projects.yml index a32a440c1..f3ea485b5 100644 --- a/.github/workflows/example-projects.yml +++ b/.github/workflows/example-projects.yml @@ -11,10 +11,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: 'temurin' cache: 'gradle' diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 9fe7fd7af..f50911884 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -37,10 +37,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: 'temurin' cache: 'gradle' diff --git a/.github/workflows/node-script-test.yml b/.github/workflows/node-script-test.yml index e437b33c0..5c3a78871 100644 --- a/.github/workflows/node-script-test.yml +++ b/.github/workflows/node-script-test.yml @@ -64,10 +64,10 @@ jobs: uses: actions/setup-node@v4 with: node-version: 18 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: 'temurin' cache: 'gradle' diff --git a/.github/workflows/release-gradle-plugin.yml b/.github/workflows/release-gradle-plugin.yml index 37f93d812..64d804e40 100644 --- a/.github/workflows/release-gradle-plugin.yml +++ b/.github/workflows/release-gradle-plugin.yml @@ -13,10 +13,10 @@ jobs: steps: - name: Checkout code uses: actions/checkout@master - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: 'temurin' cache: 'gradle' diff --git a/.github/workflows/release-server.yml b/.github/workflows/release-server.yml index d243bc8f7..1e614b32d 100644 --- a/.github/workflows/release-server.yml +++ b/.github/workflows/release-server.yml @@ -42,10 +42,10 @@ jobs: uses: actions/setup-node@v4 with: node-version: 18 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: 'temurin' cache: 'gradle' diff --git a/.github/workflows/server-load-test.yml b/.github/workflows/server-load-test.yml index 9976ed8d1..a86985e21 100644 --- a/.github/workflows/server-load-test.yml +++ b/.github/workflows/server-load-test.yml @@ -32,10 +32,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: 'temurin' cache: 'gradle' diff --git a/.github/workflows/server-ui-functional-test.yml b/.github/workflows/server-ui-functional-test.yml index 88d4897a0..a2a55f511 100644 --- a/.github/workflows/server-ui-functional-test.yml +++ b/.github/workflows/server-ui-functional-test.yml @@ -47,10 +47,10 @@ jobs: uses: actions/setup-node@v4 with: node-version: 18 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: 'temurin' cache: 'gradle' diff --git a/CHANGELOG.md b/CHANGELOG.md index 810751a1c..5393d1cfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Full list of releases and packaged server .jar file for each release: https://github.com/craigatk/projektor/releases +* v5.0.0 + * BREAKING CHANGE: Projektor server is now built with Java 17 and requires Java 17+ to run * v4.39.0 * Adding API endpoint for fetching recent test runs for a repo * v4.38.1 @@ -19,7 +21,7 @@ Full list of releases and packaged server .jar file for each release: https://gi * v4.35.0 * Use mainline when no branch specified when getting repo current coverage * v4.34.0 - * Adding /api endpoints for getting org and repo current coverage perecentage + * Adding /api endpoints for getting org and repo current coverage percentage * v4.33.2 * Adding test file name to failed tests view on dashboard * v4.33.1 diff --git a/README.md b/README.md index 476397b1d..50a061d35 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,20 @@ including the change in coverage percentage in the PR. Details on how to set this up are at https://projektor.dev/docs/github-pull-request/ +## Changelog + +### Projektor server + +[Projektor server changelog](CHANGELOG.md) + +### Projektor Gradle plugin + +[Projektor Gradle plugin changelog](https://projektor.dev/docs/gradle-plugin/#changelog) + +### Projektor Node script + +[Projektor Node script changelog](https://projektor.dev/docs/node-script/#changelog) + ## Development For information on how Projektor is developed, how to build it from source and deploy it yourself, and other diff --git a/gradle.properties b/gradle.properties index 05c6459eb..8cb3bd77f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ strikt_version=0.34.1 wiremockVersion=2.35.2 github_api_version=1.321 -logbackVersion=1.4.7 +logbackVersion=1.4.14 ok_http3_version=4.12.0 diff --git a/gradle/kotlin.gradle b/gradle/kotlin.gradle index 8a98db534..2573ccd4e 100644 --- a/gradle/kotlin.gradle +++ b/gradle/kotlin.gradle @@ -1,7 +1,7 @@ apply plugin: "kotlin" apply plugin: "org.jlleitschuh.gradle.ktlint" -sourceCompatibility = 11 +sourceCompatibility = 17 compileKotlin { kotlinOptions.jvmTarget = "$sourceCompatibility" diff --git a/publishers/gradle-plugin/build.gradle b/publishers/gradle-plugin/build.gradle index 8976765d7..7afb98f6d 100644 --- a/publishers/gradle-plugin/build.gradle +++ b/publishers/gradle-plugin/build.gradle @@ -36,7 +36,7 @@ repositories { } group = "dev.projektor" -version = "8.7.0" +version = "9.0.0" dependencies { implementation "com.squareup.okhttp3:okhttp:${ok_http3_version}" @@ -103,10 +103,10 @@ task startServerDaemon(type: com.github.psxpaul.task.JavaExecFork) { 'javadocJar', 'pluginUnderTestMetadata', 'sourcesJar', - 'test', 'processFunctionalTestResources', 'processTestResources', 'validatePlugins' + mustRunAfter 'test' classpath = tasks.getByPath(':server:server-app:shadowJar').outputs.files main = 'io.ktor.server.netty.EngineMain' stopAfter = functionalTest diff --git a/publishers/gradle-plugin/src/functionalTest/groovy/projektor/plugin/functionaltest/MultiProjectCoverageFunctionalSpec.groovy b/publishers/gradle-plugin/src/functionalTest/groovy/projektor/plugin/functionaltest/MultiProjectCoverageFunctionalSpec.groovy index d0eea5084..0ec04d5cc 100644 --- a/publishers/gradle-plugin/src/functionalTest/groovy/projektor/plugin/functionaltest/MultiProjectCoverageFunctionalSpec.groovy +++ b/publishers/gradle-plugin/src/functionalTest/groovy/projektor/plugin/functionaltest/MultiProjectCoverageFunctionalSpec.groovy @@ -75,10 +75,10 @@ class MultiProjectCoverageFunctionalSpec extends MultiProjectFunctionalSpecifica assert overallStatsResponse.successful CoverageStats overallStats = overallStatsResponse.body() - assert overallStats.statementStat.covered == 20 - assert overallStats.statementStat.missed == 16 - assert overallStats.statementStat.total == 36 - assert overallStats.statementStat.coveredPercentage == 55.56 + assert overallStats.statementStat.covered == 25 + assert overallStats.statementStat.missed == 5 + assert overallStats.statementStat.total == 30 + assert overallStats.statementStat.coveredPercentage == 83.33 assert overallStats.lineStat.covered == 5 assert overallStats.lineStat.missed == 1 @@ -138,10 +138,10 @@ class MultiProjectCoverageFunctionalSpec extends MultiProjectFunctionalSpecifica assert overallStatsResponse1.successful CoverageStats overallStats = overallStatsResponse1.body() - assert overallStats.statementStat.covered == 20 - assert overallStats.statementStat.missed == 16 - assert overallStats.statementStat.total == 36 - assert overallStats.statementStat.coveredPercentage == 55.56 + assert overallStats.statementStat.covered == 25 + assert overallStats.statementStat.missed == 5 + assert overallStats.statementStat.total == 30 + assert overallStats.statementStat.coveredPercentage == 83.33 assert overallStats.lineStat.covered == 5 assert overallStats.lineStat.missed == 1 @@ -178,7 +178,7 @@ class MultiProjectCoverageFunctionalSpec extends MultiProjectFunctionalSpecifica Coverage coverage2 = coverageResponse2.body() assert coverage2.previousTestRunId == testId1 - assert coverage2.overallStats.statementStat.coveredPercentage == 55.56 + assert coverage2.overallStats.statementStat.coveredPercentage == 83.33 assert coverage2.overallStats.statementStat.coveredPercentageDelta == 0.00 assert coverage2.overallStats.lineStat.coveredPercentage == 83.33 diff --git a/publishers/gradle-plugin/src/functionalTest/groovy/projektor/plugin/functionaltest/SingleProjectCoverageFunctionalSpec.groovy b/publishers/gradle-plugin/src/functionalTest/groovy/projektor/plugin/functionaltest/SingleProjectCoverageFunctionalSpec.groovy index 35a2f09e9..5dc90a30c 100644 --- a/publishers/gradle-plugin/src/functionalTest/groovy/projektor/plugin/functionaltest/SingleProjectCoverageFunctionalSpec.groovy +++ b/publishers/gradle-plugin/src/functionalTest/groovy/projektor/plugin/functionaltest/SingleProjectCoverageFunctionalSpec.groovy @@ -57,10 +57,10 @@ class SingleProjectCoverageFunctionalSpec extends ProjektorPluginFunctionalSpeci assert overallStatsResponse.successful CoverageStats overallStats = overallStatsResponse.body() - assert overallStats.statementStat.covered == 8 - assert overallStats.statementStat.missed == 4 - assert overallStats.statementStat.total == 12 - assert overallStats.statementStat.coveredPercentage == 66.67 + assert overallStats.statementStat.covered == 10 + assert overallStats.statementStat.missed == 0 + assert overallStats.statementStat.total == 10 + assert overallStats.statementStat.coveredPercentage == 100.00 assert overallStats.lineStat.covered == 2 assert overallStats.lineStat.missed == 0 diff --git a/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/MultiProjectWithPluginAppliedToSubprojectsSpec.groovy b/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/MultiProjectWithPluginAppliedToSubprojectsSpec.groovy index b7844e870..17a0b0a6d 100644 --- a/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/MultiProjectWithPluginAppliedToSubprojectsSpec.groovy +++ b/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/MultiProjectWithPluginAppliedToSubprojectsSpec.groovy @@ -72,7 +72,11 @@ include 'project1', 'project2', 'project3' } dependencies { - testImplementation('org.spockframework:spock-core:1.3-groovy-2.5') + testImplementation('org.spockframework:spock-core:2.3-groovy-3.0') + } + + test { + useJUnitPlatform() } projektor { diff --git a/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/coverage/CoverageMultiTaskSpec.groovy b/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/coverage/CoverageMultiTaskSpec.groovy index d4a739cab..5ba2fc02f 100644 --- a/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/coverage/CoverageMultiTaskSpec.groovy +++ b/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/coverage/CoverageMultiTaskSpec.groovy @@ -86,7 +86,7 @@ class CoverageMultiTaskSpec extends SingleProjectSpec { coverageFilePayloads.size() == 1 coverageFilePayloads[0].reportContents.contains("MyClass") - coverageFilePayloads[0].reportContents.contains('') + coverageFilePayloads[0].reportContents.contains('') where: gradleVersion | _ diff --git a/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/kotlin/KotlinDslSpec.groovy b/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/kotlin/KotlinDslSpec.groovy index bc369bca3..cf9a20072 100644 --- a/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/kotlin/KotlinDslSpec.groovy +++ b/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/kotlin/KotlinDslSpec.groovy @@ -29,7 +29,11 @@ class KotlinDslSpec extends ProjectSpec { } dependencies { - "testImplementation"("org.spockframework:spock-core:1.3-groovy-2.5") + "testImplementation"("org.spockframework:spock-core:2.3-groovy-3.0") + } + + tasks.named("test") { + useJUnitPlatform() } configure { diff --git a/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/version/MinimumVersionSpec.groovy b/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/version/MinimumVersionSpec.groovy deleted file mode 100644 index b200fa5df..000000000 --- a/publishers/gradle-plugin/src/test/groovy/projektor/plugin/testkit/version/MinimumVersionSpec.groovy +++ /dev/null @@ -1,44 +0,0 @@ -package projektor.plugin.testkit.version - -import com.github.tomakehurst.wiremock.verification.LoggedRequest -import org.gradle.testkit.runner.GradleRunner -import projektor.plugin.SpecWriter -import projektor.plugin.testkit.SingleProjectSpec -import spock.lang.Unroll - -class MinimumVersionSpec extends SingleProjectSpec { - @Unroll - def "when running with Gradle version #gradleVersion less than Gradle 7.6.1 should fail"() { - given: - buildFile << """ - projektor { - serverUrl = '${serverUrl}' - } - """.stripIndent() - - SpecWriter.createTestDirectoryWithPassingTest(projectRootDir, "SampleSpec") - - String resultsId = "ABC123" - resultsStubber.stubResultsPostSuccess(resultsId) - - when: - def result = GradleRunner.create() - .withProjectDir(projectRootDir.root) - .withArguments('test') - .withPluginClasspath() - .withGradleVersion(gradleVersion) - .buildAndFail() - - then: - result.output.contains("This version of the Projektor Gradle plugin supports Gradle 7.6.1+ only. Please upgrade the version of Gradle your project uses.") - - and: - List resultsRequests = resultsStubber.findResultsRequests() - resultsRequests.size() == 0 - - where: - gradleVersion | _ - "5.0" | _ - "6.0" | _ - } -} diff --git a/publishers/gradle-plugin/src/testFixtures/groovy/projektor/plugin/BuildFileWriter.groovy b/publishers/gradle-plugin/src/testFixtures/groovy/projektor/plugin/BuildFileWriter.groovy index e060a2354..4bdd1daa4 100644 --- a/publishers/gradle-plugin/src/testFixtures/groovy/projektor/plugin/BuildFileWriter.groovy +++ b/publishers/gradle-plugin/src/testFixtures/groovy/projektor/plugin/BuildFileWriter.groovy @@ -41,9 +41,13 @@ class BuildFileWriter { } dependencies { - implementation('org.codehaus.groovy:groovy-all:2.5.13') + implementation('org.codehaus.groovy:groovy-all:3.0.21') - testImplementation('org.spockframework:spock-core:1.3-groovy-2.5') + testImplementation('org.spockframework:spock-core:2.3-groovy-3.0') + } + + test { + useJUnitPlatform() } ${config.includeJacocoPlugin ? "jacocoTestReport { dependsOn test }": ""} diff --git a/server/server-app/build.gradle b/server/server-app/build.gradle index 77bf32247..c5434a2e1 100644 --- a/server/server-app/build.gradle +++ b/server/server-app/build.gradle @@ -51,9 +51,9 @@ dependencies { implementation "io.ktor:ktor-server-metrics-micrometer:$ktor_version" implementation 'io.micrometer:micrometer-registry-influx:1.12.5' - implementation "net.javacrumbs.shedlock:shedlock-provider-jdbc-template:4.47.0" + implementation "net.javacrumbs.shedlock:shedlock-provider-jdbc-template:5.12.0" - implementation 'org.simpleflatmapper:sfm-jooq:8.2.3' + implementation 'org.simpleflatmapper:sfm-jooq:9.0.0' implementation "ch.qos.logback:logback-classic:$logbackVersion" implementation "net.logstash.logback:logstash-logback-encoder:7.4" @@ -121,7 +121,7 @@ heroku { includes = ["${projectDir}/build/libs/server-app-1.0-all.jar".toString(), "${projectDir}/opentelemetry/opentelemetry-javaagent.jar".toString()] includeBuildDir = false appName = "projektorlive" - jdkVersion = "11" + jdkVersion = "17" } deployHeroku.dependsOn('assembleFull') diff --git a/server/server-app/src/main/kotlin/projektor/schedule/SchedulerLock.kt b/server/server-app/src/main/kotlin/projektor/schedule/SchedulerLock.kt index f4e1a69ab..31f79d9b4 100644 --- a/server/server-app/src/main/kotlin/projektor/schedule/SchedulerLock.kt +++ b/server/server-app/src/main/kotlin/projektor/schedule/SchedulerLock.kt @@ -3,6 +3,7 @@ package projektor.schedule import net.javacrumbs.shedlock.core.DefaultLockingTaskExecutor import net.javacrumbs.shedlock.core.LockConfiguration import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider +import java.time.Duration import java.time.Instant import javax.sql.DataSource @@ -11,8 +12,9 @@ class SchedulerLock(dataSource: DataSource) { private val executor = DefaultLockingTaskExecutor(lockProvider) fun executeWithLock(runnable: Runnable, lockConfig: SchedulerLockConfig) { - val lockAtMostUntil: Instant = Instant.now().plusSeconds(lockConfig.lockMinutes * 60) - executor.executeWithLock(runnable, LockConfiguration(lockConfig.lockName, lockAtMostUntil)) + val lockAtMostFor: Duration = Duration.ofMinutes(lockConfig.lockMinutes) + val lockAtLeastFor: Duration = Duration.ofSeconds(15) + executor.executeWithLock(runnable, LockConfiguration(Instant.now(), lockConfig.lockName, lockAtMostFor, lockAtLeastFor)) } } diff --git a/server/server-app/src/test/kotlin/projektor/compare/PreviousTestRunServiceTest.kt b/server/server-app/src/test/kotlin/projektor/compare/PreviousTestRunServiceTest.kt index 440cc25c4..2f3d241ae 100644 --- a/server/server-app/src/test/kotlin/projektor/compare/PreviousTestRunServiceTest.kt +++ b/server/server-app/src/test/kotlin/projektor/compare/PreviousTestRunServiceTest.kt @@ -15,6 +15,7 @@ import strikt.assertions.isNotNull import strikt.assertions.isNull import strikt.assertions.isTrue import java.time.ZoneOffset +import java.time.temporal.ChronoUnit class PreviousTestRunServiceTest : DatabaseRepositoryTestCase() { @@ -222,7 +223,7 @@ class PreviousTestRunServiceTest : DatabaseRepositoryTestCase() { val recentTestRun = runBlocking { previousTestRunService.findMostRecentRunWithCoverage(repoName, projectName, BranchSearch(branchType = BranchType.MAINLINE)) } expectThat(recentTestRun).isNotNull().and { get { publicId }.isEqualTo(newPublicId) - get { createdTimestamp }.isEqualTo(newTestRun.createdTimestamp.toInstant(ZoneOffset.UTC)) + get { createdTimestamp.truncatedTo(ChronoUnit.MILLIS) }.isEqualTo(newTestRun.createdTimestamp.toInstant(ZoneOffset.UTC).truncatedTo(ChronoUnit.MILLIS)) get { branch }.isEqualTo("main") } } @@ -263,7 +264,7 @@ class PreviousTestRunServiceTest : DatabaseRepositoryTestCase() { val recentTestRun = runBlocking { previousTestRunService.findMostRecentRun(repoName, null, BranchSearch(branchType = BranchType.MAINLINE)) } expectThat(recentTestRun).isNotNull().and { get { publicId }.isEqualTo(newPublicId) - get { createdTimestamp }.isEqualTo(newTestRun.createdTimestamp.toInstant(ZoneOffset.UTC)) + get { createdTimestamp.truncatedTo(ChronoUnit.MILLIS) }.isEqualTo(newTestRun.createdTimestamp.toInstant(ZoneOffset.UTC).truncatedTo(ChronoUnit.MILLIS)) get { branch }.isEqualTo("main") } } @@ -306,7 +307,7 @@ class PreviousTestRunServiceTest : DatabaseRepositoryTestCase() { val recentTestRun = runBlocking { previousTestRunService.findMostRecentRun(repoName, projectName, BranchSearch(branchType = BranchType.MAINLINE)) } expectThat(recentTestRun).isNotNull().and { get { publicId }.isEqualTo(newPublicId) - get { createdTimestamp }.isEqualTo(newTestRun.createdTimestamp.toInstant(ZoneOffset.UTC)) + get { createdTimestamp.truncatedTo(ChronoUnit.MILLIS) }.isEqualTo(newTestRun.createdTimestamp.toInstant(ZoneOffset.UTC).truncatedTo(ChronoUnit.MILLIS)) get { branch }.isEqualTo("main") get { passed }.isTrue() } diff --git a/server/server-app/src/test/kotlin/projektor/logging/TimeMillisJsonLoggingProviderTest.kt b/server/server-app/src/test/kotlin/projektor/logging/TimeMillisJsonLoggingProviderTest.kt index 5f9c4d2ff..a5fb3a0fd 100644 --- a/server/server-app/src/test/kotlin/projektor/logging/TimeMillisJsonLoggingProviderTest.kt +++ b/server/server-app/src/test/kotlin/projektor/logging/TimeMillisJsonLoggingProviderTest.kt @@ -3,6 +3,7 @@ package projektor.logging import ch.qos.logback.classic.Level import ch.qos.logback.classic.LoggerContext import ch.qos.logback.classic.spi.LoggingEvent +import ch.qos.logback.classic.util.LogbackMDCAdapter import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import net.logstash.logback.encoder.LogstashEncoder @@ -22,7 +23,10 @@ class TimeMillisJsonLoggingProviderTest { encoder.start() - val logger = LoggerContext().getLogger("MyLogger") + val loggerContext = LoggerContext() + loggerContext.mdcAdapter = LogbackMDCAdapter() + + val logger = loggerContext.getLogger("MyLogger") val event = LoggingEvent("MyClass", logger, Level.INFO, "The message", null, null) event.timeStamp = 12345000