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