fix(novita): resolve whole ARG names and stop leaking the sandbox - #1236
cursor[bot] wants to merge 1 commit into
Conversation
Substituting ARG defaults name by name let a shorter name consume the prefix of a longer one, so `ARG BASE` declared before `ARG BASE_IMAGE` rewrote an unbraced `FROM $BASE_IMAGE` to `<base>_IMAGE`. Expand every reference in one regex pass instead, which is order-independent and keeps undeclared names verbatim. The tbench2 example also awaited readiness above its try/finally, so a readiness timeout returned without stopping the paid sandbox. The other two Novita examples already do this correctly; a parse-level regression now covers all three. Co-authored-by: benjamin.burtenshaw <benjamin.burtenshaw@huggingface.co>
There was a problem hiding this comment.
Status at 7a865a56: 9 checks green, test (3.11) red — the red one is an infrastructure flake, not this change.
The 3.11 job never reached pytest. It died in Install dependencies:
error: Failed to fetch: `https://pypi.org/simple/modal/`
Caused by: Request failed after 3 retries
Caused by: operation timed out
test (3.12) ran the same suite on the same commit and passed, as did lint, Package CI, env locks, docs and the Docker runtime validation. Locally the full CI-equivalent suite is 2978 passed / 103 skipped on this branch.
I cannot re-run the job (gh run rerun → Resource not accessible by integration), and I do not want to push a no-op commit just to retrigger. This PR is deliberately a draft and must not merge before the v0.6.0 tag anyway, so CI will run again in full when it is refreshed onto post-release main. If someone with Actions write access wants a clean board sooner, re-running the failed job is enough.
Sent by Cursor Automation: Release
There was a problem hiding this comment.
Superseded by #1235 — do not merge this one. Three parallel runs opened the same follow-up (#1234, #1235, this); #1235 was designated the keeper and I agree with that choice on the merits, not just to break the tie: its longest-name-first substitution also expands a nested ARG default (ARG IMAGE_NAME=$REG/app → example.com/app), which matches Docker's behaviour, while the single regex pass here leaves $REG/app literal.
I moved the one piece worth keeping — tests/scripts/test_novita_examples_release_sandbox.py, which asserts every examples/novita_*.py waits for readiness inside the try that stops the sandbox — onto #1235 as a suggestion, verified passing against its head.
I cannot close this PR myself (gh pr close → Resource not accessible by integration), and a sibling run has already asked Ben to close #1234 and this one, so I am not repeating the ask. It stays a draft in the meantime; the red test (3.11) on it is the dependency-fetch flake described above, not a code failure.
Sent by Cursor Automation: Release


Summary
Fixes the two latent Novita defects tracked as follow-ups when #1191 landed:
ARGsubstitution could corrupt aFROMline, and the tbench2 example could leave a paid sandbox running.Type of Change
What was wrong
1. Shorter ARG names ate longer ones.
_resolve_from_referencessubstituted defaults name by name, in declaration order:So a Dockerfile declaring
ARG BASE=python:3.12beforeARG BASE_IMAGE=…resolved an unbracedFROM $BASE_IMAGEtoFROM python:3.12_IMAGE. Replaced with a single regex pass that matches a whole name ($NAMEor${NAME}) and looks it up, which is order-independent. Undeclared names are still left verbatim, and a substituted value is no longer re-expanded.2.
examples/novita_tbench2_simple.pyleaked a sandbox.provider.wait_for_ready(...)sat above thetry, so a readiness timeout returned without reachingfinally: provider.stop_container()— the sandbox keeps billing. Moved inside thetry. The other two Novita examples already did this correctly.RFC Status
Test Plan
All three new tests fail on
mainand pass here (verified by stashing the two source fixes: 3 failed, 107 passed → 110 passed).tests/test_core/test_novita_provider.py: prefix-colliding ARGs resolve correctly in both$BASE_IMAGEand${BASE_IMAGE}form; the result is identical whichever order the two ARGs are declared in; an undeclared$UNSET_IMAGEstays verbatim; a value that itself contains$REGISTRYis not expanded twice.tests/scripts/test_novita_examples_release_sandbox.py: parses everyexamples/novita_*.pyand asserts anywait_for_readycall sits inside atrywhosefinallycallsstop_container. This covers all three examples, so the next one cannot reintroduce the leak.Full CI-equivalent suite locally: 2978 passed, 103 skipped.
usort,ruff formatandruff checkclean.Claude Code Review
N/A