Skip to content

fix: only skip venv setup when the venv was setup - #2388

Open
tdene wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
tdene:tde/venv_completion_marker
Open

fix: only skip venv setup when the venv was setup#2388
tdene wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
tdene:tde/venv_completion_marker

Conversation

@tdene

@tdene tdene commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

skip_venv_if_present trusts based on the existence of bin/python and bin/activate.

Both these files appear long before the venv is actually built. This allows for zombie venvs to be left behind which pass the skip check but are unusable.

This PR adds a completion marker.

@tdene

tdene commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test e04eacf

@ananthsub

Copy link
Copy Markdown
Contributor

/claude review

@claude

claude Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

SHIP — no reliability concerns.

Correctly closes the mid-install-death gap in setup_env_command. uv venv creates bin/python and bin/activate in seconds, so the old skip check (skip_venv_if_present + those two files) would reuse a venv whose uv pip install had crashed halfway — a silently-incomplete environment that later fails obscurely at import/runtime.

The fix is sound:

  • .nemo_gym_venv_complete is touched only as the final &&-gated step, so it appears only on a fully-installed venv.
  • rm -f clears it first in the rebuild chain, so a death anywhere before touch leaves no marker and the next run rebuilds instead of skipping.
  • Skip now additionally requires the marker.

Backward compat is fine: pre-existing markerless venvs rebuild once (uv venv --allow-existing absorbs the re-create), a one-time cost rather than any correctness risk. Tests updated across all command-shape assertions plus a dedicated regression test for the partial-build case. No verifier/scorer, async, config, or dependency surface touched.

@tdene

tdene commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 45b436a

tdene added 2 commits August 25, 2026 03:39
Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com>
Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com>
@tdene
tdene force-pushed the tde/venv_completion_marker branch from c7e09fd to 43ee085 Compare August 25, 2026 08:56
and venv_python_fpath.exists()
and venv_activate_fpath.exists()
and venv_marker_fpath.exists()
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this new marker cannot distinguish an interrupted installation from an environment created by an older Gym release. Every existing, pre-baked, or custom venv is missing this path.

With skip_venv_if_present=true, this rejects those usable environments, and the setup branch then recursively deletes them before reinstalling.

This can fail for offline, read-only, and non-root containers despite already having a working venv.

Could we preserve markerless legacy environments, or require other evidence of an interrupted Gym-owned build for detection instead?

prefix_cmd = f" > >(sed 's/^/({prefix}) /') 2> >(sed 's/^/({prefix}) /' >&2)"
env_setup_cmd = f"{uv_venv_cmd}{prefix_cmd} && source {venv_activate_fpath} && {install_cmd}{prefix_cmd}"
# A venv that lacks the marker was not fully built; remove it.
env_setup_cmd = (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uv_venv_dir is configurable, but the new marker checks and recursive cleanup interpolate its path without shell quoting. For example, a root such as /tmp/gym cache makes rm -rf receive /tmp/gym as a separate operand, which can delete an unrelated tree.

Please quote every generated path (for example with shlex.quote) and validate ownership/containment before recursive deletion, or perform cleanup through validated Path operations instead of generated shell.


prefix_cmd = f" > >(sed 's/^/({prefix}) /') 2> >(sed 's/^/({prefix}) /' >&2)"
env_setup_cmd = f"{uv_venv_cmd}{prefix_cmd} && source {venv_activate_fpath} && {install_cmd}{prefix_cmd}"
# A venv that lacks the marker was not fully built; remove it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normal startup launches configured instances concurrently. Copied instances can resolve to the same component venv.

If the marker is absent, both setup processes can enter this block and one can delete the directory while the other is installing into it; a marker may also become visible while another writer is still active.

setup should deduplicate by resolved venv or hold a per-venv lock. alternatively this coul dbe built in a temporary staging directory and atomically replace the target directory.


assert "uv pip install" in actual_command
# A venv that lacks the marker was not fully built; remove it.
assert actual_command.split(" && ")[1] == (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This regression test only inspects the generated command string, so it cannot catch deletion of a valid legacy venv, permission failures on read-only paths, unsafe paths containing spaces, concurrent setup races, or missing log attribution.

could we execute the setup command with a fake uv and cover successful completion plus failed rm/install/touch, a markerless legacy environment, a path containing spaces, and two setups sharing one venv? The cleanup and marker actions should also be logged with the server prefix so failures are attributable during concurrent startup.

env_setup_cmd = f"{uv_venv_cmd}{prefix_cmd} && source {venv_activate_fpath} && {install_cmd}{prefix_cmd}"
# A venv that lacks the marker was not fully built; remove it.
env_setup_cmd = (
f"{{ [ -e {venv_marker_fpath} ] || rm -rf {venv_path}; }} && "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When nemo_gym_log_dir is configured, the outer 2>&1 | tee captures failures from this entire chain and pipefail preserves the nonzero status. However, only the uv commands receive the per-server prefix: rm, touch, and source errors are unlabelled in concurrent terminal output, successful deletion is silent, and the decision to classify and remove a markerless venv is never recorded. Could we log the detected venv state and selected action before deletion, and apply the server prefix to every setup step?

@github-actions github-actions Bot removed the sla:triage-overdue Review assignment is over the one-business-day SLA label Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants