Gradle Build Scans transformieren opake Build-Prozesse in transparente, analysierbare Datenstrukturen. Jeder Scan erfasst detaillierte Metriken über Build-Execution, Task-Performance, Dependency-Resolution und System-Umgebung. Diese Daten werden in einer webbasierten Oberfläche visualisiert, die Navigation durch Build-Timeline, Task-Dependencies und Performance-Metriken ermöglicht. Der primäre Nutzen liegt in der Identifikation von Performance-Bottlenecks, der Diagnose von Build-Fehlern und dem Verständnis von Build-Verhalten über Zeit und Teams hinweg.
Die Scan-Generierung erfolgt durch das --scan Flag oder
automatisiert via Plugin-Konfiguration. Build Scans werden zu Gradle
Enterprise Servern oder gradle.com hochgeladen, wo sie analysiert und
geteilt werden können. Die Persistierung ermöglicht historische
Vergleiche und Trend-Analysen. Teams nutzen Scans zur Kollaboration bei
Build-Problemen, da ein einfacher Link alle relevanten
Build-Informationen zugänglich macht.
Build Scans adressieren fundamentale Herausforderungen in der Build-Optimierung. Ohne Scans basiert Performance-Tuning auf Vermutungen und lokalem Profiling. Mit Scans werden Performance-Probleme quantifizierbar und vergleichbar. Die Aggregation von Scans über Teams zeigt systemische Probleme, die in einzelnen Builds nicht erkennbar wären. Diese Visibility transformiert Build-Performance von einem individuellen zu einem messbaren Team-Metric.
// Build Scan Konfiguration mit Gradle Enterprise
plugins {
id("com.gradle.enterprise") version "3.14"
}
gradleEnterprise {
buildScan {
// Server-Konfiguration für Self-Hosted Instance
server = "https://scans.company.internal"
// Publishing-Strategy
publishAlwaysIf(System.getenv("CI") == "true")
publishOnFailure()
// Capture Git Information
background {
val gitCommit = providers.exec {
commandLine("git", "rev-parse", "HEAD")
}.standardOutput.asText.get().trim()
val gitBranch = providers.exec {
commandLine("git", "rev-parse", "--abbrev-ref", "HEAD")
}.standardOutput.asText.get().trim()
val gitStatus = providers.exec {
commandLine("git", "status", "--porcelain")
}.standardOutput.asText.get()
value("Git Commit", gitCommit)
value("Git Branch", gitBranch)
tag(if (gitStatus.isEmpty()) "clean" else "dirty")
}
// Environment Classification
tag(when {
System.getenv("CI") != null -> "CI"
System.getenv("IDEA_INITIAL_DIRECTORY") != null -> "IDE"
else -> "CLI"
})
// Performance Metrics
buildFinished {
value("Peak JVM Memory",
"${Runtime.getRuntime().totalMemory() / 1024 / 1024} MB")
value("Available Processors",
Runtime.getRuntime().availableProcessors().toString())
// Custom Performance Analysis
val slowTasks = tasks
.filter { it.state.executed && it.duration > 5000 }
.sortedByDescending { it.duration }
.take(10)
slowTasks.forEachIndexed { index, task ->
value("Slow Task ${index + 1}",
"${task.path} (${task.duration}ms)")
}
}
// Failure Analysis
buildFinished { result ->
result.failure?.let { failure ->
value("Failure Type", failure.javaClass.simpleName)
value("Failure Message", failure.message ?: "No message")
// Stack trace sampling
val stackTrace = failure.stackTrace
.take(10)
.joinToString("\n") { it.toString() }
value("Failure Stack Sample", stackTrace)
}
}
}
}Build Scans erfassen umfassende Daten über Build-Execution und -Umgebung. Die Basis-Daten umfassen Build-Outcome, Duration, ausgeführte Tasks und deren Reihenfolge. Task-Level-Metriken dokumentieren Input-/Output-Fingerprints, Execution-Time, Cache-Status und Skip-Reasons. Diese granularen Daten ermöglichen präzise Performance-Analyse und Cache-Effectiveness-Bewertung.
System- und Umgebungsdaten kontextualisieren Build-Performance. Operating System, JVM-Version, verfügbare CPU-Cores und Memory werden erfasst. Gradle-Version, angewandte Plugins und aktivierte Features dokumentieren Build-Tool-Konfiguration. Environment-Variablen und System-Properties, gefiltert nach Sensitivität, komplettieren das Umgebungsbild. Diese Kontextdaten ermöglichen Cross-Platform-Vergleiche und Environment-spezifische Optimierungen.
// Data Capture Customization
gradleEnterprise {
buildScan {
// Selektive Datenerfassung
capture {
taskInputFiles = false // Reduziert Scan-Größe
buildLogging = true // Erfasst Console Output
testLogging = true // Erfasst Test Output
}
// Sensitive Data Filtering
obfuscation {
username { name ->
if (name.contains("service")) name else "REDACTED"
}
hostname { host ->
when {
host.endsWith(".internal") -> host
host.contains("prod") -> "production-host"
else -> "developer-machine"
}
}
ipAddress { ip ->
if (ip.startsWith("10.") || ip.startsWith("192.168.")) {
ip.substringBeforeLast(".") + ".XXX"
} else {
"EXTERNAL"
}
}
}
// Custom Data Points
val buildMetrics = mutableMapOf<String, String>()
gradle.taskGraph.whenReady {
buildMetrics["Total Tasks"] = allTasks.size.toString()
buildMetrics["Cacheable Tasks"] = allTasks.count { it.outputs.hasOutput }.toString()
value("Task Graph Metrics", buildMetrics.toString())
}
// Dependency Resolution Metrics
configurations.all {
incoming.afterResolve {
val resolvedCount = dependencies.size
val duration = resolutionDuration
value("Dependencies ${name}", "$resolvedCount in ${duration}ms")
// Large dependency detection
if (resolvedCount > 100) {
tag("large-dependencies")
value("Large Config", name)
}
}
}
}
}
// Build Scan Data Export
tasks.register("exportScanData") {
description = "Export build scan data for analysis"
doLast {
val scanDataFile = file("${buildDir}/scan-data.json")
val scanData = mapOf(
"timestamp" to Instant.now().toString(),
"project" to project.name,
"tasks" to tasks.map { task ->
mapOf(
"name" to task.name,
"type" to task.javaClass.simpleName,
"cacheable" to task.outputs.hasOutput,
"upToDate" to task.state.upToDate,
"executed" to task.state.executed
)
},
"properties" to project.properties.filterKeys {
!it.contains("password") && !it.contains("secret")
},
"repositories" to repositories.map { it.name },
"plugins" to plugins.map { it.javaClass.simpleName }
)
scanDataFile.writeText(groovy.json.JsonOutput.toJson(scanData))
}
}Dependency-Informationen dokumentieren Resolution-Prozesse und Ergebnisse. Aufgelöste Dependencies, Version-Conflicts und Resolution-Duration werden erfasst. Repository-Interactions zeigen Cache-Hits und Network-Requests. Diese Daten identifizieren langsame Repositories und problematische Dependencies. Build-Cache-Interaktionen tracken Cache-Keys, Hit-Rates und Store-Operations. Diese Metriken quantifizieren Cache-Effectiveness und identifizieren Cache-Miss-Ursachen.
Build Scans können sensitive Informationen enthalten, die Datenschutz-Compliance erfordern. Source-Code-Snippets in Error-Messages, Credentials in Environment-Variablen oder personenbezogene Daten in File-Paths stellen potentielle Datenlecks dar. Die DSGVO und andere Datenschutzgesetze verlangen bewussten Umgang mit solchen Daten. Unternehmen müssen klare Policies für Build-Scan-Usage etablieren und technische Maßnahmen zur Datensicherheit implementieren.
Die Datenminimierung reduziert Compliance-Risiken durch selektive Erfassung. Sensitive Environment-Variablen werden gefiltert oder gehashed. File-Paths werden normalisiert, um User-Directories zu obscurieren. IP-Adressen und Hostnames werden anonymisiert. Diese Techniken balancieren Analyse-Nutzen mit Datenschutz-Anforderungen. On-Premise-Hosting von Gradle Enterprise gibt volle Kontrolle über Daten-Lokalität und -Zugriff.
// Privacy-Focused Configuration
gradleEnterprise {
buildScan {
// Opt-in Model für Entwickler
val userConsent = file("${System.getProperty("user.home")}/.gradle/scan-consent")
publishAlwaysIf(userConsent.exists() && userConsent.readText().trim() == "yes")
// Data Sanitization
obfuscation {
// Environment Variable Filtering
systemProperty { name, value ->
when {
name.contains("password", ignoreCase = true) -> "***"
name.contains("token", ignoreCase = true) -> "***"
name.contains("key", ignoreCase = true) -> "***"
name.contains("secret", ignoreCase = true) -> "***"
else -> value
}
}
// Build Log Scrubbing
buildLogging {
it.replace(Regex("password=\\S+"), "password=***")
.replace(Regex("token:\\s*\\S+"), "token: ***")
.replace(Regex("/home/[^/]+"), "/home/USER")
.replace(Regex("C:\\\\Users\\\\[^\\\\]+"), "C:\\Users\\USER")
}
}
// Compliance Tags
tag("gdpr-compliant")
value("Data Retention", "30 days")
value("Anonymization Level", "high")
// Audit Trail
value("Scan Requester", System.getProperty("user.name").hashCode().toString())
value("Consent Timestamp", userConsent.lastModified().toString())
}
}
// GDPR Compliance Tasks
tasks.register("requestScanConsent") {
description = "Request user consent for build scan publishing"
doLast {
val consentFile = file("${System.getProperty("user.home")}/.gradle/scan-consent")
if (!consentFile.exists()) {
println("""
|Build Scan Consent Request
|===========================
|Build scans help improve build performance and diagnose issues.
|Scans may contain:
| - Build performance metrics
| - Task execution data
| - System environment information (anonymized)
| - Error messages and stack traces
|
|Data retention: 30 days
|Data location: EU servers only
|
|Do you consent to publishing build scans? (yes/no)
""".trimMargin())
val response = readLine()
consentFile.parentFile.mkdirs()
consentFile.writeText(response ?: "no")
if (response == "yes") {
logger.lifecycle("Consent granted. Build scans will be published.")
} else {
logger.lifecycle("Consent denied. Build scans will not be published.")
}
}
}
}
tasks.register("revokeScansConsent") {
description = "Revoke consent for build scan publishing"
doLast {
val consentFile = file("${System.getProperty("user.home")}/.gradle/scan-consent")
if (consentFile.exists()) {
consentFile.delete()
logger.lifecycle("Build scan consent revoked")
}
}
}
// Data Retention Management
tasks.register("enforceScanRetention") {
description = "Enforce build scan retention policy"
doLast {
// Requires Gradle Enterprise API access
val apiClient = GradleEnterpriseApiClient(
baseUrl = "https://scans.company.internal",
apiKey = System.getenv("GE_API_KEY")
)
val retentionDays = 30
val cutoffDate = Instant.now().minus(retentionDays, ChronoUnit.DAYS)
val scansToDelete = apiClient.getScans()
.filter { scan ->
Instant.parse(scan.timestamp).isBefore(cutoffDate)
}
scansToDelete.forEach { scan ->
apiClient.deleteScan(scan.id)
logger.lifecycle("Deleted scan ${scan.id} from ${scan.timestamp}")
}
logger.lifecycle("Retention policy enforced: ${scansToDelete.size} scans deleted")
}
}Access Control und Audit Trails sichern Build-Scan-Zugriff. Gradle Enterprise unterstützt LDAP/SAML-Integration für Authentication. Role-based Access Control limitiert Scan-Visibility nach Team oder Projekt. Audit Logs dokumentieren, wer wann auf welche Scans zugegriffen hat. Diese Controls erfüllen Compliance-Requirements und schützen sensitive Build-Informationen.
Build Scans ermöglichen systematische Performance-Analyse über Pattern-Recognition. Wiederkehrende Performance-Probleme werden durch Scan-Vergleiche identifiziert. Task-Duration-Trends zeigen schleichende Performance-Degradation. Cache-Hit-Rate-Patterns revealing ineffektive Cache-Configuration. Diese Patterns transformieren einzelne Datenpunkte in actionable Insights.
Comparative Analysis zwischen Builds identifiziert Performance-Regressionen. Baseline-Scans etablieren Performance-Erwartungen. Neue Builds werden gegen Baselines verglichen, und Deviations triggern Alerts. A/B-Testing von Build-Optimierungen quantifiziert deren Impact. Diese datengetriebene Approach macht Performance-Optimierung wissenschaftlich statt spekulativ.
// Scan Analysis Automation
tasks.register("analyzeScanTrends") {
description = "Analyze build scan trends"
doLast {
val scanHistory = fetchRecentScans(days = 7)
val analysis = performTrendAnalysis(scanHistory)
val reportFile = file("${buildDir}/reports/scan-trends.html")
reportFile.parentFile.mkdirs()
reportFile.writeText("""
<!DOCTYPE html>
<html>
<head>
<title>Build Scan Trend Analysis</title>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.metric { margin: 20px 0; padding: 15px; background: #f5f5f5; }
.warning { background: #fff3cd; border-left: 4px solid #ffc107; }
.critical { background: #f8d7da; border-left: 4px solid #dc3545; }
</style>
</head>
<body>
<h1>Build Performance Trends</h1>
<p>Analysis Period: ${analysis.startDate} to ${analysis.endDate}</p>
<div class="metric">
<h2>Build Duration Trend</h2>
<div id="durationChart"></div>
<p>Average: ${analysis.avgDuration}ms</p>
<p>Trend: ${analysis.durationTrend}</p>
</div>
<div class="metric ${if (analysis.cacheHitRate < 0.7) "warning" else ""}">
<h2>Cache Hit Rate</h2>
<div id="cacheChart"></div>
<p>Current: ${analysis.cacheHitRate * 100}%</p>
<p>Target: 70%</p>
</div>
<div class="metric">
<h2>Task Execution Patterns</h2>
<table>
<tr><th>Task</th><th>Avg Duration</th><th>Variance</th><th>Trend</th></tr>
${analysis.taskPatterns.joinToString("\n") { pattern ->
"""<tr>
<td>${pattern.task}</td>
<td>${pattern.avgDuration}ms</td>
<td>${pattern.variance}%</td>
<td>${pattern.trend}</td>
</tr>"""
}}
</table>
</div>
<div class="metric ${if (analysis.anomalies.isNotEmpty()) "critical" else ""}">
<h2>Detected Anomalies</h2>
${if (analysis.anomalies.isNotEmpty()) """
<ul>
${analysis.anomalies.joinToString("\n") { "<li>$it</li>" }}
</ul>
""" else "<p>No anomalies detected</p>"}
</div>
<script>
// Duration trend chart
Plotly.newPlot('durationChart', [{
x: ${analysis.timestamps.toJson()},
y: ${analysis.durations.toJson()},
type: 'scatter',
mode: 'lines+markers'
}], {
xaxis: { title: 'Time' },
yaxis: { title: 'Duration (ms)' }
});
// Cache hit rate chart
Plotly.newPlot('cacheChart', [{
x: ${analysis.timestamps.toJson()},
y: ${analysis.cacheHitRates.toJson()},
type: 'scatter',
mode: 'lines+markers',
line: { color: 'green' }
}], {
xaxis: { title: 'Time' },
yaxis: { title: 'Cache Hit Rate (%)', range: [0, 100] }
});
</script>
</body>
</html>
""".trimIndent())
logger.lifecycle("Trend analysis report: file://${reportFile.absolutePath}")
// Alert on critical findings
if (analysis.anomalies.isNotEmpty()) {
logger.warn("Critical anomalies detected:")
analysis.anomalies.forEach { logger.warn(" - $it") }
}
}
}
// Scan Comparison Task
tasks.register("compareScans") {
description = "Compare two build scans"
val scan1 = project.findProperty("scan1") as String?
val scan2 = project.findProperty("scan2") as String?
doLast {
requireNotNull(scan1) { "Provide scan1 property" }
requireNotNull(scan2) { "Provide scan2 property" }
val comparison = compareBuildScans(scan1, scan2)
logger.lifecycle("Build Scan Comparison")
logger.lifecycle("=====================")
logger.lifecycle("Scan 1: $scan1")
logger.lifecycle("Scan 2: $scan2")
logger.lifecycle("")
logger.lifecycle("Duration: ${comparison.duration1}ms → ${comparison.duration2}ms " +
"(${comparison.durationDiff:+}ms)")
logger.lifecycle("Tasks Executed: ${comparison.taskCount1} → ${comparison.taskCount2}")
logger.lifecycle("Cache Hit Rate: ${comparison.cacheRate1}% → ${comparison.cacheRate2}%")
logger.lifecycle("")
logger.lifecycle("Slowest Tasks Changed:")
comparison.taskDifferences
.sortedByDescending { it.durationDiff }
.take(10)
.forEach { diff ->
logger.lifecycle(" ${diff.task}: ${diff.duration1}ms → ${diff.duration2}ms")
}
}
}Team-Level-Analytics aggregieren Scans über Entwickler und Projekte. Average Build-Times per Developer identifizieren Environment-Probleme. Most-Failed-Tasks highlighting fragile Build-Komponenten. Cache-Effectiveness per Team zeigt Optimierungs-Potentiale. Diese Team-Metriken machen Build-Performance zum gemeinsamen Verantwortungsbereich statt individuellem Problem.
Gradle Enterprise erweitert Build-Scan-Capabilities für Unternehmens-Umgebungen. Self-Hosting garantiert Daten-Souveränität und Compliance. API-Access ermöglicht Custom-Integrations und Automationen. Dashboard-Features visualisieren Cross-Project-Metriken. Diese Enterprise-Features transformieren Build Scans von Debug-Tool zu Strategic-Asset.
API-basierte Automation extrahiert Scan-Daten für weitere Verarbeitung. Performance-Dashboards in Grafana konsumieren Scan-Metrics. Alerting-Systeme triggern bei Performance-Degradation. Machine-Learning-Models trainieren auf Historical Scans für Predictive Analytics. Diese Integrationen machen Build-Intelligence zum Teil der Software-Delivery-Pipeline.
// Enterprise Integration Configuration
tasks.register("configureScanIntegration") {
description = "Configure build scan enterprise integration"
doLast {
// Webhook Configuration for Scan Events
val webhookConfig = """
{
"url": "https://automation.company.com/scan-webhook",
"events": ["scan-published", "scan-failed"],
"headers": {
"Authorization": "Bearer ${System.getenv("WEBHOOK_TOKEN")}"
}
}
""".trimIndent()
// Metrics Export Configuration
val metricsExport = """
gradle.enterprise.metrics {
export {
enabled = true
endpoint = "https://metrics.company.com/influx"
interval = 60 // seconds
tags {
project = project.name
team = System.getenv("TEAM_NAME") ?: "unknown"
environment = System.getenv("ENV") ?: "dev"
}
}
}
""".trimIndent()
// Store configurations
file("${rootDir}/.gradle/enterprise-config.json").writeText(webhookConfig)
logger.lifecycle("Enterprise integration configured")
}
}
// Automated Scan Processing
abstract class ScanProcessorService : BuildService<ScanProcessorService.Params> {
interface Params : BuildServiceParameters {
val apiEndpoint: Property<String>
val apiKey: Property<String>
}
fun processScan(scanUrl: String) {
// Extract scan ID
val scanId = scanUrl.substringAfterLast("/")
// Fetch scan data via API
val scanData = fetchScanData(scanId)
// Process and store metrics
val metrics = extractMetrics(scanData)
storeMetrics(metrics)
// Check for issues
val issues = detectIssues(scanData)
if (issues.isNotEmpty()) {
notifyTeam(issues)
}
// Update team dashboard
updateDashboard(metrics)
}
}
// Register scan processor
val scanProcessor = gradle.sharedServices.registerIfAbsent(
"scanProcessor",
ScanProcessorService::class
) {
parameters {
apiEndpoint.set("https://scans.company.internal/api")
apiKey.set(providers.environmentVariable("GE_API_KEY"))
}
}
gradleEnterprise {
buildScan {
buildFinished {
if (buildScanPublished) {
scanProcessor.get().processScan(buildScanUri)
}
}
}
}Scan-Daten-Pipelines prozessieren Build-Intelligence systematisch. ETL-Prozesse extrahieren Scan-Daten, transformieren zu Business-Metrics und laden in Data-Warehouses. Analytics-Platforms ermöglichen Deep-Dive-Analysen. Predictive-Models forecasted Build-Times und Failure-Probabilities. Diese Data-Engineering-Approaches machen Build-Daten zu strategischen Business-Intelligence.