mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 21:52:59 +00:00 
			
		
		
		
	Merge branch 'mc-1.16.x' into mc-1.18.x
I dare say there's going to be some bugs with this. I was as careful as I could be, but yikes it was nasty.
This commit is contained in:
		
							
								
								
									
										105
									
								
								buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| import cc.tweaked.gradle.LicenseHeader | ||||
| import com.diffplug.gradle.spotless.FormatExtension | ||||
| import com.diffplug.spotless.LineEnding | ||||
| import java.nio.charset.StandardCharsets | ||||
|  | ||||
| plugins { | ||||
|     `java-library` | ||||
|     jacoco | ||||
|     checkstyle | ||||
|     id("com.diffplug.spotless") | ||||
| } | ||||
|  | ||||
| java { | ||||
|     toolchain { | ||||
|         languageVersion.set(JavaLanguageVersion.of(17)) | ||||
|     } | ||||
|  | ||||
|     withSourcesJar() | ||||
|     withJavadocJar() | ||||
| } | ||||
|  | ||||
| repositories { | ||||
|     mavenCentral() | ||||
|     maven("https://squiddev.cc/maven") { | ||||
|         name = "SquidDev" | ||||
|         content { | ||||
|             includeGroup("org.squiddev") | ||||
|             includeGroup("cc.tweaked") | ||||
|             // Things we mirror | ||||
|             includeGroup("com.blamejared.crafttweaker") | ||||
|             includeGroup("commoble.morered") | ||||
|             includeGroup("maven.modrinth") | ||||
|             includeGroup("mezz.jei") | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs") | ||||
|     checkstyle(libs.findLibrary("checkstyle").get()) | ||||
| } | ||||
|  | ||||
| // Configure default JavaCompile tasks with our arguments. | ||||
| sourceSets.all { | ||||
|     tasks.named(compileJavaTaskName, JavaCompile::class.java) { | ||||
|         // Processing just gives us "No processor claimed any of these annotations", so skip that! | ||||
|         options.compilerArgs.addAll(listOf("-Xlint", "-Xlint:-processing")) | ||||
|     } | ||||
| } | ||||
|  | ||||
| tasks.withType(JavaCompile::class.java).configureEach { | ||||
|     options.encoding = "UTF-8" | ||||
| } | ||||
|  | ||||
| tasks.test { | ||||
|     finalizedBy("jacocoTestReport") | ||||
|  | ||||
|     useJUnitPlatform() | ||||
|     testLogging { | ||||
|         events("skipped", "failed") | ||||
|     } | ||||
| } | ||||
|  | ||||
| tasks.withType(JacocoReport::class.java).configureEach { | ||||
|     reports.xml.required.set(true) | ||||
|     reports.html.required.set(true) | ||||
| } | ||||
|  | ||||
| spotless { | ||||
|     encoding = StandardCharsets.UTF_8 | ||||
|     lineEndings = LineEnding.UNIX | ||||
|  | ||||
|     fun FormatExtension.defaults() { | ||||
|         endWithNewline() | ||||
|         trimTrailingWhitespace() | ||||
|         indentWithSpaces(4) | ||||
|     } | ||||
|  | ||||
|     val licenser = LicenseHeader.create( | ||||
|         api = file("config/license/api.txt"), | ||||
|         main = file("config/license/main.txt"), | ||||
|     ) | ||||
|  | ||||
|     java { | ||||
|         defaults() | ||||
|         addStep(licenser) | ||||
|         removeUnusedImports() | ||||
|     } | ||||
|  | ||||
|     val ktlintConfig = mapOf( | ||||
|         "disabled_rules" to "no-wildcard-imports", | ||||
|         "ij_kotlin_allow_trailing_comma" to "true", | ||||
|         "ij_kotlin_allow_trailing_comma_on_call_site" to "true", | ||||
|     ) | ||||
|  | ||||
|     kotlinGradle { | ||||
|         defaults() | ||||
|         ktlint().editorConfigOverride(ktlintConfig) | ||||
|     } | ||||
|  | ||||
|     kotlin { | ||||
|         defaults() | ||||
|         ktlint().editorConfigOverride(ktlintConfig) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										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, "<no git hash>") { | ||||
|         ProcessHelpers.captureOut("git", "-C", project.projectDir.absolutePath, "rev-parse", "HEAD") | ||||
|     } | ||||
| 
 | ||||
|     /** Get the current git branch. */ | ||||
|     val gitBranch: Provider<String> = gitProvider(project, "<no git branch>") { | ||||
|         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, listOf()) { | ||||
|         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, default: T, supplier: () -> T): Provider<T> { | ||||
|             return project.provider { | ||||
|                 try { | ||||
|                     supplier() | ||||
|                 } catch (e: IOException) { | ||||
|                     project.logger.error("Cannot read Git repository: ${e.message}") | ||||
|                     default | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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") | ||||
|     } | ||||
| } | ||||
							
								
								
									
										73
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckLicense.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckLicense.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| package cc.tweaked.gradle | ||||
| 
 | ||||
| import com.diffplug.spotless.FormatterFunc | ||||
| import com.diffplug.spotless.FormatterStep | ||||
| import com.diffplug.spotless.generic.LicenseHeaderStep | ||||
| import java.io.File | ||||
| import java.io.Serializable | ||||
| import java.nio.charset.StandardCharsets | ||||
| 
 | ||||
| /** | ||||
|  * Similar to [LicenseHeaderStep], but supports multiple licenses. | ||||
|  */ | ||||
| object LicenseHeader { | ||||
|     /** | ||||
|      * The current year to use in templates. Intentionally not dynamic to avoid failing the build. | ||||
|      */ | ||||
|     private const val YEAR = 2022 | ||||
| 
 | ||||
|     private val COMMENT = Regex("""^/\*(.*?)\*/\n?""", RegexOption.DOT_MATCHES_ALL) | ||||
| 
 | ||||
|     fun create(api: File, main: File): FormatterStep = FormatterStep.createLazy( | ||||
|         "license", | ||||
|         { Licenses(getTemplateText(api), getTemplateText(main)) }, | ||||
|         { state -> FormatterFunc.NeedsFile { contents, file -> formatFile(state, contents, file) } }, | ||||
|     ) | ||||
| 
 | ||||
|     private fun getTemplateText(file: File): String = | ||||
|         file.readText().trim().replace("\${year}", "$YEAR") | ||||
| 
 | ||||
|     private fun formatFile(licenses: Licenses, contents: String, file: File): String { | ||||
|         val license = getLicense(contents) | ||||
|         val expectedLicense = getExpectedLicense(licenses, file.parentFile) | ||||
| 
 | ||||
|         return when { | ||||
|             license == null -> setLicense(expectedLicense, contents) | ||||
|             license.second != expectedLicense -> setLicense(expectedLicense, contents, license.first) | ||||
|             else -> contents | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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 | ||||
|         } | ||||
|         return licenses.main | ||||
|     } | ||||
| 
 | ||||
|     private fun getLicense(contents: String): Pair<Int, String>? { | ||||
|         val match = COMMENT.find(contents) ?: return null | ||||
|         val license = match.groups[1]!!.value | ||||
|             .trim().lineSequence() | ||||
|             .map { it.trimStart(' ', '*') } | ||||
|             .joinToString("\n") | ||||
|         return Pair(match.range.last + 1, license) | ||||
|     } | ||||
| 
 | ||||
|     private fun setLicense(license: String, contents: String, start: Int = 0): String { | ||||
|         val out = StringBuilder() | ||||
|         out.append("/*\n") | ||||
|         for (line in license.lineSequence()) out.append(" * ").append(line).append("\n") | ||||
|         out.append(" */\n") | ||||
|         out.append(contents, start, contents.length) | ||||
|         return out.toString() | ||||
|     } | ||||
| 
 | ||||
|     private data class Licenses(val api: String, val main: String) : Serializable { | ||||
|         companion object { | ||||
|             private const val serialVersionUID: Long = 7741106448372435662L | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										20
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/Extensions.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/Extensions.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| package cc.tweaked.gradle | ||||
| 
 | ||||
| import org.gradle.api.artifacts.dsl.DependencyHandler | ||||
| import org.gradle.api.tasks.JavaExec | ||||
| 
 | ||||
| fun DependencyHandler.annotationProcessorEverywhere(dep: Any) { | ||||
|     add("compileOnly", dep) | ||||
|     add("annotationProcessor", dep) | ||||
| 
 | ||||
|     add("testCompileOnly", dep) | ||||
|     add("testAnnotationProcessor", dep) | ||||
| } | ||||
| 
 | ||||
| fun JavaExec.copyToFull(spec: JavaExec) { | ||||
|     copyTo(spec) | ||||
|     spec.classpath = classpath | ||||
|     spec.mainClass.set(mainClass) | ||||
|     spec.javaLauncher.set(javaLauncher) | ||||
|     spec.args = args | ||||
| } | ||||
							
								
								
									
										60
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| package cc.tweaked.gradle | ||||
| 
 | ||||
| import org.gradle.api.DefaultTask | ||||
| import org.gradle.api.Plugin | ||||
| import org.gradle.api.Project | ||||
| import org.gradle.api.file.Directory | ||||
| import org.gradle.api.file.DirectoryProperty | ||||
| import org.gradle.api.provider.Provider | ||||
| import org.gradle.api.tasks.* | ||||
| import java.io.File | ||||
| 
 | ||||
| class NodePlugin : Plugin<Project> { | ||||
|     override fun apply(project: Project) { | ||||
|         val extension = project.extensions.create("node", NodeExtension::class.java) | ||||
|         project.tasks.register(NpmInstall.TASK_NAME, NpmInstall::class.java) { | ||||
|             projectRoot.convention(extension.projectRoot) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| abstract class NodeExtension(project: Project) { | ||||
|     /** The directory containing `package-lock.json` and `node_modules/`. */ | ||||
|     abstract val projectRoot: DirectoryProperty | ||||
| 
 | ||||
|     init { | ||||
|         projectRoot.convention(project.layout.projectDirectory) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** Installs node modules as dependencies. */ | ||||
| abstract class NpmInstall : DefaultTask() { | ||||
|     @get:Internal | ||||
|     abstract val projectRoot: DirectoryProperty | ||||
| 
 | ||||
|     @get:InputFile | ||||
|     @get:PathSensitive(PathSensitivity.NONE) | ||||
|     val packageLock: Provider<File> = projectRoot.file("package-lock.json").map { it.asFile } | ||||
| 
 | ||||
|     @get:OutputDirectory | ||||
|     val nodeModules: Provider<Directory> = projectRoot.dir("node_modules") | ||||
| 
 | ||||
|     @TaskAction | ||||
|     fun install() { | ||||
|         project.exec { | ||||
|             commandLine("npm", "ci") | ||||
|             workingDir = projectRoot.get().asFile | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         internal const val TASK_NAME: String = "npmInstall" | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| abstract class NpxExecToDir : ExecToDir() { | ||||
|     init { | ||||
|         dependsOn(NpmInstall.TASK_NAME) | ||||
|         executable = "npx" | ||||
|     } | ||||
| } | ||||
							
								
								
									
										35
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| package cc.tweaked.gradle | ||||
| 
 | ||||
| import org.codehaus.groovy.runtime.ProcessGroovyMethods | ||||
| import java.io.BufferedReader | ||||
| import java.io.IOException | ||||
| import java.io.InputStreamReader | ||||
| import java.util.stream.Collectors | ||||
| 
 | ||||
| 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() }.collect(Collectors.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
	 Jonathan Coates
					Jonathan Coates