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 [[https://docs.docker.com/engine/reference/builder/#arg|build-time arguments]] to change the UID and GID values used for the "somebody" and "runuser" users.
This might look something like:
```lang=Dockerfile
ARG SOMEBODY_UID=65533
ARG SOMEBODY_GID=65533
ARG RUNUSER_UID=900
ARG RUNUSER_GID=900
RUN (getent group "$SOMEBODY_GID" || groupadd -o -g "$SOMEBODY_GID" -r "somebody") && (getent passwd "$SOMEBODY_UID" || useradd -l -o -m -d "/home/somebody" -r -g "somebody" -u "$SOMEBODY_UID" "somebody") && mkdir -p "/srv/dockerize/bin" && chown "$SOMEBODY_UID":"$SOMEBODY_GID" "/srv/dockerize/bin" && mkdir -p "/opt/lib" && chown "$SOMEBODY_UID":"$SOMEBODY_GID" "/opt/lib"
RUN (getent group "$RUNUSER_GID" || groupadd -o -g "$RUNUSER_GID" -r "runuser") && (getent passwd "$RUNUSER_UID" || useradd -l -o -m -d "/home/runuser" -r -g "runuser" -u "$RUNUSER_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:
```lang=yaml
services:
web:
build:
context: .
dockerfile: .pipeline/local-python.Dockerfile
args:
SOMEBODY_UID: $LOCAL_UID
SOMEBODY_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.