In the Toolhub and Wikimedia-Developer-Portal projects I am trying to use Blubber generated Dockerfiles in combination with docker-compose to make standardized development environments. Largely this works by having containers with various runtime tools installed and then mounting the project's local git clone into the container as host volume.
This works well when the container only needs to read the mounted volume, but when it also needs to write to the volume things become trickier. Docker Desktop for MacOS has built-in magic for handling UID/GID mismatches between the running container and the host filesystem's permissions. Linux Docker does not transparently handle this same problem.
One way to make read-write host volumes work on both platforms is to use the user configuration option of a docker-compose.yaml service to set the effective runtime user of the container to the same UID+GID as the host filesystem needs (https://www.mediawiki.org/wiki/MediaWiki-Docker/Configuration_recipes/Customize_base_image).
This solution works very well for the mounted host volume read-write needs, but it causes a different set of permissions problems in that it changes the runtime effective UID/GID away from the effective UID/GID that was used at container build time. This means that the $HOME for the runtime user and any other files inside the container set to be read/write by the runtime user during build time do not match the effective UID/GID at runtime.
I have found a few ways to hack around this second level of permissions (see T295318 for the continuing saga) with other changes added at runtime after switching effective UID/GID, but they are a game of whack-a-mole and will have to be duplicated and adjusted across projects.
I believe that the side effects of the runtime UID/GID change could be avoided if Blubber generated Dockerfiles allowed using build-time arguments to change the UID and GID values used for the "somebody" and "runuser" users.
This might look something like:
ARG LIVES_UID=65533 ARG LIVES_GID=65533 ARG RUNS_UID=900 ARG RUNS_GID=900 RUN (getent group "$LIVES_GID" || groupadd -o -g "$LIVES_GID" -r "somebody") && (getent passwd "$LIVES_UID" || useradd -l -o -m -d "/home/somebody" -r -g "$LIVES_GID" -u "$LIVES_UID" "somebody") && mkdir -p "/srv/dockerize/bin" && chown "$LIVES_UID":"$LIVES_GID" "/srv/dockerize/bin" && mkdir -p "/opt/lib" && chown "$LIVES_UID":"$LIVES_GID" "/opt/lib" RUN (getent group "$RUNS_GID" || groupadd -o -g "$RUNS_GID" -r "runuser") && (getent passwd "$RUNS_UID" || useradd -l -o -m -d "/home/runuser" -r -g "runuser" -u "$RUNS_UID" "runuser")
With this modification to the Dockerfile in place, a docker-compose.yaml would be able to specify a build-time UID & GID that match the needed runtime UID & GID:
services: web: build: context: . dockerfile: .pipeline/local-python.Dockerfile args: LIVES_UID: $LOCAL_UID LIVES_GID: $LOCAL_GID user: "${LOCAL_UID}:${LOCAL_GID}"
For "normal" builds of the Dockerfile this change would be a no-op as no arguments would typically be passed to the builder and the Dockerfile contains the same default values for these variables as the previous hardcoded values.