fix(scripts): preserve uv extras across make dev restarts (#2754)#2767
fix(scripts): preserve uv extras across make dev restarts (#2754)#2767fancyboi999 wants to merge 1 commit intobytedance:mainfrom
make dev restarts (#2754)#2767Conversation
There was a problem hiding this comment.
Pull request overview
Fixes recurring loss of optional uv extras (notably postgres) across make dev / dev-container restarts by ensuring uv sync is invoked with the correct extras inferred from environment/config.
Changes:
- Update local dev launcher to detect and pass
uvextras intouv syncduring startup. - Add a stdlib-only extras detector script plus unit tests to validate env/config precedence and YAML edge cases.
- Propagate
UV_EXTRASinto Docker dev startupuv sync, and update Postgres-related guidance/error messaging.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/serve.sh | Runs extras detection before uv sync and splats resolved flags into the backend dependency sync. |
| scripts/detect_uv_extras.py | New stdlib-only resolver for UV_EXTRAS / config.yaml → --extra ... flags. |
| docker/docker-compose-dev.yaml | Ensures container startup uv sync honors UV_EXTRAS as well. |
| config.example.yaml | Updates Postgres install guidance to reflect the new extras-preservation behavior across paths. |
| backend/tests/test_detect_uv_extras.py | Adds unit tests for the detector’s env parsing and minimal YAML parsing behavior. |
| backend/packages/harness/deerflow/persistence/engine.py | Improves missing-asyncpg error message with the persistent install flow. |
3035c30 to
1c8cf8c
Compare
|
@fancyboi999 thanks for the contribution. Here are some additional comment
docker/docker-compose-dev.yaml:132 is now a ~350-character one-liner. This is hard to read, review, and maintain. Consider extracting to a shell script (e.g., scripts/docker-entrypoint-dev.sh) that can be properly formatted and linted, or at minimum use YAML multiline string syntax:
The local path (detect_uv_extras.py) supports comma/whitespace-separated extras (UV_EXTRAS=postgres,ollama), but the Docker dev path only supports a single extra — the grep validation '^[A-Za-z][A-Za-z0-9_-]*$' rejects commas. If someone sets UV_EXTRAS=postgres,ollama in their .env, local make dev works but make docker-start aborts with an error. This should either be documented as intentional or harmonized.
scripts/serve.sh:177:
Or at least don't redirect stderr during development. |
…e#2754) `make dev` ran `uv sync` unconditionally on every restart, wiping any optional extras the user had installed manually with `uv sync --all-packages --extra postgres`. The Docker image-build path already solved this via the `UV_EXTRAS` build-arg in backend/Dockerfile; the local serve.sh path and the docker-compose-dev startup command were the remaining outliers. `scripts/serve.sh` now resolves extras before `uv sync`: 1. honors `UV_EXTRAS` (parity with backend/Dockerfile and docker/docker-compose.yaml — no new convention introduced); 2. falls back to parsing config.yaml — `database.backend: postgres` or legacy `checkpointer.type: postgres` auto-pins `--extra postgres`, so the common case needs zero extra config. 3. detector stderr is no longer suppressed, so whitelist warnings or crashes surface to the dev terminal (review feedback). Detection lives in `scripts/detect_uv_extras.py` (stdlib-only — has to run before the venv exists). Extra names are validated against `^[A-Za-z][A-Za-z0-9_-]*$` so a stray shell metacharacter in `.env` cannot reach `uv sync` downstream (defense in depth). `docker/docker-compose-dev.yaml`'s startup command is now extracted to `docker/dev-entrypoint.sh` (review feedback — the inline command had grown to a ~350-char one-liner). The script: - parses comma/whitespace-separated UV_EXTRAS, applying the same `^[A-Za-z][A-Za-z0-9_-]*$` whitelist as the local detector; - emits one `--extra X` flag per token, so `UV_EXTRAS=postgres,ollama` works in Docker dev too (harmonized with local — review feedback); - calls `uv sync --all-packages` (PR bytedance#2584) so workspace member extras (deerflow-harness's postgres extra) are installed; - keeps the existing self-heal `(uv sync || (recreate venv && retry))` branch; - exposes `--print-extras` for dry-run testing. The compose file mounts the script read-only at runtime, so script edits take effect on `make docker-restart` without an image rebuild. The `--no-sync` alternative (a separate suggestion in the issue thread) was considered but rejected for dev paths because it would drop the self-heal branch and the auto-pickup of new pyproject deps. `--no-sync` is already in use for the production CMD (`backend/Dockerfile:101`) where it's appropriate. Updates the asyncpg-missing error message to include the `--all-packages` flag (matching bytedance#2584) plus the persistent install flow, and expands `config.example.yaml` so all three install paths (local / docker dev / docker image build) are documented with their multi-extra capabilities. Tests: - `tests/test_detect_uv_extras.py` (21 tests) — local-path env parsing, YAML edge cases, env-vs-config precedence, whitelist rejection of shell metacharacters. - `tests/test_dev_entrypoint.py` (15 tests) — docker-path validation via `--print-extras`, multi-extra parsing, metacharacter abort. - `tests/test_persistence_scaffold.py` (22 tests, unchanged) — passes with the merged `--all-packages --extra postgres` error message.
1c8cf8c to
d09d885
Compare
|
@WillemJiang thanks for the detailed feedback. All three addressed in 1. Long one-liner extracted to The compose command: ["sh", "/usr/local/bin/dev-entrypoint.sh"]
volumes:
- ./dev-entrypoint.sh:/usr/local/bin/dev-entrypoint.sh:ro
...Mounted read-only so script edits take effect on 2. Multi-extra harmonized between local and Docker dev
The script gained a 3. Detector stderr no longer suppressed in UV_EXTRAS_FLAGS=$("$DETECT_PYTHON" "$REPO_ROOT/scripts/detect_uv_extras.py" \
|| { echo "[serve.sh] detect_uv_extras.py failed (exit $?) — proceeding without extras" >&2; echo ""; })
Total verification (all green on the rebased branch):
Docker dev path itself was not started end-to-end (no docker daemon in my test env) — Reviewer please confirm if a real run uncovers anything. |
Summary
Fixes #2754 —
make devranuv syncunconditionally on every restart, wiping any optional extras (asyncpg / psycopg / langgraph-checkpoint-postgres / psycopg-pool) the user had installed manually withuv sync --extra postgres.The Docker image-build path already solved this via the
UV_EXTRASbuild-arg inbackend/Dockerfile; the localserve.shpath anddocker-compose-dev.yaml's startup command were the remaining outliers.What changed
scripts/serve.shnow resolves extras beforeuv sync:UV_EXTRASenv var (parity withbackend/Dockerfileanddocker/docker-compose.yaml— no new convention introduced).config.yaml—database.backend: postgresor legacycheckpointer.type: postgresauto-pins--extra postgres, so the common case needs zero extra config.scripts/detect_uv_extras.py(new) — stdlib-only resolver. Has to run before the venv exists, so it cannot depend on PyYAML.docker/docker-compose-dev.yaml— startupuv sync(and its self-heal fallback) now honorsUV_EXTRAStoo via$${UV_EXTRAS:+--extra $$UV_EXTRAS}(matchesbackend/Dockerfile:51). Users setUV_EXTRAS=postgresin project-root.env(already loaded viaenv_file: ../.env).backend/.../persistence/engine.py— asyncpg-missing error message now points at the persistent flow.config.example.yaml— install instructions cover all three paths (local / docker dev / docker image build).Why not
--no-sync?A separate suggestion in the issue thread was to switch to
uv run --no-syncfor the dev path. That would solve the symptom but in dev paths it would also drop:pyproject.tomldeps aftergit pull/ branch switch,(uv sync || (recreate venv && retry))branch indocker-compose-dev.yaml:128.--no-syncis already in use for the production CMD (backend/Dockerfile:101) where it's appropriate. For dev, keepinguv syncand letting it know which extras the user actually wants is the more robust direction.Test plan
tests/test_detect_uv_extras.py— 19 tests covering env parsing, YAML parsing edge cases (comments, quotes, nested layers), env-vs-config precedence, explicitDEER_FLOW_CONFIG_PATH, root-vs-backend lookup precedence, missing files.tests/test_persistence_scaffold.py— 22/22 still passing (the postgres error-message assertion still matches; the new wording keeps theuv sync --extra postgressubstring).tests/test_harness_boundary.py— still passing.ruff check+ruff formatclean.bash -n scripts/serve.shclean.yaml.safe_loadparsesdocker-compose-dev.yamlcleanly.uv sync --quiet(no flags) wipedasyncpgfrom venv ✅ (matches issue report)UV_EXTRAS=postgres):detect_uv_extras.py→--extra postgres→bashword-splits →uv sync --quiet --extra postgres→asyncpg,psycopg,psycopg-binary,psycopg-poolall preserved ✅DEER_FLOW_CONFIG_PATHto a temporarydatabase.backend: postgresconfig): same result, all preserved ✅docker-compose-dev.yamlmirrorsbackend/Dockerfile:51exactly (same${UV_EXTRAS:+--extra $UV_EXTRAS}pattern, escaped as$$per docker-compose convention) and yaml parses cleanly, but I could not actually start the container and inspect the venv. Reviewer please verify, or I can iterate if a reviewer reports issues.