Repository: py-cov-action/python-coverage-comment-action (python)

Issue:
`parse_diff_output()` raises AssertionError in v3.12
Hello, and thanks for creating this GH Action! I have a workflow that fails on v3.12 but succeeds on v3.11...

<details>
<summary>Stack trace from failing workflow run when using v3.12...</summary>

```bash
Notice: Generating comment for PR
Error: Critical error. This error possibly occurred because the permissions of the workflow are set incorrectly. You can see the correct setting of permissions here: https://github.com/py-cov-action/python-coverage-comment-action#basic-usage
Otherwise please look for open issues or open one in https://github.com/py-cov-action/python-coverage-comment-action/
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/coverage_comment/main.py", line 40, in main
    exit_code = action(
                ^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/coverage_comment/main.py", line 92, in action
    return process_pr(
           ^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/coverage_comment/main.py", line 121, in process_pr
    added_lines = coverage_module.get_added_lines(git=git, base_ref=base_ref)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/coverage_comment/coverage.py", line 279, in get_added_lines
    return parse_diff_output(diff)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/coverage_comment/coverage.py", line 291, in parse_diff_output
    assert current_file
AssertionError
```

</details>

It falls over on the line below, where it looks like `current_file` never gets updated: https://github.com/py-cov-action/python-coverage-comment-action/blob/08f2d84b93f75ccaf3431cefae8690571965981c/coverage_comment/coverage.py#L291

Here's an example of an input `diff` for this function that fails on v3.12, but passes when I pin the action to v3.11:

<details>
<summary>Sample diff for `parse_diff_output()`...</summary>


> **Note**
> This diff was created by inserting the following step into the workflow directly before the `python-coverage-comment-action` step:
>
> ```yaml
>      - name: Debug CI on host
>        run: |
>          sudo apt-get install -y tree \
>            && tree -a . \
>            && git fetch origin dev --depth=1000 \
>            && git diff --unified=1 FETCH_HEAD -- .
> ```
>
> Let me know if you want to see the output of `tree`, but all the files are found as expected.

Sample diff:

```bash
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7c0e612..e4ddca1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -75,2 +75,17 @@ jobs:
         run: docker exec $CONTAINER_NAME yarn lint
+      
+      - name: Debug CI in container
+        run: |
+          docker exec $CONTAINER_NAME \
+            yum install -y tree \
+            && tree -a . \
+            && git fetch origin dev --depth=1000 \
+            && git diff --unified=1 FETCH_HEAD -- .
+
+      - name: Debug CI on host
+        run: |
+          sudo apt-get install -y tree \
+            && tree -a . \
+            && git fetch origin dev --depth=1000 \
+            && git diff --unified=1 FETCH_HEAD -- .
```

</details>

If it makes a difference this is from a PR where a feature branch is getting merged into `dev`. We use `master` as our default branch. Also, we haven't yet run this action on `master` so don't yet have the `python-coverage-comment-action-data` branch.

Unfortunately this is in a private work repo so I can't give links, but let me know if you need any more info.

Cheers,
Andrew.

Pull request description:
Account for file deletions in git diff parsing
Probably closes #275

We didn't account for getting `/dev/null` without prefix as added filename for file deletions.

Patch:
diff --git a/coverage_comment/coverage.py b/coverage_comment/coverage.py
index 75c43bf..29d4bb0 100644
--- a/coverage_comment/coverage.py
+++ b/coverage_comment/coverage.py
@@ -288,11 +288,11 @@ def parse_diff_output(diff: str) -> dict[pathlib.Path, list[int]]:
             current_file = pathlib.Path(line.removeprefix(added_filename_prefix))
             continue
         if line.startswith("@@"):
-            if current_file is None:
-                raise ValueError(f"Unexpected diff output format: \n{diff}")
             lines = parse_line_number_diff_line(line)
-            result.setdefault(current_file, []).extend(lines)
-            continue
+            if len(lines) > 0:
+                if current_file is None:
+                    raise ValueError(f"Unexpected diff output format: \n{diff}")
+                result.setdefault(current_file, []).extend(lines)
 
     return result
 


Test patch:
diff --git a/tests/unit/test_coverage.py b/tests/unit/test_coverage.py
index 7d30c31..05ab09e 100644
--- a/tests/unit/test_coverage.py
+++ b/tests/unit/test_coverage.py
@@ -246,7 +246,15 @@ def test_parse_line_number_diff_line(git, line_number_diff_line, expected):
 
 
 def test_parse_diff_output(git):
-    diff = """diff --git a/README.md b/README.md
+    diff = """diff --git a/action.yml b/action.yml
+deleted file mode 100644
+index 42249d1..0000000
+--- a/action.yml
++++ /dev/null
+@@ -1,2 +0,0 @@
+-name: Python Coverage Comment
+-branding:
+diff --git a/README.md b/README.md
 index 1f1d9a4..e69de29 100644
 --- a/README.md
 +++ b/README.md
@@ -268,11 +276,14 @@ index 1f1d9a4..e69de29 100644
 +++ b/bar.txt
 @@ -8 +7,0 @@
 -foo
+diff --git a/coverage_comment/annotations.py b/coverage_comment/annotations2.py
+similarity index 100%
+rename from coverage_comment/annotations.py
+rename to coverage_comment/annotations2.py
 """
     git.register("git fetch origin main --depth=1000")()
     git.register("git diff --unified=0 FETCH_HEAD -- .")(stdout=diff)
     assert coverage.parse_diff_output(diff=diff) == {
         pathlib.Path("README.md"): [1, 3, 4, 5, 6],
         pathlib.Path("foo.txt"): [1],
-        pathlib.Path("bar.txt"): [],
     }