Two host-environment bugs in the build-env scripts that break on
macOS/Apple Silicon hosts, independent of target architecture:
- Dockerfile: groupadd fails outright when USER_GID collides with an
existing system group in the rockylinux9 base image (e.g. macOS
default user GID 20 "staff" collides with RHEL's GID 20 "games").
Guard with getent so useradd attaches to the existing group instead.
- ifcos_env: `sed -si` is GNU-only syntax and errors under BSD/macOS
sed. Do the UNIQUE_ID substitution via a portable temp-file + mv.
Per sboddy's review on the original PR: dropped the linux/amd64
platform-pin additions from this change. The stack already targets
Rocky9/x64 build outputs by design, and Docker Desktop on macOS has
no native container runtime regardless (it's a Linux VM either way),
so forcing the image to run under emulation doesn't produce anything
that's actually loadable into a native macOS Blender/Bonsai install.
That's a separate, harder problem worth solving via a native build
path instead (mirroring build_osx.yml), not by fighting emulation
here. These two fixes stand on their own merits on any host.
This change was made with the assistance of an AI tool.
Three host-portability fixes to the docker/ toolchain from #8564 so it
runs on macOS as well as Linux. All three are no-ops on native amd64
Linux.
1. Dockerfile: only groupadd when the target GID is free. macOS's default
primary group `staff` is GID 20, which already exists as `games` in
rockylinux:9, so `groupadd -g 20` aborted the image build. Guard with
`getent group "${USER_GID}" || groupadd ...`; useradd -g accepts the
existing GID.
2. ifcos_env unique(): replace GNU-only `sed -si` (BSD/macOS sed errors
"illegal option -- s") with a portable `sed > tmp && mv` rewrite of the
UNIQUE_ID line. Verified against macOS BSD sed.
3. create() + compose.yaml: build with an explicit `--platform linux/amd64`
so the locally built image's platform matches the `platform:
linux/amd64` pin in compose.yaml. Without it, on arm64 the local image
is tagged linux/arm64, compose treats the platform-mismatched image as
absent and tries to pull `ifcopenshell-build-env:updated` from Docker
Hub (which does not exist -> access denied). Also add `pull_policy:
never` as a safety net so a future mismatch surfaces as a clear "image
not found" rather than a registry auth error.
Note: on Apple Silicon the amd64 build runs under emulation and a cold
full build is slow; ccache makes incremental rebuilds tolerable. A native
Linux/Intel host or CI remains the better choice for routine use, but these
fixes turn "hard broken" into "works with a caveat" on macOS.
This change was made with the assistance of an AI tool.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dockerfile (renamed from Dockerfile_init, Dockerfile_update removed):
- Run as a non-root `builder` user matching the host UID/GID (passed as
--build-arg by create() from id -u/id -g), so build output under the
bind mount stays owned by the host user instead of root.
- Fix CCACHE_MAXSIZE: `ccache -M 5G` wrote its limit to a config file
under /ccache at image-build time, but /ccache is a volume mount
point, so that file gets shadowed by the (empty) volume the moment
the container actually runs - the cap never took effect. Set
CCACHE_MAXSIZE=5G as an image ENV instead.
- Dedupe ccache/libffi-devel, add --setopt=install_weak_deps=False
--setopt=tsflags=nodocs, add `git lfs install --system`, combine the
dnf update+install into one layer.
- Drop Dockerfile_update: it built FROM its own previous output, so
every `update` call made the image strictly larger forever (Docker
layers are append-only, `dnf clean` in a later layer can't shrink an
earlier one). `update` now just calls create(), which already runs
`dnf update -y` FROM a clean rockylinux:9 every time.
compose.yaml: pin platform: linux/amd64 so this doesn't silently run
under emulation on an ARM host.
ifcos_env:
- Split the previously-conflated stop/down into six distinct,
Compose-native lifecycle commands: up (create-or-start), down
(remove), stop, start, restart (stop+start, same container),
recreate (down+up, fresh container). Previously `stop` was aliased
to `down`, which silently removed the container instead of pausing
it.
- Implement try(): copies the built wrapper into a real Blender/Bonsai
install for manual testing, reading the target from a new
BLENDER_USER_RESOURCE .env variable and auto-detecting the built
Python version (disambiguating via PY_TGT for multi-version builds).
Deliberately kept human-only - it mutates a live Blender install, so
it shouldn't run unattended as part of an automated/AI workflow,
which should instead copy the wrapper into the repo's own
src/ifcopenshell-python/ifcopenshell/ (documented in SKILL.md).
- Fix unique(): the "has .env already got a UNIQUE_ID line" check
referenced an unset $FILE instead of $ENV_FILE, so it always
evaluated true and appended a fresh "UNIQUE_ID=dummy" line to .env
on every single `up`.
- Minor: differentiate remove()'s log message from down()'s (no longer
identical now that they're distinct operations), tidy help text
alignment and a stray double-space typo in clean().
SKILL.md: rewritten as current-state documentation (no more "fixed in
this copy" changelog framing) covering the above, plus a migration
note for anyone hitting root-owned leftovers from an older image.
Verified by actually building the image and driving every new
lifecycle command (stop/start/restart keep the same container ID;
down+up and recreate produce a new one) and try() (including the
quoted-tilde BLENDER_USER_RESOURCE edge case) against the real container.
Generated with the assistance of an AI coding tool.
First functional version, but it needs some improvements and fixes
identified as I've used it personally on one thing, and when an AI
(Claude) used it to work through the CI test errors.
I had the AI make a SKILL.md file. If the AI indicates it needs to
build the ifcopenshell binary, use this and let it rip.