Skip to content

feat(workspace): support PDM workspaces - #31

Merged
noirbizarre merged 6 commits into
mainfrom
feat/workspaces
Aug 1, 2026
Merged

noirbizarre merged 6 commits into
mainfrom
feat/workspaces

Conversation

@noirbizarre

Copy link
Copy Markdown
Owner

Adds support for PDM workspaces (requires pdm>=2.28).

Running pdm dockerize from a workspace root now installs every workspace member along with their transitive dependencies, and exposes their console scripts. Members are installed as regular non-editable packages, so the resulting image stays self-contained and never references build-time host paths.

Because members share a single lock file and a single environment, dockerize is restricted to the workspace root, consistently with install, lock and sync. Configuration is therefore read from the workspace root only.

Two pre-existing bugs surfaced along the way and are fixed here as well:

  • editable and local path requirements were passed verbatim to uv pip install, which could not consume them. This affected any local path dependency under uv, not just workspaces.
  • a non-distribution project having a src directory got it added to PYTHONPATH, pointing at a directory absent from the image.

Workspace support relies on the `Project` workspace API introduced in
PDM 2.28.0 (`workspace_project`, `is_workspace_root`, `iter_members`,
`with_workspace_dependencies`).
`get_backend_by_spec()` falls back to `pdm.backend` when the project has
no `[build-system]` table, so a non-distribution project having a `src`
directory got it added to `PYTHONPATH`, pointing at a directory which is
never copied into the image.

Skip the package dir entirely for non-distribution projects: they have no
sources of their own.
Requirement lines were passed verbatim to `uv pip install`, so an editable
requirement produced a single `-e <url>#egg=<name>` argv token, and PDM's
`file:///${PROJECT_ROOT}/...` placeholder was never expanded.

`uv` has no equivalent of PDM's `no_editable`, so rewrite editable
requirements as non-editable PEP 508 direct references, keeping the image
self-contained, and expand the requirement line through the project build
backend.
Workspace members are implicit `default` dependencies of the workspace
root, but PDM only injects them when locking, so they ended up in the lock
file without ever being installed.

Members are now installed, as regular non-editable packages, along with
their console scripts.

As members share a single lock file and environment, `dockerize` is
restricted to the workspace root, like `install`, `lock` and `sync`.
@codecov

codecov Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.82%. Comparing base (6d3e2da) to head (1964f98).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #31      +/-   ##
==========================================
+ Coverage   94.06%   94.82%   +0.76%     
==========================================
  Files           6        6              
  Lines         438      464      +26     
  Branches       61       69       +8     
==========================================
+ Hits          412      440      +28     
+ Misses         22       21       -1     
+ Partials        4        3       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

`uv` generated lock files store workspace members without their location,
only a name, a version and the editable flag, so they are read back as
named requirements which can never be matched against the local path
requirements the workspace root implicitly depends on, failing with:

    Missing package <member> from the lockfile, the lockfile may be broken.

Members are now given their path requirement back when reading the lock
file, and re-keyed accordingly, as the candidate identity depends on it.
@noirbizarre
noirbizarre merged commit 9e3872d into main Aug 1, 2026
8 checks passed
@noirbizarre
noirbizarre deleted the feat/workspaces branch August 1, 2026 10:40
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.

1 participant