From 63cb7099380dc5b44e13e019b11efb0764ecc889 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Wed, 27 Nov 2024 14:16:05 -0500 Subject: [PATCH] Huge changes, too many for one commit... but here we go! --- examples/build.gradle.kts | 43 ++- .../invert/examples/FakeCategoryRepo.kt | 3 +- .../squareup/invert/examples/FakeItemRepo.kt | 2 +- .../invert/examples/FakeNetworkGraph.kt | 8 +- .../squareup/invert/examples/FakeUserRepo.kt | 2 +- invert-collectors/build.gradle.kts | 18 ++ .../contains/LineContainsStatCollector.kt | 63 +++++ .../collectors/internal/FormattingUtils.kt | 7 + .../linesofcode/LinesOfCodeStatCollector.kt | 78 ++++++ .../invert/InvertAllCollectedDataRepo.kt | 2 +- .../invert/internal/GitDataCollector.kt | 9 + .../invert/internal/NoOpOwnershipCollector.kt | 2 +- .../internal/report/js/InvertJsReportUtils.kt | 11 +- .../report/json/InvertJsonReportWriter.kt | 4 +- .../tasks/ProjectMetadataCollector.kt | 15 +- .../js/CollectedStatTotalsJsReportModel.kt | 14 +- .../invert/models/js/MetadataJsReportModel.kt | 13 +- .../README.md | 0 .../build.gradle.kts | 3 +- .../github/pgreze/kowners/CodeOwnership.kt | 0 .../com/github/pgreze/kowners/FileUtils.kt | 0 .../com/github/pgreze/kowners/GitHelper.kt | 0 .../com/github/pgreze/kowners/GitUtils.kt | 0 .../github/pgreze/kowners/OwnersResolver.kt | 0 .../com/github/pgreze/kowners/Pattern.kt | 0 ...itHubCodeOwnersInvertOwnershipCollector.kt | 45 +++ .../pgreze/kowners/CodeOwnershipTest.kt | 0 .../pgreze/kowners/OwnersResolverTest.kt | 0 .../com/github/pgreze/kowners/PatternTest.kt | 0 invert-report/src/jsMain/kotlin/External.kt | 2 + .../kotlin/com/squareup/invert/common/Ext.kt | 2 +- .../squareup/invert/common/InvertReport.kt | 2 - .../squareup/invert/common/charts/ChartsJs.kt | 228 +++++++++++----- .../invert/common/navigation/NavGroupsRepo.kt | 2 - .../common/pages/AllModulesReportPage.kt | 152 +++++------ .../invert/common/pages/AllStatsReportPage.kt | 25 +- .../common/pages/CodeReferencesReportPage.kt | 61 ++++- .../invert/common/pages/HomeReportPage.kt | 21 +- .../common/pages/OwnerBreakdownReportPage.kt | 34 ++- .../invert/common/pages/OwnersReportPage.kt | 2 +- .../pages/SupressAnnotationReportPage.kt | 256 ------------------ .../invert/common/utils/FormattingUtils.kt | 4 +- .../navigation/NavigationComposables.kt | 14 +- .../jsMain/kotlin/ui/BootstrapComposables.kt | 133 ++++++--- ...itHubCodeOwnersInvertOwnershipCollector.kt | 44 --- settings.gradle.kts | 13 +- 46 files changed, 749 insertions(+), 588 deletions(-) create mode 100644 invert-collectors/build.gradle.kts create mode 100644 invert-collectors/src/main/kotlin/com/squareup/invert/collectors/contains/LineContainsStatCollector.kt create mode 100644 invert-collectors/src/main/kotlin/com/squareup/invert/collectors/internal/FormattingUtils.kt create mode 100644 invert-collectors/src/main/kotlin/com/squareup/invert/collectors/linesofcode/LinesOfCodeStatCollector.kt rename {owners/owners-github-codeowners => invert-owners-github}/README.md (100%) rename {owners/owners-github-codeowners => invert-owners-github}/build.gradle.kts (72%) rename {owners/owners-github-codeowners => invert-owners-github}/src/main/kotlin/com/github/pgreze/kowners/CodeOwnership.kt (100%) rename {owners/owners-github-codeowners => invert-owners-github}/src/main/kotlin/com/github/pgreze/kowners/FileUtils.kt (100%) rename {owners/owners-github-codeowners => invert-owners-github}/src/main/kotlin/com/github/pgreze/kowners/GitHelper.kt (100%) rename {owners/owners-github-codeowners => invert-owners-github}/src/main/kotlin/com/github/pgreze/kowners/GitUtils.kt (100%) rename {owners/owners-github-codeowners => invert-owners-github}/src/main/kotlin/com/github/pgreze/kowners/OwnersResolver.kt (100%) rename {owners/owners-github-codeowners => invert-owners-github}/src/main/kotlin/com/github/pgreze/kowners/Pattern.kt (100%) create mode 100644 invert-owners-github/src/main/kotlin/com/squareup/invert/owners/GitHubCodeOwnersInvertOwnershipCollector.kt rename {owners/owners-github-codeowners => invert-owners-github}/src/test/kotlin/com/github/pgreze/kowners/CodeOwnershipTest.kt (100%) rename {owners/owners-github-codeowners => invert-owners-github}/src/test/kotlin/com/github/pgreze/kowners/OwnersResolverTest.kt (100%) rename {owners/owners-github-codeowners => invert-owners-github}/src/test/kotlin/com/github/pgreze/kowners/PatternTest.kt (100%) delete mode 100644 invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/SupressAnnotationReportPage.kt delete mode 100644 owners/owners-github-codeowners/src/main/kotlin/com/squareup/invert/GitHubCodeOwnersInvertOwnershipCollector.kt diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index f10fae6..2483991 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -10,9 +10,8 @@ buildscript { dependencies { val invertVersion = "0.0.3-dev-SNAPSHOT" classpath("com.squareup.invert:invert-gradle-plugin:$invertVersion") -// classpath("com.squareup.invert:collectors-anvil-dagger:$invertVersion") -// classpath("com.squareup.invert:collectors-kotlin-java-loc:$invertVersion") -// classpath("com.squareup.invert:owners-github-codeowners:$invertVersion") + classpath("com.squareup.invert:invert-collectors:$invertVersion") + classpath("com.squareup.invert:invert-owners-github:$invertVersion") } } @@ -26,12 +25,40 @@ allprojects { } plugins { - id("com.squareup.invert") + id("com.squareup.invert") } invert { -// ownershipCollector(com.squareup.invert.GitHubCodeOwnersInvertOwnershipCollector) -// addStatCollector(com.squareup.invert.DiProvidesAndInjectsStatCollector()) -// addStatCollector(com.squareup.invert.suppress.SuppressionsStatCollector()) -// addStatCollector(com.squareup.invert.LinesOfCodeStatCollector()) + + ownershipCollector(com.squareup.invert.owners.GitHubCodeOwnersInvertOwnershipCollector) + addStatCollector( + com.squareup.invert.collectors.linesofcode.LinesOfCodeStatCollector( + name = "Kotlin", + fileExtensions = listOf("kt", "kts"), + ) + ) + addStatCollector( + com.squareup.invert.collectors.contains.LineContainsStatCollector( + statKey = "wildcard-imports", + statDescription = "Wildcard Imports", + linePredicate = { it.contains("import") && it.contains("*") }, + filePredicate = { it.extension == "kt" || it.extension == "kts" }, + ) + ) +} + +/** + * Will copy the report js files into invert so we can use them in dev cycles + */ +tasks.register("copyJsFiles") { + doLast { + copy { + from(fileTree("build/reports/invert/js") { include("*.js") }) // Replace "src/js" with your source directory + into("../invert-report/src/jsMain/resources/js") // Replace "build/js" with your target directory + } + } +} + +afterEvaluate { + rootProject.tasks.findByName("invert")!!.finalizedBy("copyJsFiles") } diff --git a/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeCategoryRepo.kt b/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeCategoryRepo.kt index 354b2e0..f2bf992 100644 --- a/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeCategoryRepo.kt +++ b/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeCategoryRepo.kt @@ -7,7 +7,6 @@ import com.squareup.invert.examples.repository.CategoryRepo import com.squareup.invert.examples.scopes.AppScope import javax.inject.Inject - @ContributesBinding(AppScope::class) class FakeCategoryRepo @Inject constructor( private val mockAccount: MockAccount @@ -17,4 +16,4 @@ class FakeCategoryRepo @Inject constructor( val categories = mockAccount.getCategories() return Response.Success(categories) } -} \ No newline at end of file +} diff --git a/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeItemRepo.kt b/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeItemRepo.kt index 72a3792..8bd88ff 100644 --- a/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeItemRepo.kt +++ b/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeItemRepo.kt @@ -19,4 +19,4 @@ class FakeItemRepo @Inject constructor( return Response.Failure() } } -} \ No newline at end of file +} diff --git a/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeNetworkGraph.kt b/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeNetworkGraph.kt index a053291..888e8a2 100644 --- a/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeNetworkGraph.kt +++ b/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeNetworkGraph.kt @@ -10,7 +10,7 @@ import javax.inject.Inject @ContributesBinding(AppScope::class) class FakeNetworkGraph @Inject constructor( - override val categoryRepo: CategoryRepo, - override val itemRepo: ItemRepo, - override val userRepo: UserRepo -) : NetworkGraph \ No newline at end of file + override val categoryRepo: CategoryRepo, + override val itemRepo: ItemRepo, + override val userRepo: UserRepo +) : NetworkGraph diff --git a/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeUserRepo.kt b/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeUserRepo.kt index 3b9a456..1115372 100644 --- a/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeUserRepo.kt +++ b/examples/networking/fake/src/main/java/com/squareup/invert/examples/FakeUserRepo.kt @@ -15,4 +15,4 @@ class FakeUserRepo @Inject constructor( override suspend fun login(loginRequest: LoginRequest): Response { return Response.Success(mockAccount.getUser()) } -} \ No newline at end of file +} diff --git a/invert-collectors/build.gradle.kts b/invert-collectors/build.gradle.kts new file mode 100644 index 0000000..c87d4b6 --- /dev/null +++ b/invert-collectors/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin("jvm") + alias(libs.plugins.vanniktech.maven.publish) + alias(libs.plugins.dokka) +} + +java { + withSourcesJar() +} + +dependencies { + compileOnly(gradleApi()) + + implementation(project(":invert-models")) + implementation(project(":invert-gradle-plugin")) + + testImplementation(libs.kotlin.test) +} diff --git a/invert-collectors/src/main/kotlin/com/squareup/invert/collectors/contains/LineContainsStatCollector.kt b/invert-collectors/src/main/kotlin/com/squareup/invert/collectors/contains/LineContainsStatCollector.kt new file mode 100644 index 0000000..a16f952 --- /dev/null +++ b/invert-collectors/src/main/kotlin/com/squareup/invert/collectors/contains/LineContainsStatCollector.kt @@ -0,0 +1,63 @@ +package com.squareup.invert.collectors.contains + +import com.squareup.invert.CollectedStat +import com.squareup.invert.InvertProjectData +import com.squareup.invert.StatCollector +import com.squareup.invert.collectors.internal.wrapCodeForMarkdown +import com.squareup.invert.models.Stat +import com.squareup.invert.models.StatDataType +import com.squareup.invert.models.StatMetadata +import java.io.File + +open class LineContainsStatCollector( + private val statKey: String, + private val statDescription: String, + private val linePredicate: (String) -> Boolean, + private val filePredicate: (File) -> Boolean = { true }, +) : StatCollector { + override fun collect( + invertProjectData: InvertProjectData, + ): List? { + val codeReferences = mutableListOf() + invertProjectData.projectDir + .walkTopDown() + .filter { it.isFile && it.length() > 0 } + .filter { filePredicate(it) } + .forEach { sourceFile -> + val relativeFilePath = sourceFile.relativeTo(invertProjectData.rootProjectDir).path + sourceFile.readLines() + .map { it.trim() } + .forEachIndexed { index, line -> + if (linePredicate(line)) { + codeReferences.add( + Stat.CodeReferencesStat.CodeReference( + filePath = relativeFilePath, + startLine = index + 1, + endLine = index + 1, + code = line.wrapCodeForMarkdown(), + ) + ) + } + } + } + + return if (codeReferences.isNotEmpty()) { + listOf( + CollectedStat( + metadata = StatMetadata( + key = statKey, + description = statDescription, + dataType = StatDataType.CODE_REFERENCES, + ), + stat = Stat.CodeReferencesStat(codeReferences) + ) + ) + } else { + null + } + } + + override fun getName(): String { + return this::class.java.name + } +} \ No newline at end of file diff --git a/invert-collectors/src/main/kotlin/com/squareup/invert/collectors/internal/FormattingUtils.kt b/invert-collectors/src/main/kotlin/com/squareup/invert/collectors/internal/FormattingUtils.kt new file mode 100644 index 0000000..aea866a --- /dev/null +++ b/invert-collectors/src/main/kotlin/com/squareup/invert/collectors/internal/FormattingUtils.kt @@ -0,0 +1,7 @@ +package com.squareup.invert.collectors.internal + +internal fun String.wrapCodeForMarkdown(language: String = "") = buildString { + appendLine("```$language") + appendLine(this@wrapCodeForMarkdown) + appendLine("```") +} \ No newline at end of file diff --git a/invert-collectors/src/main/kotlin/com/squareup/invert/collectors/linesofcode/LinesOfCodeStatCollector.kt b/invert-collectors/src/main/kotlin/com/squareup/invert/collectors/linesofcode/LinesOfCodeStatCollector.kt new file mode 100644 index 0000000..7a718c5 --- /dev/null +++ b/invert-collectors/src/main/kotlin/com/squareup/invert/collectors/linesofcode/LinesOfCodeStatCollector.kt @@ -0,0 +1,78 @@ +package com.squareup.invert.collectors.linesofcode + +import com.squareup.invert.CollectedStat +import com.squareup.invert.InvertProjectData +import com.squareup.invert.StatCollector +import com.squareup.invert.models.Stat +import com.squareup.invert.models.StatDataType +import com.squareup.invert.models.StatMetadata +import com.squareup.invert.projectSrcDir +import java.io.File + +open class LinesOfCodeStatCollector( + name: String, + private val fileExtensions: List, + keySuffix: String = fileExtensions.joinToString(","), + private val sourcesDirectory: (InvertProjectData) -> File = { + it.projectSrcDir + }, +) : StatCollector { + + companion object { + const val STAT_CATEGORY_LINES_OF_CODE = "Lines of Code" + } + + private val fileCountStatMetadata: StatMetadata = StatMetadata( + key = "file_count_$keySuffix", + description = "File Count - $name", + dataType = StatDataType.NUMERIC, + category = STAT_CATEGORY_LINES_OF_CODE, + ) + + private val linesOfCodeStatMetadata: StatMetadata = StatMetadata( + key = "lines_of_code_$keySuffix", + description = "Lines of Code - $name", + dataType = StatDataType.NUMERIC, + category = STAT_CATEGORY_LINES_OF_CODE, + ) + + override fun collect( + invertProjectData: InvertProjectData, + ): List? { + val matchingSourceFiles = sourcesDirectory(invertProjectData) + .walkTopDown() + .filter { file -> file.isFile } + .filter { fileExtensions.contains(it.extension) } + .toList() + + return if (matchingSourceFiles.isNotEmpty()) { + var totalLoc = 0 + matchingSourceFiles + .map { it.readLines().filter { line -> line.isNotBlank() } } + .forEach { + totalLoc += it.size + } + + listOf( + CollectedStat( + metadata = fileCountStatMetadata, + stat = Stat.NumericStat( + value = matchingSourceFiles.size, + ) + ), + CollectedStat( + metadata = linesOfCodeStatMetadata, + stat = Stat.NumericStat( + value = totalLoc, + ) + ) + ) + } else { + null + } + } + + override fun getName(): String { + return this::class.java.name + } +} diff --git a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/InvertAllCollectedDataRepo.kt b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/InvertAllCollectedDataRepo.kt index 83d7de6..f9448fe 100644 --- a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/InvertAllCollectedDataRepo.kt +++ b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/InvertAllCollectedDataRepo.kt @@ -19,7 +19,7 @@ class InvertAllCollectedDataRepo( private val projectMetadata: MetadataJsReportModel, ) { - val httpsRemoteRepoUrlForCommit: String = "${projectMetadata.remoteRepoUrl}/blob/${projectMetadata.gitSha}" + val httpsRemoteRepoUrlForCommit: String = "${projectMetadata.remoteRepoUrl}/blob/${projectMetadata.latestCommitGitSha}" val mavenRepoUrls = projectMetadata.mavenRepoUrls diff --git a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/GitDataCollector.kt b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/GitDataCollector.kt index 032a317..1b36457 100644 --- a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/GitDataCollector.kt +++ b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/GitDataCollector.kt @@ -63,6 +63,15 @@ internal class GitDataCollector(private val gitProjectRootDir: File) { return exec("git config --get remote.origin.url", gitProjectRootDir).stdOut.lines()[0] } + /** + * + * Example output of the command is: + * 1732637664 + */ + fun latestCommitTimestamp(): Long { + return exec("git log -1 --format=%ct", gitProjectRootDir).stdOut.lines()[0].toLong() + } + fun gitShaOfBranch(branchName: GitBranch, logger: InvertLogger): GitSha { val command = buildString { append("git log -n 1 ") diff --git a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/NoOpOwnershipCollector.kt b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/NoOpOwnershipCollector.kt index 5f550b8..445a5db 100644 --- a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/NoOpOwnershipCollector.kt +++ b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/NoOpOwnershipCollector.kt @@ -11,5 +11,5 @@ object NoOpOwnershipCollector : InvertOwnershipCollector { override fun collect( rootProjectDir: String, modulePath: ModulePath - ): OwnerInfo? = null + ): OwnerInfo? = OwnerInfo("None") } diff --git a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/report/js/InvertJsReportUtils.kt b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/report/js/InvertJsReportUtils.kt index 650070e..36f5b71 100644 --- a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/report/js/InvertJsReportUtils.kt +++ b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/report/js/InvertJsReportUtils.kt @@ -6,11 +6,11 @@ import com.squareup.invert.internal.models.CollectedOwnershipForProject import com.squareup.invert.internal.models.CollectedPluginsForProject import com.squareup.invert.internal.models.CollectedStatsForProject import com.squareup.invert.models.ConfigurationName -import com.squareup.invert.models.StatDataType import com.squareup.invert.models.DependencyId -import com.squareup.invert.models.ModulePath import com.squareup.invert.models.GradlePluginId +import com.squareup.invert.models.ModulePath import com.squareup.invert.models.Stat +import com.squareup.invert.models.StatDataType import com.squareup.invert.models.StatKey import com.squareup.invert.models.StatMetadata import com.squareup.invert.models.js.ConfigurationsJsReportModel @@ -18,6 +18,7 @@ import com.squareup.invert.models.js.DependenciesJsReportModel import com.squareup.invert.models.js.DirectDependenciesJsReportModel import com.squareup.invert.models.js.OwnershipJsReportModel import com.squareup.invert.models.js.PluginsJsReportModel +import com.squareup.invert.models.js.StatTotalAndMetadata import com.squareup.invert.models.js.StatsJsReportModel /** @@ -41,7 +42,7 @@ object InvertJsReportUtils { ) } - fun computeGlobalStats(allProjectsStatsData: StatsJsReportModel): Map { + fun computeGlobalStats(allProjectsStatsData: StatsJsReportModel): Map { val globalStats: Map = allProjectsStatsData.statInfos.values .filter { statInfo -> when (statInfo.dataType) { @@ -54,7 +55,7 @@ object InvertJsReportUtils { } } } - .associateWith { statMetadata -> + .associateWith { statMetadata: StatMetadata -> val statKey = statMetadata.key allProjectsStatsData.statsByModule.values.sumOf { statsForModule: Map -> val stat: Stat? = statsForModule[statKey] @@ -73,7 +74,7 @@ object InvertJsReportUtils { } } }.toMap() - return globalStats + return globalStats.entries.associate { it.key.key to StatTotalAndMetadata(it.key, it.value) } } /** diff --git a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/report/json/InvertJsonReportWriter.kt b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/report/json/InvertJsonReportWriter.kt index 296ff16..71799c5 100644 --- a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/report/json/InvertJsonReportWriter.kt +++ b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/report/json/InvertJsonReportWriter.kt @@ -9,9 +9,11 @@ import com.squareup.invert.internal.models.InvertPluginFileKey import com.squareup.invert.logging.InvertLogger import com.squareup.invert.logging.SystemOutInvertLogger import com.squareup.invert.models.InvertSerialization.InvertJson +import com.squareup.invert.models.StatKey import com.squareup.invert.models.StatMetadata import com.squareup.invert.models.js.CollectedStatTotalsJsReportModel import com.squareup.invert.models.js.MetadataJsReportModel +import com.squareup.invert.models.js.StatTotalAndMetadata import com.squareup.invert.models.js.StatsJsReportModel import kotlinx.serialization.KSerializer import kotlinx.serialization.builtins.ListSerializer @@ -28,7 +30,7 @@ class InvertJsonReportWriter( allProjectsStatsData: StatsJsReportModel, allPluginsData: List, allOwnersData: List, - globalStats: Map, + globalStats: Map, reportMetadata: MetadataJsReportModel, ) { writeJsonFileInDir( diff --git a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/tasks/ProjectMetadataCollector.kt b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/tasks/ProjectMetadataCollector.kt index f3c5acb..b72791f 100644 --- a/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/tasks/ProjectMetadataCollector.kt +++ b/invert-gradle-plugin/src/main/kotlin/com/squareup/invert/internal/tasks/ProjectMetadataCollector.kt @@ -30,6 +30,8 @@ object ProjectMetadataCollector { val currentBranch: GitBranch = gitDataCollector.currentBranch() val currentBranchHash = gitDataCollector.gitShaOfBranch(currentBranch, logger) + val latestCommitTimestamp = gitDataCollector.latestCommitTimestamp() + val remoteGitRepoUrl = gitDataCollector.remoteGitRepoUrl() val remoteRepoUrl = if (remoteGitRepoUrl.endsWith(".git")) { GitDataCollector.remoteRepoGitUrlToHttps( @@ -40,13 +42,14 @@ object ProjectMetadataCollector { } return MetadataJsReportModel( - time = time.epochSecond, - timeStr = formatter.format(time), - gitSha = currentBranchHash, + currentTime = time.epochSecond, + currentTimeStr = formatter.format(time), + currentTimezoneId = timeZoneId, + latestCommitTime = latestCommitTimestamp, + latestCommitTimeFormatted = formatter.format(Instant.ofEpochSecond(latestCommitTimestamp)), + latestCommitGitSha = currentBranchHash, branchName = currentBranch, - timezoneId = timeZoneId, - currentBranch = currentBranch, - currentBranchHash = currentBranchHash, + latestCommitSha = currentBranchHash, remoteRepoGit = remoteGitRepoUrl, remoteRepoUrl = remoteRepoUrl, mavenRepoUrls = repoUrls, diff --git a/invert-models/src/commonMain/kotlin/com/squareup/invert/models/js/CollectedStatTotalsJsReportModel.kt b/invert-models/src/commonMain/kotlin/com/squareup/invert/models/js/CollectedStatTotalsJsReportModel.kt index 0d8ba3d..a1b1ecf 100644 --- a/invert-models/src/commonMain/kotlin/com/squareup/invert/models/js/CollectedStatTotalsJsReportModel.kt +++ b/invert-models/src/commonMain/kotlin/com/squareup/invert/models/js/CollectedStatTotalsJsReportModel.kt @@ -1,14 +1,16 @@ package com.squareup.invert.models.js +import com.squareup.invert.models.StatKey import com.squareup.invert.models.StatMetadata import kotlinx.serialization.Serializable -/** - * Collected ownership representation for a single module - * - * Used by [InvertPluginFileKey.OWNERS] - */ @Serializable data class CollectedStatTotalsJsReportModel( - val statTotals: Map + val statTotals: Map +) + +@Serializable +data class StatTotalAndMetadata( + val metadata: StatMetadata, + val total: Int, ) diff --git a/invert-models/src/commonMain/kotlin/com/squareup/invert/models/js/MetadataJsReportModel.kt b/invert-models/src/commonMain/kotlin/com/squareup/invert/models/js/MetadataJsReportModel.kt index f80f249..cff0691 100644 --- a/invert-models/src/commonMain/kotlin/com/squareup/invert/models/js/MetadataJsReportModel.kt +++ b/invert-models/src/commonMain/kotlin/com/squareup/invert/models/js/MetadataJsReportModel.kt @@ -9,13 +9,14 @@ import kotlinx.serialization.Serializable */ @Serializable data class MetadataJsReportModel( - val time: Long, - val timezoneId: String, - val timeStr: String, - val gitSha: GitSha?, + val currentTime: Long, + val currentTimeStr: String, + val currentTimezoneId: String, + val latestCommitSha: GitSha, + val latestCommitTime: Long, + val latestCommitTimeFormatted: String, + val latestCommitGitSha: GitSha?, val branchName: GitBranch?, - val currentBranch: String, - val currentBranchHash: GitSha, val remoteRepoGit: String, val remoteRepoUrl: String, val mavenRepoUrls: List diff --git a/owners/owners-github-codeowners/README.md b/invert-owners-github/README.md similarity index 100% rename from owners/owners-github-codeowners/README.md rename to invert-owners-github/README.md diff --git a/owners/owners-github-codeowners/build.gradle.kts b/invert-owners-github/build.gradle.kts similarity index 72% rename from owners/owners-github-codeowners/build.gradle.kts rename to invert-owners-github/build.gradle.kts index d3746ae..558993b 100644 --- a/owners/owners-github-codeowners/build.gradle.kts +++ b/invert-owners-github/build.gradle.kts @@ -1,7 +1,8 @@ plugins { kotlin("jvm") id("java-gradle-plugin") -// alias(libs.plugins.vanniktech.maven.publish) + alias(libs.plugins.vanniktech.maven.publish) + alias(libs.plugins.dokka) } dependencies { diff --git a/owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/CodeOwnership.kt b/invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/CodeOwnership.kt similarity index 100% rename from owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/CodeOwnership.kt rename to invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/CodeOwnership.kt diff --git a/owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/FileUtils.kt b/invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/FileUtils.kt similarity index 100% rename from owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/FileUtils.kt rename to invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/FileUtils.kt diff --git a/owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/GitHelper.kt b/invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/GitHelper.kt similarity index 100% rename from owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/GitHelper.kt rename to invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/GitHelper.kt diff --git a/owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/GitUtils.kt b/invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/GitUtils.kt similarity index 100% rename from owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/GitUtils.kt rename to invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/GitUtils.kt diff --git a/owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/OwnersResolver.kt b/invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/OwnersResolver.kt similarity index 100% rename from owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/OwnersResolver.kt rename to invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/OwnersResolver.kt diff --git a/owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/Pattern.kt b/invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/Pattern.kt similarity index 100% rename from owners/owners-github-codeowners/src/main/kotlin/com/github/pgreze/kowners/Pattern.kt rename to invert-owners-github/src/main/kotlin/com/github/pgreze/kowners/Pattern.kt diff --git a/invert-owners-github/src/main/kotlin/com/squareup/invert/owners/GitHubCodeOwnersInvertOwnershipCollector.kt b/invert-owners-github/src/main/kotlin/com/squareup/invert/owners/GitHubCodeOwnersInvertOwnershipCollector.kt new file mode 100644 index 0000000..38b7ddf --- /dev/null +++ b/invert-owners-github/src/main/kotlin/com/squareup/invert/owners/GitHubCodeOwnersInvertOwnershipCollector.kt @@ -0,0 +1,45 @@ +package com.squareup.invert.owners + +import com.github.pgreze.kowners.OwnersResolver +import com.github.pgreze.kowners.findCodeOwnerLocations +import com.github.pgreze.kowners.findGitRootPath +import com.github.pgreze.kowners.parseCodeOwners +import com.squareup.invert.InvertOwnershipCollector +import com.squareup.invert.models.ModulePath +import com.squareup.invert.models.OwnerInfo +import java.io.File + +object GitHubCodeOwnersInvertOwnershipCollector : InvertOwnershipCollector { + override fun collect(rootProjectDir: String, modulePath: ModulePath): OwnerInfo? { + + val gitRoot = File(rootProjectDir).findGitRootPath() + ?: throw IllegalStateException("This is not a Git Repository. Could not locate the .git folder at the root.") + + val CODEOWNERS_FILES = gitRoot.findCodeOwnerLocations() + + val allLines = mutableListOf() + CODEOWNERS_FILES + .onEach { println(it.path) } + .filter { it.exists() && it.isFile } + .forEach { allLines.addAll(it.readLines()) } + + val ownersResolver by lazy { + OwnersResolver( + allLines.parseCodeOwners() + ) + } + + return OwnerInfo( + if (allLines.isEmpty()) { + "No CODEOWNERS File at ${CODEOWNERS_FILES.map { it.path }}" + } else { + // TODO FIX THIS COMPUTATION + val projectDir = File(rootProjectDir, modulePath.drop(1).replace(":", "/")) + val relativePath = projectDir.absolutePath.replace(gitRoot.absolutePath, "") + val owners = ownersResolver + .resolveOwnership(relativePath) + owners?.joinToString("/n") ?: "NONE" + } + ) + } +} \ No newline at end of file diff --git a/owners/owners-github-codeowners/src/test/kotlin/com/github/pgreze/kowners/CodeOwnershipTest.kt b/invert-owners-github/src/test/kotlin/com/github/pgreze/kowners/CodeOwnershipTest.kt similarity index 100% rename from owners/owners-github-codeowners/src/test/kotlin/com/github/pgreze/kowners/CodeOwnershipTest.kt rename to invert-owners-github/src/test/kotlin/com/github/pgreze/kowners/CodeOwnershipTest.kt diff --git a/owners/owners-github-codeowners/src/test/kotlin/com/github/pgreze/kowners/OwnersResolverTest.kt b/invert-owners-github/src/test/kotlin/com/github/pgreze/kowners/OwnersResolverTest.kt similarity index 100% rename from owners/owners-github-codeowners/src/test/kotlin/com/github/pgreze/kowners/OwnersResolverTest.kt rename to invert-owners-github/src/test/kotlin/com/github/pgreze/kowners/OwnersResolverTest.kt diff --git a/owners/owners-github-codeowners/src/test/kotlin/com/github/pgreze/kowners/PatternTest.kt b/invert-owners-github/src/test/kotlin/com/github/pgreze/kowners/PatternTest.kt similarity index 100% rename from owners/owners-github-codeowners/src/test/kotlin/com/github/pgreze/kowners/PatternTest.kt rename to invert-owners-github/src/test/kotlin/com/github/pgreze/kowners/PatternTest.kt diff --git a/invert-report/src/jsMain/kotlin/External.kt b/invert-report/src/jsMain/kotlin/External.kt index 545999d..f09f8dc 100644 --- a/invert-report/src/jsMain/kotlin/External.kt +++ b/invert-report/src/jsMain/kotlin/External.kt @@ -21,3 +21,5 @@ external fun highlightJsHighlightAll() external fun render3dGraph(domElementId: String, graphDataJson: String, width: Int, height: Int) external fun renderChartJs(domElementId: String, graphDataJson: String, onClick: (label: String, value: Int) -> Unit) +external fun renderLineChartJs(domElementId: String, graphDataJson: String, onClick: (label: String, value: Int) -> Unit) +external fun renderPlotlyTreeMap(domElementId: String, graphDataJson: String, onClick: (label: String, value: Int) -> Unit) diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/Ext.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/Ext.kt index 12c2938..61da772 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/Ext.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/Ext.kt @@ -11,4 +11,4 @@ private fun String.plusExamplesForInvertRepo(): String = if (this.contains("squa fun MetadataJsReportModel.httpsUrlForBranch() = "${remoteRepoUrl}/tree/${branchName}".plusExamplesForInvertRepo() -fun MetadataJsReportModel.httpsUrlForCommit() = "${remoteRepoUrl}/blob/${gitSha}".plusExamplesForInvertRepo() +fun MetadataJsReportModel.httpsUrlForCommit() = "${remoteRepoUrl}/blob/${latestCommitGitSha}".plusExamplesForInvertRepo() diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/InvertReport.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/InvertReport.kt index cc4c5d3..3b05cc0 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/InvertReport.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/InvertReport.kt @@ -29,7 +29,6 @@ import com.squareup.invert.common.pages.OwnerDetailReportPage import com.squareup.invert.common.pages.OwnersReportPage import com.squareup.invert.common.pages.PluginDetailReportPage import com.squareup.invert.common.pages.StatDetailReportPage -import com.squareup.invert.common.pages.SuppressAnnotationReportPage import com.squareup.invert.common.pages.UnusedModulesReportPage import invertComposeMain import kotlinx.browser.window @@ -134,7 +133,6 @@ class InvertReport( GradlePluginsReportPage, StatDetailReportPage, UnusedModulesReportPage, - SuppressAnnotationReportPage, GitHubMarkdownReportPage, GradleRepositoriesReportPage, ) diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/charts/ChartsJs.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/charts/ChartsJs.kt index 8bb8a7a..d972673 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/charts/ChartsJs.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/charts/ChartsJs.kt @@ -1,6 +1,7 @@ package com.squareup.invert.common.charts import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import com.squareup.invert.models.InvertSerialization import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -10,6 +11,7 @@ import org.jetbrains.compose.web.css.CSSSizeValue import org.jetbrains.compose.web.css.Color import org.jetbrains.compose.web.css.LineStyle import org.jetbrains.compose.web.css.border +import org.jetbrains.compose.web.css.height import org.jetbrains.compose.web.css.maxHeight import org.jetbrains.compose.web.css.maxWidth import org.jetbrains.compose.web.css.percent @@ -17,77 +19,110 @@ import org.jetbrains.compose.web.css.px import org.jetbrains.compose.web.css.style import org.jetbrains.compose.web.css.width import org.jetbrains.compose.web.dom.Canvas +import org.jetbrains.compose.web.dom.Div import renderChartJs +import renderLineChartJs +import renderPlotlyTreeMap import kotlin.math.absoluteValue import kotlin.random.Random object ChartsJs { - @Serializable - data class ScaleInfo( - val beginAtZero: Boolean = true, - ) + @Serializable + data class ScaleInfo( + val beginAtZero: Boolean = true, + ) - @Serializable - data class ChartJsScales( - val y: ScaleInfo = ScaleInfo(), - ) + @Serializable + data class ChartJsScales( + val y: ScaleInfo = ScaleInfo(), + ) - @Serializable - data class ChartJsOptions( - val scales: ChartJsScales, - ) + @Serializable + data class ChartJsOptions( + val scales: ChartJsScales, + ) - @Serializable - data class ChartJsDataset( - val label: String, - val data: Collection, - val borderWidth: Int = 1, - ) + @Serializable + data class ChartJsDataset( + val label: String, + val data: Collection, + val borderWidth: Int = 1, + ) - @Serializable - data class ChartJsData( - val labels: Collection, - val datasets: Collection, - ) + @Serializable + data class ChartJsData( + val labels: Collection, + val datasets: Collection, + ) + + @Serializable + data class ChartJsParam( + val type: String, + val data: ChartJsData, + val options: ChartJsOptions, + ) +} - @Serializable - data class ChartJsParam( - val type: String, - val data: ChartJsData, - val options: ChartJsOptions, +@Composable +private fun LineChartJsComposable( + graphDomId: String, + chartData: ChartsJs.ChartJsParam, + heightCssValue: CSSSizeValue<*>, + onClick: (label: String, value: Int) -> Unit = { _, _ -> } +) { + Canvas({ + id(graphDomId) + style { + border { + width(1.px) + style(LineStyle.Solid) + color = Color.gray + } + height(heightCssValue) + maxWidth(100.percent) + } + }) + CoroutineScope(Dispatchers.Main).launch { + renderLineChartJs( + graphDomId, + InvertSerialization.InvertJson.encodeToString( + ChartsJs.ChartJsParam.serializer(), chartData + ), + onClick = onClick ) + } } @Composable private fun ChartJsComposable( - graphDomId: String, - chartData: ChartsJs.ChartJsParam, - height: CSSSizeValue<*>, - onClick: (label: String, value: Int) -> Unit = { _, _ -> } + graphDomId: String, + chartData: ChartsJs.ChartJsParam, + heightCssValue: CSSSizeValue<*>, + onClick: (label: String, value: Int) -> Unit = { _, _ -> } ) { - // Good to go - Canvas({ - id(graphDomId) - style { - border { - width(2.px) - style(LineStyle.Solid) - color = Color.black - } - maxHeight(height) - maxWidth(100.percent) - } - }) - CoroutineScope(Dispatchers.Main).launch { - renderChartJs( - graphDomId, - InvertSerialization.InvertJson.encodeToString( - ChartsJs.ChartJsParam.serializer(), chartData - ), - onClick = onClick - ) + Canvas({ + id(graphDomId) + style { + border { + width(2.px) + style(LineStyle.Solid) + color = Color.black + } + height(heightCssValue) + maxHeight(heightCssValue) + maxWidth(100.percent) } + }) + CoroutineScope(Dispatchers.Main).launch { + renderChartJs( + graphDomId, + InvertSerialization.InvertJson.encodeToString( + ChartsJs.ChartJsParam.serializer(), chartData + ), + onClick = onClick + ) + } } @@ -95,26 +130,73 @@ private val random = Random(0) @Composable fun ChartJsChartComposable( - domId: String = "chart-${random.nextInt().absoluteValue}", - type: String = "pie", - height: CSSSizeValue<*> = 400.px, - data: ChartsJs.ChartJsData, - onClick: (String, Int) -> Unit = { _, _ -> } + domId: String = "chart-${random.nextInt().absoluteValue}", + type: String = "pie", + height: CSSSizeValue<*> = 400.px, + data: ChartsJs.ChartJsData, + onClick: (String, Int) -> Unit = { _, _ -> } ) { - ChartJsComposable( - graphDomId = domId, - height = height, - chartData = ChartsJs.ChartJsParam( - type = type, - data = data, - options = ChartsJs.ChartJsOptions( - scales = ChartsJs.ChartJsScales( - y = ChartsJs.ScaleInfo( - beginAtZero = true - ) - ) - ) - ), - onClick = onClick, + val domId = remember { "plotly-${random.nextInt().absoluteValue}" } // Remember the domId + ChartJsComposable( + graphDomId = domId, + heightCssValue = height, + chartData = ChartsJs.ChartJsParam( + type = type, + data = data, + options = ChartsJs.ChartJsOptions( + scales = ChartsJs.ChartJsScales( + y = ChartsJs.ScaleInfo( + beginAtZero = true + ) + ) + ) + ), + onClick = onClick, + ) +} + + +@Composable +fun PlotlyTreeMapComposable( + filePaths: List, + onClick: (String, Int) -> Unit = { _, _ -> } +) { + val domId = remember { "plotly-${random.nextInt().absoluteValue}" } // Remember the domId + + Div({ + id(domId) + }) + + CoroutineScope(Dispatchers.Main).launch { + renderPlotlyTreeMap( + domId, + filePaths.joinToString("\n"), + onClick = onClick ) + } +} + +@Composable +fun ChartJsLineChartComposable( + domId: String = "chart-${random.nextInt().absoluteValue}", + height: CSSSizeValue<*> = 260.px, + data: ChartsJs.ChartJsData, + onClick: (String, Int) -> Unit = { _, _ -> } +) { + LineChartJsComposable( + graphDomId = domId, + heightCssValue = height, + chartData = ChartsJs.ChartJsParam( + type = "line", + data = data, + options = ChartsJs.ChartJsOptions( + scales = ChartsJs.ChartJsScales( + y = ChartsJs.ScaleInfo( + beginAtZero = true + ) + ) + ) + ), + onClick = onClick, + ) } diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/navigation/NavGroupsRepo.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/navigation/NavGroupsRepo.kt index 1e83ac4..47ab8fb 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/navigation/NavGroupsRepo.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/navigation/NavGroupsRepo.kt @@ -26,7 +26,6 @@ import com.squareup.invert.common.pages.OwnerDetailNavRoute import com.squareup.invert.common.pages.OwnersNavRoute import com.squareup.invert.common.pages.OwnersReportPage import com.squareup.invert.common.pages.PluginDetailNavRoute -import com.squareup.invert.common.pages.SuppressAnnotationReportPage import com.squareup.invert.common.pages.UnusedModulesReportPage import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -103,7 +102,6 @@ object DefaultNavItems { "Insights", setOf( LeafModulesNavRoute.navPage.toNavItem(), UnusedModulesReportPage.navPage.toNavItem(), - SuppressAnnotationReportPage.navPage.toNavItem(), ) ), NavPageGroup( diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/AllModulesReportPage.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/AllModulesReportPage.kt index a8b634c..a2d1ddb 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/AllModulesReportPage.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/AllModulesReportPage.kt @@ -26,126 +26,126 @@ import kotlin.reflect.KClass data class AllModulesNavRoute( - val query: String? = null, + val query: String? = null, ) : BaseNavRoute(navPage) { - override fun toSearchParams() = toParamsWithOnlyPageId(this).also { map -> - query?.let { - map[QUERY_PARAM] = it - } + override fun toSearchParams() = toParamsWithOnlyPageId(this).also { map -> + query?.let { + map[QUERY_PARAM] = it } + } - companion object { + companion object { - private const val QUERY_PARAM = "query" - fun parser(params: Map): NavRoute { - val queryParam = params[QUERY_PARAM] - return AllModulesNavRoute(queryParam) - } + private const val QUERY_PARAM = "query" + fun parser(params: Map): NavRoute { + val queryParam = params[QUERY_PARAM] + return AllModulesNavRoute(queryParam) } + } } object AllModulesReportPage : InvertReportPage { - override val navPage: NavPage = NavPage( - pageId = "modules", - displayName = "Modules", - navIconSlug = "folder", - navRouteParser = { - AllModulesNavRoute.parser(it) - } - ) - override val navRouteKClass: KClass = AllModulesNavRoute::class - - override val composableContent: @Composable (AllModulesNavRoute) -> Unit = { navRoute -> - ModulesComposable(navRoute) + override val navPage: NavPage = NavPage( + pageId = "modules", + displayName = "Modules", + navIconSlug = "folder", + navRouteParser = { + AllModulesNavRoute.parser(it) } + ) + override val navRouteKClass: KClass = AllModulesNavRoute::class + + override val composableContent: @Composable (AllModulesNavRoute) -> Unit = { navRoute -> + ModulesComposable(navRoute) + } } @Composable fun ModulesComposable( - modulesNavRoute: AllModulesNavRoute, - reportDataRepo: ReportDataRepo = DependencyGraph.reportDataRepo, - navRouteRepo: NavRouteRepo = DependencyGraph.navRouteRepo + modulesNavRoute: AllModulesNavRoute, + reportDataRepo: ReportDataRepo = DependencyGraph.reportDataRepo, + navRouteRepo: NavRouteRepo = DependencyGraph.navRouteRepo ) { - val allModulesCollected by reportDataRepo.allModules.collectAsState(null) + val allModulesCollected by reportDataRepo.allModules.collectAsState(null) - val query = modulesNavRoute.query + val query = modulesNavRoute.query - if (allModulesCollected == null) { - BootstrapLoadingMessageWithSpinner() - return - } + if (allModulesCollected == null) { + BootstrapLoadingMessageWithSpinner() + return + } - val allModules = allModulesCollected!! + val allModules = allModulesCollected!! - val allModulesMatchingQuery = if (query != null && query != ":" && query.isNotEmpty()) { - allModules.filter { it.contains(query) } - } else { - allModules - } + val allModulesMatchingQuery = if (query != null && query != ":" && query.isNotEmpty()) { + allModules.filter { it.contains(query) } + } else { + allModules + } - val tabs = mutableListOf() - tabs.add(BootstrapTabData("By Name") { - BootstrapSearchBox( - query = query ?: "", - placeholderText = "Module Query...", - ) { - navRouteRepo.updateNavRoute(modulesNavRoute.copy(query = it)) - } - ModulesByNameComposable(allModulesMatchingQuery) { - navRouteRepo.updateNavRoute(ModuleDetailNavRoute(it)) - } - }) + val tabs = mutableListOf() + tabs.add(BootstrapTabData("By Name") { + BootstrapSearchBox( + query = query ?: "", + placeholderText = "Module Query...", + ) { + navRouteRepo.updateNavRoute(modulesNavRoute.copy(query = it)) + } + ModulesByNameComposable(allModulesMatchingQuery) { + navRouteRepo.updateNavRoute(ModuleDetailNavRoute(it)) + } + }) // tabs.add(BootstrapTabData("By Plugin") { // ModulesByPluginComposable( // reportDataRepo = reportDataRepo, // navRouteRepo = navRouteRepo, // ) // }) - BootstrapTabPane( - tabs - ) + BootstrapTabPane( + tabs + ) } @Composable fun ModulesByNameComposable(allModules: List?, moduleClicked: (ModulePath) -> Unit) { - ModuleListComposable(allModules) { cellValues -> - moduleClicked(cellValues[0]) - } + ModuleListComposable(allModules) { cellValues -> + moduleClicked(cellValues[0]) + } } @Composable fun ModuleListComposable(allModules: List?, limit: Int = MAX_RESULTS, onRowClicked: (List) -> Unit) { - if (allModules.isNullOrEmpty()) { - H1 { Text("Loading...") } - } else { - BootstrapTable( - headers = listOf("Module"), - rows = allModules.map { listOf(it) }, - types = listOf(String::class), - maxResultsLimitConstant = limit, - onItemClickCallback = onRowClicked, - ) - } + if (allModules.isNullOrEmpty()) { + H1 { Text("Loading...") } + } else { + BootstrapTable( + headers = listOf("Module"), + rows = allModules.map { listOf(it) }, + types = listOf(String::class), + maxResultsLimitConstant = limit, + onItemClickCallback = onRowClicked, + ) + } } @Composable fun ModulesByPluginComposable(reportDataRepo: ReportDataRepo, navRouteRepo: NavRouteRepo) { - val pluginIdToAllModulesMap by reportDataRepo.pluginIdToAllModulesMap.collectAsState(null) - pluginIdToAllModulesMap?.keys?.sorted()?.forEach { pluginId -> - val modules = pluginIdToAllModulesMap!![pluginId] - val headerText = pluginId + " (${modules?.size})" - BootstrapAccordion({ Text(headerText) }, { - ModuleListComposable(modules) { cellValues -> - navRouteRepo.updateNavRoute(ModuleDetailNavRoute(cellValues[0])) - } - }) + val pluginIdToAllModulesMap by reportDataRepo.pluginIdToAllModulesMap.collectAsState(null) + pluginIdToAllModulesMap?.keys?.sorted()?.forEach { pluginId -> + val modules = pluginIdToAllModulesMap!![pluginId] + val headerText = pluginId + " (${modules?.size})" + BootstrapAccordion({ Text(headerText) }) { + ModuleListComposable(modules) { cellValues -> + navRouteRepo.updateNavRoute(ModuleDetailNavRoute(cellValues[0])) + } } + } } diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/AllStatsReportPage.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/AllStatsReportPage.kt index 03e1d3f..ed588cc 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/AllStatsReportPage.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/AllStatsReportPage.kt @@ -1,6 +1,7 @@ package com.squareup.invert.common.pages +import PagingConstants import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -15,6 +16,7 @@ import com.squareup.invert.common.utils.FormattingUtils.formatDecimalSeparator import com.squareup.invert.models.StatDataType import com.squareup.invert.models.StatKey import com.squareup.invert.models.StatMetadata +import com.squareup.invert.models.js.StatTotalAndMetadata import org.jetbrains.compose.web.dom.A import org.jetbrains.compose.web.dom.H1 import org.jetbrains.compose.web.dom.Text @@ -81,7 +83,7 @@ fun AllStatsComposable( val statTotalsOrig by reportDataRepo.statTotals.collectAsState(null) val moduleToOwnerMapFlowValue by reportDataRepo.moduleToOwnerMap.collectAsState(null) - H1({classes("text-center")}) { Text("Stat Totals") } + H1({ classes("text-center") }) { Text("Stat Totals") } if (moduleToOwnerMapFlowValue == null) { BootstrapLoadingSpinner() @@ -99,7 +101,7 @@ fun AllStatsComposable( val statInfos = statsData.statInfos.values StatDataType.entries.forEach { statDataType: StatDataType -> - val statsOfType = statTotals.statTotals.filterKeys { it.dataType == statDataType } + val statsOfType = statTotals.statTotals.values.filter { it.metadata.dataType == statDataType } if (statsOfType.isNotEmpty()) { H1 { Text("${statDataType.displayName} Stat Counts") } StatTiles(statsOfType) { statKey -> @@ -119,7 +121,8 @@ fun AllStatsComposable( } } - BootstrapButton("View All", + BootstrapButton( + "View All", BootstrapButtonType.PRIMARY, onClick = { navRouteRepo.updateNavRoute( @@ -133,21 +136,21 @@ fun AllStatsComposable( val stats = statsData.statInfos.values - val statTotalsMap = statTotalsOrig?.statTotals + val statTotalsMap: Map? = statTotalsOrig?.statTotals BootstrapTable( headers = listOf("Key", "Description", "Type", "Category", "Count"), maxResultsLimitConstant = PagingConstants.MAX_RESULTS, rows = stats .filter { it.dataType != StatDataType.STRING } - .map { statMetadata -> + .map { statMetadata: StatMetadata -> mutableListOf( statMetadata.key, statMetadata.description, statMetadata.dataType.name, statMetadata.category ).apply { - statTotalsMap?.get(statMetadata)?.let { count -> + statTotalsMap?.get(statMetadata.key)?.let { count -> add(count.toString()) } } @@ -173,23 +176,23 @@ fun AllStatsComposable( } @Composable -fun StatTiles(statTotals: Map, onClick: (StatKey) -> Unit) { +fun StatTiles(codeReferenceStatTotals: List, onClick: (StatKey) -> Unit) { BootstrapRow { - statTotals.entries.sortedBy { it.key.description }.forEach { statTotal -> + codeReferenceStatTotals.sortedBy { it.metadata.description }.forEach { statTotalAndMetadata -> BootstrapColumn(4) { BootstrapJumbotron( centered = true, paddingNum = 2, headerContent = { - Text(statTotal.value.formatDecimalSeparator()) + Text(statTotalAndMetadata.total.formatDecimalSeparator()) } ) { A(href = "#", { onClick { - onClick(statTotal.key.key) + onClick(statTotalAndMetadata.metadata.key) } }) { - Text(statTotal.key.description) + Text(statTotalAndMetadata.metadata.description) } } } diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/CodeReferencesReportPage.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/CodeReferencesReportPage.kt index 943f9ed..7f8bd99 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/CodeReferencesReportPage.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/CodeReferencesReportPage.kt @@ -8,6 +8,7 @@ import com.squareup.invert.common.DependencyGraph import com.squareup.invert.common.InvertReportPage import com.squareup.invert.common.ModuleOwnerAndCodeReference import com.squareup.invert.common.ReportDataRepo +import com.squareup.invert.common.charts.PlotlyTreeMapComposable import com.squareup.invert.common.navigation.NavPage import com.squareup.invert.common.navigation.NavRouteRepo import com.squareup.invert.common.navigation.routes.BaseNavRoute @@ -16,6 +17,7 @@ import com.squareup.invert.models.ExtraKey import com.squareup.invert.models.ModulePath import com.squareup.invert.models.OwnerName import com.squareup.invert.models.StatDataType +import com.squareup.invert.models.js.StatTotalAndMetadata import org.jetbrains.compose.web.dom.A import org.jetbrains.compose.web.dom.H1 import org.jetbrains.compose.web.dom.H3 @@ -34,6 +36,7 @@ data class CodeReferencesNavRoute( val statKey: String? = null, val owner: String? = null, val module: String? = null, + val treemap: Boolean? = null, ) : BaseNavRoute(CodeReferencesReportPage.navPage) { override fun toSearchParams(): Map = toParamsWithOnlyPageId(this) @@ -47,6 +50,9 @@ data class CodeReferencesNavRoute( if (!module.isNullOrBlank()) { params[MODULE_PARAM] = module } + treemap?.let { + params[TREEMAP_PARAM] = treemap.toString() + } } companion object { @@ -54,6 +60,7 @@ data class CodeReferencesNavRoute( private const val STATKEY_PARAM = "statkey" private const val OWNER_PARAM = "owner" private const val MODULE_PARAM = "module" + private const val TREEMAP_PARAM = "treemap" fun parser(params: Map): CodeReferencesNavRoute { val statKey = params[STATKEY_PARAM] @@ -71,10 +78,18 @@ data class CodeReferencesNavRoute( null } } + val treemap = params[TREEMAP_PARAM]?.trim()?.let { + if (it.isNotBlank()) { + it.toBoolean() + } else { + null + } + } return CodeReferencesNavRoute( statKey = statKey, owner = owner, module = module, + treemap = treemap, ) } } @@ -130,6 +145,27 @@ fun CodeReferencesComposable( }) { Text("View Grouped by Module") } + + Text(" ") + A("#", { + onClick { + navRouteRepo.updateNavRoute( + codeReferencesNavRoute.copy( + treemap = if (codeReferencesNavRoute.treemap != null) { + !codeReferencesNavRoute.treemap + } else { + true + } + ) + ) + } + }) { + if (codeReferencesNavRoute.treemap == true) { + Text("Hide Treemap") + } else { + Text("Show Treemap") + } + } } } @@ -157,8 +193,11 @@ fun CodeReferencesComposable( BootstrapLoadingMessageWithSpinner("Loading Code References") return } - StatTiles(statTotalsOrig!!.statTotals - .filter { it.key.dataType == StatDataType.CODE_REFERENCES }) { statKey -> + val codeReferenceStatTotals: List = statTotalsOrig!!.statTotals.values + .filter { statTotalAndMetadata -> statTotalAndMetadata.metadata.dataType == StatDataType.CODE_REFERENCES } + StatTiles( + codeReferenceStatTotals + ) { statKey -> navRouteRepo.updateNavRoute( CodeReferencesNavRoute(statKey) ) @@ -206,6 +245,16 @@ fun CodeReferencesComposable( } } + if (codeReferencesNavRoute.treemap == true) { + BootstrapRow { + BootstrapColumn { + PlotlyTreeMapComposable( + filePaths = filteredByOwner.map { it.codeReference.filePath }, + ) + } + } + } + val codeReferencesByOwner = allCodeReferencesForStat.groupBy { it.owner } val totalCodeReferenceCount = allCodeReferencesForStat.size BootstrapRow { @@ -223,10 +272,8 @@ fun CodeReferencesComposable( }.sortedBy { it.displayText } ) { navRouteRepo.updateNavRoute( - CodeReferencesNavRoute( - statKey = statKey, + codeReferencesNavRoute.copy( owner = it?.value, - module = codeReferencesNavRoute.module, ) ) } @@ -248,9 +295,7 @@ fun CodeReferencesComposable( }.sortedBy { it.displayText } ) { navRouteRepo.updateNavRoute( - CodeReferencesNavRoute( - statKey = codeReferencesNavRoute.statKey, - owner = codeReferencesNavRoute.owner, + codeReferencesNavRoute.copy( module = it?.value ) ) diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/HomeReportPage.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/HomeReportPage.kt index 16a26ba..ae0a512 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/HomeReportPage.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/HomeReportPage.kt @@ -76,7 +76,7 @@ fun HomeComposable( } }) { Div({ classes("text-center") }) { - metadata.gitSha?.let { gitSha -> + metadata.latestCommitGitSha?.let { gitSha -> P({ classes("fs-6") }) { @@ -92,13 +92,6 @@ fun HomeComposable( } Text(" on " + metadata.dateDisplayStr()) } - if (metadata.branchName != metadata.currentBranch) { - P({ - classes("fs-6 text-warning bg-dark text-center".split(" ")) - }) { - Text("Report was not run on ${metadata.branchName}, but on ${metadata.currentBranch} instead with commit ${metadata.currentBranchHash}") - } - } } } } @@ -146,31 +139,31 @@ fun HomeComposable( OwnersReportPage.navPage ) { navRouteRepo.updateNavRoute(OwnersNavRoute) } - statTotals.statTotals.entries.forEach { statTotal -> + statTotals.statTotals.values.forEach { statTotalAndMetadata -> BootstrapColumn(3) { BootstrapJumbotron( centered = true, paddingNum = 2, headerContent = { - Text(statTotal.value.formatDecimalSeparator()) + Text(statTotalAndMetadata.total.formatDecimalSeparator()) } ) { A(href = "#", { onClick { navRouteRepo.updateNavRoute( - if (statTotal.key.dataType == StatDataType.CODE_REFERENCES) { - CodeReferencesNavRoute(statKey = statTotal.key.key) + if (statTotalAndMetadata.metadata.dataType == StatDataType.CODE_REFERENCES) { + CodeReferencesNavRoute(statKey = statTotalAndMetadata.metadata.key) } else { StatDetailNavRoute( pluginIds = listOf(), - statKeys = listOf(statTotal.key.key) + statKeys = listOf(statTotalAndMetadata.metadata.key) ) } ) } }) { Small { - Text(statTotal.key.description) + Text(statTotalAndMetadata.metadata.description) } } } diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/OwnerBreakdownReportPage.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/OwnerBreakdownReportPage.kt index 57ac11a..f865bad 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/OwnerBreakdownReportPage.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/OwnerBreakdownReportPage.kt @@ -22,13 +22,16 @@ import com.squareup.invert.models.StatDataType import com.squareup.invert.models.StatKey import org.jetbrains.compose.web.attributes.ATarget import org.jetbrains.compose.web.attributes.target +import org.jetbrains.compose.web.css.px import org.jetbrains.compose.web.dom.A import org.jetbrains.compose.web.dom.Br import org.jetbrains.compose.web.dom.H1 import org.jetbrains.compose.web.dom.H3 import org.jetbrains.compose.web.dom.Hr +import org.jetbrains.compose.web.dom.Li import org.jetbrains.compose.web.dom.P import org.jetbrains.compose.web.dom.Text +import org.jetbrains.compose.web.dom.Ul import ui.BootstrapColumn import ui.BootstrapLoadingMessageWithSpinner import ui.BootstrapRow @@ -171,6 +174,7 @@ fun ByOwnerComposable( val codeReferenceStatTypes = statInfosOrig!! .filter { it.dataType == StatDataType.CODE_REFERENCES } + .sortedBy { it.description } BootstrapRow { BootstrapColumn(6) { @@ -224,6 +228,28 @@ fun ByOwnerComposable( } .map { it.key } + if(navRoute.statKey.isNullOrBlank()){ + + + H3{ + Text("Select a Stat to view Owner Breakdown") + } + Ul { + codeReferenceStatTypes.forEach {statMetadata -> + Li { + A("#", { + onClick { + navRouteRepo.updateNavRoute(navRoute.copy(statKey = statMetadata.key)) + } + }) { + Text(statMetadata.description + " (" + statMetadata.key + ")") + } + } + } + } + return + } + BootstrapTabPane( codeReferenceStatTypesFilteredByNavParams .mapNotNull { statKey -> @@ -260,12 +286,14 @@ fun ByOwnerComposable( getStatDescription(statKey) + " ($countMessage)" ) { BootstrapRow { - BootstrapColumn(3) { + BootstrapColumn(6) { val bySize: Map = ownerToModulePathToCodeReferences.entries.map { it.key to it.value.values.sumOf { it.size } } .sortedByDescending { it.second }.toMap() ChartJsChartComposable( + domId = "owner_breakdown_${statKey}_bar", type = "bar", + height = 274.px, data = ChartsJs.ChartJsData( labels = bySize.keys, datasets = listOf( @@ -285,12 +313,14 @@ fun ByOwnerComposable( } ) } - BootstrapColumn(3) { + BootstrapColumn(6) { val bySize: Map = ownerToModulePathToCodeReferences.entries.map { it.key to it.value.values.sumOf { it.size } } .sortedByDescending { it.second }.toMap() ChartJsChartComposable( + domId = "owner_breakdown_${statKey}_pie", type = "pie", + height = 274.px, data = ChartsJs.ChartJsData( labels = bySize.keys, datasets = listOf( diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/OwnersReportPage.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/OwnersReportPage.kt index 595e786..68839a9 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/OwnersReportPage.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/OwnersReportPage.kt @@ -46,7 +46,7 @@ fun OwnersComposable( navRouteRepo: NavRouteRepo = DependencyGraph.navRouteRepo, ) { H1 { - Text("Owners (Using GitHub CODEOWNERS)") + Text("Owners") } val ownersCollected: Map>? by reportDataRepo.ownerNameToModulesMap.collectAsState(null) diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/SupressAnnotationReportPage.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/SupressAnnotationReportPage.kt deleted file mode 100644 index a9c38ee..0000000 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/pages/SupressAnnotationReportPage.kt +++ /dev/null @@ -1,256 +0,0 @@ -package com.squareup.invert.common.pages - - -import PagingConstants -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import com.squareup.invert.common.DependencyGraph -import com.squareup.invert.common.InvertReportPage -import com.squareup.invert.common.ReportDataRepo -import com.squareup.invert.common.charts.ChartJsChartComposable -import com.squareup.invert.common.charts.ChartsJs -import com.squareup.invert.common.navigation.NavPage -import com.squareup.invert.common.navigation.NavRouteRepo -import com.squareup.invert.common.navigation.routes.BaseNavRoute -import com.squareup.invert.common.pages.SuppressAnnotationNavRoute.Companion.parser -import com.squareup.invert.models.ModulePath -import com.squareup.invert.models.Stat -import com.squareup.invert.models.StatKey -import org.jetbrains.compose.web.dom.H3 -import org.jetbrains.compose.web.dom.Text -import ui.BootstrapColumn -import ui.BootstrapLoadingMessageWithSpinner -import ui.BootstrapRow -import ui.BootstrapTable -import kotlin.reflect.KClass - -data class SuppressAnnotationNavRoute( - val module: String? = null, - val configuration: String? = null, -) : BaseNavRoute(SuppressAnnotationReportPage.navPage) { - - override fun toSearchParams(): Map = toParamsWithOnlyPageId(this) - .also { params -> - module?.let { - params[MODULE_PARAM] = module - } - configuration?.let { - params[CONFIGURATION_PARAM] = configuration - } - } - - companion object { - - private const val MODULE_PARAM = "module" - private const val CONFIGURATION_PARAM = "configuration" - - fun parser(params: Map): SuppressAnnotationNavRoute { - return SuppressAnnotationNavRoute( - module = params[MODULE_PARAM], - configuration = params[CONFIGURATION_PARAM] - ) - } - } -} - - -object SuppressAnnotationReportPage : InvertReportPage { - override val navPage: NavPage = NavPage( - pageId = "suppress_annotation", - displayName = "Suppress Annotation", - navIconSlug = "at", - navRouteParser = { parser(it) } - ) - - override val navRouteKClass: KClass = SuppressAnnotationNavRoute::class - - override val composableContent: @Composable (SuppressAnnotationNavRoute) -> Unit = { navRoute -> - SuppressAnnotationComposable(navRoute) - } -} - -data class SuppressTypeByModule( - val modulePath: ModulePath, - val suppressType: String, - val count: Int, - val statKey: StatKey, -) - -@Composable -fun SuppressAnnotationComposable( - dependencyGraphNavRoute: SuppressAnnotationNavRoute, - reportDataRepo: ReportDataRepo = DependencyGraph.reportDataRepo, - navRouteRepo: NavRouteRepo = DependencyGraph.navRouteRepo, -) { - val allModules by reportDataRepo.allModules.collectAsState(null) - val statsData by reportDataRepo.statsData.collectAsState(null) - val statInfos by reportDataRepo.statInfos.collectAsState(null) - val statsByModule = statsData?.statsByModule - - if (allModules == null || statInfos == null) { - BootstrapLoadingMessageWithSpinner() - return - } - - val listOfSuppressTypeByModule = mutableListOf().apply { - statsByModule?.keys?.map { modulePath -> - statsByModule[modulePath]?.forEach { (statKey, stat) -> - val statMetadataForStatKey = statsData?.statInfos?.get(statKey) - if ((stat is Stat.NumericStat) && statMetadataForStatKey?.category == "suppress_annotation") { - this.add( - SuppressTypeByModule( - modulePath = modulePath, - suppressType = statMetadataForStatKey.description, - count = stat.value, - statKey = statMetadataForStatKey.key - ) - ) - } - } - } - } - - val distinctSuppressTypes: List = listOfSuppressTypeByModule.distinctBy { it.suppressType } - val suppressTypeToTotalCount = distinctSuppressTypes - .map { suppressTypeByModule: SuppressTypeByModule -> - suppressTypeByModule to listOfSuppressTypeByModule - .filter { it.suppressType == suppressTypeByModule.suppressType } - .sumOf { it.count } - } - .sortedByDescending { it.second } - - val modulePathToTotalCount: Map = listOfSuppressTypeByModule - .groupBy { it.modulePath } - .map { (gradlePath, suppressTypeByModule) -> gradlePath to suppressTypeByModule.sumOf { it.count } } - .toMap() - - - val labels = mutableListOf() - val chartData = statsByModule?.keys?.map { modulePath -> - labels.add(modulePath) - var count = 0 - val onlySuppressStatsForModule: List? = - statsByModule[modulePath]?.filter { (statKey, stat) -> - ((stat is Stat.NumericStat) && statKey.startsWith("suppress_annotation_")) - }?.map { it.value as Stat.NumericStat } - - println("onlySuppressStatsForModule") - println(onlySuppressStatsForModule) - - onlySuppressStatsForModule?.forEach { - count += it.value - } - - statsByModule[modulePath]?.forEach { entry -> - val statKey = entry.key - val stat = entry.value - if ((stat is Stat.NumericStat) && statKey.startsWith("suppress_annotation_")) { - count += stat.value - } - } - count - } ?: emptyList() - - BootstrapRow { - BootstrapColumn(12) { - H3 { - Text("Top Suppressions by Total Count") - } - BootstrapRow { - BootstrapColumn(6) { - BootstrapTable( - headers = listOf("@Suppress Type", "Count"), - rows = suppressTypeToTotalCount.map { - listOf(it.first.suppressType, it.second.toString()) - }, - types = listOf(String::class, Int::class), - maxResultsLimitConstant = PagingConstants.MAX_RESULTS, - onItemClickCallback = { row -> - navRouteRepo.updateNavRoute( - StatDetailNavRoute( - statKeys = listOf(statInfos!!.firstOrNull { it.description == row[0] }).mapNotNull { it?.key } - ) - ) - }, - sortByColumn = 1, - sortAscending = false, - ) - } - BootstrapColumn(6) { - ChartJsChartComposable( - domId = "chart-js-pie-graph1", - type = "pie", - data = ChartsJs.ChartJsData( - labels = suppressTypeToTotalCount.map { it.first.suppressType }, - datasets = listOf( - ChartsJs.ChartJsDataset( - label = "Number of Suppressions", - data = suppressTypeToTotalCount.map { it.second }, - borderWidth = 1 - ) - ), - ), - onClick = { label: String, value: Int -> - navRouteRepo.updateNavRoute( - StatDetailNavRoute( - statKeys = listOf( - statsData?.statInfos?.values?.firstOrNull { it.description == label }?.key ?: "" - ), - ) - ) - } - ) - } - } - - H3 { - Text("Modules with Most Suppressions") - } - BootstrapRow { - BootstrapColumn(6) { - BootstrapTable( - headers = listOf("Module", "Total @Suppress Count"), - rows = modulePathToTotalCount.map { - listOf(it.key, it.value.toString()) - }, - types = listOf(String::class, Int::class), - maxResultsLimitConstant = PagingConstants.MAX_RESULTS, - onItemClickCallback = { row -> - navRouteRepo.updateNavRoute( - ModuleDetailNavRoute( - path = row[0] - ) - ) - }, - sortByColumn = 1, - sortAscending = false, - ) - } - BootstrapColumn(6) { - ChartJsChartComposable( - domId = "chart-js-pie-graph", - type = "bar", - data = ChartsJs.ChartJsData( - labels = labels, - datasets = listOf( - ChartsJs.ChartJsDataset( - label = "Number of Suppressions", - data = chartData, - borderWidth = 1 - ) - ), - ), - onClick = { label: String, value: Int -> - navRouteRepo.updateNavRoute( - ModuleDetailNavRoute( - path = label - ) - ) - } - ) - } - } - } - } -} \ No newline at end of file diff --git a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/utils/FormattingUtils.kt b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/utils/FormattingUtils.kt index 0b1b715..e8d382e 100644 --- a/invert-report/src/jsMain/kotlin/com/squareup/invert/common/utils/FormattingUtils.kt +++ b/invert-report/src/jsMain/kotlin/com/squareup/invert/common/utils/FormattingUtils.kt @@ -15,7 +15,7 @@ object FormattingUtils { } internal fun MetadataJsReportModel.dateDisplayStr(): String { - val instant = Instant.fromEpochSeconds(time) + val instant = Instant.fromEpochSeconds(currentTime) val timeZone = TimeZone.currentSystemDefault() val dateTime = instant.toLocalDateTime(timeZone) @@ -25,7 +25,7 @@ object FormattingUtils { } ${dateTime.dayOfMonth}, ${dateTime.year} at " + "${dateTime.hour.toString().padStart(2, '0')}:${ dateTime.minute.toString().padStart(2, '0') - }:${dateTime.second.toString().padStart(2, '0')} ($timezoneId)" + }:${dateTime.second.toString().padStart(2, '0')} ($currentTimezoneId)" return formattedDate } diff --git a/invert-report/src/jsMain/kotlin/navigation/NavigationComposables.kt b/invert-report/src/jsMain/kotlin/navigation/NavigationComposables.kt index 1fd820f..7e3da28 100644 --- a/invert-report/src/jsMain/kotlin/navigation/NavigationComposables.kt +++ b/invert-report/src/jsMain/kotlin/navigation/NavigationComposables.kt @@ -15,8 +15,8 @@ import com.squareup.invert.common.pages.CodeReferencesNavRoute import com.squareup.invert.common.pages.StatDetailNavRoute import com.squareup.invert.common.utils.FormattingUtils.dateDisplayStr import com.squareup.invert.models.StatDataType -import com.squareup.invert.models.StatMetadata import com.squareup.invert.models.js.CollectedStatTotalsJsReportModel +import com.squareup.invert.models.js.StatTotalAndMetadata import org.jetbrains.compose.web.attributes.ATarget.Blank import org.jetbrains.compose.web.attributes.target import org.jetbrains.compose.web.dom.A @@ -43,13 +43,13 @@ fun LeftNavigationComposable( val statTotals: CollectedStatTotalsJsReportModel? by reportDataRepo.statTotals.collectAsState(null) val otherNavGroups: List = if (statTotals != null) { - statTotals!!.statTotals.entries - .groupBy { a: Map.Entry -> a.key.category } - .mapNotNull { categoryToEntries: Map.Entry>> -> - if (categoryToEntries.key == null) { + statTotals!!.statTotals.values + .groupBy { a: StatTotalAndMetadata -> a.metadata.category } + .mapNotNull { categoryToEntries -> + if (false) { null } else { - val groupTitle = categoryToEntries.key!!.replace("_", " ") + val groupTitle = categoryToEntries.key.replace("_", " ") .replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } val navItems = categoryToEntries.value.map { (statMetadata, _) -> NavItem( @@ -144,7 +144,7 @@ fun LeftNavigationComposable( } } Br() - metadata.currentBranchHash.let { currentBranchHash -> + metadata.latestCommitSha.let { currentBranchHash -> Text("Commit ") A(href = metadata.remoteRepoUrl + "/tree/${currentBranchHash}", attrs = { target(Blank) }) { Text(currentBranchHash.substring(0, 7)) diff --git a/invert-report/src/jsMain/kotlin/ui/BootstrapComposables.kt b/invert-report/src/jsMain/kotlin/ui/BootstrapComposables.kt index 581b2fb..ffcad23 100644 --- a/invert-report/src/jsMain/kotlin/ui/BootstrapComposables.kt +++ b/invert-report/src/jsMain/kotlin/ui/BootstrapComposables.kt @@ -109,9 +109,13 @@ fun BootstrapTableHeaders( } class BootstrapTabData( - val tabName: String, + val tabHeader: @Composable () -> Unit, val content: @Composable () -> Unit -) +) { + constructor(tabName: String, content: @Composable () -> Unit) : this(@Composable { + Text(tabName) + }, content) +} @Composable fun BootstrapTabPane( @@ -141,7 +145,7 @@ fun BootstrapTabPane( attr("role", "tab") attr("aria-controls", tabIds[idx]) }) { - Text(tab.tabName) + tab.tabHeader() } } } @@ -456,6 +460,7 @@ fun BootstrapNavItem(text: String, iconSlug: String, onClick: () -> Unit, active @Composable fun BootstrapAccordion( headerContent: @Composable () -> Unit, + expanded: Boolean = false, bodyContent: @Composable () -> Unit, ) { val randomInt = Random.nextInt() @@ -470,17 +475,23 @@ fun BootstrapAccordion( classes("accordion-header") }) { Button({ - classes("accordion-button collapsed".split(" ")) + val classes = mutableListOf("accordion-button").apply { if (!expanded) add("collapsed") } + classes(classes) attr("data-bs-toggle", "collapse") attr("data-bs-target", "#$accordionId") - attr("aria-expanded", "false") + attr("aria-expanded", expanded.toString()) attr("aria-controls", accordionId) }) { headerContent() } } Div({ - classes("accordion-collapse collapse".split(" ")) + val accordionCollapseClasses = mutableListOf("accordion-collapse", "collapse").apply { + if (expanded) { + add("show") + } + } + classes(accordionCollapseClasses) id(accordionId) }) { Div({ @@ -588,13 +599,49 @@ fun BootstrapLoadingSpinner() { } } +@Composable +fun BootstrapCardTitle( + classes: List = emptyList(), + content: @Composable () -> Unit +) { + Div({ + classes(classes + "card-title") + }) { + content() + } +} + +@Composable +fun BootstrapCardBody( + classes: List = emptyList(), + content: @Composable () -> Unit +) { + Div({ + classes(classes + "card-body") + }) { + content() + } +} + +@Composable +fun BootstrapCard( + classes: List = emptyList(), + content: @Composable () -> Unit +) { + Div({ + classes(classes + "card") + }) { + content() + } +} @Composable fun BootstrapRow( + classes: List = emptyList(), content: @Composable () -> Unit ) { Div({ - classes("row") + classes(classes + "row") }) { content() } @@ -603,10 +650,12 @@ fun BootstrapRow( @Composable fun BootstrapColumn( columnCount: Int = 12, + type: String = "md", + classes: List = emptyList(), content: @Composable () -> Unit ) { Div({ - classes("col-sm-$columnCount") + classes(classes + "col-$type-$columnCount") }) { content() } @@ -634,13 +683,14 @@ fun BoostrapExpandingCard( Div({ classes("card-header") }) { H6({ classes("mb-0") }) { Button({ - classes(mutableListOf( - "btn", "btn-link" - ).apply { - if (!expanded) { - add("collapsed") - } - }) + classes( + mutableListOf( + "btn", "btn-link" + ).apply { + if (!expanded) { + add("collapsed") + } + }) attr("data-bs-toggle", "collapse") attr("href", "#$collapseId") attr("aria-controls", collapseId) @@ -653,15 +703,16 @@ fun BoostrapExpandingCard( } Div({ id(collapseId) - classes(mutableListOf( - "collapsed" - ).apply { - if (expanded) { - add("show") - } else { - add("collapse") - } - }) + classes( + mutableListOf( + "collapsed" + ).apply { + if (expanded) { + add("show") + } else { + add("collapse") + } + }) }) { Div({ classes("card-body") }) { content() @@ -680,13 +731,14 @@ fun BoostrapExpandingSection( val collapseId = "collapse${randomInt}" H6({ classes("mb-0") }) { Button({ - classes(mutableListOf( - "btn" - ).apply { - if (!expanded) { - add("collapsed") - } - }) + classes( + mutableListOf( + "btn" + ).apply { + if (!expanded) { + add("collapsed") + } + }) attr("data-bs-toggle", "collapse") attr("aria-controls", collapseId) attr("aria-expanded", expanded.toString()) @@ -696,15 +748,16 @@ fun BoostrapExpandingSection( } Div({ id(collapseId) - classes(mutableListOf( - "collapsed" - ).apply { - if (expanded) { - add("show") - } else { - add("collapse") - } - }) + classes( + mutableListOf( + "collapsed" + ).apply { + if (expanded) { + add("show") + } else { + add("collapse") + } + }) }) { content() } diff --git a/owners/owners-github-codeowners/src/main/kotlin/com/squareup/invert/GitHubCodeOwnersInvertOwnershipCollector.kt b/owners/owners-github-codeowners/src/main/kotlin/com/squareup/invert/GitHubCodeOwnersInvertOwnershipCollector.kt deleted file mode 100644 index d8eb5a4..0000000 --- a/owners/owners-github-codeowners/src/main/kotlin/com/squareup/invert/GitHubCodeOwnersInvertOwnershipCollector.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.squareup.invert - -import com.github.pgreze.kowners.OwnersResolver -import com.github.pgreze.kowners.findCodeOwnerLocations -import com.github.pgreze.kowners.findGitRootPath -import com.github.pgreze.kowners.parseCodeOwners -import com.squareup.invert.models.ModulePath -import com.squareup.invert.models.OwnerInfo -import java.io.File - -object GitHubCodeOwnersInvertOwnershipCollector : InvertOwnershipCollector { - override fun collect(rootProjectDir: String, modulePath: ModulePath): OwnerInfo? { - - val gitRoot = File(rootProjectDir).findGitRootPath() - ?: throw IllegalStateException("This is not a Git Repository. Could not locate the .git folder at the root.") - - val CODEOWNERS_FILES = gitRoot.findCodeOwnerLocations() - - val allLines = mutableListOf() - CODEOWNERS_FILES - .onEach { println(it.path) } - .filter { it.exists() && it.isFile } - .forEach { allLines.addAll(it.readLines()) } - - val ownersResolver by lazy { - OwnersResolver( - allLines.parseCodeOwners() - ) - } - - return OwnerInfo( - if (allLines.isEmpty()) { - "No CODEOWNERS File at ${CODEOWNERS_FILES.map { it.path }}" - } else { - // TODO FIX THIS COMPUTATION - val projectDir = File(rootProjectDir, modulePath.drop(1).replace(":", "/")) - val relativePath = projectDir.absolutePath.replace(gitRoot.absolutePath, "") - val owners = ownersResolver - .resolveOwnership(relativePath) - owners?.joinToString("/n") ?: "NONE" - } - ) - } -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index b806af1..53519bb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,12 +1,13 @@ pluginManagement { - repositories { - google() - gradlePluginPortal() - mavenCentral() - } + repositories { + google() + gradlePluginPortal() + mavenCentral() + } } include(":invert-models") include(":invert-gradle-plugin") include(":invert-report") -include(":owners:owners-github-codeowners") +include(":invert-collectors") +include(":invert-owners-github")