diff --git a/MODULE.bazel b/MODULE.bazel index 10b2472..fcdbe4f 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "bazel-diff", - version = "12.1.0", + version = "12.1.1", compatibility_level = 0, ) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 9c1de3b..429e575 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -169,7 +169,7 @@ "//:extensions.bzl%non_module_repositories": { "general": { "bzlTransitiveDigest": "9kscpNTrmoEECh40kS6bqcncGbg5lZ8bpWLxD8wpc2Q=", - "usagesDigest": "ve8ewrWzumEcpto97uBCb2LV/hQ06Fa7XjaryJyTWWA=", + "usagesDigest": "4vwFTDKTowpbJLqDszvnDJg+nZ33V5yVfyjc1XG5BVQ=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, diff --git a/Makefile b/Makefile index 84014ef..119ce72 100644 --- a/Makefile +++ b/Makefile @@ -13,3 +13,7 @@ release_deploy_jar: build \ //cli:bazel-diff_deploy.jar \ -c opt + +.PHONY: format +format: + bazelisk run //cli/format \ No newline at end of file diff --git a/cli/BUILD b/cli/BUILD index 0ce8da4..d08069f 100644 --- a/cli/BUILD +++ b/cli/BUILD @@ -1,4 +1,3 @@ -load("@aspect_rules_lint//format:defs.bzl", "format_multirun") load("@rules_java//java:defs.bzl", "java_binary") load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library", "kt_jvm_test") @@ -143,10 +142,5 @@ java_binary( name = "ktfmt", main_class = "com.facebook.ktfmt.cli.Main", runtime_deps = ["@ktfmt//jar"], -) - -format_multirun( - name = "format", - kotlin = ":ktfmt", - visibility = ["//visibility:public"], + visibility = ["//cli/format:__pkg__"], ) diff --git a/cli/format/BUILD b/cli/format/BUILD new file mode 100644 index 0000000..1591808 --- /dev/null +++ b/cli/format/BUILD @@ -0,0 +1,7 @@ +load("@aspect_rules_lint//format:defs.bzl", "format_multirun") + +format_multirun( + name = "format", + kotlin = "//cli:ktfmt", + visibility = ["//visibility:public"], +) diff --git a/cli/src/main/kotlin/com/bazel_diff/bazel/BazelClient.kt b/cli/src/main/kotlin/com/bazel_diff/bazel/BazelClient.kt index d2f04da..9cd34c0 100644 --- a/cli/src/main/kotlin/com/bazel_diff/bazel/BazelClient.kt +++ b/cli/src/main/kotlin/com/bazel_diff/bazel/BazelClient.kt @@ -36,11 +36,12 @@ class BazelClient( // show up in // `configuredRuleInput`. Hence, one must not filter them out with `kind(rule, deps(..))`. val mainTargets = queryService.query("deps(//...:all-targets)", useCquery = true) - val repoTargets = if (repoTargetsQuery.isNotEmpty()) { - queryService.query(repoTargetsQuery.joinToString(" + ") { "'$it'" }) - } else { - emptyList() - } + val repoTargets = + if (repoTargetsQuery.isNotEmpty()) { + queryService.query(repoTargetsQuery.joinToString(" + ") { "'$it'" }) + } else { + emptyList() + } (mainTargets + repoTargets).distinctBy { it.name } } else { val buildTargetsQuery = diff --git a/cli/src/main/kotlin/com/bazel_diff/bazel/BazelQueryService.kt b/cli/src/main/kotlin/com/bazel_diff/bazel/BazelQueryService.kt index 5a39a94..7f78195 100644 --- a/cli/src/main/kotlin/com/bazel_diff/bazel/BazelQueryService.kt +++ b/cli/src/main/kotlin/com/bazel_diff/bazel/BazelQueryService.kt @@ -13,9 +13,8 @@ import kotlinx.coroutines.runBlocking import org.koin.core.component.KoinComponent import org.koin.core.component.inject -private val versionComparator = compareBy> { it.first } - .thenBy { it.second } - .thenBy { it.third } +private val versionComparator = + compareBy> { it.first }.thenBy { it.second }.thenBy { it.third } class BazelQueryService( private val workingDirectory: Path, @@ -27,27 +26,26 @@ class BazelQueryService( private val noBazelrc: Boolean, ) : KoinComponent { private val logger: Logger by inject() - private val version: Triple by lazy { - runBlocking { determineBazelVersion() } - } + private val version: Triple by lazy { runBlocking { determineBazelVersion() } } @OptIn(ExperimentalCoroutinesApi::class) private suspend fun determineBazelVersion(): Triple { val cmd = arrayOf(bazelPath.toString(), "--version") logger.i { "Executing Bazel version command: ${cmd.joinToString()}" } - val result = process( - *cmd, - stdout = Redirect.CAPTURE, - workingDirectory = workingDirectory.toFile(), - stderr = Redirect.PRINT, - destroyForcibly = true, - ) + val result = + process( + *cmd, + stdout = Redirect.CAPTURE, + workingDirectory = workingDirectory.toFile(), + stderr = Redirect.PRINT, + destroyForcibly = true, + ) if (result.resultCode != 0) { throw RuntimeException("Bazel version command failed, exit code ${result.resultCode}") } - if (result.output.size != 1 || !result.output.first().startsWith("bazel ")) { + if (result.output.size != 1 || !result.output.first().startsWith("bazel ")) { throw RuntimeException("Bazel version command returned unexpected output: ${result.output}") } // Trim off any prerelease suffixes. @@ -56,12 +54,14 @@ class BazelQueryService( return Triple(version[0], version[1], version[2]) } - // Use streamed_proto output for cquery if available. This is more efficient than the proto output. + // Use streamed_proto output for cquery if available. This is more efficient than the proto + // output. // https://github.com/bazelbuild/bazel/commit/607d0f7335f95aa0ee236ba3c18ce2a232370cdb private val canUseStreamedProtoWithCquery get() = versionComparator.compare(version, Triple(7, 0, 0)) >= 0 - // Use an output file for (c)query if supported. This avoids excessively large stdout, which is sent out on the BES. + // Use an output file for (c)query if supported. This avoids excessively large stdout, which is + // sent out on the BES. // https://github.com/bazelbuild/bazel/commit/514e9052f2c603c53126fbd9436bdd3ad3a1b0c7 private val canUseOutputFile get() = versionComparator.compare(version, Triple(8, 2, 0)) >= 0 @@ -87,20 +87,21 @@ class BazelQueryService( outputFile.inputStream().buffered().use { proto -> if (useCquery) { if (canUseStreamedProtoWithCquery) { - mutableListOf() - .apply { - while (true) { - val result = AnalysisProtosV2.CqueryResult.parseDelimitedFrom(proto) ?: break - // EOF - add(result) - } + mutableListOf() + .apply { + while (true) { + val result = + AnalysisProtosV2.CqueryResult.parseDelimitedFrom(proto) ?: break + // EOF + add(result) + } + } + .flatMap { it.resultsList } + } else { + AnalysisProtosV2.CqueryResult.parseFrom(proto).resultsList } - .flatMap { it.resultsList } - } else { - AnalysisProtosV2.CqueryResult.parseFrom(proto).resultsList - } - .mapNotNull { toBazelTarget(it.target) } - .filter { it.name in compatibleTargetSet } + .mapNotNull { toBazelTarget(it.target) } + .filter { it.name in compatibleTargetSet } } else { mutableListOf() .apply { @@ -167,8 +168,7 @@ class BazelQueryService( return str(target.label) return "" """ - .trimIndent() - ) + .trimIndent()) add(cqueryStarlarkFile.toString()) } else { add(if (canUseStreamedProtoWithCquery) "streamed_proto" else "proto") @@ -199,17 +199,19 @@ class BazelQueryService( logger.i { "Executing Query: $query" } logger.i { "Command: ${cmd.toTypedArray().joinToString()}" } - val result = process( - *cmd.toTypedArray(), - stdout = if (canUseOutputFile) Redirect.SILENT else Redirect.ToFile(outputFile), - workingDirectory = workingDirectory.toFile(), - stderr = Redirect.PRINT, - destroyForcibly = true, - ) + val result = + process( + *cmd.toTypedArray(), + stdout = if (canUseOutputFile) Redirect.SILENT else Redirect.ToFile(outputFile), + workingDirectory = workingDirectory.toFile(), + stderr = Redirect.PRINT, + destroyForcibly = true, + ) if (!allowedExitCodes.contains(result.resultCode)) { - logger.w { "Bazel query failed, output: ${result.output.joinToString("\n")}" } - throw RuntimeException("Bazel query failed, exit code ${result.resultCode}, allowed exit codes: ${allowedExitCodes.joinToString()}") + logger.w { "Bazel query failed, output: ${result.output.joinToString("\n")}" } + throw RuntimeException( + "Bazel query failed, exit code ${result.resultCode}, allowed exit codes: ${allowedExitCodes.joinToString()}") } return outputFile } diff --git a/cli/src/main/kotlin/com/bazel_diff/cli/GenerateHashesCommand.kt b/cli/src/main/kotlin/com/bazel_diff/cli/GenerateHashesCommand.kt index 05c2500..6074bf4 100644 --- a/cli/src/main/kotlin/com/bazel_diff/cli/GenerateHashesCommand.kt +++ b/cli/src/main/kotlin/com/bazel_diff/cli/GenerateHashesCommand.kt @@ -79,10 +79,10 @@ class GenerateHashesCommand : Callable { var fineGrainedHashExternalRepos: Set = emptySet() @CommandLine.Option( - names = ["--fineGrainedHashExternalReposFile"], - description = - [ - "A text file containing a newline separated list of external repos. Similar to --fineGrainedHashExternalRepos but helps you avoid exceeding max arg length. Mutually exclusive with --fineGrainedHashExternalRepos."]) + names = ["--fineGrainedHashExternalReposFile"], + description = + [ + "A text file containing a newline separated list of external repos. Similar to --fineGrainedHashExternalRepos but helps you avoid exceeding max arg length. Mutually exclusive with --fineGrainedHashExternalRepos."]) var fineGrainedHashExternalReposFile: File? = null @CommandLine.Option( diff --git a/cli/src/main/kotlin/com/bazel_diff/cli/VersionProvider.kt b/cli/src/main/kotlin/com/bazel_diff/cli/VersionProvider.kt index f3eb488..bf0220b 100644 --- a/cli/src/main/kotlin/com/bazel_diff/cli/VersionProvider.kt +++ b/cli/src/main/kotlin/com/bazel_diff/cli/VersionProvider.kt @@ -10,7 +10,7 @@ class VersionProvider : IVersionProvider { val inputStream = classLoader.getResourceAsStream("cli/version") ?: classLoader.getResourceAsStream("version") - ?: throw IllegalArgumentException( + ?: throw IllegalArgumentException( "unknown version as version file not found in resources") val version = BufferedReader(InputStreamReader(inputStream)).use { it.readText().trim() } diff --git a/cli/src/main/kotlin/com/bazel_diff/di/Modules.kt b/cli/src/main/kotlin/com/bazel_diff/di/Modules.kt index 0ac7075..c9a6136 100644 --- a/cli/src/main/kotlin/com/bazel_diff/di/Modules.kt +++ b/cli/src/main/kotlin/com/bazel_diff/di/Modules.kt @@ -34,14 +34,14 @@ fun hasherModule( excludeExternalTargets: Boolean, ): Module = module { if (fineGrainedHashExternalReposFile != null && fineGrainedHashExternalRepos.isNotEmpty()) { - System.err.println("Error: fineGrainedHashExternalReposFile and fineGrainedHashExternalRepos are mutually exclusive - please provide only one of them") + System.err.println( + "Error: fineGrainedHashExternalReposFile and fineGrainedHashExternalRepos are mutually exclusive - please provide only one of them") System.exit(1) } - val updatedFineGrainedHashExternalRepos = fineGrainedHashExternalReposFile?.let { file -> - file.readLines() - .filter { it.isNotBlank() } - .toSet() - } ?: fineGrainedHashExternalRepos + val updatedFineGrainedHashExternalRepos = + fineGrainedHashExternalReposFile?.let { file -> + file.readLines().filter { it.isNotBlank() }.toSet() + } ?: fineGrainedHashExternalRepos val cmd: MutableList = ArrayList().apply { diff --git a/cli/src/test/kotlin/com/bazel_diff/e2e/E2ETest.kt b/cli/src/test/kotlin/com/bazel_diff/e2e/E2ETest.kt index 1f9fac9..afcc634 100644 --- a/cli/src/test/kotlin/com/bazel_diff/e2e/E2ETest.kt +++ b/cli/src/test/kotlin/com/bazel_diff/e2e/E2ETest.kt @@ -589,7 +589,8 @@ class E2ETest { @Test fun testUseCqueryWithExcludeExternalTargets() { - // This test verifies the fix for the issue where using --excludeExternalTargets with --useCquery + // This test verifies the fix for the issue where using --excludeExternalTargets with + // --useCquery // would cause an empty query string to be passed to Bazel, resulting in exit code 2. val workingDirectory = extractFixtureProject("/fixture/cquery-test-base.zip") @@ -599,18 +600,19 @@ class E2ETest { val cli = CommandLine(BazelDiff()) - val exitCode = cli.execute( - "generate-hashes", - "-w", - workingDirectory.absolutePath, - "-b", - bazelPath, - "--useCquery", - // Platform is specified only to make the cquery succeed. - "--cqueryCommandOptions", - "--platforms=//:jre", - "--excludeExternalTargets", - hashesJson.absolutePath) + val exitCode = + cli.execute( + "generate-hashes", + "-w", + workingDirectory.absolutePath, + "-b", + bazelPath, + "--useCquery", + // Platform is specified only to make the cquery succeed. + "--cqueryCommandOptions", + "--platforms=//:jre", + "--excludeExternalTargets", + hashesJson.absolutePath) assertThat(exitCode).isEqualTo(0) } diff --git a/cli/src/test/kotlin/com/bazel_diff/hash/SourceFileHasherTest.kt b/cli/src/test/kotlin/com/bazel_diff/hash/SourceFileHasherTest.kt index c676fe7..50c4dd1 100644 --- a/cli/src/test/kotlin/com/bazel_diff/hash/SourceFileHasherTest.kt +++ b/cli/src/test/kotlin/com/bazel_diff/hash/SourceFileHasherTest.kt @@ -216,31 +216,34 @@ internal class SourceFileHasherTest : KoinTest { } @Test - fun testHashEmptyFileVsDeletedFile() = runBlocking { - // Create a temp directory for testing - val testDir = Files.createTempDirectory("empty_file_test") - val emptyFilePath = testDir.resolve("path/to/empty.txt") - Files.createDirectories(emptyFilePath.parent) - Files.createFile(emptyFilePath) - - val emptyFileTarget = "//path/to:empty.txt" - val hasher = SourceFileHasherImpl(testDir, null, externalRepoResolver) - - // Hash the empty file (file exists) - val emptyFileHash = hasher.digest(BazelSourceFileTarget(emptyFileTarget, seed)).toHexString() - - // Delete the file - Files.delete(emptyFilePath) - - // Hash the non-existent file - val deletedFileHash = hasher.digest(BazelSourceFileTarget(emptyFileTarget, seed)).toHexString() - - // The hashes should be different (file exists vs file missing) - assertThat(emptyFileHash).isNotEqualTo(deletedFileHash) - - // Clean up - Files.deleteIfExists(emptyFilePath.parent) - Files.deleteIfExists(testDir.resolve("path")) - Files.deleteIfExists(testDir) - } + fun testHashEmptyFileVsDeletedFile() = + runBlocking { + // Create a temp directory for testing + val testDir = Files.createTempDirectory("empty_file_test") + val emptyFilePath = testDir.resolve("path/to/empty.txt") + Files.createDirectories(emptyFilePath.parent) + Files.createFile(emptyFilePath) + + val emptyFileTarget = "//path/to:empty.txt" + val hasher = SourceFileHasherImpl(testDir, null, externalRepoResolver) + + // Hash the empty file (file exists) + val emptyFileHash = + hasher.digest(BazelSourceFileTarget(emptyFileTarget, seed)).toHexString() + + // Delete the file + Files.delete(emptyFilePath) + + // Hash the non-existent file + val deletedFileHash = + hasher.digest(BazelSourceFileTarget(emptyFileTarget, seed)).toHexString() + + // The hashes should be different (file exists vs file missing) + assertThat(emptyFileHash).isNotEqualTo(deletedFileHash) + + // Clean up + Files.deleteIfExists(emptyFilePath.parent) + Files.deleteIfExists(testDir.resolve("path")) + Files.deleteIfExists(testDir) + } }