Repository: bazel-contrib/bazel-gazelle (Go)

Issue:
Gazelle diff mode patch generation creates invalid patches
<!-- Please answer these questions before submitting your issue. Thanks! -->

### What version of gazelle are you using?
`1.22.5`

<!-- Check bazel_gazelle in WORKSPACE if you're not sure -->

### What version of rules_go are you using?
`0.46.0`

<!-- Check io_bazel_rules_go in WORKSPACE if you're not sure -->

### What version of Bazel are you using?
`7.2.1`

<!-- Run "bazel version" to find out -->

### Does this issue reproduce with the latest releases of all the above?
Yes.


### What operating system and processor architecture are you using?
MAC OS ARM64


### What did you do?
How to Reproduce:
Edit a BUILD.bazel file that has already had Gazelle run on it (I edited one in this repository to reproduce) and add new lines to the end of the file.  Next, run the command `bazel run //:gazelle -- -mode=diff -patch=temp.patch` and generate a patch file.  Next, run the command `git apply -p0 temp.patch`. 

### What did you expect to see?
Patch is successfully applied.
File modifications that added the newlines are reverted.


### What did you see instead?
> error: patch failed: BUILD.bazel:132
>error: BUILD.bazel: patch does not apply


It's also worth noting that the system `patch` utility does apply the patches successfully, however it prints a warning regarding the validity of the patch.
`No such line 134 in input file, ignoring`

*The lines numbers in the error messages will be subject to change based on the file you are editing.




Discussion:
It appears that the diff library used does not correctly ignore trailing new lines, thus resulting in invalid patches.

The diff library used is no longer being maintained, so long term this should likely migrate to a different diff library that is actively maintained, however in the short term I think we could look at simply trimming the trailing new lines after the diff object is created.

The PR I have linked contains the required changes for the new line trimming.

I wanted to gather feedback from the maintainers on the desired approach and possible paths forward.



Patch:
diff --git a/cmd/gazelle/diff.go b/cmd/gazelle/diff.go
index 23f3aea8b..f291154d8 100644
--- a/cmd/gazelle/diff.go
+++ b/cmd/gazelle/diff.go
@@ -74,6 +74,15 @@ func diffFile(c *config.Config, f *rule.File) error {
 	} else {
 		diff.ToFile = outPath
 	}
+	// Trim off trailing newline from the end of the file.  Modern diff tools typically
+	// handle this, however difflib does not and is no longer maintained, so we need to
+	// handle it here.
+	if len(diff.A) > 0 {
+		if diff.A[len(diff.A)-1] == "\n" {diff.A = diff.A[:len(diff.A)-1]}
+	}
+	if len(diff.B) > 0 {
+		if diff.B[len(diff.B)-1] == "\n" {diff.B = diff.B[:len(diff.B)-1]}
+	}
 
 	uc := getUpdateConfig(c)
 	var out io.Writer = os.Stdout


Test patch:
diff --git a/cmd/gazelle/diff_test.go b/cmd/gazelle/diff_test.go
index 739d3913c..289e19b4c 100644
--- a/cmd/gazelle/diff_test.go
+++ b/cmd/gazelle/diff_test.go
@@ -50,18 +50,17 @@ func TestDiffExisting(t *testing.T) {
 		Content: `
 --- BUILD.bazel	1970-01-01 00:00:00.000000001 +0000
 +++ BUILD.bazel	1970-01-01 00:00:00.000000001 +0000
-@@ -1,3 +1,11 @@
+@@ -1,2 +1,10 @@
 +load("@io_bazel_rules_go//go:def.bzl", "go_library")
  
  # gazelle:prefix example.com/hello
- 
++
 +go_library(
 +    name = "hello",
 +    srcs = ["hello.go"],
 +    importpath = "example.com/hello",
 +    visibility = ["//visibility:public"],
 +)
-+
 `,
 	})
 	testtools.CheckFiles(t, dir, want)
@@ -88,7 +87,7 @@ func TestDiffNew(t *testing.T) {
 		Content: `
 --- /dev/null	1970-01-01 00:00:00.000000001 +0000
 +++ BUILD.bazel	1970-01-01 00:00:00.000000001 +0000
-@@ -0,0 +1,9 @@
+@@ -0,0 +1,8 @@
 +load("@io_bazel_rules_go//go:def.bzl", "go_library")
 +
 +go_library(
@@ -97,7 +96,6 @@ func TestDiffNew(t *testing.T) {
 +    importpath = "example.com/hello",
 +    visibility = ["//visibility:public"],
 +)
-+
 `,
 	})
 	testtools.CheckFiles(t, dir, want)
@@ -160,7 +158,7 @@ func TestDiffReadWriteDir(t *testing.T) {
 	wantPatch := fmt.Sprintf(`
 --- %s	1970-01-01 00:00:00.000000001 +0000
 +++ %s	1970-01-01 00:00:00.000000001 +0000
-@@ -1 +1,11 @@
+@@ -1 +1,10 @@
 +load("@io_bazel_rules_go//go:def.bzl", "go_library")
 +
  # gazelle:prefix example.com/hello
@@ -171,7 +169,6 @@ func TestDiffReadWriteDir(t *testing.T) {
 +    importpath = "example.com/hello",
 +    visibility = ["//visibility:public"],
 +)
-+
 `,
 		filepath.Join(dir, "read", "BUILD.bazel"),
 		filepath.Join(dir, "write", "BUILD.bazel"))
diff --git a/tests/diff_mode/expectedStdout.txt b/tests/diff_mode/expectedStdout.txt
index ab6941970..632f4a04d 100644
--- a/tests/diff_mode/expectedStdout.txt
+++ b/tests/diff_mode/expectedStdout.txt
@@ -1,6 +1,6 @@
 --- /dev/null	1970-01-01 00:00:00.000000001 +0000
 +++ BUILD.bazel	1970-01-01 00:00:00.000000001 +0000
-@@ -0,0 +1,10 @@
+@@ -0,0 +1,9 @@
 +filegroup(
 +    name = "all_files",
 +    testonly = True,
@@ -9,5 +9,4 @@
 +        "WORKSPACE",
 +    ],
 +    visibility = ["//visibility:public"],
-+)
-+
++)
\ No newline at end of file
diff --git a/tests/loads_from_flag/expectedStdout.txt b/tests/loads_from_flag/expectedStdout.txt
index 0b09e419d..436dce117 100644
--- a/tests/loads_from_flag/expectedStdout.txt
+++ b/tests/loads_from_flag/expectedStdout.txt
@@ -1,7 +1,6 @@
 --- /dev/null	1970-01-01 00:00:00.000000001 +0000
 +++ BUILD.bazel	1970-01-01 00:00:00.000000001 +0000
-@@ -0,0 +1,4 @@
+@@ -0,0 +1,3 @@
 +load("@some//repo:rules.bzl", "custom_rule")
 +
-+custom_rule(name = "gen")
-+
++custom_rule(name = "gen")
\ No newline at end of file