15 Commits

Author SHA1 Message Date
4f82639658 v0.16.0 2023-05-17 00:43:15 -07:00
5429dcddd5 Strip kubeliv from contributors. 2023-04-20 00:54:06 -07:00
1a968a6489 Cleanup smart downloader utility. 2023-03-18 14:55:19 -07:00
aa2750bdc0 Start work on v0.16.0-SNAPSHOT 2023-03-16 17:44:48 -07:00
075cd169bb v0.15.0 2023-03-16 17:44:27 -07:00
d561146024 Fix samples. 2023-03-16 17:39:48 -07:00
900b430e58 v0.15.0 2023-03-16 17:36:12 -07:00
4cec9a74a9 Implement support for custom concrete items. 2023-03-16 17:35:40 -07:00
bb3d3cb6bc Start work on v0.15.0-SNAPSHOT 2023-03-13 16:27:25 -07:00
3042512a92 v0.14.0 2023-03-13 16:27:02 -07:00
6d6a71b4f8 Fix bug in update manifest generation. 2023-03-13 16:26:30 -07:00
2a38525180 Begin work on v0.14.0-SNAPSHOT 2023-03-13 16:07:18 -07:00
b910e3b2ff v0.13.0 2023-03-13 16:06:41 -07:00
89664eb5d7 Switch to plugin {} for concrete plugin dependency sets. 2023-03-13 16:00:49 -07:00
aaf15e5270 Start on v0.13.0-SANPSHOT 2023-03-13 15:51:12 -07:00
22 changed files with 122 additions and 51 deletions

View File

@ -5,4 +5,3 @@ Concrete is a Gradle plugin for Bukkit projects. Originally spawned out of the G
## Contributors ## Contributors
- [@azenla](https://github.com/azenla) - [@azenla](https://github.com/azenla)
- [@kubeliv](https://github.com/kubeliv)

View File

@ -9,7 +9,7 @@ plugins {
} }
group = "gay.pizza.foundation" group = "gay.pizza.foundation"
version = "0.12.0" version = "0.16.0"
repositories { repositories {
mavenCentral() mavenCentral()
@ -17,9 +17,9 @@ repositories {
} }
dependencies { dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10") implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21")
implementation("org.jetbrains.kotlin:kotlin-serialization:1.8.10") implementation("org.jetbrains.kotlin:kotlin-serialization:1.8.21")
implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") implementation("com.github.johnrengelman:shadow:8.1.1")
implementation("com.google.code.gson:gson:2.10.1") implementation("com.google.code.gson:gson:2.10.1")
// Implementation of crypto used in smart downloader. // Implementation of crypto used in smart downloader.
@ -128,7 +128,7 @@ publishing {
} }
tasks.withType<Wrapper> { tasks.withType<Wrapper> {
gradleVersion = "8.0" gradleVersion = "8.1.1"
} }
java { java {

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000 networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -2,9 +2,10 @@ plugins {
id("gay.pizza.foundation.concrete-root") id("gay.pizza.foundation.concrete-root")
} }
concrete { concreteRoot {
minecraftServerPath.set("server") minecraftServerPath.set("server")
paperServerVersionGroup.set("1.19") paperServerVersionGroup.set("1.19")
paperApiVersion.set("1.19.3-R0.1-SNAPSHOT") paperApiVersion.set("1.19.3-R0.1-SNAPSHOT")
acceptServerEula.set(true) acceptServerEula.set(true)
expansiveItemInclusion.set(true)
} }

View File

@ -7,6 +7,6 @@ dependencies {
implementation(project(":bukkit-plugins:common-library")) implementation(project(":bukkit-plugins:common-library"))
} }
concrete { concreteItem {
dependency(project(":bukkit-plugins:goodbye-world")) dependency(project(":bukkit-plugins:goodbye-world"))
} }

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000 networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -1,3 +1,10 @@
plugins { plugins {
id("gay.pizza.foundation.concrete-base") id("gay.pizza.foundation.concrete-base")
} }
concreteItem {
type.set("library")
fileInclusion {
tasks.jar.get().outputs.files.associateWith { "library-jar" }
}
}

View File

@ -3,7 +3,7 @@ plugins {
id("gay.pizza.foundation.concrete-plugin") id("gay.pizza.foundation.concrete-plugin")
} }
concrete { concreteRoot {
minecraftServerPath.set("server") minecraftServerPath.set("server")
paperServerVersionGroup.set("1.19") paperServerVersionGroup.set("1.19")
paperApiVersion.set("1.19.3-R0.1-SNAPSHOT") paperApiVersion.set("1.19.3-R0.1-SNAPSHOT")

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000 networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -2,7 +2,7 @@ plugins {
id("gay.pizza.foundation.concrete-root") id("gay.pizza.foundation.concrete-root")
} }
concrete { concreteRoot {
minecraftServerPath.set("server") minecraftServerPath.set("server")
paperServerVersionGroup.set("1.19") paperServerVersionGroup.set("1.19")
paperApiVersion.set("1.19.3-R0.1-SNAPSHOT") paperApiVersion.set("1.19.3-R0.1-SNAPSHOT")

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000 networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -4,6 +4,7 @@ import org.gradle.api.JavaVersion
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.withType import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@ -40,5 +41,7 @@ open class ConcreteBasePlugin : Plugin<Project> {
} }
} }
} }
project.extensions.create<ConcreteItemExtension>("concreteItem")
} }
} }

