uv plugin documentation is misleading
Metadata
Current evaluation
No evaluation has been recorded for this issue yet.
Issue body
### What needs to get done
The uv plugin documentation should be updated to explain the consequences of using certain environment variables, and potentially their workarounds (or an example snap be made to explain how to appropriately handle uv and python).
I've tested this for both core22 and core24, and both seem impacted.
I think core26+ and bare are fine, as the plugin(s) handle the interpreter differently in those cases.
### Why it needs to get done
As written, the uv plugin documentation does not adequately explain the impacts of using certain environment variables. This results in broken snaps due to unexpected behavior (at least, it's unexpected for me).
Consider the (common) case where the snap must package its own python interpreter (due to, say, a python version requirement). In this case, one must add:
```yaml
build-environment:
- UV_PYTHON: "python3.xy"
- PARTS_PYTHON_INTERPRETER: "python3.xy"
- UV_PYTHON_PREFERENCE: "only-managed"
- UV_PYTHON_DOWNLOADS: "auto"
```
The first two variables must be used to tell uv which python version to use, and the (undocumented, at least here) `PARTS_PYTHON_INTERPRETER` must be specified in order for the `uv venv` command to use the correct python version and avoid errors relating to the build being unable to find the correct python. The last two variables must be used to force uv to fetch its own python, mostly *because of this issue I'm experiencing itself*, cleaning the contents of `$CRAFT_PART_INSTALL`:
```bash
:: + uv venv --relocatable --allow-existing --python /usr/bin/python3.10 /root/parts/my-part/install
:: Using CPython 3.10.12 interpreter at: /usr/bin/python3.10
:: warning: The requested interpreter resolved to Python 3.10.12, which is incompatible with the project's Python requirement: `>=3.11, <4.0` (from `project.requires-python`)
:: Creating virtual environment at: /root/parts/my-part/install
:: Activate with: source /root/parts/my-part/install/bin/activate
:: + PARTS_PYTHON_VENV_INTERP_PATH=/root/parts/my-part/install/bin/python3.11
:: + uv sync --no-dev --no-editable --reinstall --extra cpu
:: Using CPython 3.11.0rc1 interpreter at: /root/parts/my-part/install/usr/bin/python3.11
:: Removed virtual environment at: /root/parts/my-part/install
:: Creating virtual environment at: /root/parts/my-part/install
:: Building immich-ml @ file:///root/parts/my-part/build/machine-learning
:: × Failed to build `immich-ml @ file:///root/parts/my-part/build/machine-learning`
:: ├─▶ Failed to identify base Python interpreter
:: ╰─▶ failed to canonicalize path `/root/parts/my-part/install/usr/bin/python3.11`: No such file or directory (os error 2)
```
https://github.com/canonical/craft-parts/blob/0910f51749eda1416b08e8389527f81e2ef8ce9f/craft_parts/plugins/uv_plugin.py#L108
However, when this is done, the venv target directory (`$CRAFT_PART_INSTALL`) is fully cleaned. This means that any `stage-packages` for this part will be unpacked, and then removed. You can see this with the minimal part:
```yaml
parts:
my-part:
plugin: uv
source: somesource
stage-packages: [hello]
build-environment:
- UV_PYTHON: "python3.xy"
- PARTS_PYTHON_INTERPRETER: "python3.xy"
- UV_PYTHON_PREFERENCE: "only-managed"
- UV_PYTHON_DOWNLOADS: "auto"
override-build: |
exit 1
```
And inspecting `$CRAFT_PART_INSTALL` (`snapcraft --debug`), then removing the `override-build` and checking again (you'll see that the previous contents of `$CRAFT_PART_INSTALL` no longer exist).
Below is a "minimal" reproducer:
```yaml
name: not-staging # you probably want to 'snapcraft register <name>'
base: core22 # the base snap is the execution environment for this snap
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: Single-line elevator pitch for your amazing snap # 79 char long summary
description: |
This is my-snap's description. You have a paragraph or two to tell the
most important story about your snap. Keep it under 100 words though,
we live in tweetspace and your description wants to look good in the snap
store.
grade: devel # must be 'stable' to release into candidate/stable channels
confinement: devmode # use 'strict' once you have the right plugs and slots
package-repositories:
- type: apt
ppa: deadsnakes/ppa
key-id: F23C5A6CF475977595C89F51BA6932366A755776
parts:
my-part:
plugin: uv
source: https://github.com/immich-app/immich
source-depth: 1
source-type: git
source-tag: v2.7.5
source-subdir: machine-learning
uv-extras: [cpu]
build-snaps: [astral-uv]
build-packages: [libpython3.11-dev, python3.11-dev]
stage-packages:
- gunicorn:all
- libgl1:${CRAFT_ARCH_BUILD_FOR}
- libmimalloc2.0:${CRAFT_ARCH_BUILD_FOR}
- libpython3.11:${CRAFT_ARCH_BUILD_FOR}
- python3.11:${CRAFT_ARCH_BUILD_FOR}
- python3-minimal:${CRAFT_ARCH_BUILD_FOR}
- python3.11-minimal:${CRAFT_ARCH_BUILD_FOR}
build-environment:
- UV_PYTHON: "python3.11"
- PARTS_PYTHON_INTERPRETER: "python3.11"
- UV_PYTHON_PREFERENCE: "only-managed"
- UV_PYTHON_DOWNLOADS: "auto"
```
Below is a corrected version which handles everything in the anticipated manner (following snapcraft's lead):
```yaml
name: not-staging # you probably want to 'snapcraft register <name>'
base: core22 # the base snap is the execution environment for this snap
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: Single-line elevator pitch for your amazing snap # 79 char long summary
description: |
This is my-snap's description. You have a paragraph or two to tell the
most important story about your snap. Keep it under 100 words though,
we live in tweetspace and your description wants to look good in the snap
store.
grade: devel # must be 'stable' to release into candidate/stable channels
confinement: devmode # use 'strict' once you have the right plugs and slots
package-repositories:
- type: apt
ppa: deadsnakes/ppa
key-id: F23C5A6CF475977595C89F51BA6932366A755776
parts:
my-part-deps:
plugin: nil
stage-packages:
- gunicorn:all
- libgl1:${CRAFT_ARCH_BUILD_FOR}
- libmimalloc2.0:${CRAFT_ARCH_BUILD_FOR}
python-libs:
plugin: nil
stage-packages:
- libpython3.11:${CRAFT_ARCH_BUILD_FOR}
- python3.11:${CRAFT_ARCH_BUILD_FOR}
- python3-minimal:${CRAFT_ARCH_BUILD_FOR}
- python3.11-minimal:${CRAFT_ARCH_BUILD_FOR}
my-part:
after: [python-libs]
plugin: uv
source: https://github.com/immich-app/immich
source-depth: 1
source-type: git
source-tag: v2.7.5
source-subdir: machine-learning
uv-extras: [cpu]
build-snaps: [astral-uv]
build-packages: [libpython3.11-dev, python3.11-dev]
build-environment:
- UV_PYTHON: "python3.11"
- PARTS_PYTHON_INTERPRETER: "python3.11"
override-build: |-
craftctl default
unlink "${CRAFT_PART_INSTALL}/bin/python"
ln -sf python3 "${CRAFT_PART_INSTALL}/bin/python"
ln -sf ../usr/bin/python3 "${CRAFT_PART_INSTALL}/bin/python3"
```
Evaluation history
No evaluation history available.