Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
b0c3941
Remove solver-specific velocity task tuning (#7607)
ooctipus Sep 7, 2026
de83e61
Fix RLinf uv launch instructions (#7617)
mingxueg-nv Sep 7, 2026
32c857a
Add MAPPO to multi-agent documentation commands (#7605)
kellyguo11 Sep 7, 2026
71c5260
Close the Newton viewer before dropping the reference to it (#7590)
fatimaanes Sep 7, 2026
0a62c34
Fix Warp camera runtime errors (#7596)
kellyguo11 Sep 7, 2026
7c6b4d7
[Odin] Fix preset-based agent selection for rsl_rl, rl_games and sb3 …
AntoineRichard Sep 7, 2026
4cf35cc
Fix compute_first_contact/air missing transitions as the sensor clock…
AntoineRichard Sep 7, 2026
f948fa5
Fix MJWarp USD friction loss import (#7298)
NeoZng Sep 7, 2026
5c78cae
Document the 15625 scene partition cap for Isaac RTX renderer (#7573)
mataylor-nvidia Sep 7, 2026
1ae7125
[Tasks] Fix surface gripper observation shapes (#7583)
StafaH Sep 7, 2026
aab8730
[Backport develop] Fix Franka Reach relative IK teleoperation (#7619)…
kellyguo11 Sep 7, 2026
1808627
Correct contrib Newton support in environment browser (#7620)
kellyguo11 Sep 7, 2026
6513de1
[CI] Bump Isaac Sim image to b6222dffc018 (#7600)
isaaclab-bot[bot] Sep 7, 2026
63d579e
[Tests] Skip DrLegs-Walk in the contrib environment test (#7623)
StafaH Sep 7, 2026
6fd0f1e
[Tasks] Fix pretrained checkpoint lookup for domain presets (#7594)
StafaH Sep 8, 2026
f443e8a
Default surface-gripper tasks to CPU simulation (#7627)
kellyguo11 Sep 8, 2026
7127f50
[CI][Auto Version Bump] Compile changelog fragments (schedule)
isaaclab-bot[bot] Sep 8, 2026
25fad3e
Reduce direct locomotion step overhead (#6512)
AntoineRichard Sep 8, 2026
b182de0
improve leapp export (#7326)
frlai Sep 8, 2026
c91b036
[Workflow] Update extra paths configuration for all workflows (#6644)
StafaH Sep 8, 2026
cd48f96
cuRobo install documentation change with conda GCC 14 toolchain (#7649)
njawale42 Sep 8, 2026
59d2807
[Workflow] Fix template generator setup workflows (#7645)
StafaH Sep 8, 2026
6fdc484
Bug Fixes for Mimic-Cosmos Workflows (#7500)
shauryadNv Sep 8, 2026
cdaefc5
[Tasks] Support pretrained Cartpole feature checkpoints (#7630)
kellyguo11 Sep 8, 2026
1326e94
Cache marker scene-partition tokens instead of rebuilding them each f…
mataylor-nvidia Sep 9, 2026
4db0186
[Docs] Use uv pip for Python package installation (#7643)
StafaH Sep 9, 2026
e591032
[Docs] Improve environment browser cards (#7644)
StafaH Sep 9, 2026
590ec10
[PERF] Increase VBD iterations and narrow stiffness range in deformab…
mmichelis Sep 9, 2026
b7f66e7
Log VideoRecorder cubric warning once per recorder (#7657)
kellyguo11 Sep 9, 2026
061a226
Document that SO-101 joint teleop reset pauses until leader arm is re…
rwiltz Sep 9, 2026
649ef3f
[Docs] Add interactive demos command builder (#7641)
StafaH Sep 9, 2026
10b2895
Fix streaming_gt_types docstring and visualization docs polish (#7588)
matthewtrepte Sep 9, 2026
0af962c
Fix/so101 teleop success tolerance (#7661)
rwiltz Sep 9, 2026
646d04d
Default SO-101 cube-stacking tasks to PhysX and document the selector…
rwiltz Sep 9, 2026
c588458
Resolve check_instanceable asset input through retrieve_file_path (#7…
kellyguo11 Sep 9, 2026
5ea7247
Use decord2 on supported architectures (#7668)
kellyguo11 Sep 9, 2026
24ae8af
SkillGen doc changes for uv installation & workflow (#7670)
njawale42 Sep 9, 2026
5926024
[CI][Auto Version Bump] Compile changelog fragments (schedule)
isaaclab-bot[bot] Sep 9, 2026
aea146a
Fix newton_gl visualizer CUDA illegal access with --device cpu (#7658)
matthewtrepte Sep 9, 2026
673b673
Cleanup patches, Compose agent variants through canonical preset root…
ooctipus Sep 9, 2026
1dac6b1
Fix Agibot place visualizer startup (#7678)
rebeccazhang0707 Sep 9, 2026
28d3527
[Docker] Point uv at the shipped environment in the kit-less image (#…
hujc7 Sep 9, 2026
94bf2a1
Minor Bug Fix to Mimic-Cosmos Eval (#7689)
shauryadNv Sep 9, 2026
9470e1e
Fix deferred CUDA checks before Kit GPU filtering (#7669)
ooctipus Sep 9, 2026
bb3f366
Use default ground planes in MPM demos (#7639)
maxkra15 Sep 9, 2026
7e62e78
Fix Franka Reach teleoperation stability and collisions (#7679)
maxkra15 Sep 9, 2026
1d53cb5
[DOC] Rework Newton manager docs into a contributor extension guide (…
mmichelis Sep 9, 2026
f4f2a3e
[Docs] Simplify developer documentation navigation (#7681)
StafaH Sep 9, 2026
563aa87
[Docs] Fix demo card media paths (#7680)
StafaH Sep 9, 2026
52800ab
[DOC] Unify volume, surface, and cable deformables into one guide (#7…
mmichelis Sep 9, 2026
e80f5df
Pin ovrtx-cache-warm to exact rendering node IDs (#7683)
mataylor-nvidia Sep 9, 2026
b34e3ad
Allow accepting the CloudXR EULA non-interactively (#7380)
2047767028-lang Sep 9, 2026
e8841db
[CI] Bump Isaac Sim image to 36fd22dbf520 (#7634)
isaaclab-bot[bot] Sep 9, 2026
ad4702d
[Docs] Merge tutorials into searchable how-to guides (#7682)
StafaH Sep 9, 2026
6f47c7f
Enable G1 SDG hand contact sensors (#7663)
jaybdub Sep 9, 2026
798c80e
Skip unused PyTorch3D extensions for GR00T (#7664)
jaybdub Sep 9, 2026
20ef8d7
Update Hugging Face CLI commands (#7665)
jaybdub Sep 9, 2026
03ef2c8
Preserve GR00T dependency pins during rollout (#7666)
jaybdub Sep 9, 2026
899173f
[Workflow] Fix template generator dependency and AMP workflows (#7693)
StafaH Sep 9, 2026
9e5ace1
Reorganize physical backend documentation (#7098)
AntoineRichard Sep 9, 2026
ce4156d
Fix Franka Reach OSC velocity limit and normalize OSC pose targets (#…
maxkra15 Sep 9, 2026
c3ed64e
[Deps] Update Isaac Sim 6.1 dependencies and defaults (#7685)
kellyguo11 Sep 10, 2026
738a694
Document task limitations and guard missing Pink IK dependencies (#7701)
StafaH Sep 10, 2026
bacae88
Warn about incompatible environment browser runtimes (#7704)
StafaH Sep 10, 2026
434623c
Move sensor documentation to Concepts (#7372)
AntoineRichard Sep 10, 2026
ead426e
Updated Mimic dataset docs links to 6.1 (#7710)
dengyuchenkit Sep 10, 2026
527c77e
Ignore Kit's imgui.ini UI-state file (#7694)
matthewtrepte Sep 10, 2026
f023d8c
Keep isaaclab.utils.configclass bound to the decorator (#7646)
pascal-roth Sep 10, 2026
02deca1
Add teleop extra to Galbot visualizer docs (#7721)
kellyguo11 Sep 10, 2026
037f853
Fix SIGSEGV on second play() after stop() without reset() on GPU Phys…
matthewtrepte Sep 10, 2026
e5bfe37
Disable G1 XR camera PiP by default (#7703)
hougantc-nvda Sep 10, 2026
e8e0454
[CI][Auto Version Bump] Compile changelog fragments (schedule)
isaaclab-bot[bot] Sep 10, 2026
c2bddbc
[Fix] Restore Shadow Hand tendon controls and select simplified colli…
hujc7 Sep 10, 2026
4a5adb6
Fix isaaclab app docs (#7719)
matthewtrepte Sep 10, 2026
83b0367
Restore Agibot Newton-backed visualizers (#7712)
kellyguo11 Sep 10, 2026
ddb2404
Tune visualization docs hero-tile framing and playback (#7708)
matthewtrepte Sep 10, 2026
eaf85e3
Fix benign OmniRtx*API schema errors with newton_rtx + ovphysx (#7653)
matthewtrepte Sep 10, 2026
f46baec
[Fix] Load task-owned Shadow Hand feature extractor checkpoints (#7695)
kellyguo11 Sep 10, 2026
60397e1
Improve default ground plane and visualizer backgrounds (#7609)
maxkra15 Sep 10, 2026
7100c9e
Update LEAPP export entrypoint (#7427)
frlai Sep 10, 2026
8b69bd1
Add Newton MPM surface rendering to the teapot demo (#7640)
maxkra15 Sep 10, 2026
247790e
Regenerate the Shadow Hand golden stage for the promoted asset (#7733)
AntoineRichard Sep 10, 2026
e78aa3e
[Changelog] Discover release branches for nightly compilation (#7736)
kellyguo11 Sep 10, 2026
3efb206
Add SSH and HTTPS tabs to Windows uv installation docs (#7731)
StafaH Sep 10, 2026
245e1d5
Use legacy Franka asset for PhysX Reach (#7742)
maxkra15 Sep 10, 2026
632f197
[Odin] Fix multi-agent play benchmark environment creation (#7360)
AntoineRichard Sep 10, 2026
cdb244d
[Actuators] Use native Newton path by default (#7713)
kellyguo11 Sep 10, 2026
0e840ed
[Docs] Reorganize guides, concepts, and features navigation (#7729)
StafaH Sep 10, 2026
e052e80
Fix opaque RL-Games playback failure for incompatible agent configs (…
StafaH Sep 10, 2026
56c0fe6
[Task Clean-up][Assets] Drop the Newton 1.5 tendon-adapter shim (#7468)
hujc7 Sep 10, 2026
f2255fc
Reorganize documentation sections (#7748)
kellyguo11 Sep 10, 2026
6ec8ecb
Create uv venv via uv sync before cuRobo install in SkillGen docs (#7…
njawale42 Sep 10, 2026
20daa72
Skip mesh approximation for Newton shadow visualizers (#7718)
kellyguo11 Sep 10, 2026
3f253ed
Fixed HDF5 to MP4 frame dtype conversion (#7752)
dengyuchenkit Sep 11, 2026
9d51a48
Fix LEAPP RSL-RL export startup ordering (#7754)
kellyguo11 Sep 11, 2026
03be6f9
[CI][Auto Version Bump] Compile changelog fragments (schedule)
isaaclab-bot[bot] Sep 11, 2026
412fb31
Support OvPhysX 0.6 while retaining public 0.5.11 compatibility (#7738)
AntoineRichard Sep 11, 2026
b342207
Refresh PhysX and OVPhysX contact sensor buffers on every physics ste…
AntoineRichard Sep 11, 2026
429fb63
[Docs] Populate release and develop environment benchmarks (#7699)
StafaH Sep 11, 2026
287bf0e
[CI] Bump Isaac Sim image to 223adbb0a6f1 (#7728)
isaaclab-bot[bot] Sep 11, 2026
dc9a117
Make SkillGen Rerun install and verification commands uv compatible (…
njawale42 Sep 11, 2026
b9618b6
Fixed locomanipulation NuRec SDG generation (#7756)
dengyuchenkit Sep 11, 2026
41d47ce
Fixes CI base image pulls when the registry refuses the inherited cre…
myurasov-nv Sep 11, 2026
487b5ca
Added Rendering Benchmark (#7702)
daniela-hase Sep 11, 2026
a8b4da3
[CI][Auto Version Bump] Compile changelog fragments (schedule)
isaaclab-bot[bot] Sep 12, 2026
c9c1312
Upgrade Transformers to 5.10.4 (#7803)
kellyguo11 Sep 15, 2026
f7a1c48
Exclude Git-only integrations from PyPI wheel metadata (#7807)
kellyguo11 Sep 15, 2026
16006db
[Docs] Update 3.0.0 RC1 Docker references (#7801)
kellyguo11 Sep 15, 2026
1fb402e
[OMPE-106305] Update container security dependencies (#7811)
kellyguo11 Sep 15, 2026
56036c8
[OMPE-106305] Resolve security findings and optionalize RL integratio…
kellyguo11 Sep 15, 2026
270ef40
Avoid continuous rendering between video captures (#7642)
AntoineRichard Sep 15, 2026
243659e
Deprecate isaaclab.sh and legacy RSL-RL configs (#7820)
StafaH Sep 15, 2026
e86463d
Fix implicit effort submission in ovphysx (#7782)
marcodiiga Sep 15, 2026
5f77b2f
[CI][Auto Version Bump] Compile changelog fragments (schedule)
isaaclab-bot[bot] Sep 16, 2026
2f5fddf
[OMPE-106305] Harden CI dependencies, update Starlette, and align aio…
kellyguo11 Sep 16, 2026
480398d
[RL] Update RSL-RL to 5.5.1 (#7825)
StafaH Sep 16, 2026
3326ba9
Fix environment browser preview assets (#7822)
StafaH Sep 16, 2026
9d1896d
Fix Newton joint positions in DOF space, and enable the Digit velocit…
Double7sBurger Sep 16, 2026
416a1d1
Fix OVPhysX re-reading static joint and body properties every step (#…
AntoineRichard Sep 16, 2026
538a409
Pin the published PyTorch stack to the supported versions (#7821)
StafaH Sep 16, 2026
d8e4a1a
[Docs] Fix environment command generator for soft task (#7844)
StafaH Sep 16, 2026
adeea93
Fix environment browser preview image URLs (#7849)
StafaH Sep 16, 2026
6d97590
Select Franka soft physics payload by backend (#7847)
maxkra15 Sep 16, 2026
2867f6b
Add OvStage compat for GPU_INCREMENTAL in OvStage 0.2.0 (#7855)
r-schmitt Sep 16, 2026
0c12bab
Avoid Newton VBD graph coloring hangs (#7826)
maxkra15 Sep 16, 2026
94a8ad5
[Bump] Bump Newton to 1.6.0 (#7842)
kellyguo11 Sep 16, 2026
8b58e0c
[CI] Prepare develop as repository and docs default (#7767)
kellyguo11 Sep 16, 2026
7a7575d
[Docs] Fix develop README documentation links (#7770)
kellyguo11 Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Describe the characteristic of your environment:

<!-- Please complete the following description. -->
- Commit: [e.g. 8f3b9ca]
- Isaac Sim Version: [e.g. 5.0, this can be obtained by `cat ${ISAACSIM_PATH}/VERSION`]
- Isaac Sim Version: [e.g. 6.1, this can be obtained by `cat ${ISAACSIM_PATH}/VERSION`]
- OS: [e.g. Ubuntu 22.04]
- GPU: [e.g. RTX 5090]
- CUDA: [e.g. 12.8]
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/proposal.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Describe the versions where you are observing the missing feature in:

<!-- Please complete the following description. -->
- Isaac Lab Version: [e.g. 3.0.0]
- Isaac Sim Version: [e.g. 6.0, this can be obtained by `cat ${ISAACSIM_PATH}/VERSION`]
- Isaac Sim Version: [e.g. 6.1, this can be obtained by `cat ${ISAACSIM_PATH}/VERSION`]

### Additional context

Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/question.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Describe the versions that you are currently using:

<!-- Please complete the following description. -->
- Isaac Lab Version: [e.g. 3.0.0]
- Isaac Sim Version: [e.g. 6.0, this can be obtained by `cat ${ISAACSIM_PATH}/VERSION`]
- Isaac Sim Version: [e.g. 6.1, this can be obtained by `cat ${ISAACSIM_PATH}/VERSION`]
66 changes: 56 additions & 10 deletions .github/actions/_lib/compute-deps-hash/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ runs:
.dockerignore
docker/docker-compose.yaml
docker/scripts/install_carb_env_shim.sh
docker/scripts/install_git_lfs.sh
docker/utils/volume_mounts.py
isaaclab.sh
environment.yml
Expand All @@ -62,16 +63,61 @@ runs:
deps_manifest_pattern='(setup\.py|pyproject\.toml|setup\.cfg|extension\.toml|requirements[^/]*\.txt|uv\.lock)$'

# Resolve the actual base image digest so a new push of a mutable tag
# (e.g. latest-develop) invalidates the deps cache automatically.
base_image_digest=$(docker buildx imagetools inspect \
"${ISAACSIM_BASE_IMAGE}:${ISAACSIM_VERSION}" \
--format '{{json .Manifest.Digest}}' 2>/dev/null | tr -d '"' || true)
if [ -n "${base_image_digest}" ]; then
base_image_uniq_id="${ISAACSIM_BASE_IMAGE}:${ISAACSIM_VERSION}:${base_image_digest}"
else
echo "🟠 Could not resolve base image digest, falling back to tag string"
base_image_uniq_id="${ISAACSIM_BASE_IMAGE}:${ISAACSIM_VERSION}"
fi
# (e.g. latest-develop) invalidates the deps cache. A tag that already
# carries a digest is its own identity, so it is used directly.
case "${ISAACSIM_VERSION}" in
*@sha256:*)
base_image_digest="${ISAACSIM_VERSION##*@}"
if ! printf '%s' "${base_image_digest}" | grep -Eq '^sha256:[0-9a-f]{64}$'; then
echo "::error::Base image tag ${ISAACSIM_VERSION} carries a malformed digest pin."
exit 1
fi
echo "🔵 Base image tag is digest-pinned; using it as the cache identity"
;;
*)
# Reading the manifest can fail transiently, so retry. Failing hard
# afterwards is deliberate: falling back to the tag string drops the
# digest from the cache key and hides an unreadable manifest until a
# later, far less obvious build error. stderr is kept off stdout so
# a warning can never be concatenated into the digest.
base_image_digest=""
inspect_err="$(mktemp)"
attempts=5
attempt=1
while [ "${attempt}" -le "${attempts}" ]; do
inspect_out=$(docker buildx imagetools inspect \
"${ISAACSIM_BASE_IMAGE}:${ISAACSIM_VERSION}" \
--format '{{json .Manifest.Digest}}' 2>"${inspect_err}" || true)
candidate=$(printf '%s' "${inspect_out}" | tr -d '"')
# A digest is exactly sha256: plus 64 hex characters. Anything
# looser lets stray stdout into the cache key, and a stable
# diagnostic string would then stop a changed base image from
# invalidating it.
if printf '%s' "${candidate}" | grep -Eq '^sha256:[0-9a-f]{64}$'; then
base_image_digest="${candidate}"
break
fi
echo "🟠 Base image manifest read attempt ${attempt}/${attempts} failed: $(tr '\n' ' ' < "${inspect_err}")"
# An authorization refusal will not clear on its own, so stop
# retrying and report it as the configuration error it is.
if grep -Eqi 'denied|unauthorized|forbidden|insufficient_scope|401|403' "${inspect_err}"; then
echo "::error::${ISAACSIM_BASE_IMAGE}:${ISAACSIM_VERSION} cannot be read with the credentials available to this job."
rm -f "${inspect_err}"
exit 1
fi
if [ "${attempt}" -lt "${attempts}" ]; then
sleep $((attempt * 5))
fi
attempt=$((attempt + 1))
done
rm -f "${inspect_err}"
if [ -z "${base_image_digest}" ]; then
echo "::error::Cannot read the manifest for ${ISAACSIM_BASE_IMAGE}:${ISAACSIM_VERSION} after ${attempts} attempts (see the attempt logs above)."
exit 1
fi
;;
esac
base_image_uniq_id="${ISAACSIM_BASE_IMAGE}:${ISAACSIM_VERSION}:${base_image_digest}"

mapfile -t manifest_files < <(git ls-files | grep -E "${deps_manifest_pattern}" || true)
file_hash=$(git ls-files -s "${deps_files[@]}" "${manifest_files[@]}" 2>/dev/null \
Expand Down
141 changes: 125 additions & 16 deletions .github/actions/_lib/setup-docker-config/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,146 @@ name: 'Setup docker config'
description: >
Point DOCKER_CONFIG at a temp config with the credential helper disabled and
log into nvcr.io. Shared by the docker-build and ecr-build-push-pull actions.
Idempotent: re-invoking it in the same job is a no-op, so callers (e.g.
ecr-build-push-pull delegating to docker-build) don't need to coordinate.
Idempotent: re-invoking it in the same job reuses the existing config, and a
base-image-ref already checked in this job is not probed again, so callers
(e.g. ecr-build-push-pull delegating to docker-build) don't need to coordinate.
Reads NGC_API_KEY from the environment (optional; warns when missing).
When base-image-ref names an nvcr.io image that the configured credentials are
refused, falls back to anonymous access for that registry.

inputs:
base-image-ref:
description: >
Optional "<image>:<tag>" that this job must be able to read. nvcr.io can
refuse a credential scoped to another organization a pull token for a
public repository instead of downgrading to anonymous, so when the
configured credentials are denied this image and anonymous access can read
it, the stored nvcr.io credential is dropped from the temp config. Only
nvcr.io refs are eligible; empty skips the check.
required: false
default: ''

runs:
using: composite
steps:
- shell: bash
env:
BASE_IMAGE_REF: ${{ inputs.base-image-ref }}
STRIP_HELPER: ${{ github.action_path }}/strip_registry_auth.py
run: |
# The runner's credential helper backend is broken ("not implemented")
# and causes docker login calls to fail unless we point DOCKER_CONFIG at
# a temp config with credsStore disabled. The value is written to
# $GITHUB_ENV so subsequent steps in the job inherit it; a second
# invocation sees it already set and short-circuits.
# invocation reuses it and only re-runs the base-image check.
if [ -n "${DOCKER_CONFIG:-}" ] && [ -f "${DOCKER_CONFIG}/config.json" ]; then
echo "🟢 Docker config already set up at ${DOCKER_CONFIG}, skipping"
echo "🟢 Docker config already set up at ${DOCKER_CONFIG}, keeping it"
DOCKER_CONFIG_DIR="${DOCKER_CONFIG}"
else
DOCKER_CONFIG_DIR=$(mktemp -d)
if [ -f "${HOME}/.docker/config.json" ]; then
python3 -c "import json; cfg=json.load(open('${HOME}/.docker/config.json')); cfg['credsStore']=''; cfg.pop('credHelpers',None); json.dump(cfg,open('${DOCKER_CONFIG_DIR}/config.json','w'))"
else
echo '{"credsStore":""}' > "${DOCKER_CONFIG_DIR}/config.json"
fi
export DOCKER_CONFIG="${DOCKER_CONFIG_DIR}"
echo "DOCKER_CONFIG=${DOCKER_CONFIG_DIR}" >> "$GITHUB_ENV"
# Mark this directory as ours, so a later invocation only ever edits a
# config this action created and never the runner's persistent one.
echo "SETUP_DOCKER_CONFIG_OWNED=${DOCKER_CONFIG_DIR}" >> "$GITHUB_ENV"
SETUP_DOCKER_CONFIG_OWNED="${DOCKER_CONFIG_DIR}"

if [ -n "${NGC_API_KEY:-}" ]; then
echo "🔵 Logging into nvcr.io..."
printf '%s' "${NGC_API_KEY}" | docker login -u '$oauthtoken' --password-stdin nvcr.io
else
echo "🟠 NGC_API_KEY not set - skipping nvcr.io login (normal for fork PRs)"
fi
fi

if [ -z "${BASE_IMAGE_REF:-}" ]; then
exit 0
fi

DOCKER_CONFIG_DIR=$(mktemp -d)
if [ -f "${HOME}/.docker/config.json" ]; then
python3 -c "import json; cfg=json.load(open('${HOME}/.docker/config.json')); cfg['credsStore']=''; cfg.pop('credHelpers',None); json.dump(cfg,open('${DOCKER_CONFIG_DIR}/config.json','w'))"
else
echo '{"credsStore":""}' > "${DOCKER_CONFIG_DIR}/config.json"
# ecr-build-push-pull delegates to docker-build, so this action can run
# twice in one job with the same ref. The probes are network calls with
# backoff, and the first run already settled the outcome, so a ref
# checked in this job is not probed again.
if [ "${SETUP_DOCKER_CONFIG_CHECKED_REF:-}" = "${BASE_IMAGE_REF}" ]; then
echo "🟢 ${BASE_IMAGE_REF} was already checked in this job, skipping"
exit 0
fi
export DOCKER_CONFIG="${DOCKER_CONFIG_DIR}"
echo "DOCKER_CONFIG=${DOCKER_CONFIG_DIR}" >> "$GITHUB_ENV"
echo "SETUP_DOCKER_CONFIG_CHECKED_REF=${BASE_IMAGE_REF}" >> "$GITHUB_ENV"

if [ -n "${NGC_API_KEY:-}" ]; then
echo "🔵 Logging into nvcr.io..."
docker login -u '$oauthtoken' -p "${NGC_API_KEY}" nvcr.io
else
echo "🟠 NGC_API_KEY not set - skipping nvcr.io login (normal for fork PRs)"
# Scope: only nvcr.io shows the refuse-instead-of-downgrade behaviour.
# Other registries (Docker Hub in particular) must keep their
# credentials, which also serve as pull-rate-limit budget.
base_registry="$(printf '%s' "${BASE_IMAGE_REF%%/*}" | tr '[:upper:]' '[:lower:]')"
case "${BASE_IMAGE_REF}" in
*/*) ;;
*) base_registry="" ;;
esac
case "${base_registry%%:*}" in
nvcr.io) ;;
*)
echo "🔵 ${BASE_IMAGE_REF} is not an nvcr.io image; leaving the docker config unchanged"
exit 0
;;
esac

if [ "${SETUP_DOCKER_CONFIG_OWNED:-}" != "${DOCKER_CONFIG_DIR}" ]; then
echo "🔵 ${DOCKER_CONFIG_DIR} was not created by this action; leaving it unchanged"
exit 0
fi

# One transient error must not cost a working credential, so retry and
# keep stderr: only an authorization refusal justifies dropping it.
probe_err="$(mktemp)"
trap 'rm -f "${probe_err}"' EXIT
creds_denied=""
for attempt in 1 2 3; do
if docker buildx imagetools inspect "${BASE_IMAGE_REF}" >/dev/null 2>"${probe_err}"; then
echo "🟢 Configured credentials can read ${BASE_IMAGE_REF}"
exit 0
fi
if grep -Eqi 'denied|unauthorized|forbidden|insufficient_scope|401|403' "${probe_err}"; then
creds_denied="yes"
break
fi
echo "🟠 Base image read attempt ${attempt}/3 failed for a non-authorization reason: $(tr '\n' ' ' < "${probe_err}")"
[ "${attempt}" -lt 3 ] && sleep $((attempt * 5))
done

if [ -z "${creds_denied}" ]; then
echo "::warning::${BASE_IMAGE_REF} could not be read and the failure does not look like an authorization refusal; leaving the docker config unchanged"
exit 0
fi

anon_config_dir="$(mktemp -d)"
trap 'rm -f "${probe_err}"; rm -rf "${anon_config_dir}"' EXIT
echo '{"credsStore":"","auths":{}}' > "${anon_config_dir}/config.json"
# Retried like the credentialed probe above: a transient failure here
# would otherwise keep a credential already proven to be denied, and the
# build would then fail on the very pull this fallback exists to rescue.
anon_readable=""
for attempt in 1 2 3; do
if DOCKER_CONFIG="${anon_config_dir}" \
docker buildx imagetools inspect "${BASE_IMAGE_REF}" >/dev/null 2>"${probe_err}"; then
anon_readable="yes"
break
fi
echo "🟠 Anonymous read attempt ${attempt}/3 failed: $(tr '\n' ' ' < "${probe_err}")"
if [ "${attempt}" -lt 3 ]; then
sleep $((attempt * 5))
fi
done

if [ -z "${anon_readable}" ]; then
echo "::warning::${BASE_IMAGE_REF} is unreadable with the configured credentials and anonymously; leaving the docker config unchanged"
exit 0
fi

echo "🟠 Configured credentials cannot read ${BASE_IMAGE_REF}; using anonymous access for nvcr.io"
if ! python3 "${STRIP_HELPER}" "${DOCKER_CONFIG_DIR}/config.json" "${BASE_IMAGE_REF}"; then
echo "::warning::Could not drop the stored nvcr.io credential; the build may still fail to read ${BASE_IMAGE_REF}"
fi
95 changes: 95 additions & 0 deletions .github/actions/_lib/setup-docker-config/strip_registry_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""Drop the stored credential for one image's registry from a docker config.

A registry can refuse a credential scoped to another organization a pull token
for a public repository instead of downgrading the request to anonymous, which
makes a public base image unreadable. Removing just that registry's entry lets
the pull proceed anonymously while every other registry in the same config
keeps working.

Usage: strip_registry_auth.py <config.json> <image-ref>
Exits 0 when a credential was dropped, 3 when nothing matched.
"""

from __future__ import annotations

import json
import sys

# Docker Hub is stored under several spellings; treat them as one registry.
_HUB_ALIASES = frozenset(
{
"docker.io",
"index.docker.io",
"registry-1.docker.io",
}
)


def registry_host(image_ref):
"""Return the canonical registry host for a docker image reference."""
first = image_ref.split("/")[0]
if "/" in image_ref and ("." in first or ":" in first or first == "localhost"):
# A registry may carry a port, and hosts are case-insensitive.
host = first.lower().rsplit(":", 1)[0] if ":" in first else first.lower()
else:
# No registry component means Docker Hub.
host = "index.docker.io"
if host in _HUB_ALIASES:
return "index.docker.io"
return host


def _normalize(auth_key):
"""Reduce a config.json auths key to a comparable registry host."""
trimmed = auth_key.split("://", 1)[-1]
host = trimmed.split("/", 1)[0].lower()
if ":" in host:
host = host.rsplit(":", 1)[0]
if host in _HUB_ALIASES:
return "index.docker.io"
return host


def main(argv):
config_path, image_ref = argv[1], argv[2]
target = registry_host(image_ref)

with open(config_path) as handle:
config = json.load(handle)

auths = config.get("auths") or {}
removed = sorted(key for key in auths if _normalize(key) == target)

# A credHelpers entry would re-supply the credential from an external helper
# even after its `auths` entry is gone, so the mapping for this registry is
# removed too. A credential held only by a global credsStore is outside this
# file, which is why the caller treats exit 3 as "nothing was dropped".
helpers = config.get("credHelpers") or {}
helper_keys = sorted(key for key in helpers if _normalize(key) == target)
for key in helper_keys:
del helpers[key]
if helper_keys:
config["credHelpers"] = helpers

if not removed and not helper_keys:
print(f"No stored credential for {target} was found in the docker config")
return 3

for key in removed:
del auths[key]
config["auths"] = auths
with open(config_path, "w") as handle:
json.dump(config, handle)

dropped = ", ".join(removed + helper_keys)
print(f"Dropped stored credential(s) for {target}: {dropped}")
return 0


if __name__ == "__main__":
sys.exit(main(sys.argv))
Loading
Loading