View File

@ -0,0 +1,21 @@
package gay.pizza.foundation.concrete
import org.gradle.api.DomainObjectSet
import org.gradle.api.Project
import org.gradle.api.provider.Property
import java.io.File
interface ConcreteItemExtension {
val type: Property<String>
val version: Property<String>
val dependencies: DomainObjectSet<Project>
val fileInclusion: Property<() -> Map<File, String>>
fun dependency(project: Project) {
dependencies.add(project)
}
fun fileInclusion(inclusion: () -> Map<File, String>) {
fileInclusion.set(inclusion)
}
}

View File

@ -1,12 +0,0 @@
package gay.pizza.foundation.concrete
import org.gradle.api.DomainObjectSet
import org.gradle.api.Project
interface ConcretePluginExtension {
val dependencies: DomainObjectSet<Project>
fun dependency(project: Project) {
dependencies.add(project)
}
}

View File

@ -9,8 +9,6 @@ class ConcretePluginPlugin : ConcreteBaseBukkitPlugin() {
override fun apply(project: Project) { override fun apply(project: Project) {
super.apply(project) super.apply(project)
project.extensions.create("concrete", ConcretePluginExtension::class.java)
project.plugins.apply("com.github.johnrengelman.shadow") project.plugins.apply("com.github.johnrengelman.shadow")
// During IDEA project import, if this code is active, it will print warnings. // During IDEA project import, if this code is active, it will print warnings.

View File

@ -7,4 +7,5 @@ interface ConcreteRootExtension {
val paperApiVersion: Property<String> val paperApiVersion: Property<String>
val minecraftServerPath: Property<String> val minecraftServerPath: Property<String>
val acceptServerEula: Property<Boolean> val acceptServerEula: Property<Boolean>
val expansiveItemInclusion: Property<Boolean>
} }

View File

@ -9,7 +9,7 @@ import java.nio.file.Paths
class ConcreteRootPlugin : Plugin<Project> { class ConcreteRootPlugin : Plugin<Project> {
override fun apply(project: Project) { override fun apply(project: Project) {
project.apply(plugin = "base") project.apply(plugin = "base")
project.extensions.create<ConcreteRootExtension>("concrete") project.extensions.create<ConcreteRootExtension>("concreteRoot")
val setupPaperServer = project.tasks.create<SetupPaperServer>("setupPaperServer") val setupPaperServer = project.tasks.create<SetupPaperServer>("setupPaperServer")
val runPaperServer = project.tasks.create<RunPaperServer>("runPaperServer") val runPaperServer = project.tasks.create<RunPaperServer>("runPaperServer")
runPaperServer.dependsOn(setupPaperServer) runPaperServer.dependsOn(setupPaperServer)

View File

@ -19,7 +19,7 @@ data class ExtensibleManifestItem(
*/ */
val name: String, val name: String,
/** /**
* The type of item, for example "bukkit-plugin" * The type of item.
*/ */
val type: String, val type: String,
/** /**
@ -33,7 +33,7 @@ data class ExtensibleManifestItem(
/** /**
* The files that are required to install the item. * The files that are required to install the item.
*/ */
val files: List<String> val files: List<ExtensibleManifestItemFile>
) )
/** /**
@ -45,7 +45,7 @@ data class ExtensibleManifestItemFile(
*/ */
val name: String, val name: String,
/** /**
* A type of file. For example: "plugin-jar". * A type of file.
*/ */
val type: String, val type: String,
/** /**

View File

@ -1,9 +1,13 @@
package gay.pizza.foundation.concrete package gay.pizza.foundation.concrete
import java.net.URI import java.net.URI
import java.nio.file.Files import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse.BodyHandlers
import java.nio.file.Path import java.nio.file.Path
import java.security.MessageDigest import java.security.MessageDigest
import kotlin.io.path.exists
import kotlin.io.path.inputStream
class SmartDownloader( class SmartDownloader(
private val localFilePath: Path, private val localFilePath: Path,
@ -20,23 +24,25 @@ class SmartDownloader(
} }
private fun downloadRemoteFile() { private fun downloadRemoteFile() {
val url = remoteDownloadUrl.toURL() val httpClient = HttpClient.newHttpClient()
val remoteFileStream = url.openStream() val request = HttpRequest.newBuilder()
val localFileStream = Files.newOutputStream(localFilePath) .GET()
remoteFileStream.transferTo(localFileStream) .uri(remoteDownloadUrl)
.build()
httpClient.send(request, BodyHandlers.ofFile(localFilePath))
val hashResult = checkLocalFileHash() val hashResult = checkLocalFileHash()
if (hashResult != HashResult.ValidHash) { if (hashResult != HashResult.ValidHash) {
throw RuntimeException("Download of $remoteDownloadUrl did not result in valid hash.") throw RuntimeException("Download of $remoteDownloadUrl did not result in a valid hash.")
} }
} }
private fun checkLocalFileHash(): HashResult { private fun checkLocalFileHash(): HashResult {
if (!Files.exists(localFilePath)) { if (!localFilePath.exists()) {
return HashResult.DoesNotExist return HashResult.DoesNotExist
} }
val digest = MessageDigest.getInstance("SHA-256") val digest = MessageDigest.getInstance("SHA-256")
val localFileStream = Files.newInputStream(localFilePath) val localFileStream = localFilePath.inputStream()
val buffer = ByteArray(16 * 1024) val buffer = ByteArray(16 * 1024)
while (true) { while (true) {

View File

@ -4,14 +4,21 @@ import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskAction
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.name
import kotlin.io.path.relativeTo
open class UpdateManifestTask : DefaultTask() { open class UpdateManifestTask : DefaultTask() {
@TaskAction @TaskAction
fun update() { fun update() {
val manifestsDir = ensureManifestsDirectory() val manifestsDir = ensureManifestsDirectory()
val rootPath = project.projectDir.toPath() val rootExtension = project.concreteRootExtension
val rootPath = if (rootExtension.expansiveItemInclusion.orNull == true) {
project.rootProject.rootDir
} else {
project.rootDir
}
val legacyUpdateManifest = project.findPluginProjects().mapNotNull { project -> val legacyUpdateManifest = project.findPluginProjects().mapNotNull { project ->
val paths = project.shadowJarOutputs!!.allFilesRelativeToPath(rootPath) val paths = project.shadowJarOutputs!!.files.allFilesRelativeToPath(rootPath.toPath())
if (paths.isNotEmpty()) { if (paths.isNotEmpty()) {
project.name to mapOf( project.name to mapOf(
@ -24,16 +31,27 @@ open class UpdateManifestTask : DefaultTask() {
val legacyUpdateFile = manifestsDir.resolve("update.json") val legacyUpdateFile = manifestsDir.resolve("update.json")
Files.writeString(legacyUpdateFile, Globals.gson.toJson(legacyUpdateManifest)) Files.writeString(legacyUpdateFile, Globals.gson.toJson(legacyUpdateManifest))
val extensibleUpdateManifestItems = project.findPluginProjects().mapNotNull { project -> val extensibleUpdateManifestItems = project.findItemProjects().map { project ->
val paths = project.shadowJarOutputs!!.allFilesRelativeToPath(rootPath) val concreteItemExtension = project.concreteItemExtension!!
val pathInclusion = concreteItemExtension.fileInclusion.orNull ?: {
project.shadowJarOutputs!!.files.associateWith { "plugin-jar" }
}
val paths = pathInclusion()
val dependencies = concreteItemExtension.dependencies.map { it.name }
val dependencies = project.concretePluginExtension.dependencies.map { it.name }
ExtensibleManifestItem( ExtensibleManifestItem(
name = project.name, name = project.name,
type = "bukkit-plugin", type = concreteItemExtension.type.orNull ?: "bukkit-plugin",
version = project.version.toString(), version = concreteItemExtension.version.orNull ?: project.version.toString(),
dependencies = dependencies, dependencies = dependencies,
files = paths.map { it.toUnixString() } files = paths.map { (path, type) ->
ExtensibleManifestItemFile(
name = path.name,
type = type,
path = path.toPath().relativeTo(rootPath.toPath()).toUnixString()
)
}
) )
} }

View File

@ -4,6 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.TaskOutputs import org.gradle.api.tasks.TaskOutputs
import java.io.File
import java.nio.file.FileSystems import java.nio.file.FileSystems
import java.nio.file.Path import java.nio.file.Path
@ -12,11 +13,30 @@ import java.nio.file.Path
*/ */
internal fun Project.isPluginProject() = plugins.hasPlugin(ConcretePluginPlugin::class.java) internal fun Project.isPluginProject() = plugins.hasPlugin(ConcretePluginPlugin::class.java)
/**
* Checks if the project has the [ConcreteBasePlugin] applied and is opting into item behavior.
*/
internal fun Project.isConcreteItem() =
isPluginProject() || concreteItemExtension?.type?.orNull != null
/** /**
* Finds all projects in the project's hierarchy that are plugins. * Finds all projects in the project's hierarchy that are plugins.
*/ */
internal fun Project.findPluginProjects() = allprojects.filter { project -> project.isPluginProject() } internal fun Project.findPluginProjects() = allprojects.filter { project -> project.isPluginProject() }
/**
* Finds all projects in the project's hierarchy that are items.
*/
internal fun Project.findItemProjects(): List<Project> {
val optInExpansion = concreteRootExtension.expansiveItemInclusion.orNull ?: false
val searchScope = if (optInExpansion) {
project.rootProject.allprojects
} else {
allprojects
}
return searchScope.filter { project -> project.isConcreteItem() }
}
internal fun TaskContainer.addTaskDependency(dependent: String, dependency: String) { internal fun TaskContainer.addTaskDependency(dependent: String, dependency: String) {
getByName(dependent).dependsOn(getByName(dependency)) getByName(dependent).dependsOn(getByName(dependency))
} }
@ -37,8 +57,8 @@ internal val Project.concreteRootExtension: ConcreteRootExtension
error = { "Failed to find concrete root. Did you apply the concrete root plugin?" } error = { "Failed to find concrete root. Did you apply the concrete root plugin?" }
) )
internal val Project.concretePluginExtension: ConcretePluginExtension internal val Project.concreteItemExtension: ConcreteItemExtension?
get() = extensions.getByType(ConcretePluginExtension::class.java) get() = extensions.findByType(ConcreteItemExtension::class.java)
/** /**
* Finds the concrete root project, which is the first project in the project hierarchy * Finds the concrete root project, which is the first project in the project hierarchy
@ -68,6 +88,6 @@ internal fun <T> Project.findTargetParent(valid: Project.() -> Boolean, extract:
throw RuntimeException(error()) throw RuntimeException(error())
} }
internal fun TaskOutputs.allFilesRelativeToPath(root: Path): List<Path> = files.map { root.relativize(it.toPath()) } internal fun Iterable<File>.allFilesRelativeToPath(root: Path): List<Path> = map { root.relativize(it.toPath()) }
internal fun Path.toUnixString() = toString().replace(FileSystems.getDefault().separator, "/") internal fun Path.toUnixString() = toString().replace(FileSystems.getDefault().separator, "/")

9
tools/build-samples.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
set -e
cd "$(dirname "${0}")/.."
for SAMPLE_PATH in samples/*
do
pushd "${SAMPLE_PATH}" > /dev/null
./gradlew -q build
popd > /dev/null
done