From a4d305733512a1b1ad0998e5c9d86d9d6a342eaf Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 17 Mar 2020 10:00:34 -0300 Subject: [PATCH 1/7] Remove read-only files on Windows Signed-off-by: Uilian Ries --- patch_ng.py | 3 +++ tests/run_tests.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/patch_ng.py b/patch_ng.py index 7e9444b..d5d989c 100755 --- a/patch_ng.py +++ b/patch_ng.py @@ -56,6 +56,7 @@ import posixpath import shutil import sys +import stat PY3K = sys.version_info >= (3, 0) @@ -1106,11 +1107,13 @@ def apply(self, strip=0, root=None, fuzz=False): shutil.move(filenamen, backupname) if self.write_hunks(backupname if filenameo == filenamen else filenameo, filenamen, p.hunks): info("successfully patched %d/%d:\t %s" % (i+1, total, filenamen)) + os.chmod(backupname, stat.S_IWRITE) os.unlink(backupname) if new == b'/dev/null': # check that filename is of size 0 and delete it. if os.path.getsize(filenamen) > 0: warning("expected patched file to be empty as it's marked as deletion:\t %s" % filenamen) + os.chmod(filenamen, stat.S_IWRITE) os.unlink(filenamen) else: errors += 1 diff --git a/tests/run_tests.py b/tests/run_tests.py index 3079381..f504ca4 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -446,6 +446,12 @@ def test_fuzzy_patch_after(self): self.assertTrue(pto.apply(root=treeroot, fuzz=True)) self.assertFalse(pto.apply(root=treeroot, fuzz=False)) + def test_unlink_backup_windows(self): + treeroot = join(self.tmpdir, 'rootparent') + shutil.copytree(join(TESTS, '11permission'), treeroot) + pto = patch_ng.fromfile(join(TESTS, '11permission/11permission.patch')) + self.assertTrue(pto.apply(root=treeroot)) + class TestHelpers(unittest.TestCase): # unittest setting From bd3ed65285d5f49cde0dd010ea2751d839fa7d5d Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 17 Mar 2020 10:06:30 -0300 Subject: [PATCH 2/7] Add tests for permission Signed-off-by: Uilian Ries --- tests/11permission/11permission.patch | 10 ++++++++++ tests/11permission/[result]/some_file | 10 ++++++++++ tests/11permission/some_file | 9 +++++++++ 3 files changed, 29 insertions(+) create mode 100644 tests/11permission/11permission.patch create mode 100644 tests/11permission/[result]/some_file create mode 100644 tests/11permission/some_file diff --git a/tests/11permission/11permission.patch b/tests/11permission/11permission.patch new file mode 100644 index 0000000..8f959d2 --- /dev/null +++ b/tests/11permission/11permission.patch @@ -0,0 +1,10 @@ +--- some_file ++++ some_file +@@ -2,6 +2,7 @@ + line 2 + line 3 + line 4 ++line 5 + line 6 + line 7 + line 8 diff --git a/tests/11permission/[result]/some_file b/tests/11permission/[result]/some_file new file mode 100644 index 0000000..fa2da6e --- /dev/null +++ b/tests/11permission/[result]/some_file @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line 5 +line 6 +line 7 +line 8 +line 9 +line 10 diff --git a/tests/11permission/some_file b/tests/11permission/some_file new file mode 100644 index 0000000..c374ebd --- /dev/null +++ b/tests/11permission/some_file @@ -0,0 +1,9 @@ +line 1 +line 2 +line 3 +line 4 +line 6 +line 7 +line 8 +line 9 +line 10 From 2619a22266e418defdf8a0c91329d0fa2d3e287f Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 17 Mar 2020 13:40:58 -0300 Subject: [PATCH 3/7] Force file mode when testing permission Signed-off-by: Uilian Ries --- patch_ng.py | 6 +++--- tests/run_tests.py | 24 ++++++++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/patch_ng.py b/patch_ng.py index d5d989c..a43748e 100755 --- a/patch_ng.py +++ b/patch_ng.py @@ -31,7 +31,7 @@ from __future__ import print_function __author__ = "Conan.io " -__version__ = "1.17.3" +__version__ = "1.17.4" __license__ = "MIT" __url__ = "https://github.com/conan-io/python-patch" @@ -1107,13 +1107,13 @@ def apply(self, strip=0, root=None, fuzz=False): shutil.move(filenamen, backupname) if self.write_hunks(backupname if filenameo == filenamen else filenameo, filenamen, p.hunks): info("successfully patched %d/%d:\t %s" % (i+1, total, filenamen)) - os.chmod(backupname, stat.S_IWRITE) + os.chmod(backupname, stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) os.unlink(backupname) if new == b'/dev/null': # check that filename is of size 0 and delete it. if os.path.getsize(filenamen) > 0: warning("expected patched file to be empty as it's marked as deletion:\t %s" % filenamen) - os.chmod(filenamen, stat.S_IWRITE) + os.chmod(backupname, stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) os.unlink(filenamen) else: errors += 1 diff --git a/tests/run_tests.py b/tests/run_tests.py index f504ca4..b20276f 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -37,7 +37,8 @@ import shutil import unittest import copy -from os import listdir +import stat +from os import listdir, chmod from os.path import abspath, dirname, exists, join, isdir, isfile from tempfile import mkdtemp try: @@ -171,9 +172,7 @@ def _run_test(self, testname): self._assert_dirs_equal(join(basepath, "[result]"), tmpdir, ignore=["%s.patch" % testname, ".svn", ".gitkeep", "[result]"]) - - - shutil.rmtree(tmpdir) + remove_tree_force(tmpdir) return 0 @@ -362,7 +361,7 @@ def setUp(self): def tearDown(self): os.chdir(self.save_cwd) - shutil.rmtree(self.tmpdir) + remove_tree_force(self.tmpdir) def tmpcopy(self, filenames): """copy file(s) from test_dir to self.tmpdir""" @@ -447,10 +446,15 @@ def test_fuzzy_patch_after(self): self.assertFalse(pto.apply(root=treeroot, fuzz=False)) def test_unlink_backup_windows(self): + """ Apply patch to a read-only file and don't change its filemode + """ treeroot = join(self.tmpdir, 'rootparent') shutil.copytree(join(TESTS, '11permission'), treeroot) - pto = patch_ng.fromfile(join(TESTS, '11permission/11permission.patch')) + pto = patch_ng.fromfile(join(TESTS, '11permission', '11permission.patch')) + some_file = join(TESTS, '11permission', 'some_file') + chmod(some_file, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) self.assertTrue(pto.apply(root=treeroot)) + self.assertTrue(os.stat(some_file).st_mode, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) class TestHelpers(unittest.TestCase): @@ -484,6 +488,14 @@ def test_pathstrip(self): self.assertEqual(patch_ng.pathstrip(b'path/name.diff', 1), b'name.diff') self.assertEqual(patch_ng.pathstrip(b'path/name.diff', 0), b'path/name.diff') +def remove_tree_force(folder): + for root, dirs, files in os.walk(folder): + for it in dirs: + chmod(os.path.join(root, it), stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) + for it in files: + chmod(os.path.join(root, it), stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) + shutil.rmtree(folder) + # ---------------------------------------------------------------------------- if __name__ == '__main__': From 46f12325215f27660b8c2efb7105de151640439c Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 17 Mar 2020 13:45:41 -0300 Subject: [PATCH 4/7] Force file mode when testing permission Signed-off-by: Uilian Ries --- tests/run_tests.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/run_tests.py b/tests/run_tests.py index b20276f..7f525bd 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -490,11 +490,9 @@ def test_pathstrip(self): def remove_tree_force(folder): for root, dirs, files in os.walk(folder): - for it in dirs: - chmod(os.path.join(root, it), stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) for it in files: chmod(os.path.join(root, it), stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) - shutil.rmtree(folder) + shutil.rmtree(folder, ignore_errors=True) # ---------------------------------------------------------------------------- From 21ff81e11ec1a33f75e2b09cdeb0bbc23422f0d6 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 17 Mar 2020 13:48:13 -0300 Subject: [PATCH 5/7] Do not update patch version Signed-off-by: Uilian Ries --- patch_ng.py | 2 +- tests/run_tests.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/patch_ng.py b/patch_ng.py index a43748e..51255d2 100755 --- a/patch_ng.py +++ b/patch_ng.py @@ -31,7 +31,7 @@ from __future__ import print_function __author__ = "Conan.io " -__version__ = "1.17.4" +__version__ = "1.17.3" __license__ = "MIT" __url__ = "https://github.com/conan-io/python-patch" diff --git a/tests/run_tests.py b/tests/run_tests.py index 7f525bd..6cbb593 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -489,7 +489,7 @@ def test_pathstrip(self): self.assertEqual(patch_ng.pathstrip(b'path/name.diff', 0), b'path/name.diff') def remove_tree_force(folder): - for root, dirs, files in os.walk(folder): + for root, _, files in os.walk(folder): for it in files: chmod(os.path.join(root, it), stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) shutil.rmtree(folder, ignore_errors=True) From 20a8ec2109cdfda4060e8b4f2476ebe72e994f01 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 17 Mar 2020 13:56:27 -0300 Subject: [PATCH 6/7] Set permission to the build folder Signed-off-by: Uilian Ries --- tests/run_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run_tests.py b/tests/run_tests.py index 6cbb593..6c16ef2 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -451,7 +451,7 @@ def test_unlink_backup_windows(self): treeroot = join(self.tmpdir, 'rootparent') shutil.copytree(join(TESTS, '11permission'), treeroot) pto = patch_ng.fromfile(join(TESTS, '11permission', '11permission.patch')) - some_file = join(TESTS, '11permission', 'some_file') + some_file = join(treeroot, 'some_file') chmod(some_file, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) self.assertTrue(pto.apply(root=treeroot)) self.assertTrue(os.stat(some_file).st_mode, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) From 3557828eecf99cbaade3bdacb7b1fd26ef88913c Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 17 Mar 2020 14:05:07 -0300 Subject: [PATCH 7/7] Add helper for safe unlink Signed-off-by: Uilian Ries --- patch_ng.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/patch_ng.py b/patch_ng.py index 51255d2..0ccbc82 100755 --- a/patch_ng.py +++ b/patch_ng.py @@ -179,6 +179,12 @@ def xstrip(filename): filename = re.sub(b'^[\\\\/]+', b'', filename) return filename + +def safe_unlink(filepath): + os.chmod(filepath, stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) + os.unlink(filepath) + + #----------------------------------------------- # Main API functions @@ -977,7 +983,7 @@ def apply(self, strip=0, root=None, fuzz=False): save(target, new_file) elif "dev/null" in target: source = self.strip_path(source, root, strip) - os.unlink(source) + safe_unlink(source) else: items.append(item) self.items = items @@ -1107,14 +1113,12 @@ def apply(self, strip=0, root=None, fuzz=False): shutil.move(filenamen, backupname) if self.write_hunks(backupname if filenameo == filenamen else filenameo, filenamen, p.hunks): info("successfully patched %d/%d:\t %s" % (i+1, total, filenamen)) - os.chmod(backupname, stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) - os.unlink(backupname) + safe_unlink(backupname) if new == b'/dev/null': # check that filename is of size 0 and delete it. if os.path.getsize(filenamen) > 0: warning("expected patched file to be empty as it's marked as deletion:\t %s" % filenamen) - os.chmod(backupname, stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) - os.unlink(filenamen) + safe_unlink(filenamen) else: errors += 1 warning("error patching file %s" % filenamen)