Open konrad-o opened 7 months ago
Hey @konrad-o, thanks for the very detailed report and reproducer - it made the issue very easy to reproduce and debug.
The crux of the issue is that one of the packages installed by Bundler includes directories (in .bundle/ruby/3.0.0/gems/cucumber-rails-2.2.0/tmp/aruba/test_app/tmp/cache/assets/sprockets/v3.0/
) whose names differ only in case (e.g. MZ
vs mz
). This is fine on the Linux ext4 filesystem (which is case-preserving and case-sensitive), but it doesn't work on APFS and NTFS (which are case-preserving but case-insensitive). These case conflicts manifest as the "file exists" errors.
When installing natively on macOS, bundle
doesn't care about case conflicts and seems to just merge the contents of the folders, but Synchronized File Shares are a little more cautious and won't allow case conflicts to overwrite each other on the macOS side, so these conflicts are held in a "pending" state (sort of — the details are a little more complex, but conceptually similar), meaning that when the case-conflicting files are deleted, Synchronized File Shares then propagates the pending conflicting files through, making their parent directory reappear after deletion. This is the "by design" behavior, but we can certainly add some heuristic tweaks for Docker Desktop to improve the experience. I've added a few tickets internally to clarify that the "file exists" messages indicate a case conflict and to make the deletion behavior for case conflicts a little more user-friendly.
Unfortunately neither of these fixes will address the core issue here, which is that Linux and macOS (+Windows) have fundamentally different case sensitivity/preservation behavior.
As a workaround, I would suggest adding a .syncignore
file containing .bundle/**/tmp/aruba/test_app/tmp/cache/assets/sprockets/v3.0
to app
to exclude this directory.
The actual ignore rule could be more broad or more specific, depending on the files that you want to include/exclude in synchronization back to the host. I'm happy to provide more input on the exact ignore strategy if that's helpful. Note that you'll probably want to delete and recreate the Synchronized File Share after adding the .syncignore
file.
Hey @xenoscopic! Thanks for the quick reply. Your answer pointed me into the right direction. So the actual issue was that bundle install
was creating files with the same folder paths, but with different casing, e.g.:
path/Ab/file1
path/ab/file2
Without synchronized file shares in case-insensitive file system, both files were created in the same folder (whichever came first), so it was like
path/Ab/file1
path/Ab/file2
both on host as inside running container. Docker wasn't complaining about it (not like in synchronized file shares), so it went unnoticed.
I think that your workaround would not work for us. As far as I understand how .syncignore
works it'd mean that those files would exist only on host and container needs them while running. Also the point of placing gem (package) files on host is to have them persistent across development process (starting/stopping of containers) so we'd have to either run bundle install
on every container startup or run it during image build - both approaches are inconvenient.
Thing that worked for us it to create a new APFS (Case-sensitive)
volume and run our project there.
But I still would say that not to being able to remove files in above situation, is a bug in Synchronized file shares. What do you think? BTW - is it possible to see whole list of files that cause problems? If there's many, it shows only around 10 of them.
Hey again! Just to clarify regarding the .syncignore
: it would actually keep those files in the container but not on the host (not the other way around). The ignores apply symmetrically, so anything targeted by .syncignore
won't be sync'd from host->container or from container->host. This allows you to have files that exist on the host but not in the container, but also the other way around (which I think might solve this case).
Depending on whether or not you need the files in that conflicting directory, you could still rely on persistence. So if you set up the .syncignore
to exclude only that directory, then install inside the container, and sync back to the host, then the Synchronized File Share would still have all the files except that particular test fixture. I'm not sure if that would be sufficient for your needs.
Creating a case-sensitive APFS volume is also a great idea.
Regarding the removal of the files, I would classify it as a "UX bug". The files are actually being deleted, but what's happening is that the "pending" case-conflicted files (which are waiting to be sync'd) are being brought through after the deletion (which necessitates creating their parent directories, which is allowed by Synchronized File Shares as long as there's nothing blocking it). I've created a ticket to add a heuristic to handle this behavior so that if all of the "pending" files are due to case conflicts, that we just allow them to be deleted in a way that feels more intuitive.
At the moment there's not a way to view the full list on problems and conflicts, but that's a reasonable ask. I'll create an additional ticket for exploring that idea.
Thanks for .syncignore
clarification. All works now as expected :)
Description
It's not possible to remove some files created within container while using Synchronized file shares.
When files are being deleted on the host, they disappear for a moment and are recreated soon after. I've looked at the files inside container while deleting them on the host and they seem not to disappear. The only way to remove those files is to stop the container and remove Synchronized file share.
Docker reports problems with those files, but nothing else can be done.
Reproduce
Files:
docker-compose.yml
Dockerfile
app/Gemfile
Synchronized file share
pointing toapp
folderdocker-compose up -d
docker exec -it test bash
and inside container runbundle install
(this will createGemfile.lock
and.bundle
folder) - Docker will report problems with sync share at that stage.bundle
folder on the host (in finder or IDE) - it disappears but is recreated by docker.Expected behavior
Synchronized file shares
Synchronized file share
are removed permanently when deleted on hostdocker version
docker info
Diagnostics ID
CF1919CE-7AC4-4466-8329-ADF65B1381F4/20240416120155
Additional Info
No response