mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-26 19:37:39 +00:00
Move some more build logic to buildSrc
Look, I don't enjoy having 600 LOC long build.gradle files, it's just very easy to do! This at least moves some of the complexity elsewhere, so the build script is a little more declarative.
This commit is contained in:
124
buildSrc/src/main/kotlin/cc/tweaked/gradle/CCTweakedExtension.kt
Normal file
124
buildSrc/src/main/kotlin/cc/tweaked/gradle/CCTweakedExtension.kt
Normal file
@@ -0,0 +1,124 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.NamedDomainObjectProvider
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.attributes.TestSuiteType
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.reporting.ReportingExtension
|
||||
import org.gradle.api.tasks.JavaExec
|
||||
import org.gradle.api.tasks.SourceSetContainer
|
||||
import org.gradle.configurationcache.extensions.capitalized
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
import org.gradle.testing.jacoco.plugins.JacocoCoverageReport
|
||||
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
|
||||
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
|
||||
import org.gradle.testing.jacoco.tasks.JacocoReport
|
||||
import java.io.BufferedWriter
|
||||
import java.io.IOException
|
||||
import java.io.OutputStreamWriter
|
||||
import java.util.regex.Pattern
|
||||
|
||||
abstract class CCTweakedExtension(
|
||||
private val project: Project,
|
||||
private val fs: FileSystemOperations,
|
||||
) {
|
||||
/** Get the hash of the latest git commit. */
|
||||
val gitHash: Provider<String> = gitProvider(project) {
|
||||
ProcessHelpers.captureOut("git", "-C", project.projectDir.absolutePath, "rev-parse", "HEAD")
|
||||
}
|
||||
|
||||
/** Get the current git branch. */
|
||||
val gitBranch: Provider<String> = gitProvider(project) {
|
||||
ProcessHelpers.captureOut("git", "-C", project.projectDir.absolutePath, "rev-parse", "--abbrev-ref", "HEAD")
|
||||
}
|
||||
|
||||
/** Get a list of all contributors to the project. */
|
||||
val gitContributors: Provider<List<String>> = gitProvider(project) {
|
||||
val authors: Set<String> = HashSet(
|
||||
ProcessHelpers.captureLines(
|
||||
"git", "-C", project.projectDir.absolutePath, "log",
|
||||
"--format=tformat:%an <%ae>%n%cn <%ce>%n%(trailers:key=Co-authored-by,valueonly)",
|
||||
),
|
||||
)
|
||||
val process = ProcessHelpers.startProcess("git", "check-mailmap", "--stdin")
|
||||
BufferedWriter(OutputStreamWriter(process.outputStream)).use { writer ->
|
||||
for (authorName in authors) {
|
||||
var author = authorName
|
||||
|
||||
if (author.isEmpty()) continue
|
||||
if (!author.endsWith(">")) author += ">" // Some commits have broken Co-Authored-By lines!
|
||||
writer.write(author)
|
||||
writer.newLine()
|
||||
}
|
||||
}
|
||||
val contributors: MutableSet<String> = HashSet()
|
||||
for (authorLine in ProcessHelpers.captureLines(process)) {
|
||||
val matcher = EMAIL.matcher(authorLine)
|
||||
matcher.find()
|
||||
val name = matcher.group(1)
|
||||
if (!IGNORED_USERS.contains(name)) contributors.add(name)
|
||||
}
|
||||
|
||||
contributors.sortedWith(String.CASE_INSENSITIVE_ORDER)
|
||||
}
|
||||
|
||||
fun jacoco(task: NamedDomainObjectProvider<JavaExec>) {
|
||||
val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}")
|
||||
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
||||
|
||||
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
||||
task.configure {
|
||||
finalizedBy(reportTaskName)
|
||||
|
||||
doFirst("Clean class dump directory") { fs.delete { delete(classDump) } }
|
||||
|
||||
jacoco.applyTo(this)
|
||||
extensions.configure(JacocoTaskExtension::class.java) {
|
||||
includes = listOf("dan200.computercraft.*")
|
||||
classDumpDir = classDump
|
||||
|
||||
// Older versions of modlauncher don't include a protection domain (and thus no code
|
||||
// source). Jacoco skips such classes by default, so we need to explicitly include them.
|
||||
isIncludeNoLocationClasses = true
|
||||
}
|
||||
}
|
||||
|
||||
project.tasks.register(reportTaskName, JacocoReport::class.java) {
|
||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
description = "Generates code coverage report for the ${task.name} task."
|
||||
|
||||
executionData(task.get())
|
||||
classDirectories.from(classDump)
|
||||
|
||||
// Don't want to use sourceSets(...) here as we have a custom class directory.
|
||||
val sourceSets = project.extensions.getByType(SourceSetContainer::class.java)
|
||||
sourceDirectories.from(sourceSets["main"].allSource.sourceDirectories)
|
||||
}
|
||||
|
||||
project.extensions.configure(ReportingExtension::class.java) {
|
||||
reports.register("${task.name}CodeCoverageReport", JacocoCoverageReport::class.java) {
|
||||
testType.set(TestSuiteType.INTEGRATION_TEST)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val EMAIL = Pattern.compile("^([^<]+) <.+>$")
|
||||
private val IGNORED_USERS = setOf(
|
||||
"GitHub", "Daniel Ratcliffe", "Weblate",
|
||||
)
|
||||
|
||||
private fun <T> gitProvider(project: Project, supplier: () -> T): Provider<T> {
|
||||
return project.provider {
|
||||
try {
|
||||
supplier()
|
||||
} catch (e: IOException) {
|
||||
project.logger.error("Cannot read Git Repository", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
/**
|
||||
* Configures projects to match a shared configuration.
|
||||
*/
|
||||
class CCTweakedPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.extensions.create("cct", CCTweakedExtension::class.java)
|
||||
}
|
||||
}
|
||||
65
buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckChangelog.kt
Normal file
65
buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckChangelog.kt
Normal file
@@ -0,0 +1,65 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
/**
|
||||
* Checks the `changelog.md` and `whatsnew.md` files are well-formed.
|
||||
*/
|
||||
@CacheableTask
|
||||
abstract class CheckChangelog : DefaultTask() {
|
||||
init {
|
||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
description = "Verifies the changelog and whatsnew file are consistent."
|
||||
}
|
||||
|
||||
@get:Input
|
||||
abstract val version: Property<String>
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val changelog: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val whatsNew: RegularFileProperty
|
||||
|
||||
@TaskAction
|
||||
fun check() {
|
||||
val version = version.get()
|
||||
|
||||
var ok = true
|
||||
|
||||
// Check we're targetting the current version
|
||||
var whatsNew = whatsNew.get().asFile.readLines()
|
||||
if (whatsNew[0] != "New features in CC: Tweaked $version") {
|
||||
ok = false
|
||||
logger.error("Expected `whatsnew.md' to target $version.")
|
||||
}
|
||||
|
||||
// Check "read more" exists and trim it
|
||||
val idx = whatsNew.indexOfFirst { it == "Type \"help changelog\" to see the full version history." }
|
||||
if (idx == -1) {
|
||||
ok = false
|
||||
logger.error("Must mention the changelog in whatsnew.md")
|
||||
} else {
|
||||
whatsNew = whatsNew.slice(0 until idx)
|
||||
}
|
||||
|
||||
// Check whatsnew and changelog match.
|
||||
val expectedChangelog = sequenceOf("# ${whatsNew[0]}") + whatsNew.slice(1 until whatsNew.size).asSequence()
|
||||
val changelog = changelog.get().asFile.readLines()
|
||||
val mismatch = expectedChangelog.zip(changelog.asSequence()).filter { (a, b) -> a != b }.firstOrNull()
|
||||
if (mismatch != null) {
|
||||
ok = false
|
||||
logger.error("whatsnew and changelog are not in sync")
|
||||
}
|
||||
|
||||
if (!ok) throw GradleException("Could not check release")
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ object LicenseHeader {
|
||||
)
|
||||
|
||||
private fun getTemplateText(file: File): String =
|
||||
file.readText(StandardCharsets.UTF_8).trim().replace("\${year}", "$YEAR")
|
||||
file.readText().trim().replace("\${year}", "$YEAR")
|
||||
|
||||
private fun formatFile(licenses: Licenses, contents: String, file: File): String {
|
||||
val license = getLicense(contents)
|
||||
@@ -38,8 +38,8 @@ object LicenseHeader {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getExpectedLicense(licenses: Licenses, file: File): String {
|
||||
var file: File? = file
|
||||
private fun getExpectedLicense(licenses: Licenses, root: File): String {
|
||||
var file: File? = root
|
||||
while (file != null) {
|
||||
if (file.name == "api" && file.parentFile?.name == "computercraft") return licenses.api
|
||||
file = file.parentFile
|
||||
|
||||
34
buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt
Normal file
34
buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt
Normal file
@@ -0,0 +1,34 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.codehaus.groovy.runtime.ProcessGroovyMethods
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
|
||||
internal object ProcessHelpers {
|
||||
fun startProcess(vararg command: String): Process {
|
||||
// Something randomly passes in "GIT_DIR=" as an environment variable which clobbers everything else. Don't
|
||||
// inherit the environment array!
|
||||
return Runtime.getRuntime().exec(command, arrayOfNulls(0))
|
||||
}
|
||||
|
||||
fun captureOut(vararg command: String): String {
|
||||
val process = startProcess(*command)
|
||||
val result = ProcessGroovyMethods.getText(process)
|
||||
if (process.waitFor() != 0) throw IOException("Command exited with a non-0 status")
|
||||
return result
|
||||
}
|
||||
|
||||
fun captureLines(vararg command: String): List<String> {
|
||||
return captureLines(startProcess(*command))
|
||||
}
|
||||
|
||||
fun captureLines(process: Process): List<String> {
|
||||
val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
|
||||
reader.lines().filter { it.isNotEmpty() }.toList()
|
||||
}
|
||||
ProcessGroovyMethods.closeStreams(process)
|
||||
if (process.waitFor() != 0) throw IOException("Command exited with a non-0 status")
|
||||
return out
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user