From 5ad75a2580f8657cc25a5068e88e49f58cbdc739 Mon Sep 17 00:00:00 2001 From: Logan Gorence Date: Wed, 22 Dec 2021 08:11:22 +0000 Subject: [PATCH] Add basic update checking. --- build.gradle.kts | 1 + foundation-core/build.gradle.kts | 1 + .../foundation/core/FoundationCorePlugin.kt | 2 + .../foundation/core/command/UpdateCommand.kt | 37 +++++++++++++++++ .../foundation/core/update/ModuleManifest.kt | 9 +++++ .../foundation/core/update/UpdateUtil.kt | 40 +++++++++++++++++++ foundation-core/src/main/resources/plugin.yml | 8 +++- 7 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/command/UpdateCommand.kt create mode 100644 foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/update/ModuleManifest.kt create mode 100644 foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/update/UpdateUtil.kt diff --git a/build.gradle.kts b/build.gradle.kts index ba359e5..0d06527 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -83,6 +83,7 @@ subprojects { // Serialization implementation("com.charleskorn.kaml:kaml:0.38.0") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1") // Paper API compileOnly("io.papermc.paper:paper-api:1.18.1-R0.1-SNAPSHOT") diff --git a/foundation-core/build.gradle.kts b/foundation-core/build.gradle.kts index 7d82dc7..2ae636d 100644 --- a/foundation-core/build.gradle.kts +++ b/foundation-core/build.gradle.kts @@ -1,2 +1,3 @@ dependencies { + // TODO: might be able to ship all dependencies in core? are we duplicating classes in JARs? } diff --git a/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/FoundationCorePlugin.kt b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/FoundationCorePlugin.kt index 08d91f1..e19b771 100644 --- a/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/FoundationCorePlugin.kt +++ b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/FoundationCorePlugin.kt @@ -3,6 +3,7 @@ package cloud.kubelet.foundation.core import cloud.kubelet.foundation.core.command.BackupCommand import cloud.kubelet.foundation.core.command.GamemodeCommand import cloud.kubelet.foundation.core.command.LeaderboardCommand +import cloud.kubelet.foundation.core.command.UpdateCommand import net.kyori.adventure.text.Component import org.bukkit.GameMode import org.bukkit.command.CommandExecutor @@ -41,6 +42,7 @@ class FoundationCorePlugin : JavaPlugin(), Listener { // Register commands. registerCommandExecutor("fbackup", BackupCommand(this, backupPath)) + registerCommandExecutor("fupdate", UpdateCommand()) registerCommandExecutor(listOf("survival", "s"), GamemodeCommand(GameMode.SURVIVAL)) registerCommandExecutor(listOf("creative", "c"), GamemodeCommand(GameMode.CREATIVE)) registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE)) diff --git a/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/command/UpdateCommand.kt b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/command/UpdateCommand.kt new file mode 100644 index 0000000..13726db --- /dev/null +++ b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/command/UpdateCommand.kt @@ -0,0 +1,37 @@ +package cloud.kubelet.foundation.core.command + +import cloud.kubelet.foundation.core.update.UpdateUtil +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender + +class UpdateCommand : CommandExecutor { + override fun onCommand( + sender: CommandSender, + command: Command, + label: String, + args: Array + ): Boolean { + // TODO: Move to separate thread? + val modules = UpdateUtil.fetchManifest() + val plugins = sender.server.pluginManager.plugins.associateBy { it.name.lowercase() } + + sender.sendMessage("Updates:") + modules.forEach { (name, manifest) -> + // Dumb naming problem. Don't want to fix it right now. + val plugin = if (name == "foundation-core") { + plugins["foundation"] + } else { + plugins[name.lowercase()] + } + + if (plugin == null) { + sender.sendMessage("Plugin in manifest, but not installed: $name (${manifest.version})") + } else { + sender.sendMessage("${plugin.name}: ${manifest.version} (have ${plugin.description.version})") + } + } + + return true + } +} \ No newline at end of file diff --git a/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/update/ModuleManifest.kt b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/update/ModuleManifest.kt new file mode 100644 index 0000000..1cd9743 --- /dev/null +++ b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/update/ModuleManifest.kt @@ -0,0 +1,9 @@ +package cloud.kubelet.foundation.core.update + +import kotlinx.serialization.Serializable + +@Serializable +data class ModuleManifest( + val version: String, + val artifacts: List, +) diff --git a/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/update/UpdateUtil.kt b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/update/UpdateUtil.kt new file mode 100644 index 0000000..3d83031 --- /dev/null +++ b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/update/UpdateUtil.kt @@ -0,0 +1,40 @@ +package cloud.kubelet.foundation.core.update + +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.json.Json +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse + +object UpdateUtil { + private val client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build() + + // TODO: Add environment variable override. Document it. + private const val manifestUrl = + "https://git.gorence.io/lgorence/foundation/-/jobs/artifacts/main/raw/build/manifests/update.json?job=build" + + fun fetchManifest() = fetchFile( + manifestUrl, MapSerializer(String.serializer(), ModuleManifest.serializer()), + ) + + private inline fun fetchFile(url: String, strategy: DeserializationStrategy): T { + val request = HttpRequest + .newBuilder() + .GET() + .uri(URI.create(url)) + .build() + + val response = client.send( + request, + HttpResponse.BodyHandlers.ofString() + ) + + return Json.decodeFromString( + strategy, + response.body() + ) + } +} diff --git a/foundation-core/src/main/resources/plugin.yml b/foundation-core/src/main/resources/plugin.yml index 46c27ee..5e21ddd 100644 --- a/foundation-core/src/main/resources/plugin.yml +++ b/foundation-core/src/main/resources/plugin.yml @@ -8,9 +8,13 @@ authors: - kubelet commands: fbackup: - description: Foundation Backup + description: Back the server up now usage: /fbackup - permission: foundation.backup + permission: foundation.command.backup + fupdate: + description: List updates to the Foundation plugin + usage: /fupdate + permission: foundation.command.update survival: description: Switch to survival gamemode usage: /survival