diff --git a/swh/loader/svn/ra.py b/swh/loader/svn/ra.py --- a/swh/loader/svn/ra.py +++ b/swh/loader/svn/ra.py @@ -136,6 +136,9 @@ svn_special_path_non_link_data: Optional[bytes] = None """keep track of non link file content with svn:special property set""" + executable: int = DEFAULT_FLAG + """keep track if file is executable when setting svn:executable property""" + class FileEditor: """File Editor in charge of updating file on disk and memory objects. @@ -155,7 +158,6 @@ self.directory = directory self.path = path # default value: 0, 1: set the flag, 2: remove the exec flag - self.executable = DEFAULT_FLAG self.link = None self.fullpath = os.path.join(rootpath, path) self.state = state @@ -163,9 +165,9 @@ def change_prop(self, key, value): if key == properties.PROP_EXECUTABLE: if value is None: # bit flip off - self.executable = NOEXEC_FLAG + self.state.executable = NOEXEC_FLAG else: - self.executable = EXEC_FLAG + self.state.executable = EXEC_FLAG elif key == properties.PROP_SPECIAL: # Possibly a symbolic link. We cannot check further at # that moment though, patch(s) not being applied yet @@ -272,9 +274,9 @@ self.state.svn_special_path_non_link_data = None if not is_link: # if a link, do nothing regarding flag - if self.executable == EXEC_FLAG: + if self.state.executable == EXEC_FLAG: os.chmod(self.fullpath, 0o755) - elif self.executable == NOEXEC_FLAG: + elif self.state.executable == NOEXEC_FLAG: os.chmod(self.fullpath, 0o644) # And now compute file's checksums diff --git a/swh/loader/svn/tests/test_loader.py b/swh/loader/svn/tests/test_loader.py --- a/swh/loader/svn/tests/test_loader.py +++ b/swh/loader/svn/tests/test_loader.py @@ -1619,3 +1619,62 @@ # check work directory was empty before replaying revisions assert loader.svnrepo.replay_dir_content_before_start == [] + + +def test_loader_svn_executable_property_on_svn_link_handling(swh_storage, tmp_path): + # create a repository + repo_path = os.path.join(tmp_path, "tmprepo") + repos.create(repo_path) + repo_url = f"file://{repo_path}" + + # first commit + add_commit( + repo_url, + ( + "Add an executable file and a svn link to it." + "Set svn:executable property for both paths." + ), + [ + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="hello-world", + properties={"svn:executable": "*"}, + data=b"#!/bin/bash\necho Hello World !", + ), + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="hello", + properties={"svn:executable": "*", "svn:special": "*"}, + data=b"link hello-world", + ), + ], + ) + + # second commit + add_commit( + repo_url, + ( + "Remove executable file, unset link and replace it with executable content." + "As the link was previously marked as executable, execution rights should" + "be set after turning it to a regular file." + ), + [ + CommitChange(change_type=CommitChangeType.Delete, path="hello-world"), + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="hello", + properties={"svn:special": None}, + data=b"#!/bin/bash\necho Hello World !", + ), + ], + ) + + # instantiate a svn loader checking after each processed revision that + # the repository filesystem it reconstructed does not differ from a subversion + # export of that revision + loader = SvnLoader(swh_storage, repo_url, temp_directory=tmp_path, check_revision=1) + + assert loader.load() == {"status": "eventful"} + assert_last_visit_matches( + loader.storage, repo_url, status="full", type="svn", + )