Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support uv workspaces #33874

Open
rarkins opened this issue Jan 27, 2025 · 1 comment
Open

Support uv workspaces #33874

rarkins opened this issue Jan 27, 2025 · 1 comment
Labels
manager:pep621 Python pyproject.toml files priority-3-medium Default priority, "should be done" but isn't prioritised ahead of others type:feature Feature (new functionality)

Comments

@rarkins
Copy link
Collaborator

rarkins commented Jan 27, 2025

Discussed in #33275

Originally posted by kkom December 25, 2024

How are you running Renovate?

A Mend.io-hosted app

If you're self-hosting Renovate, tell us which platform (GitHub, GitLab, etc) and which version of Renovate.

No response

Please tell us more about your question or problem

When using the uv workspaces feature, Renovate-generated PRs do not update the workspace's uv.lock file.

Here's the layout of a typical uv project using workspaces:

albatross
├── packages
│   ├── bird-feeder
│   │   ├── pyproject.toml
│   │   └── src
│   │       └── bird_feeder
│   │           ├── __init__.py
│   │           └── foo.py
│   └── seeds
│       ├── pyproject.toml
│       └── src
│           └── seeds
│               ├── __init__.py
│               └── bar.py
├── pyproject.toml
├── README.md
├── uv.lock
└── src
    └── albatross
        └── main.py

Note how there is a single uv.lock file, but multiple pyproject.toml. Dependencies of member packages in a workspace are locked in the top-level uv.lock file, but Renovate fails to update it when bumping versions.

Perhaps Renovate isn't workspace-aware and assumes that each pyproject.toml should have a corresponding uv.lock file in the same directory?

AFAIK, the right solution is to run uv lock in the workspace root.

Logs (if relevant)

Logs

Replace this text with your logs, between the starting and ending triple backticks

Reproduction forked to https://github.com/renovate-reproductions/33275

@rarkins rarkins added manager:pep621 Python pyproject.toml files priority-3-medium Default priority, "should be done" but isn't prioritised ahead of others type:feature Feature (new functionality) labels Jan 27, 2025
@latk
Copy link

latk commented Feb 10, 2025

I'm struggling with a similar scenario using the Poetry manager, where I have path-dependencies from the top-level pyproject.toml file to each workspace member package, and only a top-level lockfile exists. Poetry and uv are very similar here as far as Renovate is concerned, so I'm referring to both as {manager} in the following.

As I'm working with a self-hosted system, I unsuccessfully tried a couple of workarounds:

I can create symlinks from package/*/{manager}.lock to the top-level {manager}.lock file, in which case Renovate is able to at least resolve the currently locked versions correctly, but these symlinked lockfiles cannot be updated as they contain now-invalid relative paths.

I tried adding postUpgradeTasks to invoke {manager} lock on the top-level pyproject.toml. However, I cannot reliably ensure that the manager is installed into the environment used by the post-upgrade-tasks, unless I manually add the necessary install-tool invocations (which aren't documented, and which require manually selecting a version of each tool). I also think that these post-upgrade tasks might not be given all relevant authentication environment variables (hostRules are converted to env variables for the pep621 and poetry manager types).

Here would be my ideal Renovate algorithm for both Poetry and uv:

  • Renovates searches the pyproject.toml workspace graph, following all dependency relationships of type workspace (uv) or path (Poetry, uv). For each discovered **/pyproject.toml file:
    • If it matches an enabled: false package rule, ignore.
    • If it has its own sibling lockfile, update it independently. Treat this directory as its own root for the workspace search.
    • If there already is an inherited lockfile for this **/pyproject.toml, this conflict cannot be resolved. Raise an error. That is, enforce a tree-shaped workspace layout, reject circular dependencies or packages that are part of multiple workspaces. (The latter restriction could be lifted, but would make the updater code much more complex.)
    • Else, inherit the lockfile from the parent pyproject.toml in the dependency graph.
  • When looking up the current locked version of a dependency from a **/pyproject.toml file, use the corresponding lockfile per the previous algorithm, which may be a sibling file or an inherited lockfile.
  • When updating a dependency to a new version, adjust the version constraint in the pyproject.toml file, and then re-lock the dependencies via the corresponding lockfile.

For example, in the updater code for uv, we currently have:

async extractLockedVersions(
project: PyProject,
deps: PackageDependency[],
packageFile: string,
): Promise<PackageDependency[]> {
const lockFileName = getSiblingFileName(packageFile, 'uv.lock');
const lockFileContent = await readLocalFile(lockFileName, 'utf8');

Instead of looking up the sibling file name, this should be a sibling lockfile (if it exists) or an inherited lockfile per the workspace/path relationships.

Similarly in the code that attempts an update:

// abort if no lockfile is defined
const lockFileName = getSiblingFileName(packageFileName, 'uv.lock');
try {
const existingLockFileContent = await readLocalFile(lockFileName, 'utf8');
if (is.nullOrUndefined(existingLockFileContent)) {
logger.debug('No uv.lock found');
return null;
}

The re-locking may have to be done relative to the lockFileName, not relative to the packageFileName:

const execOptions: ExecOptions = {
cwdFile: packageFileName,
extraEnv,
docker: {},
userConfiguredEnv: config.env,
toolConstraints: [pythonConstraint, uvConstraint],
};

Equivalent changes would apply to the Poetry manager.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
manager:pep621 Python pyproject.toml files priority-3-medium Default priority, "should be done" but isn't prioritised ahead of others type:feature Feature (new functionality)
Projects
None yet
Development

No branches or pull requests

2 participants