diff --git a/MODULE.bazel b/MODULE.bazel index ab43f13..652f5aa 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "bazel-diff", - version = "11.0.3", + version = "12.0.0", compatibility_level = 0, ) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 1728fc4..9c1de3b 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -169,7 +169,7 @@ "//:extensions.bzl%non_module_repositories": { "general": { "bzlTransitiveDigest": "9kscpNTrmoEECh40kS6bqcncGbg5lZ8bpWLxD8wpc2Q=", - "usagesDigest": "B/ulmGDgTJJ7blCuwEJsJ0wV1kMPVgF36azpv5dXn58=", + "usagesDigest": "ve8ewrWzumEcpto97uBCb2LV/hQ06Fa7XjaryJyTWWA=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, diff --git a/README.md b/README.md index b70f301..239388e 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,7 @@ First, add the following snippet to your project: #### Bzlmod snippet ```bazel -bazel_dep(name = "bazel-diff", version = "11.0.3") +bazel_dep(name = "bazel-diff", version = "12.0.0") ``` You can now run the tool with: diff --git a/cli/src/main/kotlin/com/bazel_diff/hash/SourceFileHasher.kt b/cli/src/main/kotlin/com/bazel_diff/hash/SourceFileHasher.kt index 6cda7ca..be10bbe 100644 --- a/cli/src/main/kotlin/com/bazel_diff/hash/SourceFileHasher.kt +++ b/cli/src/main/kotlin/com/bazel_diff/hash/SourceFileHasher.kt @@ -88,6 +88,8 @@ class SourceFileHasherImpl : KoinComponent, SourceFileHasher { if (relativeFilenameToContentHash?.contains(filenamePathString) == true) { val contentHash = relativeFilenameToContentHash.getValue(filenamePathString) safePutBytes(contentHash.toByteArray()) + // Mark that file exists (via content hash) + putBytes(byteArrayOf(0x01)) } else { val absoluteFilePath = workingDirectory.resolve(filenamePath) val file = absoluteFilePath.toFile() @@ -98,9 +100,13 @@ class SourceFileHasherImpl : KoinComponent, SourceFileHasher { } else if (modifiedFilepaths.any { workingDirectory.resolve(it) == absoluteFilePath }) { putFile(file) } + // Mark that file exists + putBytes(byteArrayOf(0x01)) } } else { logger.w { "File $absoluteFilePath not found" } + // Mark that file is missing + putBytes(byteArrayOf(0x00)) } } safePutBytes(sourceFileTarget.seed) 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 5efd14d..c676fe7 100644 --- a/cli/src/test/kotlin/com/bazel_diff/hash/SourceFileHasherTest.kt +++ b/cli/src/test/kotlin/com/bazel_diff/hash/SourceFileHasherTest.kt @@ -2,6 +2,7 @@ package com.bazel_diff.hash import assertk.assertThat import assertk.assertions.isEqualTo +import assertk.assertions.isNotEqualTo import assertk.assertions.isNull import com.bazel_diff.bazel.BazelSourceFileTarget import com.bazel_diff.extensions.toHexString @@ -38,6 +39,7 @@ internal class SourceFileHasherTest : KoinTest { val expected = sha256 { safePutBytes(fixtureFileContent) + putBytes(byteArrayOf(0x01)) safePutBytes(seed) safePutBytes(fixtureFileTarget.toByteArray()) } @@ -53,6 +55,7 @@ internal class SourceFileHasherTest : KoinTest { val expected = sha256 { safePutBytes(fixtureFileContent) + putBytes(byteArrayOf(0x01)) safePutBytes(seed) safePutBytes(fixtureFileTarget.toByteArray()) } @@ -68,6 +71,7 @@ internal class SourceFileHasherTest : KoinTest { hasher.digest(bazelSourceFileTarget, setOf(Paths.get("some/other/path"))).toHexString() val expected = sha256 { + putBytes(byteArrayOf(0x01)) safePutBytes(seed) safePutBytes(fixtureFileTarget.toByteArray()) } @@ -89,6 +93,7 @@ internal class SourceFileHasherTest : KoinTest { val expected = sha256 { safePutBytes(externalRepoFileContent.toByteArray()) + putBytes(byteArrayOf(0x01)) safePutBytes(seed) safePutBytes(externalRepoFileTarget.toByteArray()) } @@ -104,6 +109,7 @@ internal class SourceFileHasherTest : KoinTest { val expected = sha256 { safePutBytes(fixtureFileContent) + putBytes(byteArrayOf(0x01)) safePutBytes(seed) safePutBytes(fixtureFileTarget.toByteArray()) } @@ -136,6 +142,7 @@ internal class SourceFileHasherTest : KoinTest { val actual = hasher.digest(bazelSourceFileTarget).toHexString() val expected = sha256 { + putBytes(byteArrayOf(0x00)) safePutBytes(seed) safePutBytes(target.toByteArray()) } @@ -163,6 +170,7 @@ internal class SourceFileHasherTest : KoinTest { val expected = sha256 { safePutBytes("foo-content-hash".toByteArray()) + putBytes(byteArrayOf(0x01)) safePutBytes(seed) safePutBytes(fixtureFileTarget.toByteArray()) } @@ -180,6 +188,7 @@ internal class SourceFileHasherTest : KoinTest { val expected = sha256 { safePutBytes(fixtureFileContent) + putBytes(byteArrayOf(0x01)) safePutBytes(seed) safePutBytes(fixtureFileTarget.toByteArray()) } @@ -198,10 +207,40 @@ internal class SourceFileHasherTest : KoinTest { val expected = sha256 { safePutBytes("foo-content-hash".toByteArray()) + putBytes(byteArrayOf(0x01)) safePutBytes(seed) safePutBytes(targetName.toByteArray()) } .toHexString() assertThat(actual).isEqualTo(expected) } + + @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) + } }