Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/57205.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `file.patch` state failing to detect an already-applied patch when the patch file contains absolute or prefixed paths and GNU patch 2.7.6+ is used. The reverse-apply dry-run now correctly passes `-p0` so the reject file is matched against the target without path stripping.
4 changes: 2 additions & 2 deletions salt/states/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -7900,8 +7900,8 @@ def _patch(patch_file, options=None, dry_run=False):
# Try to reverse-apply hunks from rejects file using a dry-run.
# If this returns a retcode of 0, we know that the patch was
# already applied. Rejects are written from the base of the
# directory, so the strip option doesn't apply here.
reverse_pass = _patch(patch_rejects, ["-R", "-f"], dry_run=True)
# directory, so the required strip level is 0.
reverse_pass = _patch(patch_rejects, ["-R", "-f", "-p0"], dry_run=True)
already_applied = reverse_pass["retcode"] == 0

# Check if the patch command threw an error upon execution
Expand Down
47 changes: 47 additions & 0 deletions tests/pytests/functional/states/file/test_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,53 @@ def test_patch_single_file(file, files, patches):
assert ret.comment == "Patch was already applied"


def test_patch_single_file_absolute_paths(file, state_tree, tmp_path):
"""
Regression test for issue #52329: file.patch must detect an already-applied
patch when the patch file uses absolute paths (GNU patch 2.7.6+ behaviour).

Without the ``-p0`` flag on the reverse-apply dry-run, GNU patch cannot
match the absolute path in the reject file against the target file and
returns a non-zero exit code, causing the state to report
"Patch would not apply cleanly" instead of "Patch was already applied".
"""
target = tmp_path / "target.txt"
target.write_text("line one\nline two\nline three\n")

# Construct a patch with the absolute path of the target file as the
# header (mirrors the real-world zeromq.patch from issue #52329).
abs_path = str(target)
patch_content = textwrap.dedent(
f"""\
--- {abs_path}
+++ {abs_path}
@@ -1,3 +1,3 @@
-line one
+LINE ONE
line two
line three
"""
)
patch_file = tmp_path / "absolute.patch"
patch_file.write_text(patch_content)

patch_source = "salt://absolute.patch"
patch_dest = state_tree / "absolute.patch"
patch_dest.write_text(patch_content)

# First run: apply the patch.
ret = file.patch(name=abs_path, source=patch_source)
assert ret.result is True
assert ret.changes
assert ret.comment == "Patch successfully applied"

# Second run: must detect the patch was already applied, not fail.
ret = file.patch(name=abs_path, source=patch_source)
assert ret.result is True
assert not ret.changes
assert ret.comment == "Patch was already applied"


@pytest.mark.skip_on_freebsd(
reason="Previously skipped on FreeBSD. Needs investigation as to why it currently False"
)
Expand Down
Loading