-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Kotlin scripts to build data capturing samples
- Loading branch information
Showing
11 changed files
with
753 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
...ta-capturing-gradle-samples/capture-ge-plugin-version/gradle-ge-plugin-version.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import com.gradle.enterprise.gradleplugin.GradleEnterpriseExtension | ||
|
||
/** | ||
* This Kotlin script captures the Gradle Enterprise Gradle plugin version as a custom value. | ||
*/ | ||
|
||
project.extensions.configure<GradleEnterpriseExtension>() { | ||
buildScan { | ||
val url = GradleEnterpriseExtension::class.java.classLoader.getResource("com.gradle.scan.plugin.internal.meta.buildAgentVersion.txt") | ||
val buildAgentVersion = url.readText() | ||
value("GE Gradle plugin version", buildAgentVersion) | ||
} | ||
} | ||
|
117 changes: 117 additions & 0 deletions
117
build-data-capturing-gradle-samples/capture-git-diffs/gradle-git-diffs.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import com.gradle.enterprise.gradleplugin.GradleEnterpriseExtension | ||
import com.gradle.scan.plugin.BuildScanExtension | ||
import groovy.json.JsonBuilder | ||
import groovy.json.JsonSlurper | ||
|
||
import java.nio.charset.Charset | ||
import java.util.Base64 | ||
import java.util.concurrent.TimeUnit | ||
|
||
/** | ||
* This Gradle script captures the <i>Git diff</i> in a GitHub gist, | ||
* and references the gist via a custom link. | ||
* | ||
* In order for the gist to be created successfully, you must specify the Gradle property `gistToken` where | ||
* the value is a Github access token that has gist permission on the given Github repo. | ||
* | ||
* See https://docs.github.com/en/rest/gists/gists#create-a-gist for reference. | ||
*/ | ||
|
||
project.extensions.configure<GradleEnterpriseExtension>() { | ||
|
||
val capture = Capture(gradle.startParameter.isOffline, | ||
gradle.startParameter.isContinuous, | ||
gradle.rootProject.logger, | ||
getProviders().gradleProperty("gistToken"), | ||
rootProject.name) | ||
|
||
buildScan { | ||
background { | ||
if(capture.isEnabled()) { | ||
capture.captureGitDiffInGist(buildScan) | ||
} | ||
} | ||
} | ||
} | ||
|
||
class Capture(val offline: Boolean, | ||
val continuous: Boolean, | ||
val logger: Logger, | ||
val gistTokenProvider: Provider<String>, | ||
val projectName: String) { | ||
|
||
fun isEnabled(): Boolean { | ||
val isCapturingScan = !offline && !continuous | ||
if (!isCapturingScan) { | ||
logger.warn("Build is offline or continuous. Will not publish gist.") | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
fun captureGitDiffInGist(api: BuildScanExtension): Unit { | ||
val hasCredentials = gistTokenProvider.isPresent | ||
if (!hasCredentials) { | ||
logger.warn("User has not set 'gistToken'. Cannot publish gist.") | ||
return | ||
} | ||
|
||
val diff = execAndGetStdout("git", "diff") | ||
if (!diff.isNullOrEmpty()) { | ||
try { | ||
val baseUrl = java.net.URL("https://api.github.com/gists") | ||
val credentials = Base64.getEncoder().encodeToString(gistTokenProvider.get().toByteArray()) | ||
val basicAuth = "Basic ${credentials}" | ||
|
||
val connection = baseUrl.openConnection() as java.net.HttpURLConnection | ||
connection.apply { | ||
// request | ||
setRequestProperty("Authorization", basicAuth) | ||
setRequestProperty("Accept", "application/vnd.github+json") | ||
requestMethod = "POST" | ||
doOutput = true | ||
outputStream.bufferedWriter().use { writer -> | ||
jsonRequest(writer, diff) | ||
} | ||
|
||
// response | ||
val url = JsonSlurper().parse(content as java.io.InputStream).withGroovyBuilder { getProperty("html_url") } | ||
api.link("Git diff", url as String) | ||
} | ||
logger.info("Successfully published gist.") | ||
} catch (ex: Exception) { | ||
logger.warn("Unable to publish gist", ex) | ||
} | ||
} | ||
} | ||
|
||
// this method must be static, otherwise Gradle will interpret `files` as Project.files() and this won't work | ||
private fun jsonRequest(writer: java.io.Writer, diff: String): Unit { | ||
val json = groovy.json.JsonOutput.toJson(mapOf( | ||
"description" to "Git diff for ${projectName}", | ||
"public" to false, | ||
"files" to mapOf("${projectName}.diff" to mapOf("content" to diff)))) | ||
writer.write(json) | ||
} | ||
|
||
private fun execAndGetStdout(vararg args: String): String { | ||
val process = ProcessBuilder(*args) | ||
.redirectOutput(ProcessBuilder.Redirect.PIPE) | ||
.redirectError(ProcessBuilder.Redirect.PIPE) | ||
.start() | ||
try { | ||
val finished = process.waitFor(10, TimeUnit.SECONDS) | ||
val standardText = process.inputStream.bufferedReader().readText() | ||
val ignore = process.errorStream.bufferedReader().readText() | ||
|
||
return if (finished && process.exitValue() == 0) trimAtEnd(standardText) else return "" | ||
} finally { | ||
process.destroyForcibly() | ||
} | ||
} | ||
|
||
private fun trimAtEnd(str: String): String { | ||
return ("x" + str).trim().substring(1) | ||
} | ||
|
||
} |
47 changes: 47 additions & 0 deletions
47
build-data-capturing-gradle-samples/capture-os-processes/gradle-os-processes.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import com.gradle.enterprise.gradleplugin.GradleEnterpriseExtension | ||
import com.gradle.scan.plugin.BuildScanExtension | ||
import java.nio.charset.Charset | ||
import java.util.concurrent.TimeUnit | ||
|
||
/** | ||
* This Gradle script captures the OS processes as reported by the OS 'ps' command, | ||
* and adds these as a custom value. | ||
*/ | ||
|
||
project.extensions.configure<GradleEnterpriseExtension>() { | ||
buildScan { | ||
background { | ||
Capture.captureOsProcesses(buildScan) | ||
} | ||
} | ||
} | ||
|
||
class Capture { | ||
companion object { | ||
fun captureOsProcesses(api: BuildScanExtension): Unit { | ||
val psOutput = execAndGetStdout("ps", "-o pid,ppid,time,command") | ||
api.value("OS processes", psOutput) | ||
} | ||
|
||
private fun execAndGetStdout(vararg args: String): String { | ||
val process = ProcessBuilder(*args) | ||
.redirectOutput(ProcessBuilder.Redirect.PIPE) | ||
.redirectError(ProcessBuilder.Redirect.PIPE) | ||
.start() | ||
try { | ||
val finished = process.waitFor(10, TimeUnit.SECONDS) | ||
val standardText = process.inputStream.bufferedReader().readText() | ||
val ignore = process.errorStream.bufferedReader().readText() | ||
|
||
return if (finished && process.exitValue() == 0) trimAtEnd(standardText) else return "" | ||
} finally { | ||
process.destroyForcibly() | ||
} | ||
} | ||
|
||
private fun trimAtEnd(str: String): String { | ||
return ("x" + str).trim().substring(1) | ||
} | ||
} | ||
} | ||
|
72 changes: 72 additions & 0 deletions
72
build-data-capturing-gradle-samples/capture-processor-arch/gradle-processor-arch.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import com.gradle.enterprise.gradleplugin.GradleEnterpriseExtension | ||
import com.gradle.scan.plugin.BuildScanExtension | ||
import java.nio.charset.Charset | ||
import java.util.concurrent.TimeUnit | ||
|
||
/** | ||
* This Gradle script captures the processor architecture | ||
* and adds these as a custom value. | ||
*/ | ||
|
||
project.extensions.configure<GradleEnterpriseExtension>() { | ||
buildScan { | ||
background { | ||
Capture.captureProcessorArch(buildScan) | ||
} | ||
} | ||
} | ||
|
||
class Capture { | ||
companion object { | ||
fun captureProcessorArch(api: BuildScanExtension): Unit { | ||
val osName = System.getProperty("os.name") | ||
api.value("os.name", osName) | ||
|
||
val osArch = System.getProperty("os.arch") | ||
api.value("os.arch", osArch) | ||
|
||
if (isDarwin(osName)) { | ||
if (isTranslatedByRosetta()) { | ||
api.tag("M1-translated") | ||
} else if (isM1()) { | ||
api.tag("M1") | ||
} | ||
} | ||
} | ||
|
||
private fun isDarwin(osName: String): Boolean { | ||
return osName.contains("OS X") || osName.startsWith("Darwin") | ||
} | ||
|
||
private fun isM1(): Boolean { | ||
return execAndGetStdout("uname", "-p") == "arm" | ||
} | ||
|
||
// On Apple silicon, a universal binary may run either natively or as a translated binary | ||
// https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment#Determine-Whether-Your-App-Is-Running-as-a-Translated-Binary | ||
private fun isTranslatedByRosetta(): Boolean { | ||
return execAndGetStdout("sysctl", "sysctl.proc_translated") == "sysctl.proc_translated: 1" | ||
} | ||
|
||
private fun execAndGetStdout(vararg args: String): String { | ||
val process = ProcessBuilder(*args) | ||
.redirectOutput(ProcessBuilder.Redirect.PIPE) | ||
.redirectError(ProcessBuilder.Redirect.PIPE) | ||
.start() | ||
try { | ||
val finished = process.waitFor(10, TimeUnit.SECONDS) | ||
val standardText = process.inputStream.bufferedReader().readText() | ||
val ignore = process.errorStream.bufferedReader().readText() | ||
|
||
return if (finished && process.exitValue() == 0) trimAtEnd(standardText) else return "" | ||
} finally { | ||
process.destroyForcibly() | ||
} | ||
} | ||
|
||
private fun trimAtEnd(str: String): String { | ||
return ("x" + str).trim().substring(1) | ||
} | ||
} | ||
} | ||
|
129 changes: 129 additions & 0 deletions
129
...turing-gradle-samples/capture-quality-check-issues/gradle-quality-check-issues.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import com.gradle.enterprise.gradleplugin.GradleEnterpriseExtension | ||
import com.gradle.scan.plugin.BuildScanExtension | ||
import groovy.xml.XmlSlurper | ||
import groovy.xml.slurpersupport.NodeChildren | ||
import groovy.xml.slurpersupport.NodeChild | ||
import groovy.xml.slurpersupport.GPathResult | ||
|
||
/** | ||
* This Gradle script captures issues found by reporting tasks, | ||
* and adds these as custom values. | ||
*/ | ||
|
||
project.extensions.configure<GradleEnterpriseExtension>() { | ||
buildScan { | ||
gradle.taskGraph.beforeTask { | ||
if (ReportingTask.isSupported(this) && this is Reporting<*>) { | ||
val reportTask = this as Reporting<ReportContainer<*>> | ||
reportTask.reports.withGroovyBuilder { "xml" { setProperty("enabled", true) } } | ||
} else if (ReportingTask.SPOTBUGS.isTask(this)) { | ||
this.withGroovyBuilder { | ||
val reports = getProperty("reports") | ||
val xml = (reports as NamedDomainObjectContainer<*>).create("xml") { | ||
enabled = true | ||
} | ||
} | ||
} | ||
} | ||
|
||
gradle.taskGraph.afterTask { | ||
if (ReportingTask.isSupported(this)) { | ||
val reportFile = reportFromTask(this) | ||
if (reportFile.exists()) { | ||
val report = XmlSlurper().parse(reportFile) | ||
var valueName = "" | ||
var errors = mutableListOf<String>() | ||
|
||
if (ReportingTask.CHECKSTYLE.isTask(this)) { | ||
valueName = "Verification Checkstyle" | ||
val files = report.getProperty("file") as NodeChildren | ||
files.forEach { f -> | ||
val file = f as NodeChild | ||
val filePath = project.rootProject.relativePath(file.attributes()["name"]) | ||
val checkErrors = file.getProperty("error") as NodeChildren | ||
checkErrors.forEach { e -> | ||
val error = e as NodeChild | ||
errors.add("${filePath}:${error.attributes()["line"]}:${error.attributes()["column"]} \u2192 ${error.attributes()["message"]}") | ||
} | ||
} | ||
} else if (ReportingTask.CODENARC.isTask(this)) { | ||
valueName = "Verification CodeNarc" | ||
val packages = report.getProperty("Package") as NodeChildren | ||
packages.forEach { p -> | ||
val proj = report.getProperty("Project") as NodeChildren | ||
val sourceDirectoryNode = proj.getProperty("SourceDirectory") as NodeChildren | ||
val sourceDirectory = appendIfMissing(sourceDirectoryNode.text() as String, "/") | ||
val files = (p as NodeChild).getProperty("File") as NodeChildren | ||
files.forEach { f -> | ||
val file = f as NodeChild | ||
val filePath = | ||
project.rootProject.relativePath(sourceDirectory + file.attributes()["name"]) | ||
(file.getProperty("Violation") as NodeChildren).forEach { v -> | ||
val violation = v as NodeChild | ||
errors.add("${filePath}:${violation.attributes()["lineNumber"]} \u2192 ${violation.attributes()["ruleName"]}") | ||
} | ||
} | ||
} | ||
} else if (ReportingTask.SPOTBUGS.isTask(this)) { | ||
valueName = "Verification SpotBugs" | ||
val bugs = report.getProperty("BugInstance") as NodeChildren | ||
bugs.forEach { b -> | ||
val bug = b as NodeChild | ||
val type = bug.attributes()["type"] | ||
val error = bug.breadthFirst().asSequence().filter { l -> | ||
val line = l as NodeChild | ||
l.name() == "SourceLine" | ||
}.sortedBy { l -> | ||
(l as NodeChild).parent().name() | ||
}.first() as NodeChild | ||
val startLine = error.attributes()["start"] | ||
val endLine = error.attributes()["end"] | ||
val lineNumber = if (endLine == startLine) startLine else "${startLine}-${endLine}" | ||
val className = error.attributes()["classname"] | ||
errors.add("${className}:${lineNumber} \u2192 ${type}") | ||
} | ||
} | ||
errors.forEach { e -> buildScan.value(valueName, e) } | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
fun reportFromTask(task: Task): File { | ||
if (task is Reporting<*>) { | ||
val task = task as Reporting<ReportContainer<*>> | ||
return (task.reports.withGroovyBuilder { "xml" { getProperty("destination") } } as Report).getOutputLocation().get().asFile as File | ||
} else if (ReportingTask.SPOTBUGS.isTask(task)) { | ||
val reports = task.withGroovyBuilder { getProperty("reports") as NamedDomainObjectContainer<*> } | ||
val report = (reports.named("XML") as NamedDomainObjectProvider).get() as SingleFileReport | ||
return report.getOutputLocation().get().asFile | ||
} else { | ||
throw IllegalStateException("Unsupported report task: " + task) | ||
} | ||
} | ||
|
||
fun appendIfMissing(str: String, suffix: String): String { | ||
return if (str.endsWith(suffix)) str else str + suffix | ||
} | ||
|
||
enum class SpotBugsParent { | ||
BugInstance, Method, Class | ||
} | ||
|
||
enum class ReportingTask(val className: String) { | ||
|
||
CHECKSTYLE("org.gradle.api.plugins.quality.Checkstyle"), | ||
CODENARC("org.gradle.api.plugins.quality.CodeNarc"), | ||
SPOTBUGS("com.github.spotbugs.snom.SpotBugsTask"); | ||
|
||
fun isTask(task: Task): Boolean { | ||
return task::class.java.name.contains(className) | ||
} | ||
|
||
companion object { | ||
fun isSupported(task: Task): Boolean { | ||
return values().any { it.isTask(task) } | ||
} | ||
} | ||
} |
Oops, something went wrong.