Skip to content

npm run compile-sass runs uv run, which silently reverts mounted editable packages #1487

Description

@jesperhodge

Note on Bug discovery

Claude AI found this bug when I was trying to update openedx-platform to its latest requirements. I'd recommend to reproduce the bug first to verify that the bug report is accurate.

The rest of the bug ticket has been written with the help of Claude.

Bug description

If you mount a Python package into the devstack in order to work on it, the container stops
running your local copy and runs the released version from PyPI instead. Your edits then
have no effect on anything, and nothing anywhere reports an error.

The cause is that compiling Sass rewrites the virtualenv. edx-platform defines compile-sass
as uv run --active python scripts/compile_sass.py
(package.json#L11-L12),
and uv run does not simply run a command. It first syncs the virtualenv so that installed
packages match uv.lock, and only then runs what you asked for. A mounted package is an
editable install, which by definition does not match the lock, so uv replaces it.

Tutor calls that npm script during the image build and keeps a container running it on a
loop, so the sync happens repeatedly rather than once.

On whether this belongs here or in Open edX: the trigger lives in edx-platform, but the fix
belongs in Tutor. edx-platform chose to run its own scripts through uv run, which is
reasonable inside a repo whose environment uv manages. Tutor's image is a different
situation: the virtualenv at /openedx/venv is built and owned by Tutor, and Tutor is also
the thing that installs mounted packages as editable. Tutor is therefore the layer that has
to opt out of uv's environment management.

How to reproduce

Takes about 20 seconds and needs no mounts. This uses openedx-filters only because it is a
small package that edx-platform pins. Any package in edx-platform's uv.lock behaves the
same way.

tutor dev exec lms bash

cd /openedx/edx-platform
uv pip show openedx-filters | grep Version   # note the locked version
uv pip install "openedx-filters==3.8.0"
uv pip show openedx-filters | grep Version   # 3.8.0

uv run --active python -c "pass"             # what compile-sass runs
uv pip show openedx-filters | grep Version   # reverted to the locked version

Expected: running a Python script leaves installed packages alone.

Actual: the package is silently reverted.

Confirm the proposed fix neutralizes it:

uv pip install "openedx-filters==3.8.0"
UV_NO_SYNC=1 uv run --active python -c "pass"
uv pip show openedx-filters | grep Version   # 3.8.0, preserved

No manual changes were made to the generated environment or config.yml.

Environment

  • OS: macOS.
  • Tutor: reproduced on 21.0.3-main. Code inspection confirms the same code path is still
    present on main at febb38c3, but I have not built against main itself.
  • edx-platform: master at d1cdd628, after the uv migration.
  • uv: 0.9.18, as pinned by Tutor in the Dockerfile.

Additional context

Suggested fix. Add this to the production stage of
tutor/templates/build/openedx/Dockerfile,
which development and final both inherit, so one line covers the image build and every
running container:

ENV UV_NO_SYNC=1

UV_NO_SYNC is the environment-variable form of --no-sync. It disables only uv run's
project sync. It does not affect uv pip install, and it does not affect the deliberate
uv sync --frozen in the python-requirements stage, because that is a separate build
stage and does not inherit an environment variable set in production.

Side benefit: it also removes a redundant dependency re-resolve on every build. On my setup
npm run build-dev went from 660s to 22.3s.

As a workaround today, the same line can be injected through the existing
openedx-dockerfile-pre-assets
patch hook, which sits immediately before the asset steps.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions