diff --git a/swh/loader/svn/replay.py b/swh/loader/svn/replay.py --- a/swh/loader/svn/replay.py +++ b/swh/loader/svn/replay.py @@ -743,7 +743,11 @@ # copy_tree needs sub-directories to exist in destination for root, dirs, files in os.walk(temp_path): for dir in dirs: - subdir = os.path.join(root, dir).replace(temp_path + b"/", b"") + temp_dir_fullpath = os.path.join(root, dir) + if os.path.islink(temp_dir_fullpath): + # do not create folder if it's a link or copy_tree will fail + continue + subdir = temp_dir_fullpath.replace(temp_path + b"/", b"") self.add_directory( os.fsdecode(os.path.join(dest_fullpath, subdir)) ) diff --git a/swh/loader/svn/tests/test_externals.py b/swh/loader/svn/tests/test_externals.py --- a/swh/loader/svn/tests/test_externals.py +++ b/swh/loader/svn/tests/test_externals.py @@ -1270,3 +1270,60 @@ loader.storage, repo_url, status="full", type="svn", ) check_snapshot(loader.snapshot, loader.storage) + + +def test_loader_directory_symlink_in_external( + swh_storage, repo_url, external_repo_url, tmp_path +): + # first commit on external + add_commit( + external_repo_url, + "Create dirs in an external repository", + [ + CommitChange(change_type=CommitChangeType.AddOrUpdate, path="src/apps/",), + CommitChange(change_type=CommitChangeType.AddOrUpdate, path="src/deps/",), + ], + ) + + # second commit on external + add_commit( + external_repo_url, + "Add symlink to src/deps in src/apps directory", + [ + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="src/apps/deps", + data=b"link ../deps", + properties={"svn:special": "*"}, + ), + ], + ) + + # first commit + add_commit( + repo_url, + "Add deps dir", + [CommitChange(change_type=CommitChangeType.AddOrUpdate, path="deps/")], + ) + + # second commit + add_commit( + repo_url, + "Set external to deps folder", + [ + CommitChange( + change_type=CommitChangeType.AddOrUpdate, + path="deps/", + properties={"svn:externals": (f"{external_repo_url} external")}, + ), + ], + ) + + 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", + ) + check_snapshot(loader.snapshot, loader.storage)