diff --git a/build.gradle.kts b/build.gradle.kts index 6dc2001..a9bf26e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile allprojects { repositories { + mavenLocal() mavenCentral() } } @@ -22,11 +23,17 @@ subprojects { tasks.withType { compilerOptions { - apiVersion = KotlinVersion.KOTLIN_2_2 - languageVersion = KotlinVersion.KOTLIN_2_2 + languageVersion = KotlinVersion.KOTLIN_2_0 + apiVersion = KotlinVersion.KOTLIN_2_0 jvmTarget = JvmTarget.JVM_21 progressiveMode = true freeCompilerArgs.add("-Xjsr305=strict") } } } + +tasks.named("build") { + dependsOn(":gameyfin:uberJar") +} + +extra.set("pluginDir", rootProject.layout.buildDirectory.get().asFile.resolve("plugins")) \ No newline at end of file diff --git a/gameyfin/build.gradle.kts b/gameyfin/build.gradle.kts index 8f66395..4a49985 100644 --- a/gameyfin/build.gradle.kts +++ b/gameyfin/build.gradle.kts @@ -1,4 +1,6 @@ group = "de.grimsi" +val pluginDir: File by rootProject.extra +val appMainClass = "de.grimsi.gameyfin.GameyfinApplication" plugins { id("org.springframework.boot") @@ -7,21 +9,19 @@ plugins { kotlin("jvm") kotlin("plugin.spring") kotlin("plugin.jpa") - java + application + id("com.google.devtools.ksp") version "2.0.20-1.0.24" +} + +application { + mainClass.set(appMainClass) } allOpen { annotations("javax.persistence.Entity", "javax.persistence.MappedSuperclass", "javax.persistence.Embedabble") } -configurations { - compileOnly { - extendsFrom(configurations.annotationProcessor.get()) - } -} - repositories { - mavenCentral() maven { setUrl("https://maven.vaadin.com/vaadin-addons") } @@ -68,6 +68,7 @@ dependencies { // Plugins implementation(project(":plugin-api")) + ksp("care.better.pf4j:pf4j-kotlin-symbol-processing:2.0.20-1.0.1") // Development developmentOnly("org.springframework.boot:spring-boot-devtools") @@ -85,16 +86,25 @@ dependencyManagement { } } -// Task to copy the bundled plugin JARs to the plugins directory -val copyPlugins by tasks.registering(Copy::class) { - // Directory where plugins will be copied - val pluginsDir = layout.buildDirectory.dir("plugins") - from(project(":plugins:igdb").tasks.named("jar")) - into(pluginsDir) +tasks.named("run") { + systemProperty("pf4j.pluginsDir", pluginDir.absolutePath) } -tasks.named("compileKotlin") { - dependsOn(copyPlugins) +tasks.register("uberJar") { + dependsOn(tasks.named("compileKotlin")) + archiveClassifier.set("uber") + + from(sourceSets.main.get().output) + + dependsOn(configurations.runtimeClasspath) + from({ + configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) } + }) + manifest { + attributes["Main-Class"] = appMainClass + } + + archiveBaseName.set(project.name) } tasks.withType { diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/PluginManagerConfig.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/PluginManagerConfig.kt index 6b051b0..def64de 100644 --- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/PluginManagerConfig.kt +++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/PluginManagerConfig.kt @@ -1,14 +1,25 @@ package de.grimsi.gameyfin.core +import io.github.oshai.kotlinlogging.KotlinLogging import org.pf4j.DefaultPluginManager +import org.springframework.boot.context.event.ApplicationReadyEvent import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.context.event.EventListener import java.nio.file.Path @Configuration class PluginManagerConfig { + private val log = KotlinLogging.logger {} private val pluginPath = Path.of("plugins") @Bean fun pluginManager() = DefaultPluginManager(pluginPath) + + @EventListener(ApplicationReadyEvent::class) + fun loadedPlugins() { + pluginManager().loadPlugins() + pluginManager().startPlugins() + log.info { "Loaded plugins: ${pluginManager().plugins.map { it.pluginId }}" } + } } \ No newline at end of file diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameService.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameService.kt index 5f8c4ab..b1ccde6 100644 --- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameService.kt +++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameService.kt @@ -1,10 +1,8 @@ package de.grimsi.gameyfin.games -import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataPlugin +import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataFetcher import io.github.oshai.kotlinlogging.KotlinLogging import org.pf4j.PluginManager -import org.springframework.boot.context.event.ApplicationReadyEvent -import org.springframework.context.event.EventListener import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import java.nio.file.Path @@ -16,15 +14,8 @@ class GameService( ) { private val log = KotlinLogging.logger {} - private val metadataPlugins: List - get() = pluginManager.getExtensions(GameMetadataPlugin::class.java) - - @EventListener(ApplicationReadyEvent::class) - fun loadedPlugins() { - pluginManager.loadPlugins() - pluginManager.startPlugins() - log.info { "Loaded metadata plugins: ${metadataPlugins.map { it::class.simpleName }}" } - } + private val metadataPlugins: List + get() = pluginManager.getExtensions(GameMetadataFetcher::class.java) fun createOrUpdate(game: Game): Game { return gameRepository.save(game) diff --git a/gradle.properties b/gradle.properties index 3df6128..113ed04 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,11 @@ +# Plugin versions kotlinVersion=2.0.20 vaadinVersion=24.4.13 springBootVersion=3.3.4 springCloudVersion=2023.0.3 springDependencyManagementVersion=1.1.6 pf4jVersion=3.12.0 -pf4jSpringVersion=0.9.0 \ No newline at end of file +pf4jSpringVersion=0.9.0 +# Annotation processor settings +kapt.use.k2=true +ksp.useKSP2=true \ No newline at end of file diff --git a/plugin-api/build.gradle.kts b/plugin-api/build.gradle.kts index 3aa6bdc..48a1835 100644 --- a/plugin-api/build.gradle.kts +++ b/plugin-api/build.gradle.kts @@ -12,6 +12,8 @@ dependencies { // PF4J (shared) api("org.pf4j:pf4j:${rootProject.extra["pf4jVersion"]}") + implementation(kotlin("stdlib")) + // Test dependencies testImplementation(kotlin("test")) } diff --git a/plugin-api/src/main/kotlin/de/grimsi/gameyfin/pluginapi/gamemetadata/GameMetadataPlugin.kt b/plugin-api/src/main/kotlin/de/grimsi/gameyfin/pluginapi/gamemetadata/GameMetadataFetcher.kt similarity index 82% rename from plugin-api/src/main/kotlin/de/grimsi/gameyfin/pluginapi/gamemetadata/GameMetadataPlugin.kt rename to plugin-api/src/main/kotlin/de/grimsi/gameyfin/pluginapi/gamemetadata/GameMetadataFetcher.kt index cecf755..8d55f32 100644 --- a/plugin-api/src/main/kotlin/de/grimsi/gameyfin/pluginapi/gamemetadata/GameMetadataPlugin.kt +++ b/plugin-api/src/main/kotlin/de/grimsi/gameyfin/pluginapi/gamemetadata/GameMetadataFetcher.kt @@ -2,7 +2,7 @@ package de.grimsi.gameyfin.pluginapi.gamemetadata import org.pf4j.ExtensionPoint -interface GameMetadataPlugin : ExtensionPoint { +interface GameMetadataFetcher : ExtensionPoint { fun getConfig(): Map fun setConfig(config: Map) fun fetchMetadata(gameId: String): GameMetadata diff --git a/plugins/build.gradle.kts b/plugins/build.gradle.kts new file mode 100644 index 0000000..0135936 --- /dev/null +++ b/plugins/build.gradle.kts @@ -0,0 +1,55 @@ +val pluginDir: File by rootProject.extra + +plugins { + kotlin("jvm") +} + +subprojects { + apply(plugin = "org.jetbrains.kotlin.jvm") + + dependencies { + implementation(project(":plugin-api")) + } + + tasks.register("plugin") { + archiveBaseName.set("plugin-${project.name}") + + // first taking the classes generated by the jar task + into("classes") { + with(tasks.named("jar").get()) + } + // and then we also need to include any libraries that are needed by the plugin + dependsOn(configurations.runtimeClasspath) + into("lib") { + from({ + configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") } + }) + } + archiveExtension.set("jar") + } + + tasks.register("assemblePlugin") { + from(project.tasks.named("plugin")) + into(pluginDir) + } + + tasks.jar { + manifest { + from("./src/main/resources/MANIFEST.MF") + } + } + + tasks.named("build") { + dependsOn(tasks.named("plugin")) + } +} + +tasks.register("assemblePlugins") { + dependsOn(subprojects.map { it.tasks.named("assemblePlugin") }) +} + +tasks { + "build" { + dependsOn(named("assemblePlugins")) + } +} \ No newline at end of file diff --git a/plugins/igdb/build.gradle.kts b/plugins/igdb/build.gradle.kts index 844860c..6f15a8b 100644 --- a/plugins/igdb/build.gradle.kts +++ b/plugins/igdb/build.gradle.kts @@ -1,24 +1,7 @@ plugins { - kotlin("jvm") - id("kotlin-kapt") -} - -group = "de.grimsi.gameyfin.plugins" - -repositories { - mavenCentral() + id("com.google.devtools.ksp") version "2.0.20-1.0.24" } dependencies { - implementation(project(":plugin-api")) -} - -tasks.jar { - manifest { - from("./src/main/resources/MANIFEST.MF") - } -} - -tasks.test { - useJUnitPlatform() + ksp("care.better.pf4j:pf4j-kotlin-symbol-processing:2.0.20-1.0.1") } \ No newline at end of file diff --git a/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/IgdbPlugin.kt b/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/IgdbPlugin.kt index eae22bf..78dd4bd 100644 --- a/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/IgdbPlugin.kt +++ b/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/IgdbPlugin.kt @@ -1,35 +1,48 @@ package de.grimsi.gameyfin.plugins.igdb import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadata -import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataPlugin +import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataFetcher import org.pf4j.Extension +import org.pf4j.Plugin +import org.pf4j.PluginWrapper import java.time.Instant -@Extension -class IgdbPlugin : GameMetadataPlugin { - override fun getConfig(): Map { - TODO("Not yet implemented") +class IgdbPlugin(wrapper: PluginWrapper) : Plugin(wrapper) { + + override fun start() { + println("IgdbPlugin.start()") } - override fun setConfig(config: Map) { - TODO("Not yet implemented") + override fun stop() { + println("IgdbPlugin.stop()") } - override fun fetchMetadata(gameId: String): GameMetadata { - return GameMetadata( - title = "Test Game", - description = "This is a test game", - release = Instant.now(), - userRating = 0, - criticRating = 0, - developedBy = listOf("Test Developer"), - publishedBy = listOf("Test Publisher"), - genres = listOf(), - themes = listOf(), - screenshotUrls = listOf(), - videoUrls = listOf(), - features = listOf(), - perspectives = listOf() - ) + @Extension + class IgdbMetadataFetcher : GameMetadataFetcher { + override fun getConfig(): Map { + TODO("Not yet implemented") + } + + override fun setConfig(config: Map) { + TODO("Not yet implemented") + } + + override fun fetchMetadata(gameId: String): GameMetadata { + return GameMetadata( + title = "Test Game", + description = "This is a test game", + release = Instant.now(), + userRating = 0, + criticRating = 0, + developedBy = listOf("Test Developer"), + publishedBy = listOf("Test Publisher"), + genres = listOf(), + themes = listOf(), + screenshotUrls = listOf(), + videoUrls = listOf(), + features = listOf(), + perspectives = listOf() + ) + } } } \ No newline at end of file diff --git a/plugins/igdb/src/main/resources/MANIFEST.MF b/plugins/igdb/src/main/resources/MANIFEST.MF index 1caf394..51b060a 100644 --- a/plugins/igdb/src/main/resources/MANIFEST.MF +++ b/plugins/igdb/src/main/resources/MANIFEST.MF @@ -1,5 +1,5 @@ Manifest-Version: 1.0 Plugin-Id: igdb Plugin-Class: de.grimsi.gameyfin.plugins.igdb.IgdbPlugin -Plugin-Version: 1.0.0 +Plugin-Version: 1.0.0-SNAPSHOT Plugin-Provider: grimsi diff --git a/settings.gradle.kts b/settings.gradle.kts index dd2c5c6..18a2dc5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,6 +15,10 @@ pluginManagement { } } -include("gameyfin") -include("plugin-api") -include("plugins:igdb") + +include(":plugin-api") +include(":gameyfin") + +include(":plugins") + +include(":plugins:igdb")