diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed668f579e5ba0c6963d6a42b41995bfda385153..e2ffce6de3bf17c34bf3e1f83585d47d4272fc65 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ image: - name: wnwalker/android-gradle-build-image:34.0.0-jdk17-gradle8.7 + name: wnwalker/temerity-build-image@sha256:b1ea20cd261395671a541996f7e0cc2437ecf95a813b6fcc7581b41cde167fbf variables: ORG_GRADLE_PROJECT_signingKey: $SIGNING_KEY diff --git a/.sdkmanrc b/.sdkmanrc index 955c2580880005a57da000c2c867ea6a706864bd..dc041b2d9f45321aaea04f45e17d92088480ebb6 100644 --- a/.sdkmanrc +++ b/.sdkmanrc @@ -1,3 +1,3 @@ # Enable auto-env through the sdkman_auto_env config # Add key=value pairs of SDKs to use below -java=17.0.12-tem \ No newline at end of file +java=17.0.14-tem \ No newline at end of file diff --git a/build-image/temerity-build-image.dockerfile b/build-image/temerity-build-image.dockerfile index 4363c9d3691899d12946807ecec2f9c50045766a..37e7f9d3d47a5989eddd471b2fdbff00c2d03cd9 100644 --- a/build-image/temerity-build-image.dockerfile +++ b/build-image/temerity-build-image.dockerfile @@ -1,10 +1,13 @@ # Based on code available at https://github.com/MobileDevOps/android-sdk-image/blob/9cab35c5d0433ff08cc34f14b82f0f417464bc8e/Dockerfile +# To build and publish using experimental Docker buildx: +# docker buildx build --platform linux/amd64 --push -t <docker_hub_username>/<image_name>:<tag> . --file temerity-build-image.dockerfile + # To build: # docker build -t <docker_hub_username>/<image_name>:<tag> . --file temerity-build-image.dockerfile # To publish: # docker push <docker_hub_username>/<image_name>:<tag> -FROM --platform=linux/amd64 ubuntu:24.10 +FROM bitnami/minideb:bookworm AS base LABEL maintainer="wnwalker" @@ -12,8 +15,8 @@ LABEL maintainer="wnwalker" # https://developer.android.com/studio/index.html ENV ANDROID_SDK_TOOLS_VERSION=11076708 ENV ANDROID_SDK_TOOLS_CHECKSUM=2d2d50857e4eb553af5a6dc3ad507a17adf43d115264b1afc116f95c92e5e258 -ENV GRADLE_VERSION=8.7 -ENV JAVA_VERSION=17.0.12-tem +ENV GRADLE_VERSION=8.11 +ENV JAVA_VERSION=17.0.14-tem ENV ANDROID_HOME=/opt/android-sdk-linux ENV ANDROID_SDK_ROOT=$ANDROID_HOME ENV PATH=$PATH:$ANDROID_HOME/cmdline-tools:$ANDROID_HOME/cmdline-tools/bin:$ANDROID_HOME/platform-tools @@ -42,7 +45,7 @@ RUN apt-get -qq update \ git > /dev/null \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -ENV HOME=/home/agbi +ENV HOME=/home/tbi WORKDIR $HOME/app # Install SDKMAN @@ -50,8 +53,10 @@ RUN curl -s "https://get.sdkman.io" | bash SHELL ["/bin/bash", "-c"] # Set the environment variable for sdkman to auto-answer during installation -RUN echo "sdkman_auto_answer=true" > "$HOME/.sdkman/etc/config" -RUN echo "sdkman_auto_env=true" > "$HOME/.sdkman/etc/config" +RUN mkdir -p "$HOME/.sdkman/etc/" +RUN touch "$HOME/.sdkman/etc/config" +RUN echo "sdkman_auto_answer=true" >> "$HOME/.sdkman/etc/config" +RUN echo "sdkman_auto_env=true" >> "$HOME/.sdkman/etc/config" # Install Gradle, Java RUN source "${HOME}/.sdkman/bin/sdkman-init.sh" \ diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index a7029afc90f32e9216bd7913df3fbb256adfe40a..97212ad8a534c567ec9dae79bd96a7eeffd55960 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -1,6 +1,24 @@ +/* + * Designed and developed in 2022-2024 by William Walker (wnwalker@ucsc.edu) + * Copyright 2022-2024 The Regents of the University of California. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ plugins { `kotlin-dsl` `kotlin-dsl-precompiled-script-plugins` + alias(libs.plugins.spotless) } dependencies { @@ -9,6 +27,55 @@ dependencies { implementation(libs.build.gitSemVer) } +kotlin { + compilerOptions { + jvmToolchain(libs.versions.jvm.target.get().toInt()) + } +} + +gradlePlugin { + plugins { + register("formatting-convention") { + id = "temerity.formatting-convention" + implementationClass = "edu.ucsc.its.temerity.buildlogic.convention.FormattingConventionPlugin" + } + } + plugins { + register("publication-convention") { + id = "temerity.publication-convention" + implementationClass = "edu.ucsc.its.temerity.buildlogic.convention.PublicationConventionPlugin" + } + } + plugins { + register("version-convention") { + id = "temerity.version-convention" + implementationClass = "edu.ucsc.its.temerity.buildlogic.convention.VersionConventionPlugin" + } + } +} + +spotless { + kotlin { + target("**/*.kt") + targetExclude("${project.layout.buildDirectory}/**/*.kt") + ktlint().editorConfigOverride( + mapOf( + "indent_size" to "2", + "continuation_indent_size" to "2", + ) + ) + licenseHeaderFile(file("../spotless/LicenseHeader.kt")) + trimTrailingWhitespace() + endWithNewline() + } + format("kts") { + target("**/*.kts") + targetExclude("${project.layout.buildDirectory}/**/*.kts") + licenseHeaderFile(file("../spotless/LicenseHeader.kt"), "(^(?![\\/ ]\\*).*$)") + trimTrailingWhitespace() + endWithNewline() + } +} // Excerpt from Project kt-fuzzy licensed under the MIT license // By github.com/solo-studios, author solonovamax. See NOTICE file. @@ -18,14 +85,3 @@ fun gradlePlugin(id: Provider<PluginDependency>, version: Provider<String>): Str val pluginId = id.get().pluginId return "$pluginId:$pluginId.gradle.plugin:${version.get()}" } - - -kotlin { - target { - compilations.configureEach { - kotlinOptions { - jvmToolchain(libs.versions.java.get().toInt()) - } - } - } -} \ No newline at end of file diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index 030b00897134503fe14d2bb0049a47dd1380f8ab..0d67e312ab086538cb490dd4336d7d19e4e3c3ac 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -1,3 +1,20 @@ +/* + * Designed and developed in 2022-2024 by William Walker (wnwalker@ucsc.edu) + * Copyright 2022-2024 The Regents of the University of California. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ @file:Suppress("UnstableApiUsage") rootProject.name = "build-logic" @@ -15,4 +32,4 @@ dependencyResolutionManagement { from(files("../gradle/libs.versions.toml")) } } -} \ No newline at end of file +} diff --git a/build-logic/src/main/kotlin/convention.formatting.gradle.kts b/build-logic/src/main/kotlin/convention.formatting.gradle.kts deleted file mode 100644 index 29f76de5f911b2e0b4c0e6412953d9496f8b1785..0000000000000000000000000000000000000000 --- a/build-logic/src/main/kotlin/convention.formatting.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -plugins { - id("com.diffplug.spotless") -} - -spotless { - kotlin { - target("**/*.kt") - targetExclude("${layout.buildDirectory}/**/*.kt") - ktlint().editorConfigOverride( - mapOf( - "indent_size" to "2", - "continuation_indent_size" to "2", - ) - ) - licenseHeaderFile(rootProject.file("spotless/libHeader.kt")) - trimTrailingWhitespace() - endWithNewline() - } - format("kts") { - target("**/*.kts") - targetExclude("${layout.buildDirectory}/**/*.kts") - licenseHeaderFile(rootProject.file("spotless/libHeader.kt"), "(^(?![\\/ ]\\*).*$)") - trimTrailingWhitespace() - endWithNewline() - } -} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/convention.publication.gradle.kts b/build-logic/src/main/kotlin/convention.publication.gradle.kts deleted file mode 100644 index 260041957d6228b507e11a6f11163cc8e57f5567..0000000000000000000000000000000000000000 --- a/build-logic/src/main/kotlin/convention.publication.gradle.kts +++ /dev/null @@ -1,48 +0,0 @@ -//Publishing your Kotlin Multiplatform library to Maven Central -//https://dev.to/kotlin/how-to-build-and-publish-a-kotlin-multiplatform-library-going-public-4a8k - -import org.gradle.api.publish.maven.MavenPublication -import org.gradle.api.tasks.bundling.Jar -import org.gradle.kotlin.dsl.`maven-publish` -import org.gradle.kotlin.dsl.signing -import java.util.* - -plugins { - id("maven-publish") - id("signing") -} - -publishing { - // Configure maven repository - repositories { - maven { - name = "GitlabProjectRepository" - url = uri("https://git.ucsc.edu/api/v4/projects/12162/packages/maven") - credentials(HttpHeaderCredentials::class) { - name = "Job-Token" - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - create("header", HttpHeaderAuthentication::class) - } - } - } -} - -signing { - val signingKeyId: String? by project - val signingKey: String? by project - val signingPassword: String? by project - if (!signingKey.isNullOrEmpty()){ - useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) - publishing.publications { - sign(publishing.publications) - } - } -} - -//https://github.com/gradle/gradle/issues/26132 -val signingTasks = tasks.withType<Sign>() -tasks.withType<AbstractPublishToMaven>().configureEach { - mustRunAfter(signingTasks) -} \ No newline at end of file diff --git a/build-logic/src/main/kotlin/convention.version.gradle.kts b/build-logic/src/main/kotlin/convention.version.gradle.kts deleted file mode 100644 index 1d740e35091ba4fc2b69e8f5b5e05cd8f1fbfa90..0000000000000000000000000000000000000000 --- a/build-logic/src/main/kotlin/convention.version.gradle.kts +++ /dev/null @@ -1,7 +0,0 @@ -plugins { - id("org.danilopianini.git-sensitive-semantic-versioning") -} - -gitSemVer { - maxVersionLength.set(20) -} diff --git a/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/FormattingConventionPlugin.kt b/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/FormattingConventionPlugin.kt new file mode 100644 index 0000000000000000000000000000000000000000..418c6591deacedbd9aaa0a5b6107b72964de1c6a --- /dev/null +++ b/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/FormattingConventionPlugin.kt @@ -0,0 +1,33 @@ +package edu.ucsc.its.temerity.buildlogic.convention + +import com.diffplug.gradle.spotless.SpotlessExtension +import org.gradle.api.Plugin +import org.gradle.api.Project + +class FormattingConventionPlugin : Plugin<Project> { + override fun apply(project: Project) = with(project) { + pluginManager.apply("com.diffplug.spotless") + extensions.configure(SpotlessExtension::class.java) { + kotlin { + target("**/*.kt") + targetExclude("${layout.buildDirectory}/**/*.kt") + ktlint("1.5.0").editorConfigOverride( + mapOf( + "indent_size" to "2", + "continuation_indent_size" to "2", + ), + ) + licenseHeaderFile(file("../spotless/LicenseHeader.kt")) + trimTrailingWhitespace() + endWithNewline() + } + format("kts") { + target("**/*.kts") + targetExclude("${layout.buildDirectory}/**/*.kts") + licenseHeaderFile(file("../spotless/LicenseHeader.kt"), "(^(?![\\/ ]\\*).*$)") + trimTrailingWhitespace() + endWithNewline() + } + } + } +} diff --git a/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/KmpConventionPlugin.kt b/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/KmpConventionPlugin.kt new file mode 100644 index 0000000000000000000000000000000000000000..10bda6e35a811607a7d2a7dfd078d4195c98ff15 --- /dev/null +++ b/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/KmpConventionPlugin.kt @@ -0,0 +1,4 @@ +package edu.ucsc.its.temerity.buildlogic.convention + +class KmpConventionPlugin { +} diff --git a/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/PublicationConventionPlugin.kt b/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/PublicationConventionPlugin.kt new file mode 100644 index 0000000000000000000000000000000000000000..13886f43166affdda8d741110d1354d0067cf29b --- /dev/null +++ b/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/PublicationConventionPlugin.kt @@ -0,0 +1,75 @@ +/* + * Designed and developed in 2022-2024 by William Walker (wnwalker@ucsc.edu) + * Copyright 2022-2024 The Regents of the University of California. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package edu.ucsc.its.temerity.buildlogic.convention + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.credentials.HttpHeaderCredentials +import org.gradle.api.publish.PublishingExtension +import org.gradle.api.publish.maven.tasks.AbstractPublishToMaven +import org.gradle.authentication.http.HttpHeaderAuthentication +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.credentials +import org.gradle.kotlin.dsl.provideDelegate +import org.gradle.kotlin.dsl.withType +import org.gradle.plugins.signing.Sign +import org.gradle.plugins.signing.SigningExtension +import java.net.URI + +class PublicationConventionPlugin : Plugin<Project> { + override fun apply(project: Project) = with(project) { + pluginManager.apply("maven-publish") + extensions.configure(PublishingExtension::class.java) { + // Configure maven repository + repositories { + maven { + name = "GitlabProjectRepository" + url = URI("https://git.ucsc.edu/api/v4/projects/12162/packages/maven") + credentials(HttpHeaderCredentials::class) { + name = "Job-Token" + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + create("header", HttpHeaderAuthentication::class) + } + } + } + } + + pluginManager.apply("signing") + extensions.configure(SigningExtension::class.java) { + val signingKeyId: String? by project + val signingKey: String? by project + val signingPassword: String? by project + if (!signingKey.isNullOrEmpty()) { + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + extensions.configure(PublishingExtension::class.java) { + val pubExt = this + publications { + sign(pubExt.publications) + } + } + } + } + + val signingTasks = tasks.withType<Sign>() + tasks.withType<AbstractPublishToMaven>().configureEach { + mustRunAfter(signingTasks) + } + } +} diff --git a/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/VersionConventionPlugin.kt b/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/VersionConventionPlugin.kt new file mode 100644 index 0000000000000000000000000000000000000000..54f82322fee06e19affb95eaa465ac09899a635f --- /dev/null +++ b/build-logic/src/main/kotlin/edu/ucsc/its/temerity/buildlogic/convention/VersionConventionPlugin.kt @@ -0,0 +1,31 @@ +/* + * Designed and developed in 2022-2024 by William Walker (wnwalker@ucsc.edu) + * Copyright 2022-2024 The Regents of the University of California. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package edu.ucsc.its.temerity.buildlogic.convention + +import org.danilopianini.gradle.gitsemver.GitSemVerExtension +import org.gradle.api.Plugin +import org.gradle.api.Project + +class VersionConventionPlugin : Plugin<Project> { + override fun apply(project: Project) = with(project) { + pluginManager.apply("org.danilopianini.git-sensitive-semantic-versioning") + extensions.configure(GitSemVerExtension::class.java) { + maxVersionLength.set(20) + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0070fa1bb5ece7cfb86312628307ca39e1160568..778814576534e00e449e40e3be69ba7685e4044e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,47 +1,48 @@ [versions] -agp = "8.2.2" -java = "17" -kotlin = "2.1.0" -ksp = "2.1.0-1.0.29" - -kotlinx-serialization = "1.8.0-RC" -ktor = "3.0.3" -cache4k = "0.13.0" +agp = "8.7.3" +jvm-target = "17" +jvm-version = "17.0.14" +kotlin = "2.1.10" +ksp = "2.1.10-1.0.31" + +kotlinx-serialization = "1.8.0" +ktor = "3.1.1" +cache4k = "0.14.0" statelyConcurrentCollections = "2.1.0" -slf4j = "2.0.16" -kotlinx-io = "0.6.0" +slf4j = "2.0.17" +kotlinx-io = "0.7.0" -ktorfit = "2.2.0" +ktorfit = "2.4.0" coroutines = "1.10.1" -datetime = "0.6.1" -sandwich = "2.0.10" +datetime = "0.6.2" +sandwich = "2.1.0" arrow = "2.0.0" # Main Koin version - for core, android deps -koin = "4.0.1" -koinTest = "4.0.1" -gradleBuildConfigPlugin = "5.5.1" +koin = "4.0.2" +koinTest = "4.0.2" +gradleBuildConfigPlugin = "5.5.4" akkurate = "0.11.0" exposed = "0.56.0" dotenv-vault = "0.0.3" -kermit = "2.0.4" +kermit = "2.0.5" appdirs = "1.2.0" kstore = "0.9.1" -kmpIo = "0.1.5" +kmpIo = "0.1.6" kotlinSemver = "2.0.0" jansi = "2.4.1" temerity = "[0.1.0-dev0z+41180a5,0.1.0]" -kotlinx-dataframe = "0.14.2" +kotlinx-dataframe = "0.15.0" klaxon = "5.6" -kotest = "6.0.0.M1" +kotest = "6.0.0.M2" kotest-datatest = "5.9.1" dokka = "2.0.0" -spotless = "7.0.0.BETA4" -gitSemVer = "3.1.7" -conventionalCommits = "1.0.12" -konsist = "0.16.1" +spotless = "7.0.0" +gitSemVer = "4.0.2" +conventionalCommits = "1.0.15" +konsist = "0.17.3" minSdk = "24" targetSdk = "34" @@ -66,6 +67,7 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } kotlinx-coroutines-debug = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-debug", version.ref = "coroutines" } kotlinx-coroutines-slf4j = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-slf4j", version.ref = "coroutines" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" } kotlinx-io = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinx-io" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23a4f05d39d81870f8355ca43324f027298..94113f200e61179d552438ad36de4a645738eac2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index 51505d6d476a09b0dd62e478eee32bb068ec655b..241007b54e8ef1c5f9622944596e67f9f34d3ad3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,6 +23,7 @@ dependencyResolutionManagement { maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") maven("https://plugins.gradle.org/m2") maven("https://git.ucsc.edu/api/v4/projects/12162/packages/maven") + maven("https://oss.sonatype.org/content/repositories/snapshots") } } diff --git a/spotless/libHeader.kt b/spotless/LicenseHeader.kt similarity index 99% rename from spotless/libHeader.kt rename to spotless/LicenseHeader.kt index abf1bcf00a1c94a45c4a4da32a83637499845ea9..ad7271bc23a2838d8e7d8b3e9a61de9bfa2bceb6 100644 --- a/spotless/libHeader.kt +++ b/spotless/LicenseHeader.kt @@ -14,4 +14,4 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ \ No newline at end of file + */ diff --git a/spotless/appHeader.kt b/spotless/appHeader.kt deleted file mode 100644 index dd533b16d82499dfccbd78c595c1dc01f1586ec5..0000000000000000000000000000000000000000 --- a/spotless/appHeader.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Designed and developed in 2022-2024 by William Walker (wnwalker@ucsc.edu) - * Copyright 2022-2024 The Regents of the University of California. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ \ No newline at end of file diff --git a/spotless/appHeader.xml b/spotless/appHeader.xml deleted file mode 100644 index 4dcfd18c07968cf26a0c871ed31dbaa1e9c0ede4..0000000000000000000000000000000000000000 --- a/spotless/appHeader.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Designed and developed in 2022-2024 by William Walker (wnwalker@ucsc.edu) - Copyright 2022-2024 The Regents of the University of California. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> \ No newline at end of file diff --git a/spotless/libHeader.xml b/spotless/libHeader.xml deleted file mode 100644 index fac8567168d9f7bdfa1e85605791702396c8bd0e..0000000000000000000000000000000000000000 --- a/spotless/libHeader.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Designed and developed in 2022-2024 by William Walker (wnwalker@ucsc.edu) - Copyright 2022-2024 The Regents of the University of California. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; version 2.1 of the License. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ---> \ No newline at end of file diff --git a/temerity/build.gradle.kts b/temerity/build.gradle.kts index 0ab804f4a3d9707799d5b0532699745587dce8ea..0a4a0f62af75b1a686de1f479579e2cb8e7e0fe2 100644 --- a/temerity/build.gradle.kts +++ b/temerity/build.gradle.kts @@ -15,7 +15,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import io.github.andreabrighi.gradle.gitsemver.conventionalcommit.ConventionalCommit import io.github.z4kn4fein.semver.Version import kotlinx.datetime.Clock @@ -32,10 +31,10 @@ plugins { alias(libs.plugins.kotestMultiplatform) alias(libs.plugins.buildConfig) - id("convention.formatting") - id("convention.version") + id("temerity.formatting-convention") + id("temerity.version-convention") alias(libs.plugins.dokka) - id("convention.publication") + id("temerity.publication-convention") alias(libs.plugins.dataframe) @@ -68,6 +67,7 @@ buildConfig { } } }) + buildConfigField("JVM_VERSION", provider { libs.versions.jvm.version.get() }) } gitSemVer { @@ -79,11 +79,10 @@ dependencies { } kotlin { - jvmToolchain(libs.versions.java.get().toInt()) + jvmToolchain(libs.versions.jvm.target.get().toInt()) applyDefaultHierarchyTemplate() explicitApi() - @OptIn(ExperimentalKotlinGradlePluginApi::class) compilerOptions { freeCompilerArgs.add("-Xexpect-actual-classes") } @@ -111,6 +110,7 @@ kotlin { implementation(libs.statelyConcurrentCollections) implementation(libs.kmpIo) implementation(libs.kotlinx.io) + implementation(libs.kotlinSemver) } } val commonTest by getting { @@ -132,7 +132,7 @@ kotlin { val androidMain by getting { dependencies { implementation(libs.ktor.client.android) - // TODO: Add slf4f-api + // TODO: Add logging for Android } } val jvmTest by getting { @@ -142,6 +142,7 @@ kotlin { implementation(libs.kotest.runner.junit5) implementation(libs.kotlinx.coroutines.slf4j) implementation(libs.kotlinx.coroutines.debug) + implementation(libs.kotlinx.coroutines.test) implementation(libs.kotlinx.dataframe) // Required for report exports // Used to convert dataframe rows to serializable objects @@ -149,8 +150,6 @@ kotlin { implementation(libs.koin.test) implementation(libs.koin.test.junit5) - - implementation(libs.kotlinSemver) } } } diff --git a/temerity/src/androidMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.android.kt b/temerity/src/androidMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.android.kt index bb98a1e554585bbdc31135f62c2040b2caac7e78..5e7ed34b3306cda5702f8a937b282346d9560c59 100644 --- a/temerity/src/androidMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.android.kt +++ b/temerity/src/androidMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.android.kt @@ -18,7 +18,6 @@ package edu.ucsc.its.temerity.core import co.touchlab.kermit.Logger -import edu.ucsc.its.temerity.TemClientConfig internal actual fun createCommonLogger( tag: String?, diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/TemerityApi.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/api/TemerityApi.kt similarity index 93% rename from temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/TemerityApi.kt rename to temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/api/TemerityApi.kt index cf4110f1d1ccd178aaa4965b05b9222c872ee3b1..04473d6eec4f31b9f8f64b716e1ec7c82fc57f16 100644 --- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/TemerityApi.kt +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/api/TemerityApi.kt @@ -15,10 +15,12 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -package edu.ucsc.its.temerity +package edu.ucsc.its.temerity.api import edu.ucsc.its.temerity.extensions.datetime.DateTimeExt.currentDate import edu.ucsc.its.temerity.model.AuditLogEntry +import edu.ucsc.its.temerity.model.AuditLogSortOrder +import edu.ucsc.its.temerity.model.BuildVariant import edu.ucsc.its.temerity.model.Course import edu.ucsc.its.temerity.model.Device import edu.ucsc.its.temerity.model.DeviceRecordingSession @@ -261,7 +263,7 @@ public interface TemerityApi { public suspend fun getCachedUserRoles(refresh: Boolean = true): List<String> } -// Objects used by library consumers and re-implementations of the Temerity API spec +// Properties used by library consumers and re-implementations of the Temerity API spec public val AUDIT_LOG_REQUEST_DATE_FORMAT: DateTimeFormat<LocalDate> = LocalDate.Format { dayOfMonth() @@ -291,19 +293,6 @@ public val SCHEDULED_SESSION_DATE_FORMAT: DateTimeFormat<LocalDate> = LocalDate. year() } -/** - * Enum class used to define the sort order for audit log entries. - */ -public enum class AuditLogSortOrder { - NEW_FIRST, - OLD_FIRST, - ; - - public companion object { - public fun fromBoolean(value: Boolean): AuditLogSortOrder = if (value) NEW_FIRST else OLD_FIRST - } -} - /** * Sorts a list of [AuditLogEntry] objects by their creation date. * @param sortOrder The order in which to sort the entries. @@ -314,21 +303,3 @@ public fun ArrayList<AuditLogEntry>.sortByCreationDate(sortOrder: AuditLogSortOr AuditLogSortOrder.OLD_FIRST -> sortBy { auditLogEntry -> auditLogEntry.creationTimestamp } } } - -/** - * Enum class used to define the build variant of the library. - * For debugging purposes. - */ -public enum class BuildVariant(public val variant: String) { - RELEASE("RELEASE"), - DEBUG("DEBUG"), - ; - - internal companion object { - internal fun of(variant: String): BuildVariant = when (variant) { - "RELEASE" -> RELEASE - "DEBUG" -> DEBUG - else -> throw IllegalArgumentException("Invalid build variant: $variant") - } - } -} diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.kt index eae0e55d1ac6f140e52c3baaf6a6a7d94766233a..f92bc986b6b0051a6dea235dbc2997a761e30871 100644 --- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.kt +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.kt @@ -18,7 +18,6 @@ package edu.ucsc.its.temerity.core import co.touchlab.kermit.Logger -import edu.ucsc.its.temerity.TemClientConfig internal object LoggerFactory { internal fun createLogger(tag: String?, config: TemClientConfig? = null, supportKtxNotebook: Boolean = false) = createCommonLogger(tag = tag, config = config, supportKtxNotebook = supportKtxNotebook) diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/TemClientConfig.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/TemClientConfig.kt similarity index 88% rename from temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/TemClientConfig.kt rename to temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/TemClientConfig.kt index 491f006cd23c7ba5e4e3b89a6dc90309ea65ef9b..4b2ef17f062902a894325c97f44f6ee2fdd809fa 100644 --- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/TemClientConfig.kt +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/TemClientConfig.kt @@ -15,9 +15,8 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -package edu.ucsc.its.temerity +package edu.ucsc.its.temerity.core -import edu.ucsc.its.temerity.core.TemDsl import io.ktor.client.HttpClient import io.ktor.client.HttpClientConfig import io.ktor.client.engine.HttpClientEngineConfig @@ -35,8 +34,8 @@ import kotlin.time.Duration * * @property optExpectSuccess Specifies whether to expect successful responses by default * @property optUseWebTimeout Specifies whether to use a timeout other than default for web requests - * @property optWebTimeoutDuration Specifies the timeout [Duration] to use when making web requests - * @property optCacheTimeoutDuration Specifies the timeout [Duration] to use when storing cache entries. Affects keep-alive time for cached instance data like user role type fields. + * @property optWebTimeoutDuration Specifies the timeout [kotlin.time.Duration] to use when making web requests + * @property optCacheTimeoutDuration Specifies the timeout [kotlin.time.Duration] to use when storing cache entries. Affects keep-alive time for cached instance data like user role type fields. * @property optThreadCount Specifies the number of threads to use for each client instance. Defaults to 2 at a minimum */ @TemDsl @@ -80,7 +79,7 @@ public class TemClientConfig { } /** - * Creates an custom [HttpClient] with the specified [HttpClientEngineFactory] and optional [block] configuration. + * Creates an custom [HttpClient] with the specified [io.ktor.client.engine.HttpClientEngineFactory] and optional [block] configuration. * Note that the Temerity config will be added afterwards. */ public fun <T : HttpClientEngineConfig> httpClient( diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/Temerity.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/Temerity.kt index 308ae29be437764f40e0f99a025f33276881520e..97261a23863e7f1c0bd6739d798f72434e1062c8 100644 --- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/Temerity.kt +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/core/Temerity.kt @@ -24,17 +24,14 @@ import com.skydoves.sandwich.getOrThrow import com.skydoves.sandwich.ktor.executeApiResponse import com.skydoves.sandwich.ktor.statusCode import com.skydoves.sandwich.onSuccess -import edu.ucsc.its.temerity.AuditLogSortOrder -import edu.ucsc.its.temerity.AuditLogSortOrder.NEW_FIRST import edu.ucsc.its.temerity.BuildConfig import edu.ucsc.its.temerity.BuildConfig.BUILD_DATE import edu.ucsc.its.temerity.BuildConfig.BUILD_TIMEZONE import edu.ucsc.its.temerity.BuildConfig.BUILD_VARIANT import edu.ucsc.its.temerity.BuildConfig.LIB_VERSION -import edu.ucsc.its.temerity.BuildVariant -import edu.ucsc.its.temerity.TemClientConfig -import edu.ucsc.its.temerity.TemerityApi import edu.ucsc.its.temerity.api.PlatformApi +import edu.ucsc.its.temerity.api.TemerityApi +import edu.ucsc.its.temerity.api.sortByCreationDate import edu.ucsc.its.temerity.core.Temerity.Companion.DEFAULT_WEB_TIMEOUT import edu.ucsc.its.temerity.di.LibModule.libModule import edu.ucsc.its.temerity.di.platformModule @@ -43,6 +40,9 @@ import edu.ucsc.its.temerity.extensions.coroutines.createJobScope import edu.ucsc.its.temerity.extensions.time.applyAuditLogFormat import edu.ucsc.its.temerity.extensions.time.applyScheduledSessionDateFormat import edu.ucsc.its.temerity.model.AuditLogEntry +import edu.ucsc.its.temerity.model.AuditLogSortOrder +import edu.ucsc.its.temerity.model.AuditLogSortOrder.NEW_FIRST +import edu.ucsc.its.temerity.model.BuildVariant import edu.ucsc.its.temerity.model.Course import edu.ucsc.its.temerity.model.Device import edu.ucsc.its.temerity.model.DeviceRecordingSession @@ -54,7 +54,6 @@ import edu.ucsc.its.temerity.model.User import edu.ucsc.its.temerity.model.UserGroup import edu.ucsc.its.temerity.model.UserRecordingSession import edu.ucsc.its.temerity.model.UserUpdate -import edu.ucsc.its.temerity.sortByCreationDate import io.ktor.client.HttpClient import io.ktor.client.HttpClientConfig import io.ktor.client.engine.HttpClientEngine @@ -112,6 +111,7 @@ public class Temerity internal constructor( * Constructor for Temerity * @param temApiToken The [String] API token to use for the Temerity client */ + @Suppress("Unused") internal constructor(temApiToken: String) : this(TemClientConfig.withToken(temApiToken)) /** @@ -300,80 +300,72 @@ public class Temerity internal constructor( } } - public override suspend fun getUsers(): List<User> = - withContext(libraryCoroutineDispatcher) { - val userRequest = platformApi.getUsers() - val response = userRequest.executeApiResponse<String>() - decodeResponseCatching(response) - } - - public override suspend fun getUser(userId: Long): User = - withContext(libraryCoroutineDispatcher) { - val returnedUsers = getUsers() - returnedUsers.first { it.userId == userId } - } + public override suspend fun getUsers(): List<User> = withContext(libraryCoroutineDispatcher) { + val userRequest = platformApi.getUsers() + val response = userRequest.executeApiResponse<String>() + decodeResponseCatching(response) + } - public override suspend fun createUser(primaryIdentifier: String, newUser: NewUser): HttpResponse = - withContext(libraryCoroutineDispatcher) { - val validationMessage = newUser.validate() - if (validationMessage != "Valid") { - error(validationMessage) - } else { - val serializedNewUser = createJobScope(jsonProcessingDispatcher).run { - json.encodeToString(newUser) - } - val request = platformApi.createUser(primaryIdentifier, serializedNewUser) - request.getOrThrow() - } - } + public override suspend fun getUser(userId: Long): User = withContext(libraryCoroutineDispatcher) { + val returnedUsers = getUsers() + returnedUsers.first { it.userId == userId } + } - public override suspend fun updateUser(userId: Long, userUpdate: UserUpdate): HttpResponse = - withContext(libraryCoroutineDispatcher) { - val serializedUpdatedUser = createJobScope(jsonProcessingDispatcher).run { - json.encodeToString(userUpdate) + public override suspend fun createUser(primaryIdentifier: String, newUser: NewUser): HttpResponse = withContext(libraryCoroutineDispatcher) { + val validationMessage = newUser.validate() + if (validationMessage != "Valid") { + error(validationMessage) + } else { + val serializedNewUser = createJobScope(jsonProcessingDispatcher).run { + json.encodeToString(newUser) } - val request = platformApi.setUser(userId, serializedUpdatedUser) + val request = platformApi.createUser(primaryIdentifier, serializedNewUser) request.getOrThrow() } + } - public override suspend fun deleteUser(userId: Long): HttpResponse = - withContext(libraryCoroutineDispatcher) { - val request = platformApi.deleteUser(userId) - request.getOrThrow() + public override suspend fun updateUser(userId: Long, userUpdate: UserUpdate): HttpResponse = withContext(libraryCoroutineDispatcher) { + val serializedUpdatedUser = createJobScope(jsonProcessingDispatcher).run { + json.encodeToString(userUpdate) } + val request = platformApi.setUser(userId, serializedUpdatedUser) + request.getOrThrow() + } - public override suspend fun refreshCachedUserRoles(): List<String> = - withContext(libraryCoroutineDispatcher) { - val usersRequest = platformApi.getUsers() - val returnedUsersResponse = usersRequest.executeApiResponse<String>().getOrThrow() - val returnedUserList = createJobScope(jsonProcessingDispatcher).run { - json.decodeFromString<List<User>>(returnedUsersResponse) - } - val roleTypes = returnedUserList.map { it.userType }.distinct() - cachedUserRoleList.clear() - roleTypes.forEach { - cachedUserRoleList[it.hashCode()] = it - } - roleTypes - } + public override suspend fun deleteUser(userId: Long): HttpResponse = withContext(libraryCoroutineDispatcher) { + val request = platformApi.deleteUser(userId) + request.getOrThrow() + } - public override suspend fun getCachedUserRoles(refresh: Boolean): List<String> = - withContext(libraryCoroutineDispatcher) { - if (refresh) { - refreshCachedUserRoles() - } - val returnedUserRoles = cachedUserRoleList.values.toList() - returnedUserRoles.ifEmpty { - refreshCachedUserRoles() - } + public override suspend fun refreshCachedUserRoles(): List<String> = withContext(libraryCoroutineDispatcher) { + val usersRequest = platformApi.getUsers() + val returnedUsersResponse = usersRequest.executeApiResponse<String>().getOrThrow() + val returnedUserList = createJobScope(jsonProcessingDispatcher).run { + json.decodeFromString<List<User>>(returnedUsersResponse) + } + val roleTypes = returnedUserList.map { it.userType }.distinct() + cachedUserRoleList.clear() + roleTypes.forEach { + cachedUserRoleList.put(it.hashCode(), it) } + roleTypes + } - public override suspend fun getUserGroups(userId: Long): List<UserGroup> = - withContext(libraryCoroutineDispatcher) { - val request = platformApi.getUserGroups(userId) - val response = request.executeApiResponse<String>() - decodeResponseCatching(response) + public override suspend fun getCachedUserRoles(refresh: Boolean): List<String> = withContext(libraryCoroutineDispatcher) { + if (refresh) { + refreshCachedUserRoles() } + val returnedUserRoles = cachedUserRoleList.values.toList() + returnedUserRoles.ifEmpty { + refreshCachedUserRoles() + } + } + + public override suspend fun getUserGroups(userId: Long): List<UserGroup> = withContext(libraryCoroutineDispatcher) { + val request = platformApi.getUserGroups(userId) + val response = request.executeApiResponse<String>() + decodeResponseCatching(response) + } public override suspend fun getUserGroupsOwned(userId: Long): List<UserGroup> = withContext(libraryCoroutineDispatcher) { val request = platformApi.getUserGroupsOwned(userId) diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/di/LibModule.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/di/LibModule.kt index d6fbe81f556afa013b7acc10362d538977c9b6a3..6f236350e0fcb19ecc2dc3ed45f637d8af302c6b 100644 --- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/di/LibModule.kt +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/di/LibModule.kt @@ -19,9 +19,9 @@ package edu.ucsc.its.temerity.di import com.skydoves.sandwich.ktorfit.ApiResponseConverterFactory import de.jensklingenberg.ktorfit.ktorfit -import edu.ucsc.its.temerity.TemClientConfig import edu.ucsc.its.temerity.api.PlatformApi import edu.ucsc.its.temerity.api.createPlatformApi +import edu.ucsc.its.temerity.core.TemClientConfig import edu.ucsc.its.temerity.core.Temerity.Companion.createLogger import edu.ucsc.its.temerity.core.buildHttpClient import edu.ucsc.its.temerity.extensions.coroutines.createLibraryScope diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/datetime/DateTimeExt.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/datetime/DateTimeExt.kt index f10a839c34c9e7dffb0ccc0f5ba63bc6c1ff3ef4..6251f96c2fcf2df7952b86e1aac37be3fd86dcaf 100644 --- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/datetime/DateTimeExt.kt +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/datetime/DateTimeExt.kt @@ -15,6 +15,8 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +@file:Suppress("MemberVisibilityCanBePrivate") + package edu.ucsc.its.temerity.extensions.datetime import kotlinx.datetime.Clock diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/log/FilesystemLogWriter.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/log/FilesystemLogWriter.kt index baf4c7d887f311954ef2b7cd7fe29e6472e57d47..6389ef182bcf2867d8aebb4521ff7cfca8ed1b5e 100644 --- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/log/FilesystemLogWriter.kt +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/log/FilesystemLogWriter.kt @@ -56,11 +56,10 @@ internal class FilesystemLogWriter internal constructor( } companion object { - operator fun invoke(block: Builder.() -> Unit) = - with(FilesystemLogWriterBuilder()) { - block(this) - build() - } + operator fun invoke(block: Builder.() -> Unit) = with(FilesystemLogWriterBuilder()) { + block(this) + build() + } } interface Builder { diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/DateTimeExtensions.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/DateTimeExtensions.kt index 7ed64322b5d44f0fd76206566d0b49c49e09f028..c007a54eb15a376260a7090304095527df34c294 100644 --- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/DateTimeExtensions.kt +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/DateTimeExtensions.kt @@ -17,8 +17,8 @@ */ package edu.ucsc.its.temerity.extensions.time -import edu.ucsc.its.temerity.AUDIT_LOG_REQUEST_DATE_FORMAT -import edu.ucsc.its.temerity.SCHEDULED_SESSION_DATE_FORMAT +import edu.ucsc.its.temerity.api.AUDIT_LOG_REQUEST_DATE_FORMAT +import edu.ucsc.its.temerity.api.SCHEDULED_SESSION_DATE_FORMAT import kotlinx.datetime.LocalDate import kotlinx.datetime.format @@ -26,12 +26,10 @@ import kotlinx.datetime.format * Formats a [LocalDate] object as a string in the format used by the YuJa API for audit log requests. * @return A string representation of the date in the format "dd/MM/yyyy". */ -internal fun LocalDate.applyAuditLogFormat(): String = - format(AUDIT_LOG_REQUEST_DATE_FORMAT) +internal fun LocalDate.applyAuditLogFormat(): String = format(AUDIT_LOG_REQUEST_DATE_FORMAT) /** * Formats a [LocalDate] object as a string in the format used by the YuJa API for sessions. * @return A string representation of the date in the format "dd-MM-yyyy". */ -internal fun LocalDate.applyScheduledSessionDateFormat(): String = - format(SCHEDULED_SESSION_DATE_FORMAT) +internal fun LocalDate.applyScheduledSessionDateFormat(): String = format(SCHEDULED_SESSION_DATE_FORMAT) diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/LocalDateTimeSerializer.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/LocalDateTimeSerializer.kt index 96695252d3ce72acaa684e88cb9f2a96b62533bb..030e4ecb6b50206ee0024b2ece33b3be39a6bd99 100644 --- a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/LocalDateTimeSerializer.kt +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/extensions/time/LocalDateTimeSerializer.kt @@ -17,7 +17,7 @@ */ package edu.ucsc.its.temerity.extensions.time -import edu.ucsc.its.temerity.AUDIT_LOG_TIMESTAMP_FORMAT +import edu.ucsc.its.temerity.api.AUDIT_LOG_TIMESTAMP_FORMAT import edu.ucsc.its.temerity.model.AuditLogEntry import kotlinx.datetime.LocalDateTime import kotlinx.datetime.format diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/model/AuditLogSortOrder.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/model/AuditLogSortOrder.kt new file mode 100644 index 0000000000000000000000000000000000000000..71fd0780714ddddb0726e9a505d618e2ec205280 --- /dev/null +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/model/AuditLogSortOrder.kt @@ -0,0 +1,31 @@ +/* + * Designed and developed in 2022-2024 by William Walker (wnwalker@ucsc.edu) + * Copyright 2022-2024 The Regents of the University of California. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package edu.ucsc.its.temerity.model + +/** + * Enum class used to define the sort order for audit log entries. + */ +public enum class AuditLogSortOrder { + NEW_FIRST, + OLD_FIRST, + ; + + public companion object { + public fun fromBoolean(value: Boolean): AuditLogSortOrder = if (value) NEW_FIRST else OLD_FIRST + } +} diff --git a/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/model/BuildVariant.kt b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/model/BuildVariant.kt new file mode 100644 index 0000000000000000000000000000000000000000..9276003d9f0a61ba0aa0e39d586b98f9402dcd2c --- /dev/null +++ b/temerity/src/commonMain/kotlin/edu/ucsc/its/temerity/model/BuildVariant.kt @@ -0,0 +1,36 @@ +/* + * Designed and developed in 2022-2024 by William Walker (wnwalker@ucsc.edu) + * Copyright 2022-2024 The Regents of the University of California. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package edu.ucsc.its.temerity.model + +/** + * Enum class used to define the build variant of the library. + * For debugging purposes. + */ +public enum class BuildVariant(public val variant: String) { + RELEASE("RELEASE"), + DEBUG("DEBUG"), + ; + + internal companion object { + internal fun of(variant: String): BuildVariant = when (variant) { + "RELEASE" -> RELEASE + "DEBUG" -> DEBUG + else -> throw IllegalArgumentException("Invalid build variant: $variant") + } + } +} diff --git a/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.jvm.kt b/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.jvm.kt index 9cb64854d8f7a4c32115b92efa2ad55f5cc893cd..a076421f0c82a0085d521c473b7c80225a3a1add 100644 --- a/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.jvm.kt +++ b/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/core/LoggerFactory.jvm.kt @@ -23,7 +23,6 @@ import co.touchlab.kermit.NoTagFormatter import co.touchlab.kermit.Severity.Debug import co.touchlab.kermit.loggerConfigInit import co.touchlab.kermit.platformLogWriter -import edu.ucsc.its.temerity.TemClientConfig internal actual fun createCommonLogger(tag: String?, config: TemClientConfig?, supportKtxNotebook: Boolean): Logger = when (config) { null -> { diff --git a/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/extensions/log/ColorFormatter.kt b/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/extensions/log/ColorFormatter.kt index d11bb8f44f4a9c7949808ddded9343f09f56e290..1b15cc11b595bf9a03b2f503136c03539f55395e 100644 --- a/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/extensions/log/ColorFormatter.kt +++ b/temerity/src/jvmMain/kotlin/edu/ucsc/its/temerity/extensions/log/ColorFormatter.kt @@ -33,18 +33,15 @@ private fun withBrightColor(messageStringFormatter: MessageStringFormatter = Def internal class ColorFormatter( messageStringFormatter: MessageStringFormatter = DefaultFormatter, ) : WrappingFormatter(messageStringFormatter) { - override fun prefix(severity: Severity?, tag: Tag?, message: Message) = - severity?.toAnsiColor() ?: "" + override fun prefix(severity: Severity?, tag: Tag?, message: Message) = severity?.toAnsiColor() ?: "" - override fun suffix(severity: Severity?, tag: Tag?, message: Message) = - resetColor() + override fun suffix(severity: Severity?, tag: Tag?, message: Message) = resetColor() } internal class BrightColorFormatter( messageStringFormatter: MessageStringFormatter = DefaultFormatter, ) : WrappingFormatter(messageStringFormatter) { - override fun prefix(severity: Severity?, tag: Tag?, message: Message) = - severity?.toBrightAnsiColor() ?: "" + override fun prefix(severity: Severity?, tag: Tag?, message: Message) = severity?.toBrightAnsiColor() ?: "" override fun suffix(severity: Severity?, tag: Tag?, message: Message) = resetColor() } diff --git a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevDeviceApiTests.kt b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevDeviceApiTests.kt index efec7524c820719f11fafb4a3f51598cb40545d1..4c6b0b83626df5ed163ccccab912786e1a3196ba 100644 --- a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevDeviceApiTests.kt +++ b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevDeviceApiTests.kt @@ -17,17 +17,10 @@ */ package edu.ucsc.its.temerity.test -import edu.ucsc.its.temerity.core.Temerity import kotlinx.coroutines.runBlocking -private class DevDeviceApiTests : TemerityFunSpec() { +private class DevDeviceApiTests : TemDevFunSpec() { init { - beforeTest { - testTemerity = Temerity { - configureDevEnvironment(dotenv) - } - } - test("4.2.1 - PlatformClient can fetch a list of devices") { runBlocking { val returnedDevices = testTemerity.getDevices() diff --git a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevGroupApiTests.kt b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevGroupApiTests.kt index 521acc2a60bb99349dfffc7102a1e740e8bb94f8..41be5fea20aa996d8fef57a874c3e3b210090276 100644 --- a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevGroupApiTests.kt +++ b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevGroupApiTests.kt @@ -17,17 +17,10 @@ */ package edu.ucsc.its.temerity.test -import edu.ucsc.its.temerity.core.Temerity import kotlinx.coroutines.runBlocking -private class DevGroupApiTests : TemerityFunSpec() { +private class DevGroupApiTests : TemDevFunSpec() { init { - beforeTest { - testTemerity = Temerity { - configureDevEnvironment(dotenv) - } - } - test("2.2.1 - PlatformClient can fetch a list of groups") { runBlocking { val returnedGroups = testTemerity.getGroups() diff --git a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevUserApiTests.kt b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevUserApiTests.kt index 43885a0c174e9dabb24a2fffc3bb777ff28d2e0e..07c2e7e8027e19d3444265dc92a94a14b61bc3fc 100644 --- a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevUserApiTests.kt +++ b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevUserApiTests.kt @@ -20,7 +20,6 @@ package edu.ucsc.its.temerity.test import co.touchlab.kermit.Logger import com.skydoves.sandwich.StatusCode import com.skydoves.sandwich.ktor.getStatusCode -import edu.ucsc.its.temerity.core.Temerity import edu.ucsc.its.temerity.model.NewUser import edu.ucsc.its.temerity.model.UserUpdate import io.kotest.common.ExperimentalKotest @@ -28,17 +27,11 @@ import io.kotest.matchers.shouldBe import io.ktor.http.HttpStatusCode import kotlinx.coroutines.runBlocking -private class DevUserApiTests : TemerityFunSpec() { +private class DevUserApiTests : TemDevFunSpec() { init { @OptIn(ExperimentalKotest::class) blockingTest = true - beforeTest { - testTemerity = Temerity { - configureDevEnvironment(dotenv) - } - } - test("1.2.1 - PlatformClient can fetch a list of users") { runBlocking { val returnedUsers = testTemerity.getUsers() diff --git a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevUtilityTests.kt b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevUtilityTests.kt index aa3e10bdf99fe3a9071122dffa4839d8bd57ed9b..64b5cbc5ff3176921774331c3982695df4e03c86 100644 --- a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevUtilityTests.kt +++ b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/DevUtilityTests.kt @@ -15,25 +15,21 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +@file:OptIn(KotestInternal::class) + package edu.ucsc.its.temerity.test -import edu.ucsc.its.temerity.core.Temerity import io.kotest.common.ExperimentalKotest +import io.kotest.common.KotestInternal import io.kotest.engine.runBlocking import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes -private class DevUtilityTests : TemerityFunSpec() { - +private class DevUtilityTests : TemDevFunSpec() { init { @OptIn(ExperimentalKotest::class) blockingTest = true - beforeTest { - testTemerity = Temerity { - configureDevEnvironment(dotenv) - } - } timeout = 5.minutes.inWholeMilliseconds test("Delete all Canvas Test Student Users").config(blockingTest = true, timeout = 12.hours) { diff --git a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ModelTests.kt b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ModelTests.kt index cec8e213c84032918802ac2e63629c41aa607c97..7ef1162569b947f7fe90f38ae471a216b2e198bb 100644 --- a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ModelTests.kt +++ b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ModelTests.kt @@ -21,7 +21,6 @@ import edu.ucsc.its.temerity.model.EventType import io.kotest.common.ExperimentalKotest private class ModelTests : TemerityFunSpec() { - init { @OptIn(ExperimentalKotest::class) blockingTest = true diff --git a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ProdReportTests.kt b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ProdReportTests.kt index 9589c8b017e9964c2fd66982fdc5d5f256efb1c9..87a59821f0318ea4551199a3d0f5a614608a5df0 100644 --- a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ProdReportTests.kt +++ b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ProdReportTests.kt @@ -17,8 +17,7 @@ */ package edu.ucsc.its.temerity.test -import edu.ucsc.its.temerity.AuditLogSortOrder.NEW_FIRST -import edu.ucsc.its.temerity.core.Temerity +import edu.ucsc.its.temerity.model.AuditLogSortOrder.NEW_FIRST import edu.ucsc.its.temerity.model.EventType.AUTOMATED_SESSION_FAILED_TO_START import edu.ucsc.its.temerity.model.EventType.AUTOMATED_SESSION_MONITOR import edu.ucsc.its.temerity.model.EventType.CAPTURE_ERROR @@ -47,7 +46,7 @@ import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid @OptIn(ExperimentalUuidApi::class) -private class ProdReportTests : TemerityFunSpec() { +private class ProdReportTests : TemProdFunSpec() { init { @OptIn(ExperimentalKotest::class) @@ -55,12 +54,6 @@ private class ProdReportTests : TemerityFunSpec() { val today = currentDate() - beforeTest { - testTemerity = Temerity { - configureProdEnvironment(dotenv) - } - } - timeout = 5.minutes.inWholeMilliseconds test("PlatformClient can fetch a list of devices") { diff --git a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ProdUtilityTests.kt b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ProdUtilityTests.kt index 632ec391b6437708d66aeb8e13061be3e9c6426d..4dfbc5f4ee81950826933234163307c86fa64412 100644 --- a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ProdUtilityTests.kt +++ b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/ProdUtilityTests.kt @@ -15,26 +15,22 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +@file:OptIn(KotestInternal::class) + package edu.ucsc.its.temerity.test -import edu.ucsc.its.temerity.core.Temerity import io.kotest.common.ExperimentalKotest +import io.kotest.common.KotestInternal import io.kotest.engine.runBlocking import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes -private class ProdUtilityTests : TemerityFunSpec() { +private class ProdUtilityTests : TemProdFunSpec() { init { @OptIn(ExperimentalKotest::class) blockingTest = true - beforeTest { - testTemerity = Temerity { - configureProdEnvironment(dotenv) - } - } - timeout = 5.minutes.inWholeMilliseconds test("Delete all Canvas Test Student Users").config(blockingTest = true, timeout = 12.hours) { diff --git a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/TemerityDevTest.kt b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/TemerityDevTest.kt index dd216fc4d88704d0742311d2397f67286a3302c7..c3c4f8cc5c9e9878129c86fe1ec9c7bd5c771d77 100644 --- a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/TemerityDevTest.kt +++ b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/TemerityDevTest.kt @@ -20,8 +20,8 @@ package edu.ucsc.its.temerity.test import co.touchlab.kermit.Logger import com.skydoves.sandwich.StatusCode import com.skydoves.sandwich.ktor.getStatusCode -import edu.ucsc.its.temerity.AuditLogSortOrder.NEW_FIRST import edu.ucsc.its.temerity.core.Temerity +import edu.ucsc.its.temerity.model.AuditLogSortOrder.NEW_FIRST import edu.ucsc.its.temerity.model.EventType.NEW_LOG_IN import edu.ucsc.its.temerity.model.NewUser import edu.ucsc.its.temerity.util.currentDate @@ -42,17 +42,11 @@ import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid @OptIn(ExperimentalUuidApi::class) -private class TemerityDevTest : TemerityFunSpec() { +private class TemerityDevTest : TemDevFunSpec() { init { @OptIn(ExperimentalKotest::class) blockingTest = true - beforeTest { - testTemerity = Temerity { - configureDevEnvironment(dotenv) - } - } - test("Temerity client returns a correctly-formatted version String") { val returnedVersion = testTemerity.version.toVersion() kermit.d { "Returned client version: $returnedVersion" } diff --git a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/Util.kt b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/Util.kt index 299c20b2fa6bc737fe5c8a424bff81f6d6256db8..f2c8893b9ef6c7c056651f846f405426c55c93d9 100644 --- a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/Util.kt +++ b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/Util.kt @@ -15,15 +15,19 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +@file:OptIn(KotestInternal::class) + package edu.ucsc.its.temerity.test -import edu.ucsc.its.temerity.TemClientConfig +import edu.ucsc.its.temerity.core.TemClientConfig import edu.ucsc.its.temerity.core.Temerity import edu.ucsc.its.temerity.core.Temerity.Companion.createLogger import io.github.cdimascio.dotenv.Configuration import io.github.cdimascio.dotenv.Dotenv import io.github.cdimascio.dotenv.dotenv +import io.kotest.common.KotestInternal import io.kotest.core.extensions.TestCaseExtension +import io.kotest.core.spec.Spec import io.kotest.core.spec.style.FunSpec import io.kotest.core.test.TestCase import io.kotest.core.test.TestResult @@ -68,11 +72,10 @@ private class KoinExtensionImpl( execute(testCase) } - private fun TestCase.isApplicable() = - mode == KoinLifecycleMode.Root && - isRootTest() || - mode == KoinLifecycleMode.Test && - type == TestType.Test + private fun TestCase.isApplicable() = mode == KoinLifecycleMode.Root && + isRootTest() || + mode == KoinLifecycleMode.Test && + type == TestType.Test } enum class KoinLifecycleMode { @@ -104,3 +107,21 @@ internal abstract class TemerityFunSpec : FunSpec() { internal val kermit: KermitLogger = createLogger() internal lateinit var testTemerity: Temerity } + +internal abstract class TemDevFunSpec : TemerityFunSpec() { + override suspend fun beforeSpec(spec: Spec) { + testTemerity = Temerity { + configureDevEnvironment(dotenv) + } + super.beforeSpec(spec) + } +} + +internal abstract class TemProdFunSpec : TemerityFunSpec() { + override suspend fun beforeSpec(spec: Spec) { + testTemerity = Temerity { + configureProdEnvironment(dotenv) + } + super.beforeSpec(spec) + } +} diff --git a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/konsist/TestKonsistTest.kt b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/konsist/TestKonsistTest.kt index b8920bc3153e0ab7b066652e0464589d0b6060c1..e988c97a74fe019b386df1321ab401a7d2c87427 100644 --- a/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/konsist/TestKonsistTest.kt +++ b/temerity/src/jvmTest/kotlin/edu/ucsc/its/temerity/test/konsist/TestKonsistTest.kt @@ -27,7 +27,7 @@ class TestKonsistTest : FunSpec() { init { - beforeTest { + beforeSpec { konsist = Konsist.scopeFromProject() }