Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possibility to omit file breakdown #102

Merged
merged 6 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class ReleaseReportTest {
report.components.forEach { component ->
var downloadSize = 0L
var installSize = 0L
component.files.forEach { file ->
component.files?.forEach { file ->
downloadSize += file.downloadSize
installSize += file.installSize
}
Expand All @@ -111,7 +111,7 @@ class ReleaseReportTest {
report.dynamicFeatures.forEach { feature ->
var downloadSize = 0L
var installSize = 0L
feature.files.forEach { file ->
feature.files?.forEach { file ->
downloadSize += file.downloadSize
installSize += file.installSize
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,22 @@ fun RBuilder.containerListItem(id: Int, container: FileContainer, sizeType: Meas

@RFunction
fun RBuilder.containerListItemHeader(id: Int, container: FileContainer, sizeType: Measurable.SizeType) {
val containsFiles = container.files != null
h2(classes = "accordion-header") {
button(classes = "accordion-button collapsed") {
var classes = "accordion-button collapsed"
if (!containsFiles) {
classes = "$classes disabled"
botronic marked this conversation as resolved.
Show resolved Hide resolved
}
button(classes = classes) {
attrs["data-bs-toggle"] = "collapse"
attrs["data-bs-target"] = "#module-$id-body"
span(classes = "font-monospace text-truncate me-3") { +container.name }
container.owner?.let { owner -> span(classes = "badge bg-secondary me-3") { +owner } }
span(classes = "ms-auto me-3 text-nowrap") {
var sizeClasses = "ms-auto text-nowrap"
if (containsFiles) {
sizeClasses = "$sizeClasses me-3"
}
span(classes = sizeClasses) {
+formatSize(container, sizeType)
}
}
Expand All @@ -77,7 +86,7 @@ fun RBuilder.containerListItemBody(id: Int, container: FileContainer, sizeType:
div(classes = "accordion-collapse collapse") {
attrs.id = "module-$id-body"
div(classes = "accordion-body p-0") {
fileList(container.files, sizeType)
fileList(container.files ?: emptyList(), sizeType)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ fun RBuilder.report(report: AppReport) {

val hasOwnershipInfo = report.components.any { component -> component.owner != null }
val hasDynamicFeatures = report.dynamicFeatures.isNotEmpty()
val hasFileLevelInfo = report.components.any { it.files != null }
simonschiller marked this conversation as resolved.
Show resolved Hide resolved

val tabs = listOf(
Tab("/", "Breakdown") { breakdown(report.components, sizeType) },
Tab("/insights", "Insights") { insights(report.components) },
Tab("/ownership", "Ownership", hasOwnershipInfo) { ownership(report.components, sizeType) },
Tab("/insights", "Insights") { insights(report.components, hasFileLevelInfo) },
Tab("/ownership", "Ownership", hasOwnershipInfo) { ownership(report.components, hasFileLevelInfo, sizeType) },
Tab("/dynamic", "Dynamic features", hasDynamicFeatures) { dynamicFeatures(report.dynamicFeatures, sizeType) },
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.spotify.ruler.frontend.chart.BarChartConfig
import com.spotify.ruler.frontend.formatSize
import com.spotify.ruler.frontend.chart.seriesOf
import com.spotify.ruler.models.AppComponent
import com.spotify.ruler.models.AppFile
import com.spotify.ruler.models.Measurable
import kotlinx.browser.document
import kotlinx.html.id
Expand All @@ -33,26 +34,30 @@ import react.dom.p
import react.useEffect

@RFunction
fun RBuilder.insights(components: List<AppComponent>) {
fun RBuilder.insights(components: List<AppComponent>, hasFileLevelInfo: Boolean) {
div(classes = "row mb-3") {
simonschiller marked this conversation as resolved.
Show resolved Hide resolved
fileTypeGraphs(components)
}
div(classes = "row") {
componentTypeGraphs(components)
}
div(classes = "row") {
resourcesTypeGraphs(components)

if (hasFileLevelInfo) {
val componentFiles = components.mapNotNull(AppComponent::files).flatten()
div(classes = "row mb-3") {
fileTypeGraphs(componentFiles)
}
div(classes = "row") {
resourcesTypeGraphs(componentFiles)
}
}
}

@RFunction
fun RBuilder.fileTypeGraphs(components: List<AppComponent>) {
fun RBuilder.fileTypeGraphs(files: List<AppFile>) {
val labels = arrayOf("Classes", "Resources", "Assets", "Native libraries", "Other")
val downloadSizes = LongArray(labels.size)
val installSizes = LongArray(labels.size)
val fileCounts = LongArray(labels.size)

components.flatMap(AppComponent::files).forEach { file ->
files.forEach { file ->
val index = file.type.ordinal
downloadSizes[index] += file.getSize(Measurable.SizeType.DOWNLOAD)
installSizes[index] += file.getSize(Measurable.SizeType.INSTALL)
Expand Down Expand Up @@ -122,13 +127,13 @@ fun RBuilder.componentTypeGraphs(components: List<AppComponent>) {
}

@RFunction
fun RBuilder.resourcesTypeGraphs(components: List<AppComponent>) {
fun RBuilder.resourcesTypeGraphs(files: List<AppFile>) {
val labels = arrayOf("Drawable", "Layout", "Raw", "Values", "Font", "Other")
val downloadSizes = LongArray(labels.size)
val installSizes = LongArray(labels.size)
val fileCounts = LongArray(labels.size)

components.flatMap(AppComponent::files).filter { it.resourceType != null }.forEach { file ->
files.filter { it.resourceType != null }.forEach { file ->
val index = file.resourceType!!.ordinal
downloadSizes[index] += file.getSize(Measurable.SizeType.DOWNLOAD)
installSizes[index] += file.getSize(Measurable.SizeType.INSTALL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,14 @@ import react.useState
const val PAGE_SIZE = 10

@RFunction
fun RBuilder.ownership(components: List<AppComponent>, sizeType: Measurable.SizeType) {
fun RBuilder.ownership(components: List<AppComponent>, hasFileLevelInfo: Boolean, sizeType: Measurable.SizeType) {
componentOwnershipOverview(components)
componentOwnershipPerTeam(components, sizeType)
componentOwnershipPerTeam(components, hasFileLevelInfo, sizeType)
}

@RFunction
fun RBuilder.componentOwnershipOverview(components: List<AppComponent>) {
val sizes = mutableMapOf<String, Measurable.Mutable>()
components.flatMap(AppComponent::files).forEach { file ->
val owner = file.owner ?: return@forEach
val current = sizes.getOrPut(owner) { Measurable.Mutable(0, 0) }
current.downloadSize += file.downloadSize
current.installSize += file.installSize
}

val sizes = getSizesByOwner(components)
val sorted = sizes.entries.sortedByDescending { (_, measurable) -> measurable.downloadSize }
val owners = sorted.map { (owner, _) -> owner }
val downloadSizes = sorted.map { (_, measurable) -> measurable.downloadSize }
Expand All @@ -74,18 +67,33 @@ fun RBuilder.componentOwnershipOverview(components: List<AppComponent>) {
}

@RFunction
fun RBuilder.componentOwnershipPerTeam(components: List<AppComponent>, sizeType: Measurable.SizeType) {
simonschiller marked this conversation as resolved.
Show resolved Hide resolved
val files = components.flatMap(AppComponent::files)
val owners = files.mapNotNull(AppFile::owner).distinct().sorted()
fun RBuilder.componentOwnershipPerTeam(
components: List<AppComponent>,
hasFileLevelInfo: Boolean,
sizeType: Measurable.SizeType,
) {
val files: List<AppFile>?
var owners: List<String>
if (hasFileLevelInfo) {
files = components.mapNotNull(AppComponent::files).flatten()
owners = files.mapNotNull(AppFile::owner)
} else {
files = null
owners = components.mapNotNull(AppComponent::owner)
}
owners = owners.distinct().sorted()
var selectedOwner by useState(owners.first())

val ownedComponents = components.filter { component -> component.owner == selectedOwner }
val ownedFiles = files.filter { file -> file.owner == selectedOwner }
val ownedFiles = files?.filter { file -> file.owner == selectedOwner }

val remainingOwnedFiles = ownedFiles.toMutableSet()
val remainingOwnedFiles = ownedFiles?.toMutableSet()
val processedComponents = ownedComponents.map { component ->
val ownedFilesFromComponent = component.files.filter { file -> file.owner == selectedOwner }
remainingOwnedFiles.removeAll(ownedFilesFromComponent)
val ownedFilesFromComponent = component.files?.filter { file ->
file.owner == selectedOwner
} ?: return@map component

remainingOwnedFiles?.removeAll(ownedFilesFromComponent.toSet())
component.copy(
downloadSize = ownedFilesFromComponent.sumOf(AppFile::downloadSize),
installSize = ownedFilesFromComponent.sumOf(AppFile::installSize),
Expand All @@ -94,7 +102,7 @@ fun RBuilder.componentOwnershipPerTeam(components: List<AppComponent>, sizeType:
}.toMutableList()

// Group together all owned files which belong to components not owned by the currently selected owner
if (remainingOwnedFiles.isNotEmpty()) {
if (!remainingOwnedFiles.isNullOrEmpty()) {
processedComponents += AppComponent(
name = "Other owned files",
type = ComponentType.INTERNAL,
Expand All @@ -105,13 +113,25 @@ fun RBuilder.componentOwnershipPerTeam(components: List<AppComponent>, sizeType:
)
}

val downloadSize: Long
val installSize: Long
if (ownedFiles == null) {
// If there is no file-level ownership info, use component-level ownership info
downloadSize = ownedComponents.sumOf(AppComponent::downloadSize)
installSize = ownedComponents.sumOf(AppComponent::installSize)
} else {
// Otherwise rely on file-level ownership info
downloadSize = ownedFiles.sumOf(AppFile::downloadSize)
installSize = ownedFiles.sumOf(AppFile::installSize)
}

h4(classes = "mb-3 mt-4") { +"Components and files grouped by owner" }
dropdown(owners, "owner-dropdown") { owner -> selectedOwner = owner }
div(classes = "row mt-4 mb-4") {
highlightedValue(ownedComponents.size, "Component(s)")
highlightedValue(ownedFiles.size, "File(s)")
highlightedValue(ownedFiles.sumOf(AppFile::downloadSize), "Download size", ::formatSize)
highlightedValue(ownedFiles.sumOf(AppFile::installSize), "Install size", ::formatSize)
ownedFiles?.size?.let { highlightedValue(it, "File(s)") }
highlightedValue(downloadSize, "Download size", ::formatSize)
highlightedValue(installSize, "Install size", ::formatSize)
}
containerList(processedComponents, sizeType)
}
Expand All @@ -123,3 +143,28 @@ fun RBuilder.highlightedValue(value: Number, label: String, formatter: NumberFor
span(classes = "text-muted m-0") { +label }
}
}

private fun getSizesByOwner(components: List<AppComponent>): Map<String, Measurable> {
simonschiller marked this conversation as resolved.
Show resolved Hide resolved
val sizes = mutableMapOf<String, Measurable.Mutable>()

components.forEach { component ->
// If there is no file-level ownership info, use component-level ownership info
if (component.files == null) {
val owner = component.owner ?: return@forEach
val current = sizes.getOrPut(owner) { Measurable.Mutable(0, 0) }
current.downloadSize += component.downloadSize
current.installSize += component.installSize
return@forEach
}

// Otherwise rely on file-level ownership info
component.files?.forEach fileLevelLoop@ { file ->
val owner = file.owner ?: return@fileLevelLoop
val current = sizes.getOrPut(owner) { Measurable.Mutable(0, 0) }
current.downloadSize += file.downloadSize
current.installSize += file.installSize
}
}

return sizes
}
8 changes: 8 additions & 0 deletions ruler-frontend/src/main/resources/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ body {
margin-left: unset !important;
}

.accordion-button.disabled {
pointer-events: none;
}
simonschiller marked this conversation as resolved.
Show resolved Hide resolved

.accordion-button.disabled::after {
display: none;
}

.me-custom {
margin-right: calc(24px + 1rem);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ open class RulerExtension(objects: ObjectFactory) {
val ownershipFile: RegularFileProperty = objects.fileProperty()
val defaultOwner: Property<String> = objects.property(String::class.java)

val omitFileBreakdown: Property<Boolean> = objects.property(Boolean::class.java)

// Set up default values
init {
defaultOwner.convention("unknown")
omitFileBreakdown.convention(false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class RulerPlugin : Plugin<Project> {
task.workingDir.set(project.layout.buildDirectory.dir("intermediates/ruler/${variant.name}"))
task.reportDir.set(project.layout.buildDirectory.dir("reports/ruler/${variant.name}"))

task.omitFileBreakdown.set(rulerExtension.omitFileBreakdown)

// Add explicit dependency to support DexGuard
task.dependsOn("bundle$variantName")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ abstract class RulerTask : DefaultTask() {
@get:Input
abstract val defaultOwner: Property<String>

@get:Input
abstract val omitFileBreakdown: Property<Boolean>

@get:OutputDirectory
abstract val workingDir: DirectoryProperty

Expand Down Expand Up @@ -132,7 +135,14 @@ abstract class RulerTask : DefaultTask() {
val reportDir = reportDir.asFile.get()

val jsonReporter = JsonReporter()
val jsonReport = jsonReporter.generateReport(appInfo.get(), components, features, ownershipInfo, reportDir)
val jsonReport = jsonReporter.generateReport(
appInfo.get(),
components,
features,
ownershipInfo,
reportDir,
omitFileBreakdown.get()
)
project.logger.lifecycle("Wrote JSON report to ${jsonReport.toPath().toUri()}")

val htmlReporter = HtmlReporter()
Expand Down
Loading