diff --git a/craft_application/remote/worktree.py b/craft_application/remote/worktree.py index 8576b271c..1ab28e6d6 100644 --- a/craft_application/remote/worktree.py +++ b/craft_application/remote/worktree.py @@ -51,7 +51,7 @@ def init_repo(self) -> None: if self._repo_dir.exists(): rmtree(self._repo_dir) - copytree(self._project_dir, self._repo_dir) + copytree(self._project_dir, self._repo_dir, ignore_dangling_symlinks=True) self._gitify_repository() diff --git a/docs/reference/changelog.rst b/docs/reference/changelog.rst index bc9230719..f39d79ef9 100644 --- a/docs/reference/changelog.rst +++ b/docs/reference/changelog.rst @@ -21,6 +21,8 @@ Changelog Bug fixes ========= +- ``remote-build`` no longer raises an error when dangling symlinks are present in the + project repository. - Ignore .spread-reuse files when deciding if a source is outdated. 7.1.0 (2026-07-07) diff --git a/tests/unit/remote/test_worktree.py b/tests/unit/remote/test_worktree.py index d646a5bcb..4c3700f45 100644 --- a/tests/unit/remote/test_worktree.py +++ b/tests/unit/remote/test_worktree.py @@ -38,7 +38,7 @@ def mock_base_directory(mocker, new_dir): return _mock_base_directory -@pytest.fixture(autouse=True) +@pytest.fixture def mock_copytree(mocker): """Returns a mocked `shutil.copytree()`.""" return mocker.patch("craft_application.remote.worktree.copytree") @@ -75,7 +75,7 @@ def test_worktree_init_clean_exception_wrapped(mock_git_repo): @pytest.mark.usefixtures("new_dir", "mock_copytree") def test_worktree_init_dirty(mock_base_directory, mock_git_repo): - """Test initialization of a WorkTree with a clean git repository.""" + """Test initialization of a WorkTree with a dirty git repository.""" mock_git_repo.return_value.is_clean.return_value = False worktree = WorkTree(app_name="test-app", build_id="test-id", project_dir=Path()) @@ -93,6 +93,19 @@ def test_worktree_init_dirty(mock_base_directory, mock_git_repo): ] +def test_worktree_init_dangling_symlinks(tmp_path: Path) -> None: + """Test that dangling symlinks don't break the repository init.""" + (tmp_path / "file").touch() + (tmp_path / "broken-link").symlink_to("not-real") + worktree = WorkTree(app_name="test-app", build_id="test-id", project_dir=tmp_path) + + worktree.init_repo() + + assert (worktree.repo_dir / "file").is_file() + # Broken symlinks are just ignored now + assert not (worktree.repo_dir / "broken-link").is_symlink() + + @pytest.mark.usefixtures("new_dir") def test_worktree_repo_dir(): """Verify the `repo_dir` property."""