Skip to content

Commit eed50f7

Browse files
hujc7ooctipus
andauthored
[Docker] Install the Docker images from uv.lock (isaac-sim#7405)
## Summary Docker images stop mutating Isaac Sim's shipped Python environment: the previous build replaced **41** of its packages, this one replaces **0**. All three images install from `uv.lock`, so builds are reproducible and `[tool.uv].override-dependencies` apply. # Description The lock is the only path that applies `[tool.uv].override-dependencies`, which pip cannot read. The change is contained to the Docker build; `isaaclab.sh --install` is unchanged. - **Isaac Sim image** — `uv sync` resolves into a venv at `/opt/isaaclab-venv`, built on Kit's own interpreter. Isaac Sim is untouched, still reached through the existing `_isaac_sim` symlink, and `isaaclab.sh` sources `setup_python_env.sh` so `import isaacsim` keeps working. - **Kit-less image** — `uv sync` replaces the pip-style install, retiring the duplicated `tools/wheel_builder/uv-overrides.txt`. - **cuRobo image** — same migration. Because Isaac Sim's site-packages is no longer touched, the image stops deleting the prebundled torch, re-bootstrapping `pip` with `get-pip.py`, and uninstalling `quadprog`. cuRobo is still built from its pinned commit. - **No new extras** — the images use the ones that already exist. `teleop` no longer bundles the `isaacsim` wheel, so environments that already provide Kit do not install a second, version-mismatched copy; installs that need it use `--extra teleop,isaacsim`, and the docs say so wherever XR teleop appears (agreed with the teleop owner). `test` is now the test suite alone and `dev` adds the documentation toolchain, so the images can install `test` without shipping Sphinx and its GPL-3.0-or-later `docutils`, which the Docker licence gate rejects. - **Image invariant test** — the images install with `uv sync`, which never runs `command_install`'s dangling-symlink guard, so isaac-sim#6329's protection would have been lost. A post-build step asserts no prebundled package lost its `__init__.py`, bound to the digest just built. It has already caught a real regression on a dependent branch. - **Removed** the `quadprog` uninstall (absent from the lock), the `toml` install (`install_deps.py` now uses stdlib `tomllib`), and the `imageio-ffmpeg` uninstall (excluded during the sync instead). Newton resolves to 1.5.0 from the lock's pinned commit where the pip path floated to `release-1.5` HEAD (1.5.1); regenerating `uv.lock` is the lever if a newer commit is needed. `uv.lock` is regenerated with uv 0.9.25, the version the images pin. Newer uv renormalises environment markers and rewrites ~2200 lines with no semantic change. ## Type of change - New feature (non-breaking change which adds functionality) ## Release backport - [ ] <!-- backport-active-release --> Backport this pull request to the active release branch after it merges into `develop` ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works — a post-build image-invariant test, plus building all three images and diffing their package sets against the previous build - [x] I have added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Co-authored-by: ooctipus <zhengyuz@nvidia.com>
1 parent 860facd commit eed50f7

28 files changed

Lines changed: 654 additions & 230 deletions

.dockerignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ docker/.container.cfg
2121
recordings/
2222
# ignore __pycache__
2323
**/__pycache__/
24+
**/.pytest_cache/
25+
**/.ruff_cache/
2426
**/*.egg-info/
2527
# ignore isaac sim symlink
2628
_isaac_sim

.github/actions/run-tests/run_tests.sh

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -357,13 +357,19 @@ run_tests() {
357357
fi
358358
if [ -n \"\${TEST_EXTRA_UV_PACKAGES:-}\" ]; then
359359
echo \"Installing extra packages with uv: \${TEST_EXTRA_UV_PACKAGES}\"
360-
# isaaclab.sh prints an informational line before command output, and pip
361-
# installs scripts into the user base when Isaac Sim site-packages is read-only.
362-
isaac_python=\"\$(./isaaclab.sh -p -c 'import sys; print(sys.executable)' | tail -n 1)\"
363-
isaac_user_site=\"\$(./isaaclab.sh -p -c 'import site; print(site.getusersitepackages())' | tail -n 1)\"
364-
uv_executable=\"\$(./isaaclab.sh -p -c 'import pathlib, site; print(pathlib.Path(site.getuserbase()) / \"bin\" / \"uv\")' | tail -n 1)\"
365-
if [ ! -x \"\${uv_executable}\" ]; then
366-
bash /with-python-package-retries.sh ./isaaclab.sh -p -m pip install uv
360+
# isaaclab.sh prints an [INFO] banner on stdout around the command output, so the
361+
# banner is filtered rather than positionally skipped.
362+
isaac_python=\"\$(./isaaclab.sh -p -c 'import sys; print(sys.executable)' | grep -v '^\[INFO\]' | tail -n 1)\"
363+
isaac_user_site=\"\$(./isaaclab.sh -p -c 'import site; print(site.getusersitepackages())' | grep -v '^\[INFO\]' | tail -n 1)\"
364+
# The image ships uv on PATH. Fall back to the user base only for images that do
365+
# not: pip installs into the venv, not the user base, when the interpreter is a venv,
366+
# so the user-base path is never created there.
367+
uv_executable=\"\$(command -v uv || true)\"
368+
if [ -z \"\${uv_executable}\" ]; then
369+
uv_executable=\"\$(./isaaclab.sh -p -c 'import pathlib, site; print(pathlib.Path(site.getuserbase()) / \"bin\" / \"uv\")' | grep -v '^\[INFO\]' | tail -n 1)\"
370+
if [ ! -x \"\${uv_executable}\" ]; then
371+
bash /with-python-package-retries.sh ./isaaclab.sh -p -m pip install uv
372+
fi
367373
fi
368374
bash /with-python-package-retries.sh \"\${uv_executable}\" pip install --python \"\${isaac_python}\" --target \"\${isaac_user_site}\" \${TEST_EXTRA_UV_PACKAGES}
369375
# Isaac Sim puts bundled packages ahead of the user site. Overlay only

.github/workflows/build.yaml

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
# -e TEST_FILTER_PATTERN="isaaclab_physx" \
4747
# -e TEST_INCLUDE_FILES="test_rigid_object_collection.py" \
4848
# -v "$PWD":/workspace/isaaclab isaac-lab-base:latest \
49-
# -c 'cd /workspace/isaaclab && /isaac-sim/python.sh -m pytest tools -v'
49+
# -c 'cd /workspace/isaaclab && ./isaaclab.sh -p -m pytest tools -v'
5050
#
5151
# Remember to REVERT all temporary changes before merging!
5252
# =============================================================================
@@ -169,6 +169,25 @@ jobs:
169169
echo "ci_image_tag=isaac-lab-ci:${sanitized_ref}-${SHA}" >> "$GITHUB_OUTPUT"
170170
echo "CI image tag: isaac-lab-ci:${sanitized_ref}-${SHA}"
171171
172+
test-dockerfile-contract:
173+
name: dockerfile-contract
174+
# Static analysis of the Dockerfile text: no image, no GPU, no Isaac Sim.
175+
runs-on: ubuntu-latest
176+
steps:
177+
- uses: actions/checkout@v6
178+
179+
- name: Set up uv
180+
uses: astral-sh/setup-uv@v6
181+
with:
182+
enable-cache: true
183+
184+
- name: Run Dockerfile contract tests
185+
shell: bash
186+
run: |
187+
set -euo pipefail
188+
uv run --no-project --with pytest \
189+
python -m pytest -q docker/test/test_dockerfile_nonroot.py
190+
172191
#region build jobs
173192
build:
174193
name: Build Base Docker Image
@@ -182,6 +201,12 @@ jobs:
182201
fetch-depth: 1
183202
lfs: true
184203

204+
# The GPU runners have no uv on PATH; the invariant check below needs it.
205+
- name: Set up uv
206+
uses: astral-sh/setup-uv@v6
207+
with:
208+
enable-cache: true
209+
185210
- name: Build and push to ECR
186211
uses: ./.github/actions/ecr-build-push-pull
187212
with:
@@ -191,6 +216,20 @@ jobs:
191216
dockerfile-path: docker/Dockerfile.base
192217
cache-tag: cache-base
193218

219+
# #6329 aborts the pip install when it strands a prebundled package's __init__.py
220+
# (nvbugs 6343978: 14 Isaac Sim extensions fail to load). The images install with
221+
# ``uv sync``, which never runs that guard, so assert the same invariant on the image.
222+
- name: Verify image invariants
223+
shell: bash
224+
env:
225+
IMAGE_TAG: ${{ needs.config.outputs.ci_image_tag }}
226+
run: |
227+
set -euo pipefail
228+
IMAGE_DIGEST="$(docker image inspect --format '{{.Id}}' "${IMAGE_TAG}")"
229+
export IMAGE_DIGEST
230+
uv run --no-project --with pytest \
231+
python -m pytest -q docker/test/test_image_invariants.py
232+
194233
build-curobo:
195234
name: Build cuRobo Docker Image
196235
runs-on: [self-hosted, gpu]
@@ -661,16 +700,6 @@ jobs:
661700
# Folded from the former standalone verify-base-non-root job: reuses the
662701
# base image already pulled by run-package-tests to keep the regression
663702
# check without burning a separate runner.
664-
- name: Run Dockerfile non-root regression test
665-
shell: bash
666-
run: |
667-
set -euo pipefail
668-
docker run --rm \
669-
-v "$PWD":/workspace/isaaclab \
670-
--entrypoint bash \
671-
"${{ needs.config.outputs.ci_image_tag }}" \
672-
-lc 'cd /workspace/isaaclab && /isaac-sim/python.sh -m pytest docker/test/test_dockerfile_nonroot.py -q'
673-
674703
- name: Verify Base runtime user is non-root
675704
shell: bash
676705
run: |
@@ -711,16 +740,6 @@ jobs:
711740
# Folded from the former standalone verify-curobo-non-root job: reuses
712741
# the curobo image already pulled by run-package-tests to keep the
713742
# regression check without burning a separate runner.
714-
- name: Run Dockerfile non-root regression test
715-
shell: bash
716-
run: |
717-
set -euo pipefail
718-
docker run --rm \
719-
-v "$PWD":/workspace/isaaclab \
720-
--entrypoint bash \
721-
"${{ needs.config.outputs.ci_image_tag }}-curobo" \
722-
-lc 'cd /workspace/isaaclab && /isaac-sim/python.sh -m pytest docker/test/test_dockerfile_nonroot.py -q'
723-
724743
- name: Verify cuRobo runtime user is non-root
725744
shell: bash
726745
run: |

.github/workflows/docs.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ jobs:
6666

6767
- name: Install docs dependencies
6868
run: |
69-
bash "$GITHUB_WORKSPACE/.github/actions/_lib/with-python-package-retries.sh" uv sync --extra test
69+
bash "$GITHUB_WORKSPACE/.github/actions/_lib/with-python-package-retries.sh" uv sync --extra dev
7070
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
7171
7272
- name: Build current version docs
@@ -98,7 +98,7 @@ jobs:
9898

9999
- name: Install docs dependencies
100100
run: |
101-
bash "$GITHUB_WORKSPACE/.github/actions/_lib/with-python-package-retries.sh" uv sync --extra test
101+
bash "$GITHUB_WORKSPACE/.github/actions/_lib/with-python-package-retries.sh" uv sync --extra dev
102102
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
103103
104104
- name: Generate multi-version docs

.github/workflows/license-check.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ jobs:
6262
ISAACSIM_ACCEPT_EULA: YES
6363
run: |
6464
bash "$GITHUB_WORKSPACE/.github/actions/_lib/with-python-package-retries.sh" \
65-
uv sync --extra all --extra isaacsim --extra test --extra rlinf --extra mimic
65+
uv sync --extra all --extra isaacsim --extra dev --extra rlinf --extra mimic
6666
# ``[tool.uv.pip] prerelease = "allow"`` lets unpinned tools float onto
6767
# prereleases. pip-licenses 6.0.0a1 reports an empty License where 5.x reports
6868
# ``UNKNOWN``, which license-exceptions.json keys on, and joins multi-license

docker/Dockerfile.base

Lines changed: 85 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
# Base image
1010
ARG ISAACSIM_BASE_IMAGE_ARG=nvcr.io/nvidia/isaac-sim
1111
ARG ISAACSIM_VERSION_ARG=6.0.0
12+
13+
# uv, pinned by digest. Declared after the ARGs so those stay global for the base FROM.
14+
FROM ghcr.io/astral-sh/uv:0.12.9@sha256:8b940d3a9d65bed080436972241af2e21c84b5e8c9193f7014ed71479ee795ff AS uv
15+
1216
FROM ${ISAACSIM_BASE_IMAGE_ARG}:${ISAACSIM_VERSION_ARG} AS base
1317
ARG ISAACSIM_VERSION_ARG
1418
ENV ISAACSIM_VERSION=${ISAACSIM_VERSION_ARG}
@@ -32,10 +36,19 @@ ARG DOCKER_USER_HOME_ARG
3236
ENV DOCKER_USER_HOME=${DOCKER_USER_HOME_ARG}
3337
ENV HOME=${DOCKER_USER_HOME}
3438

35-
# Set environment variables
39+
# Locale and apt behaviour.
3640
ENV LANG=C.UTF-8
3741
ENV DEBIAN_FRONTEND=noninteractive
38-
ENV PIP_RETRIES=12
42+
43+
# uv. Dependencies resolve from uv.lock into this venv instead of Isaac Sim's own
44+
# site-packages; isaaclab.sh sources setup_python_env.sh so ``isaacsim`` stays importable.
45+
# uv is pinned to Kit's interpreter: ``python-preference = "only-managed"`` would otherwise
46+
# download a CPython that cannot load Kit's extension modules.
47+
ARG VENV_PATH_ARG=/opt/isaaclab-venv
48+
ENV VIRTUAL_ENV=${VENV_PATH_ARG}
49+
ENV UV_PROJECT_ENVIRONMENT=${VENV_PATH_ARG}
50+
ENV UV_PYTHON=${ISAACSIM_ROOT_PATH_ARG}/kit/python/bin/python3
51+
ENV UV_PYTHON_PREFERENCE=only-system
3952
ENV UV_HTTP_RETRIES=12
4053

4154
# Base image may end with a non-root user; switch to root for system-level
@@ -71,27 +84,24 @@ RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \
7184

7285
# copy files necessary for installing Isaac Lab and its dependencies
7386
# this way we minimize the chance of cache invalidation on file edits
74-
COPY ../isaaclab.* ../environment.yml ../pyproject.toml ${ISAACLAB_PATH}/
87+
COPY --from=uv /uv /uvx /usr/local/bin/
88+
COPY ../isaaclab.* ../environment.yml ../pyproject.toml ../uv.lock ${ISAACLAB_PATH}/
7589
COPY ../tools/ ${ISAACLAB_PATH}/tools/
76-
COPY ../source/ ${ISAACLAB_PATH}/source/
77-
78-
# Fix the line endings for the shell scripts (Windows git may add \r)
79-
RUN find ${ISAACLAB_PATH} -type f -name "*.sh" -exec sed -i 's/\r$//' {} +
80-
81-
# Ensure isaaclab.sh has execute permissions
82-
RUN chmod +x ${ISAACLAB_PATH}/isaaclab.sh
8390

8491
# Set up a symbolic link between the installed Isaac Sim root folder and _isaac_sim in the Isaac Lab directory
8592
RUN ln -sf ${ISAACSIM_ROOT_PATH} ${ISAACLAB_PATH}/_isaac_sim
8693

87-
# Install toml (used by tools/install_deps.py) and pytest (needed to run
88-
# in-container tests; moved to optional [test] extra in pyproject.toml)
89-
RUN ${ISAACLAB_PATH}/isaaclab.sh -p -m pip install toml pytest
94+
# Created before the venv so every install layer can set ownership as it writes, instead of
95+
# a recursive chown afterwards: on overlayfs a chown copies up every file it touches, which
96+
# costs both build time and a duplicate of the venv in the image.
97+
# The uid/gid 1000 match GitHub runner bind mounts used by Docker tests.
98+
# --non-unique is required because some base image revisions already carry
99+
# another user or group at uid/gid 1000.
100+
RUN groupadd --non-unique --gid 1000 isaaclab \
101+
&& useradd --non-unique --uid 1000 --gid 1000 -M -l -s /bin/bash -d ${DOCKER_USER_HOME} isaaclab
90102

91-
# Install apt dependencies for extensions that declare them in their extension.toml,
92-
RUN ${ISAACLAB_PATH}/isaaclab.sh -p ${ISAACLAB_PATH}/tools/install_deps.py apt ${ISAACLAB_PATH}/source && \
93-
apt-get -y autoremove && apt-get clean && \
94-
rm -rf /var/lib/apt/lists/*
103+
# Created up front so the install steps below resolve into it.
104+
RUN uv venv --seed "${VIRTUAL_ENV}"
95105

96106
# for singularity usage, have to create the directories that will binded
97107
RUN mkdir -p ${ISAACSIM_ROOT_PATH}/kit/cache && \
@@ -113,41 +123,79 @@ RUN touch /bin/nvidia-smi && \
113123
mkdir -p /var/run/nvidia-persistenced && \
114124
touch /var/run/nvidia-persistenced/socket
115125

116-
# robomimic declares imageio-ffmpeg for optional video helpers. Keep its
117-
# bundled FFmpeg binary out of the distributed image and let users who
118-
# enable video recording install MoviePy and its video backend explicitly.
119-
RUN --mount=type=cache,target=${DOCKER_USER_HOME}/.cache/pip \
120-
${ISAACLAB_PATH}/isaaclab.sh --install && \
121-
${ISAACLAB_PATH}/isaaclab.sh -p -m pip uninstall -y imageio-ffmpeg
126+
# Workspace manifests only: ``uv sync --no-install-workspace`` needs every member's
127+
# pyproject.toml to build an installation plan, but not their sources. Resolving the ~240
128+
# third-party packages here means a source edit re-runs only the workspace install below.
129+
COPY --parents ../source/*/pyproject.toml ${ISAACLAB_PATH}/
130+
131+
# The image's package set. Kit ships in the base image, so the teleop stack comes without the
132+
# ``isaacsim`` wheel; ``teleop`` itself is left exactly as tuned.
133+
ARG IMAGE_EXTRAS="--extra sb3 --extra skrl --extra rl-games --extra rsl-rl --extra viser --extra rerun --extra mimic --extra teleop --extra test"
122134

123-
# HACK: Remove install of quadprog dependency
124-
RUN ${ISAACLAB_PATH}/isaaclab.sh -p -m pip uninstall -y quadprog
135+
136+
RUN --mount=type=cache,target=/root/.cache/uv \
137+
cd ${ISAACLAB_PATH} && \
138+
uv sync --frozen --inexact ${IMAGE_EXTRAS} \
139+
--no-install-workspace --no-install-package imageio-ffmpeg && \
140+
chown -R isaaclab:isaaclab ${VIRTUAL_ENV} && \
141+
chmod -R a+rwX ${VIRTUAL_ENV}/lib ${VIRTUAL_ENV}/bin
142+
143+
COPY ../source/ ${ISAACLAB_PATH}/source/
144+
145+
# Fix the line endings for the shell scripts (Windows git may add \r), then restore the
146+
# launcher's mode; one layer because both walk the tree the COPY above just wrote. ``sed -i``
147+
# rewrites the file under the build umask, so an explicit 755 keeps it runnable by a
148+
# container started with a uid other than the image's own.
149+
RUN find ${ISAACLAB_PATH} -type f -name "*.sh" -exec sed -i 's/\r$//' {} + \
150+
&& chmod 755 ${ISAACLAB_PATH}/isaaclab.sh
151+
152+
# Install apt dependencies for extensions that declare them in their extension.toml,
153+
RUN ${ISAACLAB_PATH}/isaaclab.sh -p ${ISAACLAB_PATH}/tools/install_deps.py apt ${ISAACLAB_PATH}/source && \
154+
apt-get -y autoremove && apt-get clean && \
155+
rm -rf /var/lib/apt/lists/*
156+
157+
# robomimic and moviepy declare imageio-ffmpeg for optional video helpers. Its bundled
158+
# FFmpeg binary stays out of the distributed image; users who enable video recording install
159+
# MoviePy and its video backend explicitly.
160+
RUN --mount=type=cache,target=/root/.cache/uv \
161+
cd ${ISAACLAB_PATH} && \
162+
uv sync --frozen --inexact ${IMAGE_EXTRAS} \
163+
--no-install-package imageio-ffmpeg && \
164+
find ${VIRTUAL_ENV} \! -user isaaclab \
165+
-exec chown isaaclab:isaaclab {} + -exec chmod a+rwX {} +
166+
167+
# Isaac Sim's prebundled packages sit on PYTHONPATH ahead of the venv for launch paths that
168+
# never import isaaclab (``runheadless.sh``, ``isaac-sim.streaming.sh``), so its torch would
169+
# shadow the venv's and load a libtorch_cuda.so needing an NCCL symbol the bundled NCCL does
170+
# not export (nvbugs 6343978). Repoint them at the venv; the pip install path did this itself.
171+
RUN ${ISAACLAB_PATH}/isaaclab.sh -p -c \
172+
"from isaaclab.cli.commands.install import _repoint_prebundle_packages; _repoint_prebundle_packages()"
173+
174+
# uv venv/uv sync above needed Kit's interpreter as the venv base; from here on ``uv pip``
175+
# should target the venv, including the interactive ``pip`` alias below.
176+
ENV UV_PYTHON=${VENV_PATH_ARG}/bin/python
125177

126178
# aliasing isaaclab.sh and python for convenience
127179
RUN echo "export ISAACLAB_PATH=${ISAACLAB_PATH}" >> ${DOCKER_USER_HOME}/.bashrc && \
128180
echo "alias isaaclab=${ISAACLAB_PATH}/isaaclab.sh" >> ${DOCKER_USER_HOME}/.bashrc && \
129-
echo "alias python=${ISAACLAB_PATH}/_isaac_sim/python.sh" >> ${DOCKER_USER_HOME}/.bashrc && \
130-
echo "alias python3=${ISAACLAB_PATH}/_isaac_sim/python.sh" >> ${DOCKER_USER_HOME}/.bashrc && \
131-
echo "alias pip='${ISAACLAB_PATH}/_isaac_sim/python.sh -m pip'" >> ${DOCKER_USER_HOME}/.bashrc && \
132-
echo "alias pip3='${ISAACLAB_PATH}/_isaac_sim/python.sh -m pip'" >> ${DOCKER_USER_HOME}/.bashrc && \
133-
echo "alias tensorboard='${ISAACLAB_PATH}/_isaac_sim/python.sh ${ISAACLAB_PATH}/_isaac_sim/tensorboard'" >> ${DOCKER_USER_HOME}/.bashrc && \
181+
echo "alias python='${ISAACLAB_PATH}/isaaclab.sh -p'" >> ${DOCKER_USER_HOME}/.bashrc && \
182+
echo "alias python3='${ISAACLAB_PATH}/isaaclab.sh -p'" >> ${DOCKER_USER_HOME}/.bashrc && \
183+
echo "alias pip='uv pip'" >> ${DOCKER_USER_HOME}/.bashrc && \
184+
echo "alias pip3='uv pip'" >> ${DOCKER_USER_HOME}/.bashrc && \
185+
echo "alias tensorboard='${VIRTUAL_ENV}/bin/tensorboard'" >> ${DOCKER_USER_HOME}/.bashrc && \
134186
echo "export TZ=$(date +%Z)" >> ${DOCKER_USER_HOME}/.bashrc && \
135187
echo "shopt -s histappend" >> ${DOCKER_USER_HOME}/.bashrc && \
136188
echo "PROMPT_COMMAND='history -a'" >> ${DOCKER_USER_HOME}/.bashrc
137189

138-
# Create the non-root runtime user after root-only image setup is complete.
139-
# The uid/gid 1000 match GitHub runner bind mounts used by Docker tests.
140-
# --non-unique is required because some base image revisions already carry
141-
# another user or group at uid/gid 1000.
142-
RUN groupadd --non-unique --gid 1000 isaaclab \
143-
&& useradd --non-unique --uid 1000 --gid 1000 -M -l -s /bin/bash -d ${DOCKER_USER_HOME} isaaclab
144-
145190
# The Isaac Sim image sets HUB__ARGS__DETECT_ONLY=true, which forbids OmniHub from starting.
146191
# omni.client still asks it to launch, so every Kit startup retries ~39 times before giving
147192
# up. Letting OmniHub start also needs its cache at /var/cache/hub, owned by the isaac-sim
148193
# user (uid 1234) upstream, to be writable by the uid 1000 runtime user.
149194
ENV HUB__ARGS__DETECT_ONLY=false
150195

196+
# CI installs optional extras into the venv at test time (e.g. ``pytetwild`` for the
197+
# tetrahedralization feature) and a volume-mounted run executes as the runner's uid rather
198+
# than the image's, so the paths pip writes must be writable by a uid the image cannot know.
151199
RUN chown -R isaaclab:isaaclab \
152200
${ISAACLAB_PATH} \
153201
${DOCKER_USER_HOME} \

0 commit comments

Comments
 (0)