Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies {
// Plugins
compileOnly("com.gradleup.shadow:shadow-gradle-plugin:9.0.0")
compileOnly("me.modmuss50.mod-publish-plugin:me.modmuss50.mod-publish-plugin.gradle.plugin:675051c")
compileOnly("io.papermc.hangar-publish-plugin:io.papermc.hangar-publish-plugin.gradle.plugin:0.1.4")
}

// Set Kotlin JVM version
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import me.modmuss50.mpp.ModPublishExtension
import me.modmuss50.mpp.platforms.curseforge.Curseforge
import me.modmuss50.mpp.platforms.modrinth.Modrinth
import org.gradle.api.Action
import xyz.srnyx.gradlegalaxy.data.platforms.PluginPlatform
import xyz.srnyx.gradlegalaxy.enums.PluginPlatform


data class PublishingPlatformConfig(
Expand All @@ -16,5 +16,23 @@ data class PublishingPlatformConfig(
val dryRun: Boolean = false,
val modrinthAction: Action<Modrinth> = Action {},
val curseForgeAction: Action<Curseforge> = Action {},
val hangarAction: Action<HangarAction> = Action {},
val action: Action<ModPublishExtension> = Action {},
)

class HangarAction {
val dependencies: MutableList<HangarDependency> = mutableListOf()

fun optional(id: String) {
dependencies.add(HangarDependency(id, false))
}

fun required(id: String) {
dependencies.add(HangarDependency(id, true))
}
}

data class HangarDependency(
val id: String,
val required: Boolean,
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package xyz.srnyx.gradlegalaxy.data.platforms
package xyz.srnyx.gradlegalaxy.enums


enum class PluginPlatform {
Expand Down
10 changes: 10 additions & 0 deletions src/main/kotlin/xyz/srnyx/gradlegalaxy/enums/ReleaseChannel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package xyz.srnyx.gradlegalaxy.enums

import me.modmuss50.mpp.ReleaseType


enum class ReleaseChannel(val mpp: ReleaseType, val hangar: String) {
RELEASE(ReleaseType.STABLE, "Release"),
BETA(ReleaseType.BETA, "Beta"),
ALPHA(ReleaseType.ALPHA, "Alpha"),
}
50 changes: 49 additions & 1 deletion src/main/kotlin/xyz/srnyx/gradlegalaxy/utility/Scripts.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.booleanOrNull
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.gradle.api.DefaultTask
Expand All @@ -26,10 +28,14 @@ import xyz.srnyx.gradlegalaxy.data.annoyingapi.AnnoyingMetadata
import xyz.srnyx.gradlegalaxy.data.annoyingapi.RuntimeLibrary
import xyz.srnyx.gradlegalaxy.data.config.annoyingapi.GenerateRuntimeLibraryEnumConfig
import xyz.srnyx.gradlegalaxy.data.config.annoyingapi.RuntimeLibrariesConfig
import xyz.srnyx.gradlegalaxy.data.platforms.PluginPlatform
import xyz.srnyx.gradlegalaxy.enums.PluginPlatform
import xyz.srnyx.gradlegalaxy.enums.Repository
import xyz.srnyx.gradlegalaxy.enums.repository
import java.io.File
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import kotlin.apply

import kotlin.text.replace
Expand Down Expand Up @@ -106,6 +112,11 @@ fun Project.hasShadowPlugin(): Boolean = try {
*/
fun Project.hasModPublishPlugin(): Boolean = plugins.hasPlugin("me.modmuss50.mod-publish-plugin")

/**
* @return If the `io.papermc.hangar-publish-plugin` plugin is applied
*/
fun Project.hasHangarPublishPlugin(): Boolean = plugins.hasPlugin("io.papermc.hangar-publish-plugin")

/**
* Checks if the `maven-publish` plugin is applied
*
Expand Down Expand Up @@ -270,6 +281,36 @@ fun String.dotsToBrackets(): String = replace(".", "{}")
*/
fun String.processRelocationTo(): String = replace("{package}.libs.", "").dotsToBrackets()

/**
* Retrieve the versions of the specified platform from Hangar
*
* @param platform The platform to retrieve versions for (default: `PAPER`)
*
* @return The versions of the specified platform in a [LinkedHashSet] sorted by version (highest to lowest)
*/
fun retrieveHangarPlatformVersions(platform: String = "PAPER"): LinkedHashSet<String> {
// Make API request
val response = HttpClient.newBuilder().build().send(
HttpRequest.newBuilder()
.uri(URI.create("https://hangar.papermc.io/api/v1/platforms/${platform}/versions"))
.GET()
.build(),
HttpResponse.BodyHandlers.ofString())
if (response.statusCode() != 200) {
throw IllegalStateException("Failed to retrieve Hangar platform versions for $platform: ${response.statusCode()} ${response.body()}")
}

// Flatten versions
val versions = LinkedHashSet<String>()
for (element in json.decodeFromString<JsonArray>(response.body())) {
val jsonObject = element.jsonObject
// Add subVersions first as version is lowest
for (subVersion in jsonObject["subVersions"]!!.jsonArray) versions.add(subVersion.jsonPrimitive.content)
versions.add(jsonObject["version"]!!.jsonPrimitive.content)
}
return versions
}
Comment thread
srnyx marked this conversation as resolved.

/**
* Relocates the specified package to the specified package
*
Expand All @@ -285,6 +326,13 @@ fun Project.relocate(
tasks.named<ShadowJar>("shadowJar") { relocate(from, to, action) }
}

/**
* Adds a task to generate the `platforms.json` resources file, listing out the plugin's publishing platforms
*
* @param platforms The platforms to add to the `platforms.json` file
*
* @return The task that generates the `platforms.json` resources file
*/
fun Project.addPlatformsResourceFileTask(platforms: Map<PluginPlatform, String>): TaskProvider<Task> {
val platformsFile = project.layout.buildDirectory.file("resources/main/platforms.json").get().asFile
val platformsProvider = project.provider { json.encodeToString(mapOf("platforms" to platforms)) }
Expand Down
160 changes: 109 additions & 51 deletions src/main/kotlin/xyz/srnyx/gradlegalaxy/utility/Setups.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package xyz.srnyx.gradlegalaxy.utility

import io.papermc.hangarpublishplugin.HangarPublishExtension
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonArray
Expand Down Expand Up @@ -34,11 +35,13 @@ import xyz.srnyx.gradlegalaxy.data.config.annoyingapi.AnnoyingSetupConfig
import xyz.srnyx.gradlegalaxy.data.config.annoyingapi.CustomRuntimeLibrariesConfig
import xyz.srnyx.gradlegalaxy.data.config.annoyingapi.MetadataConfig
import xyz.srnyx.gradlegalaxy.data.config.dependency.MockBukkitConfig
import xyz.srnyx.gradlegalaxy.data.config.publishing.HangarAction
import xyz.srnyx.gradlegalaxy.data.config.publishing.PublishingEnvConfig
import xyz.srnyx.gradlegalaxy.data.config.publishing.PublishingPlatformConfig
import xyz.srnyx.gradlegalaxy.data.config.publishing.PublishingSimpleConfig
import xyz.srnyx.gradlegalaxy.data.platforms.PluginPlatform
import xyz.srnyx.gradlegalaxy.data.pom.DeveloperData
import xyz.srnyx.gradlegalaxy.enums.PluginPlatform
import xyz.srnyx.gradlegalaxy.enums.ReleaseChannel
import xyz.srnyx.gradlegalaxy.enums.Repository
import xyz.srnyx.gradlegalaxy.enums.repository
import java.io.File
Expand Down Expand Up @@ -455,84 +458,89 @@ fun Project.setupPublishingEnv(
}

/**
* Sets up publishing for project platforms (GitHub, Modrinth, CurseForge)
* Sets up publishing for project platforms (GitHub, Modrinth, CurseForge, Hangar)
*
* @param config The configuration for setting up publishing for project platforms
* @param gitHubConfig The configuration for setting up publishing for GitHub
* @param modrinthConfig The configuration for setting up publishing for Modrinth
* @param curseForgeConfig The configuration for setting up publishing for CurseForge
* @param action The action to perform after setting up publishing for project platforms
*/
fun Project.setupPublishingPlatforms(
config: PublishingPlatformConfig,
) {
check(hasModPublishPlugin()) { "Mod Publish plugin is not applied!" }

val minecraftVersionEnd = config.minecraftVersionEnd ?: "latest"
// Identifiers
val modrinthIdentifier = config.platforms[PluginPlatform.MODRINTH]
val curseForgeIdentifier = config.platforms[PluginPlatform.CURSEFORGE]
val hangarIdentifier = config.platforms[PluginPlatform.HANGAR]

// Release channel
val releaseChannel: ReleaseChannel = when {
inGitHubPublish -> ReleaseChannel.RELEASE
inGitHubPreRelease -> ReleaseChannel.BETA
else -> ReleaseChannel.ALPHA
}

// Primary file
val primaryFile = tasks.named<Jar>(if (hasShadowPlugin()) "shadowJar" else "jar").flatMap { it.archiveFile }

// Changelog
// File exists: file contents
// In GitHub workflow:
// Non-STABLE: "github.com/REPO/commit/SHA"
// STABLE: release link
// Else: "No changelog specified"
val changelogFile = file("Changelogs/${project.version}.md")
val changelogText: String = when {
// File
changelogFile.exists() -> changelogFile.readText()

inGitHubWorkflow -> run {
val gitHubRepository =
getEnvironmentVariable("GITHUB_REPOSITORY") ?: return@run "No changelog specified"
val githubLink = "https://github.com/${gitHubRepository}"

// Non-STABLE: commit SHA
if (releaseChannel != ReleaseChannel.RELEASE) return@run "${githubLink}/commit/${getEnvironmentVariable("GITHUB_SHA")}"

// STABLE: release link
"${githubLink}/releases/tag/${project.version}"
}

else -> "No changelog specified"
}

// Setup publishing
extensions.configure<ModPublishExtension>("publishMods") {
// Dry run
dryRun.set(config.dryRun)

// Mod loaders
modLoaders.set(config.loaders)
type.set(releaseChannel.mpp)
changelog.set(changelogText)

// Display name
val event = getEnvironmentVariable("GITHUB_EVENT_PATH")
?.let { json.decodeFromString<JsonObject>(File(it).readText()) }
displayName.set(event
// Release name
?.get("release")?.jsonObject?.get("name")?.jsonPrimitive?.contentOrNull
displayName.set(
event
// Release name
?.get("release")?.jsonObject?.get("name")?.jsonPrimitive?.contentOrNull
// Commit name
?: event?.get("commits")?.jsonArray?.firstOrNull()?.jsonObject?.get("message")?.jsonPrimitive?.contentOrNull
?.lines()?.firstOrNull() // Only use commit title/summary, remove description
// Project version
?: project.version.toString())

// Type
type.set(when {
inGitHubPublish -> STABLE
inGitHubPreRelease -> BETA
else -> ALPHA
})
?: event?.get("commits")?.jsonArray?.firstOrNull()?.jsonObject?.get("message")?.jsonPrimitive?.contentOrNull
?.lines()?.firstOrNull() // Only use commit title/summary, remove description
// Project version
?: project.version.toString()
)

// Primary file (shadowJar or jar)
file.set(tasks.named<Jar>(if (hasShadowPlugin()) "shadowJar" else "jar").flatMap { it.archiveFile })
file.set(primaryFile)

// Additional files (javadocJar and sourcesJar)
val javadocJarTask = tasks.findByName("javadocJar") as? Jar
val sourcesJarTask = tasks.findByName("sourcesJar") as? Jar
javadocJarTask?.let { additionalFiles.from(it) }
sourcesJarTask?.let { additionalFiles.from(it) }

// Changelog
// File exists: file contents
// In GitHub workflow:
// Non-STABLE: "github.com/REPO/commit/SHA"
// STABLE: release link
// Else: "No changelog specified"
val changelogFile = file("Changelogs/${project.version}.md")
changelog.set(when {
// File
changelogFile.exists() -> changelogFile.readText()

inGitHubWorkflow -> run {
val gitHubRepository = getEnvironmentVariable("GITHUB_REPOSITORY") ?: return@run "No changelog specified"
val githubLink = "https://github.com/${gitHubRepository}"

// Non-STABLE: commit SHA
if (type.get() != STABLE) return@run "${githubLink}/commit/${getEnvironmentVariable("GITHUB_SHA")}"

// STABLE: release link
"${githubLink}/releases/tag/${project.version}"
}

else -> "No changelog specified"
})
val minecraftVersionEnd = config.minecraftVersionEnd ?: "latest"

// Modrinth
val modrinthIdentifier = config.platforms[PluginPlatform.MODRINTH]
if (modrinthIdentifier != null) {
val token = getEnvironmentVariable("MODRINTH_TOKEN")
if (dryRun.get() || token != null) modrinth {
Expand All @@ -555,7 +563,6 @@ fun Project.setupPublishingPlatforms(
}

// CurseForge
val curseForgeIdentifier = config.platforms[PluginPlatform.CURSEFORGE]
if (curseForgeIdentifier != null) {
val token = getEnvironmentVariable("CURSEFORGE_TOKEN")
if (dryRun.get() || token != null) curseforge {
Expand All @@ -580,4 +587,55 @@ fun Project.setupPublishingPlatforms(

config.action(this)
}

// Hangar Publish Plugin
if (hasHangarPublishPlugin() && hangarIdentifier != null) {
val token = getEnvironmentVariable("HANGAR_TOKEN")
if (token != null) {
extensions.configure<HangarPublishExtension>("hangarPublish") { publications.register("plugin") {
version.set(project.version.toString())
id.set(hangarIdentifier)
channel.set(releaseChannel.hangar)
changelog.set(changelogText)
apiKey.set(token)

platforms { paper {
jar.set(primaryFile)

// Get Hangar's supported Minecraft versions
val hangarMinecraftVersions = retrieveHangarPlatformVersions("PAPER")
if (!hangarMinecraftVersions.contains(config.minecraftVersionStart)) {
throw IllegalArgumentException("Hangar does not support start Minecraft version ${config.minecraftVersionStart}")
}

if (config.minecraftVersionEnd != null) {
// start -> end
if (!hangarMinecraftVersions.contains(config.minecraftVersionEnd)) {
throw IllegalArgumentException("Hangar does not support end Minecraft version ${config.minecraftVersionEnd}")
}
platformVersions.set(listOf(config.minecraftVersionStart + "-${config.minecraftVersionEnd}"))
} else {
// start -> latest
val semanticVersionStart = SemanticVersion(config.minecraftVersionStart)
platformVersions.set(hangarMinecraftVersions
.map { SemanticVersion(it) }
.filter { it >= semanticVersionStart }
.map { it.toString() })
}
Comment thread
srnyx marked this conversation as resolved.

// Dependencies
dependencies {
val hangarAction = HangarAction()
config.hangarAction.execute(hangarAction)
hangarAction.dependencies.forEach { dependency ->
hangar(dependency.id) { required.set(dependency.required) }
}
}
} }
} }

// Ensure publishAllPublicationsToHangar runs with/after publishMods
tasks.named("publishMods") { finalizedBy("publishAllPublicationsToHangar") }
Comment thread
srnyx marked this conversation as resolved.
}
}
}