Use FileUtils.moveDirectory to support cross-filesystem moves by alphae-nix · Pull Request #343 · Cosium/git-code-format-maven-plugin

Problem

When running tests locally with ./mvnw --batch-mode clean verify, moveFiles() fails with:

java.nio.file.DirectoryNotEmptyException: /tmp/git-code-format-maven-plugin-test3626331939795592289/NonRootModuleTest_GIVEN_bad_formatted_files_WHEN_format_code_THEN_all_files_should_have_correct_format[3.5.0]_non-root-module

	at java.base/sun.nio.fs.UnixFileSystem.ensureEmptyDir(UnixFileSystem.java:797)
	at java.base/sun.nio.fs.UnixFileSystem.move(UnixFileSystem.java:900)
	at java.base/sun.nio.fs.UnixFileSystemProvider.move(UnixFileSystemProvider.java:289)
	at java.base/java.nio.file.Files.move(Files.java:1319)
	at com.cosium.code.format.AbstractTest.moveFiles(AbstractTest.java:148)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)

Root cause

On Ubunut, /tmp is mounted as tmpfs, while the project directory lives on a disk filesystem (ext4, in my case).

Files.move() delegates to UnixFileSystemProvider.move()UnixFileSystem.move()UnixCopyFile.move(), which calls the native method rename. This native method wraps the Linux rename syscall, which fails with cross-device link
error when source and target are on different filesystems. Java handles this fallback for files (copy + delete) with the copyOption, but for non-empty directories it has no recursive fallback it calls ensureEmptyDir() on the source and immediately throws DirectoryNotEmptyException. cf. this comment on Files.move

Why it passes on GitHub Actions and not locally

The repo GitHub Actions Ubuntu runners do not mount /tmp as tmpfs, it is part of the root filesystem. Source and target are on the same filesystem, rename succeeds, and the tests pass. cf this comment

Fix

Replace Files.move() with FileUtils.moveDirectory(), which performs a recursive copy + delete and correctly handles cross-filesystem directory moves