Skip to content

Commit

Permalink
Add Plugin Update Checker (#1121)
Browse files Browse the repository at this point in the history
* add plugin checker

* use pdvrieze xmlutil & refactor logs

---------

Co-authored-by: topi314 <[email protected]>
  • Loading branch information
Neuheit and topi314 authored Jan 31, 2025
1 parent 249bf84 commit 11789f0
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 4 deletions.
13 changes: 13 additions & 0 deletions LavalinkServer/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugins {
application
kotlin("jvm")
id("org.jetbrains.dokka")
kotlin("plugin.serialization")
alias(libs.plugins.maven.publish.base)
}

Expand All @@ -30,6 +31,15 @@ java {
sourceCompatibility = JavaVersion.VERSION_17
}

kotlin {
sourceSets {
all {
languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi")
languageSettings.optIn("nl.adaptivity.xmlutil.ExperimentalXmlUtilApi")
}
}
}

configurations {
compileOnly {
extendsFrom(annotationProcessor.get())
Expand All @@ -42,6 +52,9 @@ dependencies {
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
}

implementation(libs.xmlutil.jdk)
implementation(libs.xmlutil.serialization)

implementation(libs.bundles.metrics)
implementation(libs.bundles.spring) {
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package lavalink.server.bootstrap

import dev.arbjerg.lavalink.protocol.v4.Version
import nl.adaptivity.xmlutil.core.KtXmlReader
import nl.adaptivity.xmlutil.serialization.XML
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLClassLoader
import java.nio.channels.Channels
Expand Down Expand Up @@ -44,7 +48,7 @@ class PluginManager(val config: PluginsConfig) {
loadPluginManifests(jar).map { manifest -> PluginJar(manifest, file) }
}
}
?.onEach { log.info("Found plugin '${it.manifest.name}' version ${it.manifest.version}") }
?.onEach { log.info("Found plugin '${it.manifest.name}' version '${it.manifest.version}'") }
?: return

val declarations = config.plugins.map { declaration ->
Expand Down Expand Up @@ -83,6 +87,44 @@ class PluginManager(val config: PluginsConfig) {
val file = File(directory, declaration.canonicalJarName)
downloadJar(file, url)
}

checkPluginForUpdates(declaration)
}
}

private fun checkPluginForUpdates(declaration: Declaration) {
val baseSplitPath = declaration.url.split('/').dropLast(2)
val metadataUrl = baseSplitPath.joinToString("/") + "/maven-metadata.xml"

val url = URL(metadataUrl)
val conn = url.openConnection() as HttpURLConnection

if (conn.responseCode != HttpURLConnection.HTTP_OK) {
log.warn("Failed to check for updates for ${declaration.name}: ${conn.responseMessage}")
return
}

val metadata: Metadata
conn.inputStream.use {
metadata = XML.decodeFromReader(Metadata.serializer(), KtXmlReader(it))
}

val current = Version.fromSemver(declaration.version)
var latest = metadata.versioning.latest
if (latest.isEmpty()) {
latest = metadata.versioning.release
}
if (latest.isEmpty()) {
latest = (metadata.versioning.versions.lastOrNull() ?: "").toString()
}

if (latest.isEmpty()) {
return
}

val latestVersion = Version.fromSemver(latest)
if (latestVersion > current) {
log.warn("A newer version of '${declaration.name}' was found: '$latestVersion', The current version is '$current' please update the version in your configuration.")
}
}

Expand All @@ -98,7 +140,7 @@ class PluginManager(val config: PluginsConfig) {
return PathMatchingResourcePatternResolver()
.getResources("classpath*:lavalink-plugins/*.properties")
.map { parsePluginManifest(it.inputStream) }
.onEach { log.info("Found plugin '${it.name}' version ${it.version}") }
.onEach { log.info("Found plugin '${it.name}' version '${it.version}'") }
}

private fun loadJars(): List<PluginManifest> {
Expand Down Expand Up @@ -132,14 +174,15 @@ class PluginManager(val config: PluginsConfig) {
for (entry in it.entries()) {
if (entry.isDirectory ||
!entry.name.endsWith(".class") ||
allowedPaths.none(entry.name::startsWith)) continue
allowedPaths.none(entry.name::startsWith)
) continue

cl.loadClass(entry.name.dropLast(6).replace("/", "."))
classCount++
}
}

log.info("Loaded ${file.name} ($classCount classes)")
log.info("Loaded '${file.name}' ($classCount classes)")
return manifests
}

Expand Down
31 changes: 31 additions & 0 deletions LavalinkServer/src/main/java/lavalink/server/bootstrap/maven.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package lavalink.server.bootstrap

import kotlinx.serialization.Serializable
import nl.adaptivity.xmlutil.serialization.XmlChildrenName
import nl.adaptivity.xmlutil.serialization.XmlElement
import nl.adaptivity.xmlutil.serialization.XmlSerialName

@Serializable
@XmlSerialName("metadata")
data class Metadata(
@XmlElement(true)
val groupId: String,
@XmlElement(true)
val artifactId: String,
@XmlElement(true)
val versioning: Versioning
)

@Serializable
@XmlSerialName("versioning")
data class Versioning(
@XmlElement(true)
val latest: String,
@XmlElement(true)
val release: String,
@XmlElement(true)
@XmlChildrenName("version")
val versions: List<String>,
@XmlElement(true)
val lastUpdated: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,70 @@ data class Version(
return Version(semver, major.toInt(), minor.toInt(), patch.toInt(), preRelease)
}
}

override fun toString(): String {
var baseSemver = "${major}.${minor}.${patch}"

if(!preRelease.isNullOrEmpty())
baseSemver += "-${preRelease}"

return baseSemver
}

operator fun compareTo(other: Version): Int {
// Compare major, minor, and patch
val majorDiff = major - other.major
if (majorDiff != 0) return majorDiff

val minorDiff = minor - other.minor
if (minorDiff != 0) return minorDiff

val patchDiff = patch - other.patch
if (patchDiff != 0) return patchDiff

// Compare prerelease (null means no prerelease and is greater)
return when {
preRelease.isNullOrEmpty() && other.preRelease.isNullOrEmpty() -> 0
preRelease.isNullOrEmpty() && !other.preRelease.isNullOrEmpty() -> 1
!preRelease.isNullOrEmpty() && other.preRelease.isNullOrEmpty() -> -1
!preRelease.isNullOrEmpty() && !other.preRelease.isNullOrEmpty() -> comparePreRelease(preRelease, other.preRelease)
else -> 0
}
}

private fun comparePreRelease(part1: String, part2: String): Int {
val components1 = part1.split(".")
val components2 = part2.split(".")
val maxLength = maxOf(components1.size, components2.size)

for (i in 0 until maxLength) {
val comp1 = components1.getOrNull(i)
val comp2 = components2.getOrNull(i)

if (comp1 == null) return -1 // `part1` is shorter and considered smaller
if (comp2 == null) return 1 // `part2` is shorter and considered smaller

val isNumeric1 = comp1.all { it.isDigit() }
val isNumeric2 = comp2.all { it.isDigit() }

when {
isNumeric1 && isNumeric2 -> {
// Compare numerically
val diff = comp1.toInt() - comp2.toInt()
if (diff != 0) return diff
}
isNumeric1 -> return -1 // Numeric parts come before string parts
isNumeric2 -> return 1 // String parts come after numeric parts
else -> {
// Compare lexicographically
val diff = comp1.compareTo(comp2)
if (diff != 0) return diff
}
}
}

return 0 // Parts are equal
}
}

@Serializable
Expand Down
3 changes: 3 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ fun VersionCatalogBuilder.common() {
library("kotlinx-serialization-json", "org.jetbrains.kotlinx", "kotlinx-serialization-json").version("1.7.0")
library("kotlinx-datetime", "org.jetbrains.kotlinx", "kotlinx-datetime").version("0.6.0")

library("xmlutil-jdk", "io.github.pdvrieze.xmlutil", "core-jdk").version("0.90.3")
library("xmlutil-serialization", "io.github.pdvrieze.xmlutil", "serialization-jvm").version("0.90.3")

library("logback", "ch.qos.logback", "logback-classic").version("1.5.6")
library("sentry-logback", "io.sentry", "sentry-logback").version("7.10.0")
library("oshi", "com.github.oshi", "oshi-core").version("6.4.11")
Expand Down

0 comments on commit 11789f0

Please sign in to comment.