Migrate your 12-factor charm to use the uv plugin¶
In the upcoming V2 release of the paas-charm library and charms
using the 26.04 base, the uv plugin will be the default.
V1 charms use the charm plugin by default, and
converting your V1 charm is considered a breaking change.
This guide walks you
through manually converting a V1 12-factor charm that uses
the legacy charm plugin to the modern
uv plugin backed by uv.
Why migrate?
The uv plugin provides fast, reproducible Python dependency
resolution via a lock file (uv.lock), replaces the loose
requirements.txt approach with a proper pyproject.toml,
and removes the need for the charm-strict-dependencies workaround.
Replace requirements.txt with pyproject.toml¶
Delete requirements.txt from your charm directory and create a
pyproject.toml in its place, declaring your charm’s Python
dependencies with pinned versions.
For example, if your requirements.txt looks like:
cosl
dpcharmlibs-interfaces==1.0.2
jsonschema >=4.19,<4.20
ops >= 2.6
pydantic==2.13.3
paas-charm==1.11.3
Then replace the file with its equivalent pyproject.toml:
[project]
name = "my-charm-k8s" # use your charm's name
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"cosl==1.9.1",
"dpcharmlibs-interfaces==1.0.2",
"jinja2==3.1.6",
"jsonschema==4.26",
"ops==3.7.1",
"pydantic==2.13.3",
"paas-charm==1.11.3",
]
Tip
Pin every dependency to an exact version so that the generated
uv.lock is fully reproducible across machines and CI environments.
Generate uv.lock¶
With pyproject.toml in place, run uv sync inside the charm
directory to resolve dependencies and generate the lock file:
cd path/to/your/charm
uv sync
This creates (or refreshes) uv.lock.
The lock file must be present before you run charmcraft pack.
Update charmcraft.yaml¶
Change the plugin key¶
Replace the plugin: charm block with plugin: uv.
- The key differences are:
Add
source: .Change
plugin: charm→plugin: uvRemove
charm-strict-dependencies: falseAdd
astral-uvtobuild-snaps
For example, if your charmcraft.yaml contains the following snippet:
parts:
charm:
charm-strict-dependencies: false
plugin: charm
build-snaps:
- rustup
override-build: |-
rustup default stable
craftctl default
Then update your file to use:
parts:
charm:
source: .
plugin: uv
build-snaps:
- astral-uv
- rustup
override-build: |-
rustup default stable
craftctl default
(If applicable) Add a config part for paas-config.yaml¶
If your charm ships a paas-config.yaml file, you need a dedicated
dump part to stage it into the packed charm. Without this part,
the file will not be included in your charm because the uv plugin
only stages Python artifacts.
parts:
charm:
source: .
plugin: uv
build-snaps:
- astral-uv
- rustup
override-build: |-
rustup default stable
craftctl default
config:
plugin: dump
source: .
stage:
- paas-config.yaml
Pack the charm¶
Always run uv sync before packing to ensure the lock file is
up-to-date, then pack as usual:
cd path/to/your/charm
uv sync
charmcraft pack
Summary of file changes¶
File |
Action |
|---|---|
|
Delete |
|
Create – declare pinned dependencies |
|
Generate via |
|
Update |
Complete parts example¶
parts:
charm:
source: .
plugin: uv
build-snaps:
- astral-uv
- rustup
override-build: |-
rustup default stable
craftctl default
parts:
charm:
source: .
plugin: uv
build-snaps:
- astral-uv
- rustup
override-build: |-
rustup default stable
craftctl default
config:
plugin: dump
source: .
stage:
- paas-config.yaml